Beginning of 2020, I was stunned hearing from a front-end developer CSS variables are now fully supported by all browsers (even Edge)! This implies we can start using CSS(3) variables within our Lightning Components on the Salesforce Platform!

Showcase: Curious in an example on how CSS variables can be applied? In a later article the focus is put on the application of CSS variables, including a demo and public repository.

What are CSS3 variables?

Like in Apex and Javascript one can now also define variables in CSS, avoiding duplicate (‘hard-coded’) values and therewith improve maintainability and readability.

:root{
  --primary-colour: #f15d22;
}

h1, h2, h3{
  color: var( --primary-colour );
}

button{
  background: var( --primary-colour );
}
Variable definition
  • Variables can be defined within any CSS Selector and are identified with the ‘--‘-prefix;
  • Variables are case-sensitive (!), implying --myVar != --myvar;
  • Variables follow the cascade policy and thus, each variable is available for all child elements;
  • Variables can be overridden, but will only effect the children of the selector where it is overridden in. For adjacent and above positioned elements, the variable remains defined with the original value.
Variable retrieval
  • A variable can be referenced by calling var( [variableName], [fallbackValue] );, where fallbackValue is optional;
  • The fallback value is applied, when the variable cannot be found, e.g. when the variable was defined outside the scope of the current selector;
  • CSS Declarations are ignored when the variables returns empty, or makes no sense
    (e.g. div{ color: var( --default-margin ); } will be ignored).

Example: responsive design

Aside from being able to define colours in one central place, allowing to easily and consistently change the complete look and feel of your page (see first example above), there is one more use-case which gets me enthusiastic:

:root{
  --content-margin: 2rem;
}

p{ 
  margin: var( --content-margin );
}

@media only screen and ( max-width: 420px ){
  :root{
    --content-margin: 1rem;
  }
}

In the above code snippet you see another way to setup a websites’ responsive design. Instead of redefining all selectors within the @media-tag, the --content-margin variable is changed, directly impacting all child-elements of the :root-element which referencing it.

Update variables by Javascript

Comparable to all DOM element properties, CSS variables can also be retrieved, set and updated by Javascript. This allows one to conditionally update the look and feel of the website. E.g. on customer interaction (button click), or by Javascript validation, such as window size, browser, domain and such.

// get variable from inline style
let curMargin = element.style.getPropertyValue( "--content-margin" );
// get variable from wherever
getComputedStyle( element ).getPropertyValue( "--content-margin" );
// halve the current content-margin
element.style.setProperty( "--content-margin", curMargin / 2 );

How to benefit in Lightning Web Components?

Next to the benefits of higher maintainability and easier to read styling, there is one more significant impact I fancy of using CSS Variables in Lightning Web Components: reusability of child components!

Definition Intermezzo:
By definition of online Web Components, Lightning Web Component have a Scoped CSS. This implies, CSS defined within the shadow DOM will not ‘leak outside’, nor will page styles bleed into it. Example: when in component A you set the body-background to pink, this only applies to the body of component A, not to any page that includes A.

Salesforce forces this by adding the name of the component to each of it’s DOM elements and also append this to all CSS selectors. The CSS, therefore, only applies to elements which satisfy the CSS selector AND contain the components’ name attribute (see code example below).

// ButtonComponent.html - Template file
<div c-buttonComponent_buttonComponent class="custom-button" > [...] </div>

// ButtonComponent.css - Stylesheet file
.custom-button[c-buttonComponent_buttonComponent]{
  background:  blue;
}

Because of this scoped CSS, LWC components strictly follow their own styling and are a bit harder to reuse. Imagine the button component in the snippet above, which you’d like to show on different pages/communities where different colour schemes are applied. Due to the scoped CSS you cannot ‘overrule’ the colour codes from the parent, and the button will always have a blue background. But… that is where CSS Variables come in!

Fortunately, CSS variables do come through within an Isolated DOM and therefore you can have your child-component shown in any way you’d like! The calling/parent component will set the variable, which the child component will show.

:host{
  --background-colour: orange;
}
.custom-button{
  background:  var( --background-colour, 'blue' );
}

Update May 27 – Reusable child components with default CSS Variable values

To make your child components really reusable, you don’t want to have them depend on the calling component providing the CSS variable or not. Unfortunately, I found the fallback value is only applied when an invalid value was retrieved. Luckily, there is a trick to get around this by using Javascript, as is shown below. A more detailed explanation of why and how this is working, can be found here.

_hasRendered = false;
renderedCallback(){
  if( !this._hasRendered ){
    // Verify whether :host{} variables are inherited from the parent
    let computedStyles = window.getComputedStyle( this.template.host, null );
    let accentColor = computedStyles.getPropertyValue( '--accent-color' );
    
    // When accent color was not yet defined, define the variable
    if( !accentColor || accentColor.length == 0 ){
      this.template.host.style.setProperty( '--accent-color', 'purple' );
    }
    
    this._hasRendered = true;
  }
}

Conclusion

CSS Variables allow you to:

  • improve maintainability – colours are changed in only one spot;
  • increase readability – one can easily see which margins are ‘related’, or which elements share the same colours (no need to compare hex-values 🙂 ) ;
  • simplify LWC reusability – components can be setup flexibly to allow the parent to (optionally) define the styling. In case not defined, the fallback value is applied.

How useful was this post?

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Leave a comment

Your email address will not be published. Required fields are marked *