Skip to content

Latest commit

 

History

History
538 lines (390 loc) · 14.9 KB

File metadata and controls

538 lines (390 loc) · 14.9 KB

INDEX


CSS Variables (custom properties)

They let us define variables in CSS that can be reused throughout the stylesheet.

  • they're also called custom properties

    • they're called like that as they act as css-properties where they're inherited and they cascade (override based on specificity)
  • It enables us to abstract values and work with them in a more dynamic way like props in React

    <div style="--primary: red"></div>
    div {
      color: var(--primary);
    }
    • It's different than writing inline style directly, as it make the code abstract and maintainable
  • Difference between CSS variables and Sass variables:

    variables


How to use it

  • Declaring them is usually in the :root selector (global scope) or in the element itself (local scope)

    :root {
      --primary: blue;
    }
    • because the :root pseudo-class represents the root <html> element and is identical to the selector html, except that its specificity is higher (because :root is a pseudo-class selector and html is a type selector)
      • We do this so that the variable is available everywhere in the stylesheet (Global scope)
      • We can use them because they're inherited from the root element

:root is an alias for html, so by hanging CSS variables on the top-level element, they become globally available (every element, after all, is below the root HTML tag). But you can attach CSS variables to any element, not just the root one!

  • Using css variables is done by using the var() function:

    .box {
      background-color: var(--primary);
    }

Scope

"Not Global": A common misconception is that CSS variables are "global". When we attach a CSS variable to an element, it's only available to that element and its children.

Rules of scope like in Javascript also applies in css variables, but:

  • Sass variables are scoped on {} blocks (block-scoped) (lexical-scoped)

  • CSS variables are scoped on elements (element(local)-scoped) (dynamic-scoped)

    • this is great if you want the variable-value to only be applied in this element and elements inside it (its descendant elements) only

    • also it's great if you want to override the variable-value in a specific element and its descendant elements only

      • we can do this by:

        • declaring the variable in the element itself

          div {
            --primary: red;
          }
        • or declaring it in the inlined-style of the element

          <div style="--primary: red"></div>
        • or declaring it using javascript

          document.documentElement.style.setProperty('--primary', 'red');
/* global scope */
:root {
  --main-bg-color: coral;
}

/* local scope */
#div1 {
  --main-bg-color: coral;
  background-color: var(--main-bg-color);
}

Fallbacks (Default values)

It's a way to provide a default value for the variable if it's not defined, but if it's invalid, the browser will set the value to unset which is equivalent to initial and use the fallback value

  • when using the variable, We can use a Fall-back variable/ value after a comma (,) and you can make many fallbacks

    div {
      color:var(--primary, orange)
    
      /* to request the fallback value manually -> use (initial) */
      color: initial
    }
  • Another way:

    @property --primary {
      syntax: '<color>'; /* Type of the value (optional) */
      inherits: true;
      initial-value: orange;
    }
  • DRY fallback strategies:

    • use the variable in the fallback value

      div {
        --color-initial: black;
        color: var(--color, var(--color-initial));
      }
    • Pseudo-private properties

      div {
        --__color: var(--color, black);
        color: var(--__color);
      }
    • Using @property

      @property --color {
        syntax: '<color>';
        inherits: true;
        initial-value: black;
      }
      
      div {
        color: var(--color);
      }

Variables inheritance

By default, all CSS variables are inheritable and cascade. This is why CSS variables hung on :root are available globally.

  • css variables are inherited from the parent element if it's declared in the parent element to the (classes / pseudo-elements / pseudo-classes / IDs) in the same element

    button {
      --color: red;
      color: var(--color);
    }
    
    button:hover {
      background: black;
      color: var(--color); /* red -> inherited */
    }
    
    button.pink {
      border-color: #f06;
      color: var(--color); /* red -> inherited */
    }
    
    button.pink:hover {
      background: #f06;
      color: var(--color); /* red -> inherited */
    }
  • you can disable inheritance by:

    • setting the property to initial on the element itself

      :root {
        --corner-size: 2em;
      }
      
      p {
        border-radius: initial; /* won't inherit and will be initial value */
      }
      
      p .text {
        --corner-size: inherit; /* Will be inherited */
      }
    • Or register property using: @property:

      • @property allows us to register our properties / variables and control how they behave (configure it), but watch out for browser support

        <style>
          @property --text-color {
            syntax: '<color>';
            inherits: false;
            initial-value: black;
          }
        
          main {
            --text-color: deeppink;
            color: var(--text-color);
          }
        
          section {
            color: var(--text-color);
          }
        </style>
        
        <main>
          This text will have a color of deeppink.
          <section>This text will have a color of black (initial value).</section>
        </main>

Invalid At Computed Values Time (IACVT)

IACVT: it's when calling the variable result in error (due to empty value or wrong value or unsupported value by browser), so the value will be set to unset which is equivalent to initial IACVT

Parse time: CSS is read and converted to CSSOM; computed value: relative units resolved to px; used value: final values for painting, % widths resolved to px.

Fallbacks IACVT

/* Ex: */
div {
  background: red;
  --accent-color: 42deg; /* not a valid color */
  background: var(--accent-color, orange);
  /* result will be (color-transparent) */

  /* or */
  background: red;
  background: var(
    --accent-color,
    42deg
  ); /* no value for --accent-color variable, and 42deg is not a valid color for the fallback */
}

IACVT

  • This is because the --accent-color variable is not a valid color, so the browser will set the value to unset which is equivalent to initial which is equivalent to transparent.
    • It didn't fallback to orange because the variable is not a valid color
    • It also didn't use the initial value of the property (red) because it was overridden by the variable to unset

