Part 5: Custom Properties, Responsive Design, and Modern CSS

Introduction

This part covers the tooling layer that ties the rest of CSS together in a maintainable way: custom properties for design tokens, media queries for responsive layouts, and a set of modern CSS features that remove the need for JavaScript or pre-processors for things that used to require them. I use all of these in the stylesheets I write for documentation sites, tool dashboards, and web UIs.


CSS Custom Properties (Variables)

Custom properties let you define a value once and reuse it throughout the stylesheet. Changes in one place propagate everywhere β€” this is how I manage design tokens (colours, spacing, typography) in CSS without a build step.

Defining and Using

/* Define at :root β€” available everywhere */
:root {
  --color-primary: #3b82f6;
  --color-primary-dark: #2563eb;
  --color-text: #1a202c;
  --color-muted: #718096;
  --color-bg: #ffffff;
  --color-bg-subtle: #f7fafc;
  --color-border: #e2e8f0;
  --color-danger: #e53e3e;
  --color-success: #38a169;

  --font-body: system-ui, -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;

  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  --font-size-2xl: 1.5rem;

  --spacing-1: 0.25rem;
  --spacing-2: 0.5rem;
  --spacing-3: 0.75rem;
  --spacing-4: 1rem;
  --spacing-6: 1.5rem;
  --spacing-8: 2rem;
  --spacing-12: 3rem;

  --radius-sm: 0.25rem;
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
  --radius-full: 9999px;

  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}

Fallback Values

Scoped Custom Properties

Custom properties are scoped to the element they are defined on and all its descendants. This enables component-level theming:

Dark Mode with Custom Properties

All elements using var(--color-text) automatically switch without writing any additional selectors.


Responsive Design

Mobile-First Approach

I write styles for mobile first, then progressively enhance for wider screens. This means the default styles apply to narrow viewports and media queries add complexity for larger ones.

My Breakpoint Tokens

Note: CSS custom properties cannot be used inside media query conditions (a current limitation), so I reference the raw values in @media rules and keep this as documentation.

Media Query Syntax

prefers-reduced-motion

Always respect this preference for transitions and animations:

Or more broadly:

Container Queries

Container queries respond to the size of a parent element rather than the viewport. This is a significant addition that makes truly reusable components possible.

The .card switches layout based on how wide its container is β€” regardless of the viewport. The same component renders differently when placed in a wide sidebar versus a narrow panel, without separate media queries.


Modern CSS Features

CSS Nesting

Native CSS nesting β€” no pre-processor needed:

Nesting is supported in all modern browsers as of 2024.

:is() and :where()

:is() matches any element in the list. Its specificity equals the most specific argument.

:where() works the same way but has zero specificity β€” useful for resets:

:has() β€” The Parent Selector

Selects an element based on its descendants:

:has() is one of the most powerful additions to recent CSS β€” things previously requiring JavaScript can now be done in CSS.

clamp() for Fluid Typography

clamp(min, value, max): uses the middle value but never goes below min or above max.

min() and max()

CSS Transitions

transition syntax: property duration easing-function delay

Common easing functions:

  • ease β€” slow β†’ fast β†’ slow (default)

  • ease-in β€” slow start

  • ease-out β€” slow end (best for exits)

  • ease-in-out β€” slow start and end (best for enters)

  • linear β€” constant speed

  • cubic-bezier(x1, y1, x2, y2) β€” custom curve

CSS Animations

scroll-behavior

Anchor links (#section-id) will scroll smoothly. Respect prefers-reduced-motion:

scroll-margin-top

Prevents anchor-linked sections from scrolling under a sticky header:

accent-color

Styles native form controls (checkboxes, radio buttons, range inputs) with a brand colour:

color-scheme

Tells the browser the page supports light/dark modes β€” affects browser-native elements like scrollbars and form controls:


Typography

A consistent typographic scale makes pages readable without much CSS:

system-ui uses the OS's default UI font (San Francisco on macOS/iOS, Segoe UI on Windows, Roboto on Android) β€” good performance, no font loading, familiar to users.


Utility Classes I Keep in Every Project


Summary

Feature
Key Point

CSS custom properties

Define design tokens at :root, re-use with var()

Scoped variables

Override variables at component level for theming

Dark mode

Override custom properties inside prefers-color-scheme: dark

Mobile-first

Default styles for small screens, enhance with min-width queries

Container queries

Respond to parent size β€” makes truly reusable components

CSS nesting

Group related rules β€” no pre-processor needed

:is() / :where()

Match lists of selectors; :where() has zero specificity

:has()

Select based on descendants β€” replaces many JS patterns

clamp(min, val, max)

Fluid sizing between fixed bounds

transition

Smooth state changes

@keyframes + animation

Repeating or triggered motion

scroll-margin-top

Fix anchor links under sticky headers

accent-color

Brand colour for native form controls

prefers-reduced-motion

Always provide motion-safe fallback


Series Complete

This series covered CSS from first principles through to modern features:

Part
Topic

Selectors, Specificity, and the Cascade

Box Model, Display, and Positioning

Flexbox

CSS Grid

Custom Properties, Responsive Design, and Modern CSS

With HTML and CSS covered, the next step is JavaScript β€” handling behaviour, interactivity, and DOM manipulation.

Last updated