Component recipes
Define named groups of rules, compose them, apply them at breakpoints, and override them per element.
1 min readv1
A recipe is a named attribute string in components. Referencing it by name (no =) expands it.
Defining#
TypeScript
components: {
btn: "d=inline-flex; ali=center; g=8px; p=8px 16px; bdrd=8px; ftw=600; cur=pointer; ts=all .15s",
"btn-primary": "btn; bgc=$color.brand; cl=#fff; hover:bgc=$color.brand-hover",
"btn-danger": "btn; bgc=#dc2626; cl=#fff; hover:bgc=#b91c1c",
card: "p=24px; bdrd=12px; bd=1px solid #e5e7eb; dark:bd=1px solid #333",
"card-hover": "card; ts=transform .2s; hover:tf=translateY(-4px)",
}HTML
Badge
<div nib="d=flex; g=10px; flxw=wrap">
<button nib="btn-primary">Primary</button>
<button nib="btn-outline">Outline</button>
<span nib="badge">Badge</span>
</div>Overriding#
Add rules after the recipe. For the same property, the rule that comes later in the attribute wins:
HTML
<button nib="btn-primary; bgc=#0891b2; hover:bgc=#0e7490">Teal variant</button>Output: one class per recipe#
A recipe becomes a single class that holds all of its declarations, including its states:
CSS
.nib-1nfp073 { padding: 24px; border-radius: 12px }
.dark .nib-1nfp073 { border: 1px solid #333 }
.nib-1nfp073:hover { transform: translateY(-4px) }With a breakpoint#
HTML
<section nib="md:card">Recipes in dynamic code#
Recipes work everywhere the engine runs — including useNib("btn-primary"), v-nib, use:nib and
[nib]. They do not exist as literal class names.
A recipe library to start from#
TypeScript
components: {
container: "w=100%; mxw=1200px; mi=auto; px=24px",
stack: "d=flex; flxd=column; g=16px",
row: "d=flex; ali=center; g=12px",
center: "d=grid; pli=center",
input: "w=100%; p=8px 12px; bdrd=8px; bd=1px solid #cbd5e1; otl=none; focus:bdc=$color.brand; focus:bxsh=0 0 0 3px rgba(79,82,217,.2)",
badge: "d=inline-flex; ali=center; p=2px 10px; bdrd=999px; fts=12px; ftw=600",
"sr-only": "pos=absolute; w=1px; h=1px; p=0; m=-1px; ovf=hidden; clip=rect(0,0,0,0); whs=nowrap; bd=0",
}