Cycle IACVT

  • When we have a cycle in the variable-value, the browser will set the value to unset which is equivalent to initial

    div {
      --primary-color: var(--secondary-color);
      --secondary-color: var(--primary-color);
    }
    • Here, the browser will set the value of --primary-color to unset which is equivalent to initial because it's dependent on --secondary-color which is dependent on --primary-color, and the cycle will continue forever

      • Note: the browser will not throw an error, it will just set the value to unset which is equivalent to initial

Unsupported value IACVT

  • Example for IACVT with lch color function, which may not be supported by some browsers IACVT

Space Toggles Hack

  • Space Toggles Hack: it's a way to toggle between two values using a space character Space Toggles Hack

  • For no space in the variable-value:

    • Color will be orange (indicating no value for --color)
  • For space in the variable-value:

    • Color will be unset (transparent) (indicating error for --color) because the space character is not a valid color
    • The space character is used to toggle between the two values

variables token: Number, string, images

In css variables, you can make the variable contains a number, a string, or an image, but if you tried to mix them, it will not work (it works with SASS variables though!)

:root {
  /* works ✅ */
  --height: 100vh;
  height: var(--height);

  /* Doesn't work ❌ */
  --height: 100;
  height: var(--height) vh;

  /* Fix (works) ✅ */
  --height-scale: 100;
  --height: calc(var(--height-scale) * 1vh);
}
  • Tokens are the values that can be used in the variable, and they can't contain parts of tokens

  • You can make the variable contains the following: tokens

    • the opening part of a css function, ex: linear-gradient(

      html {
      --type: "linear-gradient(";
      background: var(--type) white, black );
      color: red;
      }
    • a number without a unit like --p: 65;

      calc(var(--p) * 100vw);
      • use variables for pure-data like 65, not css-values like 65%

      • this way you can use the number for any css-value with any unit unlike if you made the variable with css-value

        • but watch out for numbers as string or integers as here we don't have type-coercion
    • image string string-variable


Css variables with Javascript

  • One of the main difference between the old sass variables and the new css custom properties is that in custom-properties, you can use Javascript to change the variables values after the compilation and the loading in the browser which will enable more dynamic styling

  • You can get / set css-variable-value of an element in javascript by having the variable declared in the root before, then change it's value locally in the element styles:

    // Get variable from inline style
    element.style.getPropertyValue('--foo');
    
    // Get variable from wherever
    getComputedStyle(element).getPropertyValue('--foo');
    
    // Set variable on inline style
    element.style.setProperty('--foo', 38 + 4);
    
    // --------------------------------------------------- //
    
    // or change it in the root element
    let root = document.documentElement;
    
    document.addEventListener('pointermove', evt => {
      let x = evt.clientX / innerWidth;
      let y = evt.clientY / innerHeight;
      root.style.setProperty('--mouse-x', x);
      root.style.setProperty('--mouse-y', y);
    });
  • Reactivity

    • CSS variables are reactive—when their value changes, any properties that reference that value also change. In this case, clicking the button causes us to update the value for --inflated-size, which automatically updates the button's font-size property.

      <button id="button">Click me!</button>
      :root {
        --inflated-size: 1em;
      }
      
      button {
        font-size: var(--inflated-size);
      }
      const button = document.getElementById('button');
      button.addEventListener('click', () => {
        const inflatedSize = parseFloat(getComputedStyle(button).getPropertyValue('--inflated-size'));
        button.style.setProperty('--inflated-size', inflatedSize * 1.1 + 'em'); // now the button will be 10% bigger as the value of the variable is increased by 10%
      });
    • This is very useful when working with dynamic styles in JS frameworks like React or Vue

      const App = () => {
        const [color, setColor] = useState('red');
        return (
          <div style={{ '--color': color }}>
            <button onClick={() => setColor('blue')}>Change color</button>
          </div>
        );
      };

Common Use Cases

  • CSS Reset variables

    :root {
      /* 1. colors */
      --primary: #333;
      --secondary: #f06;
    
      /* 2. font */
      --font: 'Arial', sans-serif;
    
      /* 3. spacing */
      --padding: 1em;
      --margin: 1em;
      --max-width: 1200px;
    
      /* 4. border-radius */
      --radius: 5px;
    
      /* 5. box-shadow */
      --shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    
      /* 6. transition */
      --transition: 0.3s ease all;
    }
  • Responsive design

    body {
      font-size: calc(90% * var(--font-size-scale));
    }
    
    @media (min-width: 800px) {
      :root {
        --font-size-scale: 1.2;
      }
    }
  • Color Control

    • We can use css variables to control the color intensity to create dynamic palettes

      article {
        --hue: calc(50 - var(--intensity) * 10);
        --color: hsl(var(--hue, 200) 70% 40%);
        background: var(--color);
      }
      <article style="--intensity: 1">yellow</article>
      <article style="--intensity: 2">brown</article>
      <article>default blue</article>
      <article style="--intensity: 3">red</article>

      color-control


Working with Browsers that don't support CSS variables

  • We can't depend on fallbacks for browsers that don't support css variables (doesn't support var() function), so we can:

    • use postcss to convert the css variables to static values

    • Or, use @supports to check if the browser supports css variables or not

      .box {
        background: red;
        color: blue;
      }
      
      @supports (--css: variables) {
        .box {
          background: var(--primary);
          color: var(--secondary);
        }
      }