Merge branch 'feat/v0.13.2-consolidation-design-system'
This commit is contained in:
commit
f27af786ac
16 changed files with 995 additions and 14 deletions
|
|
@ -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 |
|
||||
|
|
|
|||
66
apps/web/src/components/ui/ButtonLoading.stories.tsx
Normal file
66
apps/web/src/components/ui/ButtonLoading.stories.tsx
Normal 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,
|
||||
},
|
||||
};
|
||||
37
apps/web/src/components/ui/ContentFadeIn.stories.tsx
Normal file
37
apps/web/src/components/ui/ContentFadeIn.stories.tsx
Normal 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',
|
||||
},
|
||||
};
|
||||
143
apps/web/src/components/ui/DesignTokens.stories.tsx
Normal file
143
apps/web/src/components/ui/DesignTokens.stories.tsx
Normal 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 />,
|
||||
};
|
||||
99
apps/web/src/components/ui/EmptyState.stories.tsx
Normal file
99
apps/web/src/components/ui/EmptyState.stories.tsx
Normal 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>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
|
@ -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/`
|
||||
|
|
|
|||
34
packages/design-system/package.json
Normal file
34
packages/design-system/package.json
Normal 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"
|
||||
}
|
||||
116
packages/design-system/src/components/index.ts
Normal file
116
packages/design-system/src/components/index.ts
Normal 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';
|
||||
52
packages/design-system/src/index.ts
Normal file
52
packages/design-system/src/index.ts
Normal 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';
|
||||
157
packages/design-system/src/tokens/colors.ts
Normal file
157
packages/design-system/src/tokens/colors.ts
Normal 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;
|
||||
21
packages/design-system/src/tokens/index.ts
Normal file
21
packages/design-system/src/tokens/index.ts
Normal 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';
|
||||
27
packages/design-system/src/tokens/motion.ts
Normal file
27
packages/design-system/src/tokens/motion.ts
Normal 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;
|
||||
57
packages/design-system/src/tokens/spacing.ts
Normal file
57
packages/design-system/src/tokens/spacing.ts
Normal 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;
|
||||
58
packages/design-system/src/tokens/typography.ts
Normal file
58
packages/design-system/src/tokens/typography.ts
Normal 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;
|
||||
20
packages/design-system/src/utils.ts
Normal file
20
packages/design-system/src/utils.ts
Normal 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));
|
||||
}
|
||||
16
packages/design-system/tsconfig.json
Normal file
16
packages/design-system/tsconfig.json
Normal 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"]
|
||||
}
|
||||
Loading…
Reference in a new issue