Deterministic styling for stateful component systems
Tasty compiles declarative state maps into mutually exclusive selectors, so styles resolve deterministically instead of depending on source order or specificity. That makes complex styles easier to define, extend, and maintain.
Why Tasty
Built for reusable, stateful components that need predictable styling as they evolve
Deterministic State Resolution
State maps compile into mutually exclusive selectors, so one branch wins by construction instead of through source order or specificity.
Predictable to Extend
Add variants, overrides, and new states without reopening selector logic by hand. The component model stays maintainable as systems grow.
Extensible Style Semantics
Define custom props, tokens, units, aliases, and parser rules for your design system, then compile them down to standard CSS output.
Recommended Methodology
The docs define a clear component model for design systems: root + sub-elements, governed public APIs, typed style props where they help, and wrapper-based extension.
Broad State Coverage
Pseudo-classes, attributes, media queries, container queries, root states, parent states, `:has()`, and `@supports` all fit into the same state-map model.
Flexible Rendering Paths
Use the same styling model in runtime React, add SSR when the app renders on the server, or choose build-time extraction when zero-runtime delivery is the goal.
How It Actually Works
Every state map compiles into mutually exclusive selectors per property
Tasty DSL
Inputconst Button = tasty({
as: 'button',
styles: {
fill: {
'': '#primary',
':hover': '#primary-hover',
':active': '#primary-pressed',
'[disabled]': '#surface',
},
},
});Exclusive CSS Selectors
Output/* [disabled] wins outright */
.t0.t0[disabled] {
background: var(--surface-color);
}
/* :active is excluded when disabled */
.t0.t0:active:not([disabled]) {
background: var(--primary-pressed-color);
}
/* :hover is excluded when :active or disabled */
.t0.t0:hover:not(:active):not([disabled]) {
background: var(--primary-hover-color);
}
/* default is excluded when anything above matches */
.t0.t0:not(:hover):not(:active):not([disabled]) {
background: var(--primary-color);
}Tasty DSL
Input// Define a reusable state alias
configure({
states: {
'@dark': '@root(schema=dark) | (!@root(schema) & @media(prefers-color-scheme: dark))',
},
});
// Use the alias in styles
const Text = tasty({
// You can also define `@dark` here
styles: {
color: {
'': '#text',
'@dark': '#text-on-dark',
},
},
});Exclusive CSS Selectors
Output/* Branch 1: Explicit dark schema */
:root[data-schema="dark"] .t0.t0 {
color: var(--text-on-dark-color);
}
/* Branch 2: No schema attribute + OS prefers dark */
@media (prefers-color-scheme: dark) {
:root:not([data-schema]) .t0.t0 {
color: var(--text-on-dark-color);
}
}
/* Default: no schema + OS does not prefer dark */
@media (not (prefers-color-scheme: dark)) {
:root:not([data-schema="dark"]) .t0.t0 {
color: var(--text-color);
}
}
/* Default: schema is set but not dark */
:root:not([data-schema="dark"])[data-schema] .t0.t0 {
color: var(--text-color);
}Each branch is guarded so one rule wins by construction. No specificity arithmetic. No source-order accidents.
That is what lets components compose, extend, and stay predictable as states intersect.
Tokens, Units, and Color Systems
Define a shared styling language with global tokens, state-aware values, and OKHSL-friendly color authoring
configure({
tokens: {
'$gap': '8px',
'$radius': '10px',
'$border-width': '1px',
'#surface': {
'': '#fff',
'@dark': 'okhsl(255 18% 12%)',
},
'#text': {
'': 'okhsl(255 12% 16%)',
'@dark': 'okhsl(255 15% 96%)',
},
'#primary': {
'': 'okhsl(272 75% 55%)',
'@dark': 'okhsl(272 70% 72%)',
},
},
});const violet = glaze(272, 75);
violet.colors({
surface: {
lightness: 98, saturation: 0.2,
},
text: {
base: 'surface', lightness: '-62',
contrast: 'AAA', saturation: 0.08,
},
'accent-surface': {
lightness: 52, mode: 'fixed',
},
'shadow-md': {
type: 'shadow', bg: 'surface',
fg: 'text', intensity: 12,
},
});Use configure() to define the tokens your design system owns. Those values become shared CSS custom properties, and they can use state maps too, so themes and breakpoints reuse the same vocabulary everywhere.
Tasty also supports OKHSL natively. When you want full light, dark, and high-contrast palettes with automatic WCAG-aware contrast solving, use Glaze as the companion palette generator.
See It In Action
Patterns from the recommended design-system model
State Maps
Declare intersecting states once and let Tasty generate the exclusive selectors that keep the outcome deterministic.
const Button = tasty({
as: 'button',
styles: {
fill: {
'': '#primary',
':hover': '#hover',
':active': '#pressed',
'[disabled]': '#surface',
},
color: {
'': '#on-primary',
'[disabled]': '#text.40',
},
transition: 'theme',
},
});styleProps & modProps
Expose CSS layout controls as typed props with styleProps, and modifier states as direct props with modProps — no mods object needed.
import { tasty, POSITION_STYLES } from '@tenphi/tasty';
const Button = tasty({
as: 'button',
styleProps: POSITION_STYLES,
modProps: {
isLoading: Boolean,
size: ['small', 'medium', 'large'] as const,
},
styles: {
padding: {
'': '1.5x 3x',
'size=small': '1x 2x',
'size=large': '2x 4x',
},
fill: {
'': '#primary',
isLoading: '#primary.5',
},
color: '#on-primary',
radius: true,
cursor: { '': 'pointer', isLoading: 'wait' },
},
});
<Button size="large" placeSelf="end">Submit</Button>
<Button isLoading>Saving...</Button>Root + Sub-Elements
Model compound components around a root state context so inner parts react together without duplicated modifier wiring.
const Alert = tasty({
styles: {
padding: '3x',
fill: {
'': '#surface',
'type=danger': '#danger.10',
},
border: {
'': '1bw solid #border',
'type=danger': '1bw solid #danger',
},
Icon: {
color: {
'': '#text-secondary',
'type=danger': '#danger',
},
},
Message: {
color: '#text',
},
},
elements: { Icon: 'span', Message: 'div' },
});
<Alert mods={{ type: 'danger' }}>
<Alert.Icon>!</Alert.Icon>
<Alert.Message>Something went wrong</Alert.Message>
</Alert>Configuration
Define the styling language once, then build components and product APIs on top of it.
import { configure } from '@tenphi/tasty';
configure({
tokens: {
'#primary': 'oklch(55% 0.25 265)',
'#surface': '#fff',
'#text': '#111',
},
states: {
'@mobile': '@media(w < 768px)',
'@dark': '@root(schema=dark)',
},
recipes: {
card: {
padding: '4x',
fill: '#surface',
radius: '1r',
border: true,
},
},
});Companion Tooling
Linting, editor support, and palette generation around the core engine
Glaze
Generate OKHSL-based light, dark, and high-contrast palettes with WCAG-aware contrast solving, then export them as Tasty-ready color tokens.
ColorsESLint Plugin
Catch invalid style properties, malformed state keys, missing tokens, and other DSL mistakes before they reach the browser.
LintingVS Code Extension
Add syntax highlighting for tokens, units, states, presets, and the rest of the Tasty authoring model inside TS and TSX files.
DXIn the Wild
Articles about the project and products powered by Tasty
Articles
Where It's Used
Cube UI Kit
100+ production components built with Tasty from the ground up. The design system that shaped the styling model under real-world pressure.
Component LibraryCube Cloud
The universal semantic layer for data modeling, access control, caching, and APIs — powered by Cube UI Kit and Tasty in production.
Enterprise Producttasty.style
This very website — the marketing site, docs, and interactive playground for Tasty, built with Next.js and styled entirely with Tasty.
Documentation Sitetenphi.me
Portfolio and blog of Tasty's author, built with Astro and styled with Tasty for layout, components, and article pages.
Personal SiteStart with runtime, add structure as needed
Install the runtime, build a first component, then layer in shared configuration, methodology, SSR, or zero-runtime only where your system needs them.
$ pnpm add @tenphi/tasty