Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ npm install @animus-ui/next-plugin # Next.js

Two files define your design system:

**`theme.ts`** — define your tokens:
**`theme.ts`** — define your theme:

```tsx
import { createTheme } from '@animus-ui/system';

export const tokens = createTheme()
export const theme = createTheme()
.addBreakpoints({ sm: 480, md: 768, lg: 1024 })
.addColors({
gray: { 50: '#fafafa', 500: '#555', 900: '#080808' },
Expand All @@ -75,12 +75,17 @@ export const tokens = createTheme()
text: 'gray.900',
},
})
.addScale({ name: 'space', values: { sm: '0.5rem', md: '1rem', lg: '1.5rem' } })
.addScale({
name: 'space',
values: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
})
.build();

// Type augmentation — token names autocomplete everywhere
type AppTheme = typeof theme;

declare module '@animus-ui/system' {
interface Theme extends typeof tokens {}
interface Theme extends AppTheme {}
}
```

Expand Down Expand Up @@ -108,6 +113,18 @@ export const { system: ds, createGlobalStyles } = createSystem()
.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:

```tsx
import { system as kitSystem, theme as kitTheme } from '@acme/kit';

export const theme = createTheme().extend(kitTheme).build();
export const { system: ds } = createSystem().extend(kitSystem).build();
```

**`vite.config.ts`**:

