What is the best way to style elements in SCSS based on a class in the parent, such as a theme class

466 Views Asked by At

This is an old project. The theme adds a class to body or another root element. I need to style fonts, backgrounds, based on the theme, but this often leads to structures as:

.theme-light {
...
    .elem1 {
         ...
          .elem2 {
                 color: black;

and duplicate code for theme-dark. Or, the use of !important which we all know should be...prosecuted by law. The ideal selector would be something based on parent, as:

.elem2 {
    & <??? > .theme-light {
         color: black;  

} ....

.elem2 {
    & .theme-dark {
         color: white;
 }

but that is just not possible.

1

There are 1 best solutions below

0
Martin On

The best way is to use css vars (that will be resolved during runtime).

.theme-light {
  --copy-color: black;
  --copy-background-color: white;
  --primary-color: blue;
  --primary-contrast-color: white;
}

.theme-dark {
  --copy-color: #dddddd;
  --copy-background-color: black;
  --primary-color: red;
  --primary-contrast-color: black;
}
.some-article-element {
  color: var(--copy-color);
  background: var(--copy-background-color);
}

.some-button-element {
  color: var(--primary-color);
  background: var(--primary-contrast-color);
  border: 1px solid var(--primary-color);
}

If for some reason you can't use css vars or won't re-write large parts of the styles you could limit the changes to those component files you want to update via this method:

.some-article-component {
  $component: &;

  // normal component styles
  // unaffected by theme
  font-family: Arial;
  font-size: 16px;
  line-height: 24px;

  // theme specific styles
  @at-root .theme-light #{$component} {
    color: black;
    background: white;
  }
  @at-root .theme-dark #{$component} {
    color: white;
    background: black;
  }
}

EDIT: you probably don't need to capture the current selector in the last example and can just use the ampersand directly:

  @at-root .theme-dark & {
    color: white;
    background: black;
  }