Creating Components
A guide to creating abstract component styles for your design system.
Overview
Traditionally, we define components like .btn
or .card
by writing CSS rules. In Master CSS, we use config.components
to abstract and make them more flexible.
export default { components: { btn: { '': 'inline-flex font:semibold …', 'sm': 'r:6 px:3x font:12 h:8x', 'md': 'r:6 px:4x font:14 h:10x', } }}
An abstract style consists of one or more syntaxes, you can provide a class name for this set of classes and reuse it anywhere.
Try making the viewport width smaller
You can even conditionally apply abstract styles via at-rules:
<button class="btn btn-md btn-sm@<sm …">Submit</button>
Generated CSS
@layer base, theme, preset, components, general;@layer components { .btn { display: inline-flex; font-weight: 600 } .btn-md { border-radius: 0.375rem; padding-left: 1rem; padding-right: 1rem; font-size: 0.875rem; height: 2.5rem } @media (width<52.125rem) { .btn-sm\@\<sm { border-radius: 0.375rem; padding-left: 0.75rem; padding-right: 0.75rem; font-size: 0.75rem; height: 2rem } }}
Basic usage
Add a style
Create an abstract style using Master CSS syntaxes.
export default { components: { btn: 'inline-flex h:10x …' }}
Apply the btn
style to the button element:
<button class="btn">Submit</button>
Generated CSS
@layer base, theme, preset, components, general;@layer components { .btn { display: inline-flex; height: 2.5rem }}
Add a style with states
Create an abstract style with state selectors and applying conditionally.
export default { components: { btn: 'outline:2|invert:focus outline-offset:2:focus …' }}
Try clicking the button to see the outline effect
<button class="btn">Submit</button>
Generated CSS
@layer base, theme, preset, components, general;@layer theme { .light, :root { --invert: 0 0 0 } .dark { --invert: 255 255 255 }}@layer components { .btn:focus { outline: 0.125rem rgb(var(--invert)) solid; outline-offset: 0.125rem }}
Add styles in a nested structure
Create and manage a set of abstract styles in a nested structure. Rather than repeating the same style names over and over again, you can write one style inside another. Master CSS will automatically combine the outer style’s name with the inner style’s.
export default { components: { card: { '': 'r:2x …', /* .card */ header: 'bb:1|gray …', /* .card-header */ content: 'p:5x …', /* .card-content */ footer: 'bt:1|gray …', /* .card-footer */ } }}
Apply the components:
<div class="card"> <div class="card-header">…</div> <div class="card-content">…</div> <div class="card-footer">…</div></div>
Generated CSS
@layer base, theme, preset, components, general;@layer theme { .light, :root { --gray: 162 161 163 } .dark { --gray: 137 136 138 }}@layer components { .card { border-radius: 0.5rem } .card-content { padding: 1.25rem } .card-footer { border-top: 0.0625rem rgb(var(--gray)) solid } .card-header { border-bottom: 0.0625rem rgb(var(--gray)) solid }}
The empty string ''
represents an outer style, much like Sass's &
.
Extend an existing style
Create a new abstract style by extending an existing style and adding additional syntax.
export default { components: { a: 'fg:lime', b: 'text:underline a' }}
You can see that b
inherits the text lime color of a
:
<span class="a">a</span><span class="b">b</span>
Generated CSS
@layer base, theme, preset, components, general;@layer theme { .light, :root { --text-lime: 76 141 7 } .dark { --text-lime: 145 217 26 }}@layer components { .b { -webkit-text-decoration: underline; text-decoration: underline; color: rgb(var(--text-lime)) } .a { color: rgb(var(--text-lime)) }}
Managing styles
As usual, you can split the styles into multiple files for management. When the project is small in scale, you don't necessarily have to insist on splitting style files.
Create button styles
Let's implement a rich button.
import type { Config } from '@master/css'export default { components: { btn: { '': 'center-content inline-flex font:semibold outline-offset:-1', 'xs': 'r:4 px:2x font:12 h:6x', 'sm': 'r:6 px:3x font:12 h:8x', 'md': 'r:6 px:4x font:14 h:10x', 'lg': 'r:8 px:5x font:16 h:12x', 'xl': 'r:10 px:6x font:16 h:14x', 'rounded': { 'xs': 'rounded px:3x font:12 h:6x', 'sm': 'rounded px:4x font:12 h:8x', 'md': 'rounded px:5x font:14 h:10x', 'lg': 'rounded px:6x font:16 h:12x', 'xl': 'rounded px:7x font:16 h:14x', } } }} as Config
Extend them uniformly in the master.css.ts entry file.
import type { Config } from '@master/css'import button from './styles/button'export default { extends: [ button ]} as Config
To apply the button sizes.
<button class="btn btn-xs …">Submit</button><button class="btn btn-sm …">Submit</button><button class="btn btn-md …">Submit</button><button class="btn btn-lg …">Submit</button><button class="btn btn-xl …">Submit</button>
To apply the button sizes that are finely tuned for rounded styles.
<button class="btn btn-rounded-xs …">Submit</button><button class="btn btn-rounded-sm …">Submit</button><button class="btn btn-rounded-md …">Submit</button><button class="btn btn-rounded-lg …">Submit</button><button class="btn btn-rounded-xl …">Submit</button>
To design a scalable style, you should ensure the single responsibility of class composition; otherwise, you may end up using a lot of !important
to override rules or the @preset
layer to lower default styles, ultimately leading to chaotic styles.
Create common variables and styles
We define variables and modes to create tokens supporting light/dark modes in advance. This not only simplifies template markup but also reduces CSS rule output.
import type { Config } from '@master/css'export default { variables: { yellow: { ring: { '@light': '$(black)/.1', '@dark': '$(white)/.3' } }, touch: { yellow: { '@light': '$(yellow-30)', '@dark': '$(yellow-40)' } }, text: { 'yellow-contrast': { '@light': '$(yellow-90)', '@dark': '$(yellow-95)' } } }, components: { yellow: 'outline:1|yellow-ring bg:yellow fg:yellow-contrast', touch: { yellow: 'bg:touch-yellow:hover' } },} as Config
- Variables
yellow-ring
is an outline color that complements a yellow background.touch-yellow
is a background color on hover.text-yellow-contrast
is a contrasting color fine-tuned against a yellow background.
- Components
.yellow
sets a background, foreground and outline style for a yellow theme..yellow-touch
sets a background, foreground and outline style for a yellow theme interaction.
<button class="btn btn-md yellow touch-yellow">Submit</button>