CSS Variable Scoping

@sirajchokshi

Table of Contents

  1. Theming
  2. Responsive Design

CSS variables are often underutilized in favor of CSS-in-JS solutions. I work in a lot of styled-component and Emotion projects and I find myself reaching for CSS variables more and more.

CSS variables:

  1. Are scoped the an element and it’s children
  2. Work with the calc() function
  3. Can be changed at runtime

Theming

import styled from "@emotion/styled";
import { variant } from "styled-system";
import * as Menu from "@radix-ui/react-menu";
 
const MenuItem = styled(Menu.Item)`
  background: var(--background);
  color: var(--color);
 
  ${({ theme }) =>
    variant({
      prop: "intent",
      variants: {
        "default:hover": {
          "--color": theme.colors.grey900,
          "--background": `${theme.colors.grey400}`,
        },
        "danger:hover": {
          "--color": theme.colors.red800,
          "--background": theme.colors.red100,
        },
      },
    })}
`;
 
export function Menu() {
  return (
    <Menu.Root>
      <Menu.Content>
        <MenuItem>View Details</MenuItem>
        <MenuItem>Add Record</MenuItem>
        <Menu.Separator />
        <MenuItem intent="danger">Delete</MenuItem>
      </Menu.Content>
    </Menu.Root>
  );
}

In the above example, we are using the variant function from styled-system to create a button component that can be themed. Note how we map theme colors, named for their color, to CSS variables named for their user-facing intent.

Responsive Design

For scalable designs (e.g. graphics made with code) we can use calculations with CSS variables to properly scale children up and down without having to manually calculate the values.

This example uses BEM with SCSS, but the same concept applies to any CSS solution.

.card {
  &__wrapper {
    --card-padding: 1rem;
 
    padding: var(--card-padding);
 
    @media (min-width: 1200px) {
      --card-padding: 1.625rem;
    }
 
    @media (min-width: 720px) {
      --card-padding: 1.25rem;
    }
  }
 
  &__main {
    position: absolute;
    top: var(--card-padding);
    left: var(--card-padding);
    width: calc(100% - (var(--card-padding) * 2));
    height: calc(100% - (var(--card-padding) * 2));
  }
}