Global and Component Style Settings with CSS Variables


0

Ever since I learned about CSS Variables a few years back, my absolute favorite feature has been the ability to scope variables to components. But to be honest, I haven’t been putting this feature to much use over the years, until I created my own pattern library last year to speed up prototyping and client work. That’s where scoped CSS variables really shined for me. So I want to share my favorite two ways to use CSS Variables to organize and maintain styles in my projects today.

Over the last few months, I’ve started approaching the way I organize and manage my CSS differently…

Global Project Settings

Today, at the beginning of each project, I create a _settings.scss stylesheet. This stylesheet contains the global settings for the project. These settings are usually derived from the design style guide provided by the design team I’d be working with.

partial screenshot of a style guide showing values for color swatches and box shadow styles
An example of visual settings defined in a style guide for my current client project. The style guide contains color swatches, brand colors, interaction and UI colors, font styles, type scales, spacing scales, icons, etc. I used the style guide as a starting point in my CSS, as I created and defined the global project styles by deriving their values from their visual equivalents in the style guide.

Just like the style guide contains settings for visual styles like colors, box shadow styles, font styles, type scales, etc., the _settings stylesheet contains variables that serve as the code equivalent of those settings, and that are used across the CSS to maintain visual consistency across the project.


:root {
    /* UI Colors */
    --primary-hue: 12;
    --color--primary: hsl(var(--primary-hue), 100%, 44%);
    --color--primary--hover: hsl(var(--primary-hue), 100%, 39%);
    --color--primary--active: hsl(var(--primary-hue), 84%, 30%);
    /* ... */
    --border-color: #ebebeb;
    /* Box Shadows */
    --shadow-01: 0px 2px 4px rgba(37, 37, 37, 0.1);
    --shadow-02: 0px 4px 8px rgba(37, 37, 37, 0.1);
    --shadow-03: 0px 8px 16px rgba(37, 37, 37, 0.1);
    --shadow-04: 0px 16px 24px rgba(37, 37, 37, 0.1);
    --shadow-05: 0px 24px 32px rgba(37, 37, 37, 0.1);
    /* ... */
}
An example of global style settings defined in a _settings.scss style sheet in my current client project.

.card {
    /* ... */
    box-shadow: var(--shadow-01);
    border: 1px solid var(--border-color);
    transition: box-shadow .2s linear;
    &:hover,
    &:focus {
        box-shadow: var(--shadow-03);
    }
}

If at any point during the project any of those settings need to change, I know exactly where to go make that change, and I know it will propagate consistently throughout my entire system.


In addition to these settings, I’ve been finding the most value and convenience in using CSS variables to define local, component-scoped styles…

Quicker Prototyping with “Skeleton” Components

Over the years, and in the interest of saving myself time and speeding up prototyping ideas and client work, I’ve created a library of UI and design patterns that I find myself needing to recreate on most of my projects. The library now contains a growing collection of easily reusable UI patterns that I can reliably copy-paste into my projects when I need them. Each pattern is progressively enhanced using modern CSS and JavaScript, and is cross-browser and cross-platform accessible from the ground up.

Since I created the library as an ‘internal’ project, it currently lives in a private Github repository, and behind a password on the .dev domain of my site.

screenshot of the Skeleton component library

I named the library ‘Skeleton’, and I built it with Fractal. I’ve been using Fractal for a couple of years now. I chose it over other pattern library tools because it fit my needs perfectly — I wanted a tool that was unopinionated and flexible enough to allow me to set up and structure my project the way I wanted to. Fractal fit the description perfectly because it is agnostic as to the way I develop or the tools I use. And I was further sold on it after reading about how Rachel Andrew uses it to manage CSS using a pattern-first approach, which was exactly what I was going for. I particularly love that components can be nested into folders to make it easier to locate particular components, and how you structure the folders is completely up to you.

I organize my patterns so that each pattern lives in its own directory, containing the component’s HTML template, CSS, and vanilla JavaScript files, along with any additional Fractal-specific assets (such as config files).

partial screenshot of the folder structure in my component library

Using this structure, each of my patterns is self-contained. And I can include and concatenate the pattern’s styles and scripts in my projects as I need.

/* styles.scss */
@import "accordion";
@import "modal";

To quote Tyler Sticka in “Tips for Portable Patterns”: Patterns, like songs, are easier to remix when each master track is separated.


My goal from creating this library is to create a tool that allows me to prototype faster, and that’s flexible and efficient enough to use across different projects. And since patterns are usually styled differently across my projects, I wanted a way to simplify the process of customizing or “configuring” them for each project. Enter CSS Variables.

Scoped Component Settings

Because I don’t want to spend a lot of time overriding and undoing styles when I use a pattern in a new project, I created this library with the components having little to no styling by default — mostly white (no colors), minimal spacing, and only borders where visually appropriate. So the patterns literally look like skeletons of sort, hence the name. Now when I need to use one of these components, I have little CSS to override before they’re ready to be plugged into the new project.

For each pattern, I’ve found myself modifying the same properties whenever I needed to use it — like the font, colors (text, background, border), box shadow, spacing, etc. So I figured it would be useful and time-saving if I created variables for those properties, define those variables in the ‘root’ of the component, and ‘pass in’ the values for these variables when I use the pattern as I need. This way I can customize or theme the component by changing the property values in one rule set, instead of having to jump between multiple ones to do so.

I use variables to abstract all styles that I usually need to override. So every property that changes across projects is usually “promoted” into a variable. Then, if, at any point during the project, I need to tweak a pattern’s style(s), I know exactly where to do it. This makes the styles for each pattern more readable and more maintainable, which is even more important for when someone else needs to modify the CSS.

This approach works quite well with my approach to organizing my CSS files. I like to organize my CSS into separate style sheets per patterns, that includes all the pattern’s styles and responsive behavior. There will be exceptions to this rule… for example, styles for “atoms” (like buttons, input fields, etc.) that are re-used across patterns are defined in one style sheet. And even then, when an atom’s styling gets a little complex (e.g. styling a custom file upload input), I may create a separate style sheet for that. I’ll go into a little more detail about the way I organize my CSS in another article.

Example

Like most of you, I find myself creating a

Captcha!