Merge branch 'feat/v0.13.2-consolidation-design-system'

This commit is contained in:
senke 2026-03-13 10:16:09 +01:00
commit f27af786ac
16 changed files with 995 additions and 14 deletions

View file

@ -1399,21 +1399,22 @@ Les tests de biais éthiques exigés par les specs sont absents. La coverage n'e
### v0.13.2 — Consolidation Design System
**Statut** : ⏳ TODO
**Statut** : ✅ DONE
**Priorité** : P2
**Durée estimée** : 2-3 jours
**Prerequisite** : v0.13.0 complète
**Complété le** : 2026-03-13
**Tâches**
- [ ] **TASK-DS-001** : Migrer composants SUMI vers `packages/design-system/`
- [ ] **TASK-DS-002** : Extraire design tokens (couleurs, typo, spacing)
- [ ] **TASK-DS-003** : Compléter documentation Storybook
- [x] **TASK-DS-001** : Migrer composants SUMI vers `packages/design-system/` — package restructuré avec src/, exports map, component registry
- [x] **TASK-DS-002** : Extraire design tokens (couleurs, typo, spacing) — 4 fichiers tokens TypeScript
- [x] **TASK-DS-003** : Compléter documentation Storybook — 4 nouvelles stories + design tokens showcase
**Critères d'acceptation**
- [ ] `packages/design-system/` contient les composants UI de base
- [ ] Design tokens centralisés
- [ ] Stories à jour pour les composants principaux
- [x] `packages/design-system/` contient les composants UI de base
- [x] Design tokens centralisés
- [x] Stories à jour pour les composants principaux
---
@ -1616,7 +1617,7 @@ Toutes les conditions suivantes doivent être remplies avant de taguer v1.0.0 :
| v0.12.9 | Tests Éthiques & Coverage CI | P1 | ✅ DONE | 2-3j | v0.12.6.3 |
| v0.13.0 | Conformité Features Partielles | P2 | ✅ DONE | 5-7j | v0.12.9 |
| v0.13.1 | Conformité Audio & Player | P2 | ✅ DONE | 4-5j | v0.13.0 |
| v0.13.2 | Consolidation Design System | P2 | ⏳ TODO | 2-3j | v0.13.0 |
| v0.13.2 | Consolidation Design System | P2 | ✅ DONE | 2-3j | v0.13.0 |
| v0.13.3 | Polish Sécurité Avancée | P3 | ⏳ TODO | 3-4j | v0.13.0 |
| v0.13.4 | Polish Audio & Player | P3 | ⏳ TODO | 3-4j | v0.13.1 |
| v0.13.5 | Polish Marketplace & Compliance | P3 | ⏳ TODO | 3-4j | v0.13.0 |

View file

@ -0,0 +1,66 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ButtonLoading } from './button-loading';
const meta: Meta<typeof ButtonLoading> = {
title: 'UI/ButtonLoading',
component: ButtonLoading,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],
},
size: { control: 'select', options: ['default', 'sm', 'lg', 'icon'] },
},
};
export default meta;
type Story = StoryObj<typeof ButtonLoading>;
export const Default: Story = {
args: {
children: 'Submit',
isLoading: false,
},
};
export const Loading: Story = {
args: {
children: 'Submit',
isLoading: true,
},
};
export const LoadingWithText: Story = {
args: {
children: 'Save Changes',
isLoading: true,
loadingText: 'Saving...',
},
};
export const Destructive: Story = {
args: {
children: 'Delete',
variant: 'destructive',
isLoading: true,
loadingText: 'Deleting...',
},
};
export const Outline: Story = {
args: {
children: 'Export',
variant: 'outline',
isLoading: true,
loadingText: 'Exporting...',
},
};
export const Small: Story = {
args: {
children: 'Upload',
size: 'sm',
isLoading: true,
},
};

View file

