TaskBuild .button with custom-property props (--bg, --fg, --pad-x, --pad-y) and three variants. .button.danger overrides --bg. .button.success overrides --bg. .button.large overrides --pad-x and --pad-y.
Custom properties as a component API
100 XP8 min
Theory
Components that accept "props" via CSS vars
.button {
--bg: #3B82F6;
--fg: white;
--pad-x: 20px;
--pad-y: 10px;
background: var(--bg);
color: var(--fg);
padding: var(--pad-y) var(--pad-x);
border-radius: 8px;
border: none;
}
/* Variants override the vars */
.button.danger { --bg: #DC2626; }
.button.success { --bg: #22C55E; }
.button.small { --pad-x: 12px; --pad-y: 6px; }
.button.large { --pad-x: 28px; --pad-y: 14px; }The COMPONENT defines its API via internal custom properties. VARIANTS just override the props.
Why this beats classes-for-everything
Compare to the BEM way:
/* BEM β adds a new rule per variant */
.button { background: #3B82F6; padding: 10px 20px; }
.button--danger { background: #DC2626; }
.button--small { padding: 6px 12px; }With BEM, the variant needs to know which CSS properties to override. With custom-property props, the component declares its API ONCE β variants just set the props.
Use cases
- Spacing tokens (
--gap,--padding). - Sizing tokens (
--max-width,--min-height). - Visual tokens (
--bg,--fg,--accent). - Animation timing (
--duration).
The contract
- Define vars at the component root (
.button { --bg: ... }). - Use them in the component's CSS (
background: var(--bg)). - Allow variants to OVERRIDE them.
- Optionally use the fallback syntax
var(--bg, fallback-default)for resilience.
This is THE modern way to build a CSS design system without preprocessors or CSS-in-JS.
π
Sign up to start coding
Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β card required, cancel anytime.
Sign up β free trial βFirst 10 lessons in each track are free. No card needed for those.