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';
const Card = 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
<Card elevation="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 tokens:
import { createTheme } from '@animus-ui/system';
export const tokens = 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 everywhere
declare module '@animus-ui/system' {
interface Theme extends typeof tokens {}
}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 groups
export const { system: ds, createGlobalStyles } = createSystem()
.addGroup('surface', { ...color, ...border, ...shadows, ...background })
.addGroup('space', space)
.addGroup('text', typography)
.addGroup('arrange', { ...flex, ...layout })
.build();vite.config.ts:
import react from '@vitejs/plugin-react';
import { animusExtract } from '@animus-ui/vite-plugin';
import { defineConfig } from 'vite';
export default defineConfig({
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