[Feature Request] Page-level Custom CSS in Multipage Appsp

Hi all,

I am trying to set up custom CSS on a multipage app based on a page-level variable. Currently, this is not possible. This means that I need to adjust the page-level variable to be global.

Ideally, I would be able to set custom CSS on a global level as well as an app level similar to how code currently behaves.

Cheers, Nikita

I'm going to go ahead and assume the styling you're trying to apply is something you're unable to do using the Appearance properties in the right panel when you've selected a component or in the App theme settings?

If so, you're CSS will be dependant on a selector whos value is created by the retool backend and aren't guaranteed to be the same in the future

// Select parent <div> that all pages are rendered inside of.
// Attributes applied here will also be applied to every 'child'/page
//   -"_mainContent_" is generated by retool and may change in the future
div[class^="_mainContent_"]

// Same as above, but it depends on a different attribute. 
//   -this attribute might not exist in the future
//   -ViewerPageLayout and MainContent are names generated by retool and may change in future updates
div[data-testid="ViewerPageLayout::MainContent"]

// Selects all Page <div>s where 'data-context-menu-frame' attribute starts with '$main'
//   -"data-context-menu-frame" values are all generated by retool and may change, as of right now these are not affected by the component name or labels and seem to always start with "$main"
div[data-context-menu-frame^="$main"]

// Selects Page1 <div>
div[data-context-menu-frame="$main"]

// Selects Page2 <div>
div[data-context-menu-frame="$main2"]

hopefully this gets you started. If you're wondering how I found these, I made a new multipage app and added a few pages. Then i clicked on preview in the top right, then right-click the page and select 'inspect':


you can then click on the 'FOOBAR' page to see the html change:

If you want the direct selector you can right-click the div element on the right, then select 'copy' and you'll see different options for getting that specific element:

1 Like

Yes, exactly! Thanks for looking into it; unfortunately, I also already got this far – the issue I am running into is that I have a variable defined on a page level (let's say backgroundColor as an easy example).
Now, I want to create a custom CSS rule using this variable. Typically, the following would work:

._retool-button1 * {
    background: {{ backgroundColor.value }};
}

However, given that the custom CSS is on an app level and the variable defined on a page level, the CSS rule does not apply properly.
As described before, this is solvable by turning backgroundColor into an app-level variable, but then you end up having many app-level code snippets that are only relevant on a page level.

Ah I see, so this might be a bit too hacky but since page code/variables are evaluated on page load you could try creating a JS Query and enable 'Run this query on page load' and edit/update the styles in code [Stackoverflow link]. It's actually super hacky and I haven't gotten to work yet (from what I can tell) but this is what I have in a global JS query:

const sheet = new CSSStyleSheet();
//sheet.insertRule("strong { color: red; }");
sheet.replaceSync(" div { background-color: blue; }");
console.log("NEW SHEET:", sheet);
window.document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];

// print all adopted style sheets
for (let i = 0; i < document.adoptedStyleSheets.length; i++) {
    console.log(document.adoptedStyleSheets[i]);
}

you might have to use getElementByTagName('head') then insert a new link element with your css?

I'm not too sure how retool renders the react elements, but the usual way is with a 'root' element which you'd get using getElementById('root').... I'm WAYYY out of my depth here but you might have to use .attachShadow({mode:"open"}) on some whatever element react is rendering to:

const sheet = new CSSStyleSheet();
sheet.insert(" div { background-color: blue; }");

const elem = window.document.getElementById('root);
const shadow = elem.attachShadow({mode:"open"});
shadow.adoptedStyleSheets = [sheet]
1 Like

you could also try adding a function at the window scope in the 'preloaded JS' section to get/set styles:


window.getStyleSheets = function() {
  const ret = window.document.styleSheets;
  console.log("STYLE SHEETS:", ret);
  return ret;
}

window.setStyleSheets = function(styles){
  var style = document.createElement('style');
    style.type = 'text/css';

    if (style.styleSheet) {
        // IE
        style.styleSheet.cssText = styles;
    } else {
        // Other browsers
        style.innerHTML = styles;
    }

    document.getElementsByTagName("head")[0].appendChild( style );
}

this should be available to page and global queries(i think?), you'd use it like:

const ret = window.setStyleSheets('div {background-color:"red"}')
1 Like

Hi @nikita_from_bunch,

As @bobthebear so eloquently stated, Custom CSS bares a lot of risks as "CSS will be dependent on a selector who's value is created by the retool backend and aren't guaranteed to be the same in the future".

Which is why we recommend using component level styling from 'Appearance' in the inspector panel, as this would be specific to the page the component is on.

And then using 'App Theme' for styles that you want across the entire app.

From your example with the button background color, this can be done from the component's inspector under Appearance.

If you want page 1 to have a red button and page 2 to have a blue button, you can set those since the components on each page are unique.

For your use case, are you looking to, for example, have that button on one page change dynamically based on a variable changing?

Hi Jack,

I am aware of custom CSS baring risks in Retool, and am limiting my use to classnames such as "_retool-myComponent".
Unfortunately, my use cases for custom CSS are not covered by Retool native styling functionalities, which is why I am asking this question.

Cheers, Nikita

Thanks for the discovery work! Will give it a try and see if I can further limit the complexity of the necessary workaround.

1 Like

I found a ticket for adding in page level CSS, but the notes on it from the engineer team are that users should be using out Retool Theming to accomplish this styling.

I let them know about your use case and added your +1 so I will keep you updated on any further news that I hear. Is the issue with having all the variables be globally scopes is that there is float?