```tsx
Expand Down
15 changes: 14 additions & 1 deletion e2e/next-app/src/ds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
space,
typography,
} from '@animus-ui/system/groups';
import { ds as testDs } from '@animus-ui/test-ds';
import { system as testDs } from '@animus-ui/test-ds/definition';

// ─── Transforms ─────────────────────────────────────────────

Expand All @@ -21,6 +21,10 @@ export const size = createTransform('size', (value) => {

// ─── Tokens ─────────────────────────────────────────────────

// DELIBERATE legacy lane (openspec: first-class-extension, Migration Plan
// step 2 / G6): this fixture keeps the deprecated `tokens` export name — the
// loader accepts it as the fallback spelling while `theme` is the documented
// name. Do not rename during the deprecation window.
export const tokens = createTheme()
.addBreakpoints({ sm: 640, md: 768, lg: 1024, xl: 1280 })
.addColors({
Expand Down Expand Up @@ -189,6 +193,15 @@ declare module '@animus-ui/system' {

// ─── System ─────────────────────────────────────────────────

// DELIBERATE legacy lane (openspec: first-class-extension, Migration Plan
// step 2 / G6): this fixture is the `createSystem({ includes: [...] })`
// deprecation-window witness — the alias keeps its frozen semantics (type
// admission + discovery anchor, NO runtime registry merge), so every group
// the components need is still registered locally. react-router-app covers
// the `from()` chain; vite-app/next16-app/vinext-app use `.extend()`
// (showcase remains on `includes:` pending its deferred migration —
// registry row 13; see its ds.ts). Do not migrate this lane until removal
// is specced.
export const {
system: ds,
createGlobalStyles,
Expand Down
36 changes: 14 additions & 22 deletions e2e/next16-app/src/ds.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { createSystem, createTheme, createTransform } from '@animus-ui/system';
import {
border,
color,
flex,
layout,
positioning,
shadows,
space,
typography,
} from '@animus-ui/system/groups';
import { ds as testDs } from '@animus-ui/test-ds';
import { shadows } from '@animus-ui/system/groups';
import { system as testDs } from '@animus-ui/test-ds/definition';

// ─── Transforms ─────────────────────────────────────────────

Expand All @@ -21,7 +12,7 @@ export const size = createTransform('size', (value) => {

// ─── Tokens ─────────────────────────────────────────────────

export const tokens = createTheme()
export const theme = createTheme()
.addBreakpoints({ sm: 640, md: 768, lg: 1024, xl: 1280 })
.addColors({
gray: {
Expand Down Expand Up @@ -164,7 +155,7 @@ export const tokens = createTheme()
})
.build();

export type TestTheme = typeof tokens;
export type TestTheme = typeof theme;

declare module '@animus-ui/system' {
interface Theme extends TestTheme {}
Expand All @@ -176,15 +167,16 @@ export const {
system: ds,
createGlobalStyles,
createKeyframes,
} = createSystem({
includes: [testDs],
})
.addGroup('space', space)
.addGroup('layout', { ...layout, ...flex })
.addGroup('text', typography)
.addGroup('surface', { ...color, ...border, ...shadows })
.addGroup('positioning', positioning)
.build();
// extend()-form lane (openspec: first-class-extension, D1): test-ds's
// registries MERGE into this system — the kit alone provides the space/
// layout/text/surface/positioning groups the components use. The only
// LOCAL registration is the additive, transform-free `shadows` prop set
// (boxShadow/shadow/textShadow — the kit does not register them, and the
// Card/Button styles resolve their `shadows`-scale values through the
// registry). Re-spreading kit groups would coalesce under D12 transform
// equality (name + captured source); this lane stays pure-extend + additive
// as the recommended consumption shape.
} = createSystem().extend(testDs).addProps(shadows).build();

// ─── Keyframes ──────────────────────────────────────────────

Expand Down
4 changes: 2 additions & 2 deletions e2e/packed-app/src/ds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
typography,
} from '@animus-ui/system/groups';

export const tokens = createTheme()
export const theme = createTheme()
.addBreakpoints({ sm: 640, md: 768, lg: 1024 })
.addColors({
blue: { 100: '#dbeafe', 500: '#3b82f6', 700: '#1d4ed8' },
Expand Down Expand Up @@ -60,7 +60,7 @@ export const tokens = createTheme()
})
.build();

export type PackedAppTheme = typeof tokens;
export type PackedAppTheme = typeof theme;

declare module '@animus-ui/system' {
interface Theme extends PackedAppTheme {}
Expand Down
32 changes: 27 additions & 5 deletions e2e/react-router-app/src/ds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ import {
space,
typography,
} from '@animus-ui/system/groups';
import { ds as testDs } from '@animus-ui/test-ds';
import { system as testDs } from '@animus-ui/test-ds/definition';

// DELIBERATE legacy lane (openspec: first-class-extension, Migration Plan
// step 2 / G6): this fixture keeps the deprecated `tokens` export name — it
// witnesses the loader's accepted fallback (rust-system-loader › "tokens
// fallback accepted"). Do not rename to `theme` while the deprecation window
// is open; every non-legacy fixture already uses `theme`.
export const tokens = createTheme()
.addColors({
blue: { 100: '#dbeafe', 500: '#3b82f6', 700: '#1d4ed8' },
gray: { 100: '#f5f5f5', 500: '#737373', 800: '#262626', 950: '#0a0a0a' },
green: { 500: '#22c55e', 700: '#15803d' },
red: { 500: '#ef4444', 700: '#b91c1c' },
})
.addColorModes('dark', {
dark: {
primary: { _: 'blue.500', hover: 'blue.700' },
secondary: 'green.500',
danger: 'red.500',
background: 'gray.950',
surface: 'gray.800',
Expand All @@ -26,6 +33,7 @@ export const tokens = createTheme()
},
light: {
primary: { _: 'blue.700', hover: 'blue.500' },
secondary: 'green.700',
danger: 'red.700',
background: 'gray.100',
surface: 'gray.100',
Expand All @@ -39,14 +47,21 @@ export const tokens = createTheme()
0: '0',
4: '0.25rem',
8: '0.5rem',
12: '0.75rem',
16: '1rem',
24: '1.5rem',
32: '2rem',
},
})
.addScale({
name: 'fontSizes',
values: { 14: '0.875rem', 16: '1rem', 24: '1.5rem', 32: '2rem' },
values: {
12: '0.75rem',
14: '0.875rem',
16: '1rem',
24: '1.5rem',
32: '2rem',
},
})
.build();

Expand All @@ -56,9 +71,16 @@ declare module '@animus-ui/system' {
interface Theme extends ReactRouterTheme {}
}

export const { system: ds, createGlobalStyles } = createSystem({
includes: [testDs],
})
// DELIBERATE legacy lane (openspec: first-class-extension, Migration Plan
// step 2 / G6): this fixture is the `from()` deprecation-window witness — the
// chain keeps from()'s frozen semantics (type admission + discovery anchor,
// NO runtime registry merge), so every group the components need is still
// registered locally. next-app covers the `includes:` constructor alias;
// vite-app/next16-app/vinext-app use `.extend()` (showcase remains on
// `includes:` pending its deferred migration — registry row 13; see its
// ds.ts). Do not migrate this lane until removal is specced.
export const { system: ds, createGlobalStyles } = createSystem()
.from(testDs)
.addGroup('space', space)
.addGroup('layout', { ...layout, ...flex })
.addGroup('text', typography)
Expand Down
41 changes: 22 additions & 19 deletions e2e/vinext-app/src/ds.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import { createSystem, createTheme } from '@animus-ui/system';
import {
border,
color,
flex,
layout,
space,
typography,
} from '@animus-ui/system/groups';
import { ds as testDs } from '@animus-ui/test-ds';
import { system as testDs } from '@animus-ui/test-ds/definition';

export const tokens = createTheme()
export const theme = createTheme()
.addColors({
blue: { 100: '#dbeafe', 500: '#3b82f6', 700: '#1d4ed8' },
gray: { 100: '#f5f5f5', 500: '#737373', 800: '#262626', 950: '#0a0a0a' },
green: { 500: '#22c55e', 700: '#15803d' },
red: { 500: '#ef4444', 700: '#b91c1c' },
})
.addColorModes('dark', {
dark: {
primary: { _: 'blue.500', hover: 'blue.700' },
secondary: 'green.500',
danger: 'red.500',
background: 'gray.950',
surface: 'gray.800',
Expand All @@ -26,6 +20,7 @@ export const tokens = createTheme()
},
light: {
primary: { _: 'blue.700', hover: 'blue.500' },
secondary: 'green.700',
danger: 'red.700',
background: 'gray.100',
surface: 'gray.100',
Expand All @@ -39,30 +34,38 @@ export const tokens = createTheme()
0: '0',
4: '0.25rem',
8: '0.5rem',
12: '0.75rem',
16: '1rem',
24: '1.5rem',
32: '2rem',
},
})
.addScale({
name: 'fontSizes',
values: { 14: '0.875rem', 16: '1rem', 24: '1.5rem', 32: '2rem' },
values: {
12: '0.75rem',
14: '0.875rem',
16: '1rem',
24: '1.5rem',
32: '2rem',
},
})
.build();

export type VinextTheme = typeof tokens;
export type VinextTheme = typeof theme;

declare module '@animus-ui/system' {
interface Theme extends VinextTheme {}
}

export const { system: ds, createGlobalStyles } = createSystem({
includes: [testDs],
})
.addGroup('space', space)
.addGroup('layout', { ...layout, ...flex })
.addGroup('text', typography)
.addGroup('surface', { ...color, ...border })
// extend()-form lane (openspec: first-class-extension, D1): test-ds's
// registries MERGE into this system — every group the components use
// (space, layout, plus the kit's text/surface/positioning) arrives through
// `.extend(testDs)` alone. Nothing is re-registered locally — re-spreading
// kit groups would coalesce under D12 transform equality (name + captured
// source), but pure extension is the recommended consumption shape.
export const { system: ds, createGlobalStyles } = createSystem()
.extend(testDs)
.build();

export const globalStyles = createGlobalStyles({
Expand Down
Loading