ZUI Is a CSS Library That Refuses to Be a Framework’s Prisoner

A close look at how ZUI uses CSS Layers, OKLCH color, shared controllers, and thin wrappers to make one design system work across many runtimes.

8 min read • View on GitHub • More from MrMartineau

A large workbench where a single core mechanism sends power into several different machines at once. The central engine represents shared CSS tokens and layers, while the surrounding machines suggest framework wrappers that all receive the same source of truth. The image explains how ZUI tries to make the browser the runtime and frameworks the adapters.
ZUI’s core move is to keep the design system centralized in CSS, then let framework wrappers tap into it.
Key Takeaways

The browser, not the framework, is the product

Most UI libraries start with a framework and then spread outward. ZUI flips that order. It treats the browser as the runtime, then builds thin adapters for React, Astro, Solid, Svelte, and Vue around a shared CSS core.

That matters because it changes where truth lives. In ZUI, the design system is not trapped inside React props or a framework-specific component API. It sits in CSS layers, tokens, and shared variant logic that every wrapper can reuse.

That is the core thesis of the repo: ZUI is not trying to win by being the biggest component kit. It is trying to make the styling and behavior model portable enough that the framework becomes incidental.

Why CSS Layers are the real architecture

This diagram shows why ZUI’s wrappers stay thin: the real logic lives in shared layers and shared variants before any framework gets involved.

@layer zui.reset, zui.base, zui.components, zui.utilities;

@layer zui.base {
  :root {
    --space-2: 0.5rem;
    --radius-md: 0.75rem;
  }
}

The cascade order is explicit, and that is the point. ZUI does not hope that specificity behaves. It declares the order up front: reset, base, components, utilities. Utility styles can override component defaults without a battle.

That is a small technical choice with a big product effect. It means the system is easier to reason about, easier to extend, and less likely to collapse into override spaghetti as teams customize it.

A close-up laboratory scene where a calibration dial marked with lightness values feeds into a button surface. One side of the button is dark, the other light, and a threshold line determines when the text flips for readability. The image explains how ZUI uses color math to make accessible text color decisions without extra JavaScript.
ZUI’s color system uses modern CSS math to decide readable foreground color from the background itself.

Tokens, OKLCH, and the auto-color trick

ZUI’s color work is not a decorative layer on top of the system. It is the system. The repo uses generated color tokens in OKLCH, which matters because OKLCH is built for perceptual consistency. Hues can vary without the palette falling apart visually.

The sharpest example is the auto-color behavior. Instead of asking each consumer to pick a text color manually, ZUI can infer readable foreground color from the background’s lightness and alpha. That turns a common accessibility chore into a browser-level calculation.

:root {
  --button-bg: oklch(0.72 0.16 260);
  --auto-color: oklch(from var(--button-bg) calc(l < 0.55 ? 0.98 : 0.12) 0 0);
}

The exact implementation is more nuanced than the sketch above, but the idea is simple: let CSS do the work. The less state that needs to live in JavaScript, the less state can drift across frameworks.

ProblemTraditional approachZUI approach
Readable button textPick a text color by hand or in JSCompute foreground behavior from the token itself
Palette consistencyHand-maintained shades across componentsGenerated OKLCH tokens with a shared system
Accessibility logicRepeated in each component wrapperCentralized in the CSS source of truth

One variant map, many frameworks

ZUI’s component wrappers look thin because the interesting part happens before the framework layer ever renders anything. Shared variant definitions, built with CVA, map props like size and variant to the same class logic across environments.

That means a button in React and a button in Astro do not each invent their own idea of what primary or large means. They both consume the same variant source, so design intent stays aligned even when the runtime changes.

import { cva } from 'class-variance-authority';

export const buttonVariants = cva('zui-button', {
  variants: {
    variant: {
      primary: 'zui-button--primary',
      ghost: 'zui-button--ghost'
    },
    size: {
      sm: 'zui-button--sm',
      lg: 'zui-button--lg'
    }
  }
});

This is where the repo’s philosophy becomes practical. Cross-framework consistency is not being enforced by documentation or discipline alone. It is being encoded into the shared variant layer.

DimensionFramework-first kitCSS-first ZUI
Source of truthComponent props and framework APIsShared CSS tokens and shared variant maps
Framework dependenceHighLow
Cross-framework driftLikelyReduced
Behavior reuseOften duplicated per wrapperShared once, consumed everywhere

The controller pattern keeps behavior from drifting

The same logic applies to behavior. For more complex components like AppShell, ZUI uses vanilla TypeScript controllers so the interaction model lives outside any one framework. The wrapper becomes a shell around behavior instead of the owner of behavior.

That is a smart trade-off. Framework-specific state often drifts over time because each wrapper grows its own edge cases. A shared controller keeps keyboard shortcuts, layout state, and resize behavior consistent no matter which runtime mounts it.

class AppShellController {
  constructor(root: HTMLElement) {
    this.root = root;
    this.restoreState();
    this.bindResizeObserver();
  }

  restoreState() {
    const collapsed = localStorage.getItem('sidebar-collapsed') === 'true';
    this.root.dataset.collapsed = String(collapsed);
  }
}

The pattern is boring in the best way. A framework wrapper instantiates the controller, then gets out of the way. That keeps logic portable and makes the browser itself feel like the execution layer.

The small details that make it feel native

ZUI also leans on browser-native primitives where it can. Dialog and popover behavior is handled with the platform in mind, not with a heavyweight abstraction by default. That trims the amount of code needed for common UI interactions.

The no-flash hydration detail is especially telling. Restoring local state before the main bundle takes over prevents the visible layout jump that often makes cross-framework UI feel brittle. It is a small polish move with outsized perceived quality.

The components are also polymorphic where that helps the user. If a button is actually a link, it can behave like one. That kind of detail makes a system feel native instead of merely styled.

PatternWhy it mattersWhat ZUI does
No-flash hydrationAvoids visible layout jumpsRestores state early from localStorage
Native popover and dialogReduces custom overlay logicUses platform primitives when possible
Polymorphic componentsMatches semantic intentSwitches between link and button behavior

What ZUI is really competing against

The competition here is not just another component library. It is a set of architectural defaults. React-first kits make the framework the center. Utility-first stacks often push consistency into class composition. Older CSS libraries can be portable, but they usually lack the layered token and behavior model ZUI is aiming for.

ApproachCenter of gravityStrengthWeakness
React-first component kitsFramework APIFast adoption inside one ecosystemDrift and lock-in across runtimes
Utility-first stacksClass compositionFlexible stylingBehavior and state still need structure
Traditional CSS librariesStylesheetsFramework agnosticOften too blunt for modern component behavior
ZUIShared CSS and shared logicPortable design system with thin adaptersDepends on modern browser capabilities

That last row is the real story. ZUI is not trying to win by being the most universal in the abstract. It is trying to be portable in the places that matter: styling, tokens, state, and component behavior.

Why this repo feels unusually mature

The tooling tells the same story as the code. ADRs, a ubiquitous language document, semantic-release, Biome, a VS Code extension, and a Raycast extension are not cosmetic extras. They are signs that the project is being built as a system, not as a collection of demos.

That matters because design systems often fail at the seams between implementation and maintenance. ZUI looks intentionally organized to reduce seam friction before it becomes technical debt.

If the thesis holds, the long-term bet is simple: the browser changes more slowly than frameworks do. A UI library that treats CSS as architecture is betting on the slower-moving layer of the stack.