Type-driven CSS-in-JS with static extraction. Zero runtime.
A design system builder where the TypeScript types ARE the product. Define components with a builder chain that enforces cascade ordering — the types guarantee that every legal component produces valid, extractable CSS at build time.
No Emotion. No styled-components. No runtime style injection. The builder chain compiles to static CSS via @layer, extracted by a Rust pipeline.
import{ds}from'./ds';constCard=ds.styles({padding: '{space.md}',borderRadius: 8,backgroundColor: '{colors.surface}',}).variant({elevation: {prop: 'elevation',variants: {flat: {boxShadow: 'none'},raised: {boxShadow: '{shadows.sm}'},floating: {boxShadow: '{shadows.lg}'},},},}).states({disabled: {opacity: 0.5,pointerEvents: 'none'},}).system({surface: true,space: true}).asElement('div');// Fully typed — elevation, disabled, + all surface and space props<Cardelevation="raised"p={16}bg="surface.hover"disabled/>;# The design system builder
npm install @animus-ui/system
# Pick your bundler plugin
npm install @animus-ui/vite-plugin # Vite
npm install @animus-ui/next-plugin # Next.jsTwo files define your design system:
theme.ts — define your theme:
import{createTheme}from'@animus-ui/system';exportconsttheme=createTheme().addBreakpoints({sm: 480,md: 768,lg: 1024}).addColors({gray: {50: '#fafafa',500: '#555',900: '#080808'},blue: {400: '#3d94ff',700: '#003d99'},}).addColorModes('dark',{dark: {primary: 'blue.400',bg: 'gray.900',text: 'gray.50',},light: {primary: 'blue.700',bg: 'gray.50',text: 'gray.900',},}).addScale({name: 'space',values: {sm: '0.5rem',md: '1rem',lg: '1.5rem'},}).build();// Type augmentation — token names autocomplete everywheretypeAppTheme=typeoftheme;declare module '@animus-ui/system'{interfaceThemeextendsAppTheme{}}ds.ts — configure your system:
import{createSystem}from'@animus-ui/system';import{space,color,typography,layout,flex,border,shadows,background,}from'@animus-ui/system/groups';// Pre-built groups compose into your own semantic groupsexportconst{system: ds, createGlobalStyles }=createSystem().addGroup('surface',{ ...color, ...border, ...shadows, ...background}).addGroup('space',space).addGroup('text',typography).addGroup('arrange',{ ...flex, ...layout}).build();Consuming a published design-system kit? .extend() (available on both
builders, first in the chain) merges the kit's registries and tokens into
yours — its props type-check, extract, and resolve through your single merged
config, and your local definitions win on conflict:
import{systemaskitSystem,themeaskitTheme}from'@acme/kit';exportconsttheme=createTheme().extend(kitTheme).build();exportconst{system: ds}=createSystem().extend(kitSystem).build();vite.config.ts:
importreactfrom'@vitejs/plugin-react';import{animusExtract}from'@animus-ui/vite-plugin';import{defineConfig}from'vite';exportdefaultdefineConfig({plugins: [react(),animusExtract({system: './src/ds.ts'})],});Each method maps to a CSS @layer. The type system enforces the ordering.
ds.styles() → @layer base always-on styles
.variant() → @layer variants prop-driven variations
.compound() → @layer compounds variant combinations
.states() → @layer states boolean interaction states
.system() → @layer system opt into prop groups (space, color, etc.)
.props() → @layer custom component-scoped dynamic props
.asElement() → seal as typed React component
| Package | Purpose |
|---|---|
@animus-ui/system | Builder chain, theme, types, runtime |
@animus-ui/vite-plugin | Static CSS extraction for Vite |
@animus-ui/next-plugin | Static CSS extraction for Next.js |
@animus-ui/extract | Rust/NAPI extraction engine (used internally by plugins) |
@animus-ui/properties | CSS property data (transitive dep of system) |
- Compiler completeness: If the types accept it, the pipeline extracts it. No silent failures for well-typed code.
- Token refs:
'{colors.primary}'resolves tovar(--color-primary)at build time. Color modes shift the value automatically. - Pre-built groups: Import
space,color,typography, etc. from@animus-ui/system/groupsand compose them into your own semantic groups. - Slot composition:
compose()wires components into families with shared variant propagation via React context. - Terminals:
.asElement('div')for HTML elements,.asComponent(Existing)for wrapping React components. Both produce typed, extractable output.
@animus-ui/core and @animus-ui/theming are the original Emotion-based packages. They are pinned at their last published versions and no longer actively developed.
MIT