0

Is there a way to apply dynamic CSS with selectors via Vaadin Flow? Obviously, you can set CSS pretty easily in most circumstances. My specific problem is that I have dynamic background colors for elements that I want users to define themselves. And even then, it would be easy except for the colors differ between light and dark mode.

For example, I previously had this CSS in my styles.css:

html {
    --section-background-color-01: #fc7c7c;
    --section-background-color-02: #fcb67c;
    --section-background-color-03: #fced7c;
    --section-background-color-04: #dafc7c;
    --section-background-color-05: #8bfc7c;
    --section-background-color-06: #7cfce9;
    --section-background-color-07: #7cc9fc;
    --section-background-color-08: #7c7efc;
    --section-background-color-09: #b37cfc;
    --section-background-color-10: #fc7cfa;
    --section-background-color-11: #fc7cd2;
    --section-background-color-12: #fc7c9e;
}
html[theme~="dark"] {
    --section-background-color-01: #420000;
    --section-background-color-02: #422300;
    --section-background-color-03: #414200;
    --section-background-color-04: #174200;
    --section-background-color-05: #004203;
    --section-background-color-06: #004238;
    --section-background-color-07: #001f42;
    --section-background-color-08: #000042;
    --section-background-color-09: #200042;
    --section-background-color-10: #320042;
    --section-background-color-11: #42003d;
    --section-background-color-12: #42001e;
}

I was then applying it in my code as such:

VerticalLayout section1 = new VerticalLayout();
section1.getStyle().setBackgroundColor("var(--section-background-color-01)");

The problem is trying to address this dynamically whilst also accounting for the dark theme.

I can do the following:

String userSelectedColor = "#ffffff";
UI.getCurrent().getStyle().set("--section-background-color-01", userSelectedColor);
...

section1.getStyle().setBackgroundColor("var(--section-background-color-01)");

This does work, but the color is the same no matter what the theme is. This is not what I'm looking for. I need to have the color change between instances.

I should probably clarify, I don't have access to the light/dark mode server-side to be able to react dynamically. It's handled with a component from the directory, Theme Select - https://vaadin.com/directory/component/theme-select

This component is pure browser and I don't know if there even is a way to get the value from it. It's otherwise been completely ideal for our purposes.

I had a thought and tried the following CSS:


html {
    --section-background-light-color-01: #fc7c7c;
    --section-background-light-color-02: #fcb67c;
    --section-background-light-color-03: #fced7c;
    --section-background-light-color-04: #dafc7c;
    --section-background-light-color-05: #8bfc7c;
    --section-background-light-color-06: #7cfce9;
    --section-background-light-color-07: #7cc9fc;
    --section-background-light-color-08: #7c7efc;
    --section-background-light-color-09: #b37cfc;
    --section-background-light-color-10: #fc7cfa;
    --section-background-light-color-11: #fc7cd2;
    --section-background-light-color-12: #fc7c9e;
    --section-background-dark-color-01: #420000;
    --section-background-dark-color-02: #422300;
    --section-background-dark-color-03: #414200;
    --section-background-dark-color-04: #174200;
    --section-background-dark-color-05: #004203;
    --section-background-dark-color-06: #004238;
    --section-background-dark-color-07: #001f42;
    --section-background-dark-color-08: #000042;
    --section-background-dark-color-09: #200042;
    --section-background-dark-color-10: #320042;
    --section-background-dark-color-11: #42003d;
    --section-background-dark-color-12: #42001e;
}

html {
    --section-background-color-01: var(--section-background-light-color-01);
    --section-background-color-02: var(--section-background-light-color-02);
    --section-background-color-03: var(--section-background-light-color-03);
    --section-background-color-04: var(--section-background-light-color-04);
    --section-background-color-05: var(--section-background-light-color-05);
    --section-background-color-06: var(--section-background-light-color-06);
    --section-background-color-07: var(--section-background-light-color-07);
    --section-background-color-08: var(--section-background-light-color-08);
    --section-background-color-09: var(--section-background-light-color-09);
    --section-background-color-10: var(--section-background-light-color-10);
    --section-background-color-11: var(--section-background-light-color-11);
    --section-background-color-12: var(--section-background-light-color-12);
}
html[theme~="dark"] {
    --section-background-color-01: var(--section-background-dark-color-01);
    --section-background-color-02: var(--section-background-dark-color-02);
    --section-background-color-03: var(--section-background-dark-color-03);
    --section-background-color-04: var(--section-background-dark-color-04);
    --section-background-color-05: var(--section-background-dark-color-05);
    --section-background-color-06: var(--section-background-dark-color-06);
    --section-background-color-07: var(--section-background-dark-color-07);
    --section-background-color-08: var(--section-background-dark-color-08);
    --section-background-color-09: var(--section-background-dark-color-09);
    --section-background-color-10: var(--section-background-dark-color-10);
    --section-background-color-11: var(--section-background-dark-color-11);
    --section-background-color-12: var(--section-background-dark-color-12);
}

That works by itself. But my problem now is that my colors applied through Java are not working at all.

String userSelectedColorLight = "#ffffff";
String userSelectedColorDark = "#000000";
UI.getCurrent().getStyle().set("--section-background-light-color-01", userSelectedColorLight);
UI.getCurrent().getStyle().set("--section-background-dark-color-01", userSelectedColorDark);
...

section1.getStyle().setBackgroundColor("var(--section-background-color-01)");

The light/dark colors from above are not being applied. At best, only my default colors from the CSS are being applied. It would seem that I can't chain CSS variables through flow. At least, not as I'm currently doing it.

What is really frustrating me at this point is that I did find this bit of older Vaadin documentation - https://vaadin.com/docs/v8/framework/articles/DynamicallyInjectingCSS

