Examples
A walk through three small SCSS projects — design tokens, a responsive grid, and a themed component library — that together exercise variables, maps, functions, mixins, modules, breakpoints, and dark mode. Copy them as starting points for your own design system.
Tokens, grid, and a themed Button
EXAMPLE
// ============================================================
// 1) _tokens.scss — design tokens as data
// ============================================================
@use 'sass:map';
$tokens: (
'space': ('xs': .25rem, 'sm': .5rem, 'md': 1rem, 'lg': 2rem, 'xl': 4rem),
'radius': ('sm': 4px, 'md': 8px, 'lg': 16px, 'pill': 999px),
'shadow': (
'sm': 0 1px 2px rgba(0,0,0,.06),
'md': 0 4px 12px rgba(0,0,0,.10),
'lg': 0 12px 30px rgba(0,0,0,.18),
),
'color': (
'brand-500': #2563eb, 'brand-600': #1e40af,
'gray-50': #f8fafc, 'gray-900': #0f172a,
'success': #16a34a, 'danger': #dc2626,
),
);
@function token($path...) {
$value: $tokens;
@each $key in $path { $value: map.get($value, $key); }
@if $value == null { @error 'Unknown token: #{$path}'; }
@return $value;
}
// ============================================================
// 2) _grid.scss — a 12-column responsive grid mixin
// ============================================================
@use './tokens' as t;
@use 'sass:math';
$breakpoints: ('sm': 480px, 'md': 768px, 'lg': 1024px, 'xl': 1280px);
@mixin respond-to($name) {
@if not map-has-key($breakpoints, $name) { @error 'unknown bp #{$name}'; }
@media (min-width: map-get($breakpoints, $name)) { @content; }
}
.grid {
display: grid;
gap: t.token('space', 'md');
grid-template-columns: repeat(12, 1fr);
}
.col-12 { grid-column: span 12; }
@include respond-to('md') {
.col-md-6 { grid-column: span 6; }
.col-md-4 { grid-column: span 4; }
}
@include respond-to('lg') {
.col-lg-3 { grid-column: span 3; }
}
// ============================================================
// 3) _button.scss — themable component using CSS variables
// ============================================================
@use './tokens' as t;
:root {
--btn-bg: #{t.token('color','brand-500')};
--btn-bg-h: #{t.token('color','brand-600')};
--btn-fg: white;
--btn-radius: #{t.token('radius','md')};
--btn-px: #{t.token('space','md')};
--btn-py: #{t.token('space','sm')};
}
[data-theme='dark'] {
--btn-bg: #{t.token('color','gray-50')};
--btn-bg-h: #{t.token('color','brand-500')};
--btn-fg: #{t.token('color','gray-900')};
}
.btn {
display: inline-flex; align-items: center; gap: t.token('space','sm');
padding: var(--btn-py) var(--btn-px);
border-radius: var(--btn-radius);
background: var(--btn-bg); color: var(--btn-fg);
border: 0; cursor: pointer;
box-shadow: t.token('shadow','sm');
transition: background .15s ease, box-shadow .15s ease;
&:hover { background: var(--btn-bg-h); box-shadow: t.token('shadow','md'); }
&:focus-visible { outline: 2px solid t.token('color','brand-500'); outline-offset: 2px; }
&[disabled] { opacity: .5; cursor: not-allowed; }
}
.btn--danger { --btn-bg: #{t.token('color','danger')}; --btn-bg-h: #b91c1c; }
.btn--ghost { --btn-bg: transparent; --btn-fg: #{t.token('color','brand-500')};
box-shadow: inset 0 0 0 1px #{t.token('color','brand-500')}; }
Why it matters
Tokens + CSS variables is the combination that scales: SCSS gives you compile-time enforcement and naming, CSS variables give you runtime theming. Hand-edit fewer files for a brand refresh, and let pages flip themes without a bundle reload.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…