Why do we prefer CSS (- variable) to SASS ($ variable)?

Original author: Sebastiano Guerriero
  • Transfer
Since the release of the CodyHouse Framework, a couple of months ago, many users have asked us why we chose CSS variables instead of SASS variables, although we use SASS in this environment. In this article, I’ll talk about the benefits of using custom properties and why they became crucial in our workflow.



Definition of Variables


In this article, I assume that you are familiar with the basics of custom CSS and SASS properties (or any other CSS preprocessor). If not, let's start with a basic example:

In SCSS:

$color-primary: hsl(220, 90%, 56%);
.link {
  color: $color-primary;
}

In CSS:

:root {
  --color-primary: hsl(220, 90%, 56%);
}
.link {
  color: var(--color-primary);
}

Custom properties allow you to define variables without the need for CSS extensions (i.e. no need to use SASS).

They are the same? Not really! Unlike SASS variables, user properties 1) are limited to the element on which they are declared, 2) a cascade, 3) they can be controlled in JavaScript. These three features open up a whole world of new possibilities. Let me show you some practical examples!

Create and apply color themes


Here is an example of how you could create two (simplified) color themes using SASS variables:

$color-primary: blue;
$color-text: black;
$color-bg: white;
/* Инверсия */
$color-primary-invert: red;
$color-text-invert: white;
$color-bg-invert: black;
.component {
  color: $color-text;
  background-color: $color-bg;
  a {
    color: $color-primary;
  }
}
.component--dark {
  color: $color-text-invert;
  background-color: $color-bg-invert;
  a {
    color: $color-primary-invert;
  }
}

In the above example, we have a “default” theme and a “dark” theme, where we invert the colors for the background and text. Note that in a dark topic, we need to go through each property that used color variables and update them with a new variable.

As long as we stick to simplified (unrealistic) examples, no problems arise. But what if we have a component with a lot of elements? We would have to rewrite all the properties where color variables are used, and replace the variables with new ones. And if you change the main component, you should double-check all modifiers. Yes ... not so convenient!

When creating our framework, we came up with a different approach based on CSS variables. First of all, let's define variables for color:

:root, [data-theme="default"] {
  --color-primary: blue;
  /* Цветовой контраст */
  --color-bg: white;
  --color-contrast-lower: hsl(0, 0%, 95%);
  --color-contrast-low: hsl(240, 1%, 83%);
  --color-contrast-medium: hsl(240, 1%, 48%);
  --color-contrast-high: hsl(240, 4%, 20%);
  --color-contrast-higher: black;
}
[data-theme] {
  background-color: var(--color-bg);
  color: var(--color-contrast-high);
}
[data-theme="dark"] {
  --color-primary: red;
  /* Цветовой контраст */
  --color-bg: black;
  --color-contrast-lower: hsl(240, 6%, 15%);
  --color-contrast-low: hsl(252, 4%, 25%);
  --color-contrast-medium: hsl(240, 1%, 57%);
  --color-contrast-high: hsl(0, 0%, 89%);
  --color-contrast-higher: white;
}

Note: in the above example, we use the data- * attributes to apply the color theme, but this has nothing to do with CSS variables and SASS variables. In addition, we determined the scale of neutral values ​​using the nomenclature based on the “level of contrast”. The

important point is that we do not need to create new color variables for our second, third, fourth ... (dark) themes. Unlike SASS, we can override the value of existing custom properties.

Here's how to apply color variables to a component:

.component {
  color: var(--color-contrast-higher);
  background-color: var(--color-bg);
  border-bottom: 1px solid var(--color-contrast-low);
  a {
    color: var(--color-primary);
  }
}

What about the dark version of the component? We do not need additional CSS. Since we redefine and do not replace variables, we need to apply the correct variables for the color only when the component is first created. It doesn't matter how complicated the component becomes after you set the color themes in the _colors.scss file and apply the color variables to the elements of your components.

You can apply color themes in a very simple way:


In the above example, we applied a dark theme to the section and a default theme to the .child element. That's right, you can invest in color themes!

This is made possible through the use of CSS properties. Which made it possible to do such interesting things as this in the shortest possible time.



Here are some links if you want to know more about managing colors with the CodyHouse framework:


Font Scale Management


A modular scale is a set of harmonious (dimensional) values ​​that apply to typographic elements. Here's how you can set the font size in SCSS using SASS variables:

$text-xs: 0.694em;
$text-sm: 0.833em;
$text-base-size: 1em;
$text-md: 1.2em;
$text-lg: 1.44em;
$text-xl: 1.728em;

A standard approach would be to create a font scale using a third-party tool (or perform mathematical operations), and then import the values ​​into your style, as in the example above.

When creating the framework, we decided to include the entire scale formula in the _typography.scss file . Here's how we set the font size using CSS variables:

:root {
  // Базовый размер шрифта
  --text-base-size: 1em;
  // Шкала типов
  --text-scale-ratio: 1.2;
  --text-xs: calc((1em / var(--text-scale-ratio)) / var(--text-scale-ratio));
  --text-sm: calc(var(--text-xs) * var(--text-scale-ratio));
  --text-md: calc(var(--text-sm) * var(--text-scale-ratio) * var(--text-scale-ratio));
  --text-lg: calc(var(--text-md) * var(--text-scale-ratio));
  --text-xl: calc(var(--text-lg) * var(--text-scale-ratio));
  --text-xxl: calc(var(--text-xl) * var(--text-scale-ratio));
  --text-xxxl: calc(var(--text-xxl) * var(--text-scale-ratio));
}

What is the advantage of this approach? This makes it possible to control the entire printing system by editing only two variables: --text-base-size (size of the base font) and --text-scale-ratio (scale factor).