It shows this example of doing exactly what I want:

  select.addValueChangeListener(new ValueChangeListener() {
    @Override
    public void valueChange( ValueChangeEvent event ) {
      // Get the new font family
      String fontFamily = select.getValue().toString();
      // Get the stylesheet of the page
      Styles styles = Page.getCurrent().getStyles();
      // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
      styles.add(".v-app .v-textarea.text-label { font-family:" + fontFamily + "; }");
    }
  });

A full CSS definition with the selector is able to be applied. That code, however, is for Vaadin 8, and does not seem to have an equivalent in Vaadin 24.

I need to have custom CSS that will dynamically react to the theme of the page (ie, dark/light mode). Is there a way that I can chain together CSS variable definitions globally for my current view (ala my '--section-background-light-color' and '--section-background-dark-color' attempt above), or is there a way that I can push a full CSS definition that includes the selectors?


UPDATE:

I got a workaround by simply injecting my desired result as raw HTML into my page:

{
    ColorSettings sectionColors = getCurrentUser().getSectionColorSettings();

    StringBuilder sb = new StringBuilder();

    sb.append("<style>");
    sb.append("html {");
    sb.append("--section-background-color-01: ").append(sectionColors.getLightModeBackgroundColor01()).append(";");
    sb.append("--section-background-color-02: ").append(sectionColors.getLightModeBackgroundColor02()).append(";");
    sb.append("--section-background-color-03: ").append(sectionColors.getLightModeBackgroundColor03()).append(";");
    sb.append("--section-background-color-04: ").append(sectionColors.getLightModeBackgroundColor04()).append(";");
    sb.append("--section-background-color-05: ").append(sectionColors.getLightModeBackgroundColor05()).append(";");
    sb.append("--section-background-color-06: ").append(sectionColors.getLightModeBackgroundColor06()).append(";");
    sb.append("--section-background-color-07: ").append(sectionColors.getLightModeBackgroundColor07()).append(";");
    sb.append("--section-background-color-08: ").append(sectionColors.getLightModeBackgroundColor08()).append(";");
    sb.append("--section-background-color-09: ").append(sectionColors.getLightModeBackgroundColor09()).append(";");
    sb.append("--section-background-color-10: ").append(sectionColors.getLightModeBackgroundColor10()).append(";");
    sb.append("--section-background-color-11: ").append(sectionColors.getLightModeBackgroundColor11()).append(";");
    sb.append("--section-background-color-12: ").append(sectionColors.getLightModeBackgroundColor12()).append(";");
    sb.append("}");

    sb.append("html[theme~=\"dark\"] {");
    sb.append("--section-background-color-01: ").append(sectionColors.getDarkModeBackgroundColor01()).append(";");
    sb.append("--section-background-color-02: ").append(sectionColors.getDarkModeBackgroundColor02()).append(";");
    sb.append("--section-background-color-03: ").append(sectionColors.getDarkModeBackgroundColor03()).append(";");
    sb.append("--section-background-color-04: ").append(sectionColors.getDarkModeBackgroundColor04()).append(";");
    sb.append("--section-background-color-05: ").append(sectionColors.getDarkModeBackgroundColor05()).append(";");
    sb.append("--section-background-color-06: ").append(sectionColors.getDarkModeBackgroundColor06()).append(";");
    sb.append("--section-background-color-07: ").append(sectionColors.getDarkModeBackgroundColor07()).append(";");
    sb.append("--section-background-color-08: ").append(sectionColors.getDarkModeBackgroundColor08()).append(";");
    sb.append("--section-background-color-09: ").append(sectionColors.getDarkModeBackgroundColor09()).append(";");
    sb.append("--section-background-color-10: ").append(sectionColors.getDarkModeBackgroundColor10()).append(";");
    sb.append("--section-background-color-11: ").append(sectionColors.getDarkModeBackgroundColor11()).append(";");
    sb.append("--section-background-color-12: ").append(sectionColors.getDarkModeBackgroundColor12()).append(";");
    sb.append("}");
    sb.append("</style>");

    Html style = new Html(sb.toString());
    UI.getCurrent().addComponentAsFirst(style);
}

This works, but there has to be a better way to do this, right? Something proper via Vaadin?

1
  • your solution is the best one i can guess. You should add it as an answer and accept it. There's no better way in my mind. Commented Apr 19 at 10:10

1 Answer 1

1

Your second solution – the one where you dynamically assign a value to a mode-specific custom property – is actually correct, but with one small mistake:

You're defining the non-mode-specific properties on the doc root level (html selector):

html {
    --section-background-color-01: var(--section-background-light-color-01);
    ...

But your Flow code assigns a new value to the mode-specific property on doc body level:

UI.getCurrent().getStyle().set("--section-background-light-color-01", something);

because UI.getCurrent() maps to the body, not the html root.

So the assignment to --section-background-light-color-01 happens on a lower level than the declaration of --section-background-color-01, and css custom properties don't reactively update if another custom property assigned as their value is updated.

So the solution is simply to replace the html selector with body.

Sign up to request clarification or add additional context in comments.

2 Comments

Unfortunately, that didn't work completely. I do think you're on to something as it did PARTIALLY work. Unfortunately, only the "--section-background-color-01: var(--section-background-light-color-01);" is being utilized. It doesn't actually switch to the "--section-background-color-01: var(--section-background-dark-color-01);" I would assume that the selector "body[theme~="dark"]" is not valid, as the theme attribute is not applied to the body tag.
yes, that's right, the dark theme attribute is applied to the html element, so the selector for the dark mode colors should be e.g. [theme~="dark"] > body {...}

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.