@ -0,0 +1,37 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ContentFadeIn } from './ContentFadeIn';
const meta: Meta<typeof ContentFadeIn> = {
title: 'UI/ContentFadeIn',
component: ContentFadeIn,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof ContentFadeIn>;
export const Default: Story = {
args: {
children: (
<div className="p-6 bg-surface-card rounded-lg border border-border">
<h3 className="text-lg font-heading font-bold mb-2">Content loaded</h3>
<p className="text-muted-foreground">
This content fades in with a smooth 200ms transition using SUMI easing.
</p>
</div>
),
},
};
export const Card: Story = {
args: {
children: (
<div className="p-4 bg-surface-card rounded-lg border border-border space-y-3">
<div className="w-full h-32 bg-muted rounded-md" />
<h4 className="font-heading font-semibold">Track Title</h4>
<p className="text-sm text-muted-foreground">Artist Name</p>
</div>
),
className: 'max-w-sm',
},
};

View file

@ -0,0 +1,143 @@
import type { Meta, StoryObj } from '@storybook/react';
/**
* SUMI Design Tokens Visual reference for the design system.
* Shows all color pigments, typography, spacing, and motion tokens.
*/
function ColorSwatch({ name, value, cssVar }: { name: string; value: string; cssVar: string }) {
return (
<div className="flex items-center gap-3">
<div
className="w-10 h-10 rounded-md border border-border flex-shrink-0"
style={{ backgroundColor: value }}
/>
<div>
<div className="text-sm font-medium text-foreground">{name}</div>
<div className="text-xs text-muted-foreground font-mono">{cssVar}</div>
</div>
</div>
);
}
function TokenSection({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="space-y-3">
<h3 className="text-lg font-heading font-bold text-foreground border-b border-border pb-2">{title}</h3>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">{children}</div>
</div>
);
}
function DesignTokensShowcase() {
return (
<div className="space-y-8 p-4">
<div>
<h2 className="text-2xl font-heading font-bold mb-1">SUMI Design System v2.0</h2>
<p className="text-muted-foreground">"L'encre et la lumière" Ink and Light</p>
</div>
<TokenSection title="Pigments">
<ColorSwatch name="Accent (Indigo)" value="#7c9dd6" cssVar="--sumi-accent" />
<ColorSwatch name="Accent Hover" value="#93afe0" cssVar="--sumi-accent-hover" />
<ColorSwatch name="Vermillion" value="#d4634a" cssVar="--sumi-vermillion" />
<ColorSwatch name="Sage" value="#7a9e6c" cssVar="--sumi-sage" />
<ColorSwatch name="Gold" value="#c9a84c" cssVar="--sumi-gold" />
<ColorSwatch name="Live" value="#e05a5a" cssVar="--sumi-live" />
</TokenSection>
<TokenSection title="Backgrounds">
<ColorSwatch name="Void" value="#0c0c0f" cssVar="--sumi-bg-void" />
<ColorSwatch name="Base" value="#121215" cssVar="--sumi-bg-base" />
<ColorSwatch name="Raised" value="#1a1a1f" cssVar="--sumi-bg-raised" />
<ColorSwatch name="Overlay" value="#222228" cssVar="--sumi-bg-overlay" />
<ColorSwatch name="Hover" value="#2a2a31" cssVar="--sumi-bg-hover" />
<ColorSwatch name="Active" value="#32323a" cssVar="--sumi-bg-active" />
</TokenSection>
<TokenSection title="Text">
<ColorSwatch name="Primary" value="#f0ede8" cssVar="--sumi-text-primary" />
<ColorSwatch name="Secondary" value="#a8a4a0" cssVar="--sumi-text-secondary" />
<ColorSwatch name="Tertiary" value="#706c68" cssVar="--sumi-text-tertiary" />
<ColorSwatch name="Disabled" value="#4a4844" cssVar="--sumi-text-disabled" />
<ColorSwatch name="Link" value="#8baade" cssVar="--sumi-text-link" />
</TokenSection>
<div className="space-y-3">
<h3 className="text-lg font-heading font-bold text-foreground border-b border-border pb-2">Typography</h3>
<div className="space-y-2">
<p style={{ fontFamily: 'var(--sumi-font-heading)', fontSize: 'var(--sumi-text-4xl)' }} className="font-bold">
Heading Space Grotesk (4xl)
</p>
<p style={{ fontFamily: 'var(--sumi-font-heading)', fontSize: 'var(--sumi-text-2xl)' }} className="font-semibold">
Heading Space Grotesk (2xl)
</p>
<p style={{ fontFamily: 'var(--sumi-font-body)', fontSize: 'var(--sumi-text-base)' }}>
Body Inter (base, 14px)
</p>
<p style={{ fontFamily: 'var(--sumi-font-mono)', fontSize: 'var(--sumi-text-sm)' }} className="text-muted-foreground">
Mono JetBrains Mono (sm)
</p>
</div>
</div>
<div className="space-y-3">
<h3 className="text-lg font-heading font-bold text-foreground border-b border-border pb-2">Spacing</h3>
<div className="space-y-1">
{[
{ name: 'space-1', size: '4px' },
{ name: 'space-2', size: '8px' },
{ name: 'space-3', size: '12px' },
{ name: 'space-4', size: '16px' },
{ name: 'space-6', size: '24px' },
{ name: 'space-8', size: '32px' },
].map(({ name, size }) => (
<div key={name} className="flex items-center gap-3">
<div
className="h-4 bg-primary/30 rounded-sm"
style={{ width: size }}
/>
<span className="text-xs font-mono text-muted-foreground">
--sumi-{name} ({size})
</span>
</div>
))}
</div>
</div>
<div className="space-y-3">
<h3 className="text-lg font-heading font-bold text-foreground border-b border-border pb-2">Border Radius</h3>
<div className="flex gap-4 flex-wrap">
{[
{ name: 'xs', size: '2px' },
{ name: 'sm', size: '4px' },
{ name: 'md', size: '6px' },
{ name: 'lg', size: '12px' },
{ name: 'xl', size: '16px' },
{ name: 'full', size: '9999px' },
].map(({ name, size }) => (
<div key={name} className="flex flex-col items-center gap-1">
<div
className="w-12 h-12 bg-primary/20 border border-primary/40"
style={{ borderRadius: size }}
/>
<span className="text-xs font-mono text-muted-foreground">{name}</span>
</div>
))}
</div>
</div>
</div>
);
}
const meta: Meta = {
title: 'Design System/Tokens',
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj;
export const AllTokens: Story = {
render: () => <DesignTokensShowcase />,
};

View file

@ -0,0 +1,99 @@
import type { Meta, StoryObj } from '@storybook/react';
import { EmptyState } from './empty-state';
import { Inbox, Search, Music, Users } from 'lucide-react';
const meta: Meta<typeof EmptyState> = {
title: 'UI/EmptyState',
component: EmptyState,
tags: ['autodocs'],
argTypes: {
size: { control: 'select', options: ['sm', 'md', 'lg'] },
variant: { control: 'select', options: ['default', 'centered', 'card'] },
},
};
export default meta;
type Story = StoryObj<typeof EmptyState>;
export const Default: Story = {
args: {
title: 'No items found',
description: 'There are no items to display yet.',
},
};
export const WithIcon: Story = {
args: {
icon: <Inbox className="w-full h-full" />,
title: 'Your inbox is empty',
description: 'New messages will appear here when you receive them.',
},
};
export const WithAction: Story = {
args: {
icon: <Music className="w-full h-full" />,
title: 'No tracks uploaded',
description: 'Upload your first track to get started.',
action: {
label: 'Upload a track',
onClick: () => {},
},
},
};
export const SearchEmpty: Story = {
args: {
icon: <Search className="w-full h-full" />,
title: 'No results found',
description: 'Try adjusting your search criteria or browse by genre.',
action: {
label: 'Clear search',
onClick: () => {},
variant: 'outline',
},
},
};
export const Small: Story = {
args: {
size: 'sm',
title: 'No followers yet',
icon: <Users className="w-full h-full" />,
},
};
export const Large: Story = {
args: {
size: 'lg',
icon: <Music className="w-full h-full" />,
title: 'Your library is empty',
description: 'Discover music and add it to your library.',
action: { label: 'Explore', onClick: () => {} },
},
};
export const CardVariant: Story = {
args: {
variant: 'card',
icon: <Inbox className="w-full h-full" />,
title: 'Drop files here',
description: 'Drag and drop files to upload.',
},
};
export const CenteredVariant: Story = {
args: {
variant: 'centered',
icon: <Music className="w-full h-full" />,
title: 'Nothing playing',
description: 'Select a track to start listening.',
},
decorators: [
(Story) => (
<div className="h-96 flex items-stretch">
<Story />
</div>
),
],
};

View file

@ -1,10 +1,87 @@
# @veza/design-system
**État v0.941** : Package sous-utilisé.
**SUMI Design System v2.0** — "L'encre et la lumière" (Ink and Light)
- Les tokens et primitives UI sont principalement définis dans `apps/web/src/index.css` et `DESIGN_TOKENS.md`.
- Les composants de l'app utilisent `apps/web/src/components/ui/` (shadcn/ui, etc.).
- Ce package fournit `dist/` (Button, Input, etc.) pour la route `/design-system` (démo visuelle).
- **Recommandation** : Conserver pour la démo ; migrer vers `apps/web` si besoin de composants partagés.
The centralized design system for the Veza platform. Provides design tokens, component type registry, and utilities.
Voir `docs/FEATURE_STATUS.md` et `apps/web/docs/DESIGN_TOKENS.md`.
## Structure
```
packages/design-system/
├── src/
│ ├── index.ts # Barrel exports
│ ├── utils.ts # cn() utility
│ ├── tokens/
│ │ ├── index.ts # All token exports
│ │ ├── colors.ts # Background, surface, text, pigment, semantic colors
│ │ ├── typography.ts # Font families, sizes, weights, line heights
│ │ ├── spacing.ts # Spacing scale, border radius, z-index, layout
│ │ └── motion.ts # Duration and easing tokens
│ └── components/
│ └── index.ts # Component type registry
└── package.json
```
## Usage
### Design Tokens (TypeScript)
```typescript
import { pigments, fontFamilies, spacing } from '@veza/design-system/tokens';
// Colors
pigments.accent.base // '#7c9dd6'
pigments.vermillion.base // '#d4634a'
// Typography
fontFamilies.heading // "'Space Grotesk', 'Inter', sans-serif"
// Spacing
spacing['4'] // '16px'
```
### Design Tokens (CSS)
The CSS custom properties are the primary token interface, defined in `apps/web/src/index.css`:
```css
color: var(--sumi-accent);
padding: var(--sumi-space-4);
font-family: var(--sumi-font-heading);
border-radius: var(--sumi-radius-md);
```
### Components
Components are implemented in `apps/web/src/components/ui/` and imported via path alias:
```typescript
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Dialog } from '@/components/ui/dialog';
```
See `apps/web/.storybook/` for Storybook documentation of all components.
## Themes
- **Dark** (default) — Ink on void
- **Light** — Washi paper aesthetic (`[data-theme="light"]`)
- **High Contrast** — WCAG AA 4.5:1+ (`[data-contrast="high"]`)
- **Compact Density** — Reduced spacing (`[data-density="compact"]`)
## Color System — The 4 Pigments
| Pigment | Hex | Usage |
|---------|-----|-------|
| **Accent** (Indigo) | `#7c9dd6` | Primary actions, links, focus |
| **Vermillion** | `#d4634a` | Errors, destructive, live |
| **Sage** | `#7a9e6c` | Success, online |
| **Gold** | `#c9a84c` | Warnings, achievements |
## References
- Design tokens source: `apps/web/src/index.css`
- Token documentation: `apps/web/docs/DESIGN_TOKENS.md`
- Storybook: `apps/web/.storybook/`
- Component source: `apps/web/src/components/ui/`

View file

@ -0,0 +1,34 @@
{
"name": "@veza/design-system",
"version": "2.0.0",
"description": "SUMI Design System — Design tokens, utilities, and component re-exports for the Veza platform",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./tokens": "./src/tokens/index.ts",
"./tokens/colors": "./src/tokens/colors.ts",
"./tokens/typography": "./src/tokens/typography.ts",
"./tokens/spacing": "./src/tokens/spacing.ts",
"./tokens/motion": "./src/tokens/motion.ts"
},
"files": [
"src/"
],
"scripts": {
"typecheck": "tsc --noEmit"
},
"dependencies": {
"clsx": "^2.0.0",
"tailwind-merge": "^3.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"devDependencies": {
"typescript": "^5.9.0"
},
"license": "UNLICENSED"
}

View file

@ -0,0 +1,116 @@
/**
* SUMI Design System v2.0 Component Registry
*
* This file documents the canonical component set of the SUMI design system.
* Components are implemented in apps/web/src/components/ui/ and imported
* from there using the @/components/ui/ path alias.
*
* To use in the web app:
* import { Button } from '@/components/ui/button';
* import { Card } from '@/components/ui/card';
*
* This registry exists for documentation and type-checking purposes.
*/
/**
* SUMI Component Categories:
*
* PRIMITIVES
* Button Primary action element (variants: default, destructive, outline, secondary, ghost, link)
* Input Text input field
* Textarea Multi-line text input
* Label Form field label
* Checkbox Binary selection
* RadioGroup Single selection from options
* Switch Toggle control
* Slider Range input
* Select Dropdown selection
*
* LAYOUT
* Card Content container with border
* Accordion Collapsible content sections
* Tabs Tabbed content panels
* Sidebar Navigation sidebar
* ScrollArea Custom scrollable container
* Table Data table
*
* FEEDBACK
* Alert Informational message
* Badge Status indicator
* Dialog Modal dialog
* Toast Temporary notification
* Tooltip Hover information
* HoverCard Rich hover popup
* Skeleton Loading placeholder
* Progress Progress indicator
* LoadingSpinner Animated spinner
*
* NAVIGATION
* DropdownMenu Contextual menu
* ContextMenu Right-click menu
* NavigationProgress Page transition indicator
*
* SPECIALIZED
* FileUpload File upload with drag & drop
* AvatarUpload Avatar image upload with cropper
* DatePicker Date selection
* VirtualizedList Virtualized scrolling for large lists
* OptimizedImage Responsive image with blur placeholder
* AnimatedNumber Animated numeric display
*/
// Component type exports for type-safety when referencing SUMI components
export type SumiComponentName =
// Primitives
| 'Button'
| 'Input'
| 'Textarea'
| 'Label'
| 'Checkbox'
| 'RadioGroup'
| 'Switch'
| 'Slider'
| 'Select'
| 'FloatingInput'
// Layout
| 'Card'
| 'Accordion'
| 'Tabs'
| 'Sidebar'
| 'ScrollArea'
| 'Table'
| 'Collapsible'
// Feedback
| 'Alert'
| 'Badge'
| 'Dialog'
| 'ConfirmationDialog'
| 'Toast'
| 'Tooltip'
| 'HoverCard'
| 'Skeleton'
| 'Progress'
| 'LoadingSpinner'
| 'LoadingState'
| 'ErrorBoundary'
| 'ErrorDisplay'
// Navigation
| 'DropdownMenu'
| 'ContextMenu'
| 'NavigationProgress'
| 'ScrollToTop'
// Specialized
| 'Avatar'
| 'AvatarUpload'
| 'FileUpload'
| 'DatePicker'
| 'ImageCropper'
| 'VirtualizedList'
| 'OptimizedImage'
| 'AnimatedNumber'
| 'DataList'
| 'FormField'
| 'FAB'
| 'FocusTrap'
| 'KeyboardShortcutsPanel'
| 'WaveformVisualizer';

View file

@ -0,0 +1,52 @@
/**
* @veza/design-system SUMI Design System v2.0
* "L'encre et la lumière" Ink and Light
*
* This package provides:
* - Design tokens (colors, typography, spacing, motion) as TypeScript objects
* - Component type registry for the SUMI component set
* - Utility functions (cn)
*
* Components are implemented in apps/web/src/components/ui/ and should be
* imported from there using the @/components/ui/ path alias.
*
* Usage:
* import { colors, typography } from '@veza/design-system/tokens';
* import { pigments } from '@veza/design-system/tokens/colors';
*/
// ═══ Design Tokens ═══
export {
colors,
backgrounds,
surfaces,
borders,
text,
pigments,
semantic,
glass,
shadows,
lightTheme,
typography,
fontFamilies,
fontSizes,
lineHeights,
letterSpacings,
fontWeights,
spacingTokens,
spacing,
radius,
zIndex,
layout,
motion,
durations,
easings,
} from './tokens';
export type { SumiColor } from './tokens';
// ═══ Component Registry ═══
export type { SumiComponentName } from './components';
// ═══ Utilities ═══
export { cn } from './utils';

View file

@ -0,0 +1,157 @@
/**
* SUMI Design System v2.0 Color Tokens
* "L'encre et la lumière" Ink and Light
*
* Source of truth: apps/web/src/index.css
* These tokens mirror the CSS custom properties for use in TypeScript.
*/
// ═══ DARK THEME (default) ═══
export const backgrounds = {
void: '#0c0c0f',
base: '#121215',
raised: '#1a1a1f',
overlay: '#222228',
hover: '#2a2a31',
active: '#32323a',
wash: '#18181d',
} as const;
export const surfaces = {
inset: '#101013',
subtle: '#1e1e24',
card: '#1a1a1f',
elevated: '#242430',
} as const;
export const borders = {
faint: 'rgba(255,255,255, 0.06)',
default: 'rgba(255,255,255, 0.10)',
strong: 'rgba(255,255,255, 0.16)',
focus: 'rgba(139,170,220, 0.50)',
accent: 'rgba(139,170,220, 0.30)',
} as const;
export const text = {
primary: '#f0ede8',
secondary: '#a8a4a0',
tertiary: '#706c68',
disabled: '#4a4844',
inverse: '#121215',
link: '#8baade',
} as const;
// ═══ PIGMENTS — The 4 accent pigments ═══
export const pigments = {
accent: {
base: '#7c9dd6',
hover: '#93afe0',
active: '#6b8dc6',
muted: 'rgba(124,157,214, 0.20)',
subtle: 'rgba(124,157,214, 0.12)',
emphasis: '#5a7fba',
},
vermillion: {
base: '#d4634a',
hover: '#de7a64',
subtle: 'rgba(212,99,74, 0.12)',
},
sage: {
base: '#7a9e6c',
hover: '#8eb280',
subtle: 'rgba(122,158,108, 0.12)',
},
gold: {
base: '#c9a84c',
hover: '#d6b860',
subtle: 'rgba(201,168,76, 0.12)',
},
} as const;
// ═══ SEMANTIC ═══
export const semantic = {
success: pigments.sage.base,
successSubtle: pigments.sage.subtle,
warning: pigments.gold.base,
warningSubtle: pigments.gold.subtle,
error: pigments.vermillion.base,
errorSubtle: pigments.vermillion.subtle,
info: pigments.accent.base,
live: '#e05a5a',
online: pigments.sage.base,
} as const;
// ═══ GLASS ═══
export const glass = {
bg: 'rgba(18,18,21, 0.80)',
border: 'rgba(255,255,255, 0.08)',
blur: '12px',
} as const;
// ═══ SHADOWS ═══
export const shadows = {
xs: '0 1px 2px rgba(0,0,0,0.30)',
sm: '0 2px 4px rgba(0,0,0,0.25), 0 1px 2px rgba(0,0,0,0.20)',
md: '0 4px 12px rgba(0,0,0,0.30), 0 2px 4px rgba(0,0,0,0.15)',
lg: '0 8px 24px rgba(0,0,0,0.35), 0 4px 8px rgba(0,0,0,0.20)',
xl: '0 16px 48px rgba(0,0,0,0.40), 0 8px 16px rgba(0,0,0,0.20)',
'2xl': '0 24px 64px rgba(0,0,0,0.50)',
glow: '0 0 0 3px rgba(124,157,214,0.25)',
glowLg: '0 0 20px rgba(124,157,214,0.15)',
} as const;
// ═══ LIGHT THEME ═══
export const lightTheme = {
backgrounds: {
void: '#f5f2ed',
base: '#faf8f5',
raised: '#ffffff',
overlay: '#f0ede8',
hover: '#e8e4df',
active: '#ddd9d3',
wash: '#f7f5f0',
},
surfaces: {
inset: '#ede9e4',
subtle: '#f5f2ed',
card: '#ffffff',
elevated: '#ffffff',
},
borders: {
faint: 'rgba(0,0,0, 0.06)',
default: 'rgba(0,0,0, 0.10)',
strong: 'rgba(0,0,0, 0.18)',
focus: 'rgba(90,127,186, 0.50)',
accent: 'rgba(90,127,186, 0.25)',
},
text: {
primary: '#1a1a1f',
secondary: '#5c5854',
tertiary: '#8a8680',
disabled: '#b8b4b0',
inverse: '#f0ede8',
link: '#5a7fba',
},
} as const;
// ═══ ALL COLORS ═══
export const colors = {
backgrounds,
surfaces,
borders,
text,
pigments,
semantic,
glass,
shadows,
lightTheme,
} as const;
export type SumiColor = typeof colors;

View file

@ -0,0 +1,21 @@
/**
* SUMI Design System v2.0 Design Tokens
*
* Centralized design tokens extracted from CSS custom properties.
* Use these for TypeScript-level access to the design system values.
*
* For CSS usage, prefer the CSS custom properties directly:
* var(--sumi-accent), var(--sumi-space-4), etc.
*
* For JS/TS usage (e.g., inline styles, canvas, SVG):
* import { colors, typography, spacing, motion } from '@veza/design-system/tokens';
*/
export { colors, backgrounds, surfaces, borders, text, pigments, semantic, glass, shadows, lightTheme } from './colors';
export type { SumiColor } from './colors';
export { typography, fontFamilies, fontSizes, lineHeights, letterSpacings, fontWeights } from './typography';
export { spacingTokens, spacing, radius, zIndex, layout } from './spacing';
export { motion, durations, easings } from './motion';

View file

@ -0,0 +1,27 @@
/**
* SUMI Design System v2.0 Motion & Animation Tokens
*
* Source of truth: apps/web/src/index.css
*/
export const durations = {
instant: '75ms',
fast: '150ms',
normal: '200ms',
slow: '300ms',
slower: '500ms',
} as const;
export const easings = {
default: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
out: 'cubic-bezier(0.33, 1, 0.68, 1)',
in: 'cubic-bezier(0.32, 0, 0.67, 0)',
inOut: 'cubic-bezier(0.65, 0, 0.35, 1)',
bounce: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
spring: 'cubic-bezier(0.175, 0.885, 0.32, 1.1)',
} as const;
export const motion = {
durations,
easings,
} as const;

View file

@ -0,0 +1,57 @@
/**
* SUMI Design System v2.0 Spacing & Layout Tokens
*
* Source of truth: apps/web/src/index.css
*/
export const spacing = {
'0.5': '2px',
'1': '4px',
'1.5': '6px',
'2': '8px',
'2.5': '10px',
'3': '12px',
'4': '16px',
'5': '20px',
'6': '24px',
'8': '32px',
'10': '40px',
'12': '48px',
'16': '64px',
'20': '80px',
} as const;
export const radius = {
xs: '2px',
sm: '4px',
md: '6px',
lg: '12px',
xl: '16px',
'2xl': '20px',
full: '9999px',
} as const;
export const zIndex = {
base: 0,
raised: 10,
dropdown: 100,
sticky: 200,
overlay: 300,
modal: 400,
popover: 500,
toast: 600,
tooltip: 700,
max: 999,
} as const;
export const layout = {
maxWidth: '1400px',
maxWidthContent: '1200px',
} as const;
export const spacingTokens = {
spacing,
radius,
zIndex,
layout,
} as const;

View file

@ -0,0 +1,58 @@
/**
* SUMI Design System v2.0 Typography Tokens
*
* Source of truth: apps/web/src/index.css
*/
export const fontFamilies = {
body: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
heading: "'Space Grotesk', 'Inter', sans-serif",
mono: "'JetBrains Mono', 'SF Mono', 'Consolas', monospace",
serif: "'Noto Serif JP', Georgia, serif",
} as const;
export const fontSizes = {
'4xl': '2.25rem', // 36px
'3xl': '1.875rem', // 30px
'2xl': '1.5rem', // 24px
xl: '1.25rem', // 20px
lg: '1.125rem', // 18px
md: '1rem', // 16px
base: '0.875rem', // 14px
sm: '0.8125rem', // 13px
xs: '0.75rem', // 12px
} as const;
export const lineHeights = {
none: '1',
tight: '1.25',
snug: '1.375',
normal: '1.5',
relaxed: '1.625',
loose: '1.75',
} as const;
export const letterSpacings = {
tighter: '-0.03em',
tight: '-0.015em',
normal: '0',
wide: '0.025em',
wider: '0.05em',
widest: '0.1em',
} as const;
export const fontWeights = {
light: 300,
regular: 400,
medium: 500,
semibold: 600,
bold: 700,
} as const;
export const typography = {
fontFamilies,
fontSizes,
lineHeights,
letterSpacings,
fontWeights,
} as const;

View file

@ -0,0 +1,20 @@
/**
* SUMI Design System Utility Functions
*
* Canonical implementation of cn() for merging Tailwind classes.
* Also available at apps/web/src/lib/utils.ts.
*/
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
/**
* Merge Tailwind CSS classes with deduplication.
* Combines clsx (conditional classes) + tailwind-merge (conflict resolution).
*
* @example
* cn('px-4 py-2', isActive && 'bg-primary', className)
*/
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}

View file

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}