"Can not you do the same thing using variables sass» ? Not if you want to change typography at specific points in media queries:

:root {
  @include breakpoint(md) {
    --text-base-size: 1.25em;
    --text-scale-ratio: 1.25;
  }
}

This piece is the cornerstone of our flexible approach. Since we use relative Ems units, when changing --text-base-size (the size of the base font), this affects both typography and spacing. As a result, you get a system that resizes all your components with virtually no need to set media queries at the component level.



Here are a few useful related links:


Distance scale control


The interval scale is equivalent to the type scale, but applies to spatial values. Once again, the inclusion of the scale formula in the structure allowed us to control the interval system and make it responsive:

:root {
    --space-unit:  1em;
    --space-xxxxs: calc(0.125 * var(--space-unit)); 
    --space-xxxs:  calc(0.25 * var(--space-unit));
    --space-xxs:   calc(0.375 * var(--space-unit));
    --space-xs:    calc(0.5 * var(--space-unit));
    --space-sm:    calc(0.75 * var(--space-unit));
    --space-md:    calc(1.25 * var(--space-unit));
    --space-lg:    calc(2 * var(--space-unit));
    --space-xl:    calc(3.25 * var(--space-unit));
    --space-xxl:   calc(5.25 * var(--space-unit));
    --space-xxxl:  calc(8.5 * var(--space-unit));
    --space-xxxxl: calc(13.75 * var(--space-unit));
}
@supports(--css: variables) {
  :root {
    @include breakpoint(md) {
      --space-unit:  1.25em;
    }
  }
}

This approach becomes especially powerful in combination with the typography method discussed above. With just a few lines of CSS, you get responsive components:



Another thing that I like about using Ems modules with this spacing system is that if the spacing and typography sizes look right at a particular point in the media request, they are almost certainly look right at all points of media queries, regardless of whether you are updating the --space-unit value . Therefore, I can design without having to resize the browser windows (unless I want to change the behavior of the component).

More related links:


Editing “vertical rhythm” at the component level


Unlike the SASS variable, we can override the value of CSS variables. One way to use this property is to add custom properties to other custom properties, thereby creating “controls” that you can edit at the component level.

Here is an example, when you set the vertical spacing for a text component, you probably want to specify line-height and margin-bottom for your elements:

.article {
  h1, h2, h3, h4 {
    line-height: 1.2;
    margin-bottom: $space-xs;
  }
  ul, ol, p, blockquote {
    line-height: 1.5;
    margin-bottom: $space-md;
  }
}

However, this pace varies depending on where the text is used. For example, if you want your text to be more concise, you need to create a component variant (modifier) ​​in which you apply different values ​​of the intervals:

.article--sm {
  h1, h2, h3, h4 {
    line-height: 1.1;
    margin-bottom: $space-xxxs;
  }
  ul, ol, p, blockquote {
    line-height: 1.4;
    margin-bottom: $space-sm;
  }
}

... and so every time, if you want to update the "vertical rhythm."

Here is an alternative approach based on CSS variables:

.text-component {
  --component-body-line-height: calc(var(--body-line-height) * var(--line-height-multiplier, 1));
  --component-heading-line-height: calc(var(--heading-line-height) * var(--line-height-multiplier, 1));
  --line-height-multiplier: 1;
  --text-vspace-multiplier: 1;
  h1, h2, h3, h4 {
    line-height: var(--component-heading-line-height);
    margin-bottom: calc(var(--space-xxxs) * var(--text-vspace-multiplier));
  }
  h2, h3, h4 {
    margin-top: calc(var(--space-sm) * var(--text-vspace-multiplier));
  }
  p, blockquote, ul li, ol li {
    line-height: var(--component-body-line-height);
  }
  ul, ol, p, blockquote, .text-component__block, .text-component__img {
    margin-bottom: calc(var(--space-sm) * var(--text-vspace-multiplier));
  }
}

--line-height-multiplier and --text-vspace-multiplier are two text component control variables. When we create the modifier of the .text-component class , for editing the vertical interval we only need to redefine these two variables:

.article.text-component { //  Например сообщение в блоге
  --line-height-multiplier: 1.13; // Увеличить высоту строки статьи
  --text-vspace-multiplier: 1.2; // Увеличить вертикальный интервал
}

In case you want to try it yourself and take note:



Abstracting Behavioral Components


The ability to override the value of a component can be used in different ways. In general, at any time when you can abstract the behavior of a component in one or more variables, you make your life easier when this component requires editing (or you need to create a component variant).

An example is our Auto Sized Grid component , where we use a CSS grid to create a layout, where gallery elements automatically fill the available space based on the minimum width specified in CSS, then we abstract the min-width of the elements, storing it in a variable.


The minimum width value is the only thing you need to change when creating the Auto Size grid component option.

What's the catch?


In a nutshell: browser support ... You can use CSS variables in all the ways described in this article using the PostCss plugin . There are a number of limitations for older browsers (documentation for more information on the limitations of using CSS variables today.

Can I use CSS variables with a preprocessor?


Yes! While SASS (or any other preprocessor) allows you to do what you cannot do in CSS, and you need it, why not use it? SASS is not a library that users must download when accessing your site. This is a tool in your workflow. We use SASS, for example, to define color functions that work with CSS variables.

Conclusion


In this article, we looked at several examples that demonstrate the advantage of using CSS variables over SASS variables. We focused on how they allow you to create “controls” that speed up the process of changing components or set rules that affect typography and intervals. We have considered a lot, and I hope that you can learn something from this post and use it in your work.

Only registered users can participate in the survey. Please come in.

Do you use CSS variables?

  • 33.1% Yes 55
  • 50.6% No, I'm doing the old fashioned $ variable 84
  • 16.2% I do not use in principle 27

Also popular now: