@carbonid1/design-system 5.0.3 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Badge/Badge.d.ts +6 -0
- package/dist/Badge/Badge.js +23 -0
- package/{src/Badge/Badge.types.ts → dist/Badge/Badge.types.d.ts} +8 -11
- package/dist/Badge/Badge.types.js +1 -0
- package/dist/Button/Button.d.ts +7 -0
- package/dist/Button/Button.js +34 -0
- package/dist/Button/Button.types.d.ts +8 -0
- package/dist/Button/Button.types.js +1 -0
- package/dist/ContextMenu/ContextMenu.d.ts +56 -0
- package/dist/ContextMenu/ContextMenu.js +40 -0
- package/dist/Kbd/Kbd.d.ts +6 -0
- package/dist/Kbd/Kbd.js +38 -0
- package/dist/Kbd/Kbd.types.d.ts +8 -0
- package/dist/Kbd/Kbd.types.js +1 -0
- package/dist/ProgressRing/ProgressRing.consts.d.ts +4 -0
- package/dist/ProgressRing/ProgressRing.consts.js +4 -0
- package/dist/ProgressRing/ProgressRing.d.ts +10 -0
- package/dist/ProgressRing/ProgressRing.js +12 -0
- package/dist/Select/Select.d.ts +2 -0
- package/dist/Select/Select.js +85 -0
- package/dist/Select/Select.types.d.ts +26 -0
- package/dist/Select/Select.types.js +1 -0
- package/dist/Slider/Slider.d.ts +13 -0
- package/dist/Slider/Slider.js +5 -0
- package/dist/ThemeCycler/ThemeCycler.d.ts +1 -0
- package/dist/ThemeCycler/ThemeCycler.js +30 -0
- package/dist/ThemeProvider/ThemeProvider.d.ts +5 -0
- package/dist/ThemeProvider/ThemeProvider.js +4 -0
- package/dist/Toaster/Toaster.d.ts +2 -0
- package/dist/Toaster/Toaster.js +4 -0
- package/dist/Tooltip/Tooltip.d.ts +15 -0
- package/dist/Tooltip/Tooltip.js +59 -0
- package/dist/helpers/cn/cn.d.ts +2 -0
- package/dist/helpers/cn/cn.js +5 -0
- package/dist/helpers/getModKey/getModKey.d.ts +1 -0
- package/dist/helpers/getModKey/getModKey.js +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +16 -0
- package/package.json +17 -35
- package/skills/design-system/SKILL.md +17 -0
- package/skills/design-system/references/theming.md +53 -14
- package/themes/dashboard.css +22 -4
- package/themes/reader.css +51 -6
- package/src/Badge/Badge.tsx +0 -53
- package/src/Button/Button.tsx +0 -67
- package/src/Button/Button.types.ts +0 -11
- package/src/ContextMenu/ContextMenu.tsx +0 -159
- package/src/Kbd/Kbd.tsx +0 -56
- package/src/Kbd/Kbd.types.ts +0 -10
- package/src/ProgressRing/ProgressRing.consts.ts +0 -4
- package/src/ProgressRing/ProgressRing.tsx +0 -68
- package/src/Select/Select.test.tsx +0 -129
- package/src/Select/Select.tsx +0 -156
- package/src/Select/Select.types.ts +0 -30
- package/src/Slider/Slider.test.tsx +0 -29
- package/src/Slider/Slider.tsx +0 -53
- package/src/ThemeCycler/ThemeCycler.tsx +0 -37
- package/src/ThemeProvider/ThemeProvider.tsx +0 -18
- package/src/Toaster/Toaster.tsx +0 -7
- package/src/Tooltip/Tooltip.tsx +0 -107
- package/src/helpers/cn/cn.ts +0 -6
- package/src/helpers/getModKey/getModKey.test.ts +0 -23
- package/src/helpers/getModKey/getModKey.ts +0 -2
- package/src/index.ts +0 -36
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { Badge, badgeVariants } from './Badge/Badge';
|
|
2
|
+
export type { BadgeProps } from './Badge/Badge.types';
|
|
3
|
+
export { Button, buttonVariants } from './Button/Button';
|
|
4
|
+
export type { ButtonProps } from './Button/Button.types';
|
|
5
|
+
export { Kbd, kbdVariants } from './Kbd/Kbd';
|
|
6
|
+
export type { KbdProps } from './Kbd/Kbd.types';
|
|
7
|
+
export { ProgressRing } from './ProgressRing/ProgressRing';
|
|
8
|
+
export { Slider } from './Slider/Slider';
|
|
9
|
+
export { ContextMenu } from './ContextMenu/ContextMenu';
|
|
10
|
+
export { Select } from './Select/Select';
|
|
11
|
+
export type { SelectOption, SelectGroup, SelectOptionState, SelectProps, } from './Select/Select.types';
|
|
12
|
+
export { Tooltip } from './Tooltip/Tooltip';
|
|
13
|
+
export { ThemeProvider } from './ThemeProvider/ThemeProvider';
|
|
14
|
+
export { ThemeCycler } from './ThemeCycler/ThemeCycler';
|
|
15
|
+
export { Toaster } from './Toaster/Toaster';
|
|
16
|
+
export { toast } from 'sonner';
|
|
17
|
+
export { useTheme } from 'next-themes';
|
|
18
|
+
export { cn } from './helpers/cn/cn';
|
|
19
|
+
export { getModKey } from './helpers/getModKey/getModKey';
|
|
20
|
+
export { cva } from 'class-variance-authority';
|
|
21
|
+
export type { VariantProps } from 'class-variance-authority';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { Badge, badgeVariants } from './Badge/Badge';
|
|
2
|
+
export { Button, buttonVariants } from './Button/Button';
|
|
3
|
+
export { Kbd, kbdVariants } from './Kbd/Kbd';
|
|
4
|
+
export { ProgressRing } from './ProgressRing/ProgressRing';
|
|
5
|
+
export { Slider } from './Slider/Slider';
|
|
6
|
+
export { ContextMenu } from './ContextMenu/ContextMenu';
|
|
7
|
+
export { Select } from './Select/Select';
|
|
8
|
+
export { Tooltip } from './Tooltip/Tooltip';
|
|
9
|
+
export { ThemeProvider } from './ThemeProvider/ThemeProvider';
|
|
10
|
+
export { ThemeCycler } from './ThemeCycler/ThemeCycler';
|
|
11
|
+
export { Toaster } from './Toaster/Toaster';
|
|
12
|
+
export { toast } from 'sonner';
|
|
13
|
+
export { useTheme } from 'next-themes';
|
|
14
|
+
export { cn } from './helpers/cn/cn';
|
|
15
|
+
export { getModKey } from './helpers/getModKey/getModKey';
|
|
16
|
+
export { cva } from 'class-variance-authority';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbonid1/design-system",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Shared React UI primitives + design tokens (themes, postcss config)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
"**/*.css"
|
|
15
15
|
],
|
|
16
16
|
"exports": {
|
|
17
|
-
".":
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
18
21
|
"./themes/reader": "./themes/reader.css",
|
|
19
22
|
"./themes/dashboard": "./themes/dashboard.css",
|
|
20
23
|
"./postcss": "./postcss.mjs"
|
|
21
24
|
},
|
|
22
25
|
"files": [
|
|
23
|
-
"
|
|
26
|
+
"dist",
|
|
24
27
|
"themes",
|
|
25
28
|
"postcss.mjs",
|
|
26
29
|
"skills",
|
|
@@ -28,43 +31,26 @@
|
|
|
28
31
|
],
|
|
29
32
|
"dependencies": {
|
|
30
33
|
"@tailwindcss/postcss": "^4.2.0",
|
|
34
|
+
"class-variance-authority": "^0.7.1",
|
|
35
|
+
"clsx": "^2.1.1",
|
|
36
|
+
"lucide-react": "^1.8.0",
|
|
31
37
|
"next-themes": "^0.4.6",
|
|
38
|
+
"react-hotkeys-hook": "^5.2.4",
|
|
32
39
|
"sonner": "^2.0.7",
|
|
40
|
+
"tailwind-merge": "^3.5.0",
|
|
33
41
|
"tailwindcss": "^4.2.0"
|
|
34
42
|
},
|
|
35
43
|
"peerDependencies": {
|
|
36
|
-
"@base-ui/react": "^1.
|
|
37
|
-
"class-variance-authority": "^0.7.0",
|
|
38
|
-
"clsx": "^2.0.0",
|
|
39
|
-
"lucide-react": ">=0.400.0",
|
|
44
|
+
"@base-ui/react": "^1.4.0",
|
|
40
45
|
"postcss": "^8.0.0",
|
|
41
46
|
"react": "^19.0.0",
|
|
42
|
-
"react-dom": "^19.0.0"
|
|
43
|
-
"react-hotkeys-hook": "^5.0.0",
|
|
44
|
-
"tailwind-merge": "^3.0.0"
|
|
45
|
-
},
|
|
46
|
-
"peerDependenciesMeta": {
|
|
47
|
-
"@base-ui/react": {
|
|
48
|
-
"optional": true
|
|
49
|
-
},
|
|
50
|
-
"class-variance-authority": {
|
|
51
|
-
"optional": true
|
|
52
|
-
},
|
|
53
|
-
"clsx": {
|
|
54
|
-
"optional": true
|
|
55
|
-
},
|
|
56
|
-
"lucide-react": {
|
|
57
|
-
"optional": true
|
|
58
|
-
},
|
|
59
|
-
"tailwind-merge": {
|
|
60
|
-
"optional": true
|
|
61
|
-
}
|
|
47
|
+
"react-dom": "^19.0.0"
|
|
62
48
|
},
|
|
63
49
|
"publishConfig": {
|
|
64
50
|
"access": "public"
|
|
65
51
|
},
|
|
66
52
|
"devDependencies": {
|
|
67
|
-
"@base-ui/react": "^1.4.
|
|
53
|
+
"@base-ui/react": "^1.4.1",
|
|
68
54
|
"@storybook/addon-docs": "^10.3.5",
|
|
69
55
|
"@storybook/addon-themes": "^10.3.5",
|
|
70
56
|
"@storybook/addon-vitest": "^10.3.5",
|
|
@@ -76,24 +62,20 @@
|
|
|
76
62
|
"@types/react-dom": "^19.2.3",
|
|
77
63
|
"@vitejs/plugin-react": "^6.0.1",
|
|
78
64
|
"@vitest/browser-playwright": "^4.1.4",
|
|
79
|
-
"class-variance-authority": "^0.7.1",
|
|
80
|
-
"clsx": "^2.1.1",
|
|
81
65
|
"jsdom": "^29.0.2",
|
|
82
|
-
"lucide-react": "^1.8.0",
|
|
83
66
|
"next-themes": "^0.4.6",
|
|
84
67
|
"playwright": "^1.59.1",
|
|
85
|
-
"react": "^19.2.
|
|
86
|
-
"react-dom": "^19.2.
|
|
87
|
-
"react-hotkeys-hook": "^5.2.4",
|
|
68
|
+
"react": "^19.2.6",
|
|
69
|
+
"react-dom": "^19.2.6",
|
|
88
70
|
"sonner": "^2.0.7",
|
|
89
71
|
"storybook": "^10.3.5",
|
|
90
|
-
"tailwind-merge": "^3.5.0",
|
|
91
72
|
"tailwindcss": "^4.2.2",
|
|
92
73
|
"vite": "^8.0.8",
|
|
93
74
|
"vitest": "^4.1.4",
|
|
94
75
|
"@carbonid1/tsconfig": "0.3.0"
|
|
95
76
|
},
|
|
96
77
|
"scripts": {
|
|
78
|
+
"build": "tsc",
|
|
97
79
|
"storybook": "storybook dev -p 6006",
|
|
98
80
|
"build-storybook": "storybook build",
|
|
99
81
|
"test": "vitest --run",
|
|
@@ -39,6 +39,23 @@ If you're editing `@carbonid1/design-system` itself (adding a primitive, changin
|
|
|
39
39
|
- **Touch targets** — icon buttons `p-1.5` minimum, icons `w-4 h-4` minimum
|
|
40
40
|
- **Avatars** — only with real images, no letter/initial placeholders
|
|
41
41
|
|
|
42
|
+
## Elevation
|
|
43
|
+
|
|
44
|
+
Four-tier contract — every themed surface picks one of these tokens. Layering is recessed: cards are sheets on the desk, controls are carved INTO sheets, popovers lift OFF the desk.
|
|
45
|
+
|
|
46
|
+
| Token | Role | Pair with |
|
|
47
|
+
| ------------------ | ---------------------------------------- | ---------------------- |
|
|
48
|
+
| `bg-background` | Page (the desk) | — |
|
|
49
|
+
| `bg-card` | Raised paper on the desk — peak surface | `shadow-card` |
|
|
50
|
+
| `bg-surface-inset` | Inset block carved into a card | `inset-shadow-surface` |
|
|
51
|
+
| `bg-popover` | Floating layer above everything | `shadow-popover` |
|
|
52
|
+
|
|
53
|
+
`bg-surface-inset` covers form inputs, preview boxes, quote blocks, and language tabs. `bg-popover` covers drawers, menus, and command palettes.
|
|
54
|
+
|
|
55
|
+
**`bg-muted`, `bg-accent`, `bg-secondary` are RESERVED for interaction states** — hover backgrounds, skeleton placeholders, badge fills, soft tints. They are never the background of a card or an inset block. Mixing them in breaks the layering contract; see [references/theming.md](references/theming.md) for the deeper rationale.
|
|
56
|
+
|
|
57
|
+
**Empty placeholders** — when a slot is meant to be filled (`AddBookCard`, file drop zone, audio preview slot before recording), render a transparent or background-matched surface with `border-dashed`. The empty state reads as a missing piece on its parent surface — never as a recessed input or a card. Once filled, swap to a solid border and the surface adopts its real elevation.
|
|
58
|
+
|
|
42
59
|
## Destructive Actions
|
|
43
60
|
|
|
44
61
|
Two tiers. Pick by how hard the action is to redo, not how scary it feels.
|
|
@@ -31,20 +31,33 @@ Tokens are defined in `globals.css` (`:root` + `.dark`), bridged in `@theme inli
|
|
|
31
31
|
|
|
32
32
|
### Base tokens (from shadcn naming)
|
|
33
33
|
|
|
34
|
-
| Token
|
|
35
|
-
|
|
|
36
|
-
| `--background`
|
|
37
|
-
| `--foreground`
|
|
38
|
-
| `--card`
|
|
39
|
-
| `--popover`
|
|
40
|
-
| `--
|
|
41
|
-
| `--
|
|
42
|
-
| `--
|
|
43
|
-
| `--
|
|
44
|
-
| `--
|
|
45
|
-
| `--
|
|
46
|
-
| `--
|
|
47
|
-
| `--
|
|
34
|
+
| Token | Role | Sub-variants |
|
|
35
|
+
| ------------------ | --------------------------------------------------------------- | ------------ |
|
|
36
|
+
| `--background` | Page background (the desk) | — |
|
|
37
|
+
| `--foreground` | Default text | — |
|
|
38
|
+
| `--card` | Raised card surfaces | foreground |
|
|
39
|
+
| `--popover` | Floating popover/drawer/menu surfaces | foreground |
|
|
40
|
+
| `--surface-inset` | Inset blocks carved into a card (inputs, preview boxes) | — |
|
|
41
|
+
| `--primary` | Interactive color (buttons, links, selections, focus, progress) | foreground |
|
|
42
|
+
| `--secondary` | Supporting surfaces | foreground |
|
|
43
|
+
| `--muted` | De-emphasized surfaces and text | foreground |
|
|
44
|
+
| `--accent` | Hover/expanded tints | foreground |
|
|
45
|
+
| `--destructive` | Errors, danger | — |
|
|
46
|
+
| `--border` | Default borders | — |
|
|
47
|
+
| `--input` | Input borders | — |
|
|
48
|
+
| `--ring` | Focus rings | — |
|
|
49
|
+
|
|
50
|
+
### Elevation shadows
|
|
51
|
+
|
|
52
|
+
Each elevated surface pairs with a tuned shadow. Tailwind utilities: `shadow-card`, `inset-shadow-surface`, `shadow-popover`.
|
|
53
|
+
|
|
54
|
+
| Token | Pairs with | Notes |
|
|
55
|
+
| ------------------------ | ------------------ | ---------------------------------------------------- |
|
|
56
|
+
| `--card-shadow` | `bg-card` | Subtle drop, neutral or hue-tinted per theme |
|
|
57
|
+
| `--surface-inset-shadow` | `bg-surface-inset` | Hairline inset top in light, `none` in dark |
|
|
58
|
+
| `--popover-shadow` | `bg-popover` | Stronger drop with larger geometry in dark |
|
|
59
|
+
|
|
60
|
+
Why dark mode skips the inset shadow: top-edge highlights fake a "light source from above" convention that reads as a sharp border artifact on dark surfaces, not as a soft recess. Tonal contrast (a wider inset-vs-card lightness delta) carries the elevation instead.
|
|
48
61
|
|
|
49
62
|
### Custom tokens
|
|
50
63
|
|
|
@@ -84,6 +97,32 @@ Tokens are defined in `globals.css` (`:root` + `.dark`), bridged in `@theme inli
|
|
|
84
97
|
<span className="bg-my-token/10"> // opacity modifier for tints
|
|
85
98
|
```
|
|
86
99
|
|
|
100
|
+
## Surfaces & Elevation
|
|
101
|
+
|
|
102
|
+
Four-tier contract that every themed surface picks from. Layering is *recessed* (cards are sheets on the desk; inputs are carved INTO sheets; popovers lift OFF the desk) — the inverse of Material's monotonic stack.
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
bg-background page (the desk)
|
|
106
|
+
bg-card raised paper on the desk → shadow-card
|
|
107
|
+
bg-surface-inset inset INTO a card → inset-shadow-surface (light only)
|
|
108
|
+
bg-popover floats above everything → shadow-popover
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**`bg-muted`, `bg-accent`, `bg-secondary` are RESERVED for interaction states** — hover backgrounds, skeleton placeholders, badge fills, soft tints. They are NEVER the background of a card or an inset block. The four tokens above are the only source of truth for surface elevation; mixing the interaction-state tokens in breaks the layering contract.
|
|
112
|
+
|
|
113
|
+
### Empty-slot convention
|
|
114
|
+
|
|
115
|
+
When a region is meant to be filled (`AddBookCard`, audio drop zone, preview slot before recording), render it as a *background-matched or transparent* surface with `border-dashed` and a foreground border token (`border-border` or similar). The slot reads as a missing piece on its parent surface — never as a recessed input or a card. Once filled, the dashed border switches to solid and the surface adopts its real elevation.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// Empty slot on a card surface
|
|
119
|
+
<div className="border-border bg-card rounded-lg border-2 border-dashed p-4">
|
|
120
|
+
<PlusIcon /> Add a book
|
|
121
|
+
</div>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Don't use `bg-surface-inset` for empty placeholders — that signals "a control lives here", not "a slot is missing".
|
|
125
|
+
|
|
87
126
|
## Notes
|
|
88
127
|
|
|
89
128
|
- **`::selection`** uses solid oklch values, not opacity modifiers — semi-transparent backgrounds create visible seams at line boundaries.
|
package/themes/dashboard.css
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/* @carbonid1/tailwind-config/dashboard
|
|
2
2
|
*
|
|
3
|
-
* Dashboard theme
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Dashboard theme — for data-dense UIs (CoI Calculator, admin panels,
|
|
4
|
+
* production-chain visualizers). Visual register: neutral-cool surfaces
|
|
5
|
+
* with subtle elevation (high-information UIs benefit from low-noise
|
|
6
|
+
* chrome that lets data take focus).
|
|
7
|
+
*
|
|
8
|
+
* Same elevation contract as reader.css — see that file for the full table.
|
|
9
|
+
* Surfaces here are neutral (chroma 0 in light, ~hue 260 in dark) instead
|
|
10
|
+
* of warm paper, but the layering rules (background < card; surface-inset
|
|
11
|
+
* recessed into card; popover floats with shadow) are identical.
|
|
7
12
|
*
|
|
8
13
|
* Consumer responsibilities:
|
|
9
14
|
* - Optionally import tw-animate-css for animate utilities.
|
|
@@ -25,6 +30,10 @@
|
|
|
25
30
|
--color-card-foreground: var(--card-foreground);
|
|
26
31
|
--color-popover: var(--popover);
|
|
27
32
|
--color-popover-foreground: var(--popover-foreground);
|
|
33
|
+
--color-surface-inset: var(--surface-inset);
|
|
34
|
+
--shadow-card: var(--card-shadow);
|
|
35
|
+
--shadow-popover: var(--popover-shadow);
|
|
36
|
+
--inset-shadow-surface: var(--surface-inset-shadow);
|
|
28
37
|
--color-primary: var(--primary);
|
|
29
38
|
--color-primary-foreground: var(--primary-foreground);
|
|
30
39
|
--color-primary-muted: var(--primary-muted);
|
|
@@ -63,6 +72,11 @@
|
|
|
63
72
|
--card-foreground: oklch(0.145 0 0);
|
|
64
73
|
--popover: oklch(1 0 0);
|
|
65
74
|
--popover-foreground: oklch(0.145 0 0);
|
|
75
|
+
--surface-inset: oklch(0.965 0 0);
|
|
76
|
+
--card-shadow: 0 1px 2px 0 oklch(0.2 0 0 / 0.06);
|
|
77
|
+
--popover-shadow:
|
|
78
|
+
0 8px 24px -4px oklch(0.2 0 0 / 0.1), 0 4px 8px -2px oklch(0.2 0 0 / 0.06);
|
|
79
|
+
--surface-inset-shadow: inset 0 1px 1px 0 oklch(0.2 0 0 / 0.04);
|
|
66
80
|
--primary: oklch(0.546 0.245 262.881);
|
|
67
81
|
--primary-foreground: oklch(1 0 0);
|
|
68
82
|
--primary-muted: oklch(0.97 0.014 254);
|
|
@@ -96,6 +110,10 @@
|
|
|
96
110
|
--card-foreground: oklch(0.967 0.003 265);
|
|
97
111
|
--popover: oklch(0.278 0.03 257);
|
|
98
112
|
--popover-foreground: oklch(0.967 0.003 265);
|
|
113
|
+
--surface-inset: oklch(0.225 0.028 261);
|
|
114
|
+
--card-shadow: 0 1px 2px 0 oklch(0 0 0 / 0.2);
|
|
115
|
+
--popover-shadow: 0 12px 32px -4px oklch(0 0 0 / 0.4), 0 4px 8px -2px oklch(0 0 0 / 0.2);
|
|
116
|
+
--surface-inset-shadow: none;
|
|
99
117
|
--primary: oklch(0.78 0.13 256);
|
|
100
118
|
--primary-foreground: oklch(1 0 0);
|
|
101
119
|
--primary-muted: oklch(0.282 0.091 267 / 0.2);
|
package/themes/reader.css
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
/* @carbonid1/tailwind-config/reader
|
|
2
2
|
*
|
|
3
|
-
* Reader theme
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Reader theme — for reading-app consumers (InkVoice, future longform/journal
|
|
4
|
+
* apps). Visual register: warm paper layered on a desk. Light mode is a
|
|
5
|
+
* hue-75 warm-paper palette with tonal layering and recessed inputs; dark
|
|
6
|
+
* mode is a cool ink palette (~hue 260) with the same elevation hierarchy.
|
|
7
|
+
*
|
|
8
|
+
* --- Elevation contract (use this to pick the right surface token) --------
|
|
9
|
+
*
|
|
10
|
+
* Token Role Light Dark
|
|
11
|
+
* ───────────────── ────────────────────────────────────── ───────── ─────────
|
|
12
|
+
* bg-background page (the desk) warm off cool dark
|
|
13
|
+
* bg-card raised paper on the desk brightest peak
|
|
14
|
+
* bg-surface-inset inset block carved INTO a card darker darker than card
|
|
15
|
+
* (form inputs, preview boxes,
|
|
16
|
+
* quote blocks, language tabs)
|
|
17
|
+
* bg-popover floating layer above everything = card = card
|
|
18
|
+
* (drawers, menus, command palette) + shadow + shadow
|
|
19
|
+
*
|
|
20
|
+
* --- Shadow contract (paired with the surface above) ----------------------
|
|
21
|
+
*
|
|
22
|
+
* shadow-card on bg-card subtle drop
|
|
23
|
+
* inset-shadow-surface on bg-surface-inset hairline inset top (light only)
|
|
24
|
+
* shadow-popover on bg-popover strong drop
|
|
25
|
+
*
|
|
26
|
+
* Usage rules (which tokens are surfaces, empty-slot convention, etc.) live
|
|
27
|
+
* in the design-system skill — see SKILL.md / references/theming.md.
|
|
28
|
+
*
|
|
29
|
+
* TODO: dark mode currently mirrors the existing cool palette. A dedicated
|
|
30
|
+
* warm-dark "ink on cream" pass is deferred — separate visual decision.
|
|
6
31
|
*
|
|
7
32
|
* Consumer responsibilities:
|
|
8
33
|
* - Import tw-animate-css and shadcn base layer separately if needed.
|
|
@@ -27,6 +52,10 @@
|
|
|
27
52
|
--color-card-foreground: var(--card-foreground);
|
|
28
53
|
--color-popover: var(--popover);
|
|
29
54
|
--color-popover-foreground: var(--popover-foreground);
|
|
55
|
+
--color-surface-inset: var(--surface-inset);
|
|
56
|
+
--shadow-card: var(--card-shadow);
|
|
57
|
+
--shadow-popover: var(--popover-shadow);
|
|
58
|
+
--inset-shadow-surface: var(--surface-inset-shadow);
|
|
30
59
|
--color-primary: var(--primary);
|
|
31
60
|
--color-primary-foreground: var(--primary-foreground);
|
|
32
61
|
--color-primary-muted: var(--primary-muted);
|
|
@@ -59,12 +88,19 @@
|
|
|
59
88
|
|
|
60
89
|
/* Light mode tokens */
|
|
61
90
|
:root {
|
|
62
|
-
--background: oklch(
|
|
91
|
+
--background: oklch(0.985 0.005 75);
|
|
63
92
|
--foreground: oklch(0.145 0 0);
|
|
64
|
-
--card: oklch(
|
|
93
|
+
--card: oklch(0.995 0.003 75);
|
|
65
94
|
--card-foreground: oklch(0.145 0 0);
|
|
66
|
-
--popover: oklch(
|
|
95
|
+
--popover: oklch(0.995 0.003 75);
|
|
67
96
|
--popover-foreground: oklch(0.145 0 0);
|
|
97
|
+
--surface-inset: oklch(0.962 0.007 75);
|
|
98
|
+
/* Shadows tinted with the warm hue so they read as ambient occlusion on
|
|
99
|
+
* paper, not pure black smudges. */
|
|
100
|
+
--card-shadow: 0 1px 2px 0 oklch(0.2 0.02 75 / 0.06);
|
|
101
|
+
--popover-shadow:
|
|
102
|
+
0 8px 24px -4px oklch(0.2 0.02 75 / 0.1), 0 4px 8px -2px oklch(0.2 0.02 75 / 0.06);
|
|
103
|
+
--surface-inset-shadow: inset 0 1px 1px 0 oklch(0.2 0.02 75 / 0.04);
|
|
68
104
|
--primary: oklch(0.546 0.245 262.881);
|
|
69
105
|
--primary-foreground: oklch(1 0 0);
|
|
70
106
|
--primary-muted: oklch(0.97 0.014 254);
|
|
@@ -98,6 +134,15 @@
|
|
|
98
134
|
--card-foreground: oklch(0.967 0.003 265);
|
|
99
135
|
--popover: oklch(0.278 0.03 257);
|
|
100
136
|
--popover-foreground: oklch(0.967 0.003 265);
|
|
137
|
+
/* Inset/card delta is wider than light mode — lightness differences read
|
|
138
|
+
* as weaker in the dark range, so the recess needs more room to land. */
|
|
139
|
+
--surface-inset: oklch(0.225 0.028 261);
|
|
140
|
+
/* Inset shadow disabled: top-edge highlights fake a light-source-from-above
|
|
141
|
+
* convention that reads as a sharp border artifact on dark surfaces rather
|
|
142
|
+
* than a soft recess. The tonal delta above carries the elevation instead. */
|
|
143
|
+
--card-shadow: 0 1px 2px 0 oklch(0 0 0 / 0.2);
|
|
144
|
+
--popover-shadow: 0 12px 32px -4px oklch(0 0 0 / 0.4), 0 4px 8px -2px oklch(0 0 0 / 0.2);
|
|
145
|
+
--surface-inset-shadow: none;
|
|
101
146
|
--primary: oklch(0.78 0.13 256);
|
|
102
147
|
--primary-foreground: oklch(1 0 0);
|
|
103
148
|
--primary-muted: oklch(0.282 0.091 267 / 0.2);
|
package/src/Badge/Badge.tsx
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { cn } from '../helpers/cn/cn'
|
|
4
|
-
import { cva } from 'class-variance-authority'
|
|
5
|
-
import { X } from 'lucide-react'
|
|
6
|
-
import type { BadgeProps } from './Badge.types'
|
|
7
|
-
|
|
8
|
-
const badgeVariants = cva(
|
|
9
|
-
'inline-flex max-w-full min-w-0 items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium',
|
|
10
|
-
{
|
|
11
|
-
variants: {
|
|
12
|
-
variant: {
|
|
13
|
-
default: 'bg-muted text-muted-foreground',
|
|
14
|
-
primary: 'bg-primary-border/30 text-primary',
|
|
15
|
-
success: 'bg-success/15 text-success-foreground',
|
|
16
|
-
attention: 'bg-attention-muted text-attention-foreground',
|
|
17
|
-
destructive: 'bg-destructive/15 text-destructive',
|
|
18
|
-
highlight: 'bg-highlight-muted text-highlight-foreground',
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
defaultVariants: { variant: 'default' },
|
|
22
|
-
},
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
const Badge = ({
|
|
26
|
-
className,
|
|
27
|
-
variant,
|
|
28
|
-
onRemove,
|
|
29
|
-
removeLabel,
|
|
30
|
-
children,
|
|
31
|
-
...props
|
|
32
|
-
}: BadgeProps) => {
|
|
33
|
-
const resolvedRemoveLabel =
|
|
34
|
-
removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove')
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<span className={cn(badgeVariants({ variant }), className)} {...props}>
|
|
38
|
-
{onRemove ? <span className="truncate">{children}</span> : children}
|
|
39
|
-
{onRemove && (
|
|
40
|
-
<button
|
|
41
|
-
type="button"
|
|
42
|
-
aria-label={resolvedRemoveLabel}
|
|
43
|
-
onClick={onRemove}
|
|
44
|
-
className="-me-0.5 inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-full opacity-70 hover:opacity-100 focus-visible:ring-2 focus-visible:ring-current focus-visible:ring-offset-1 focus-visible:ring-offset-transparent focus-visible:outline-none"
|
|
45
|
-
>
|
|
46
|
-
<X className="size-3" aria-hidden="true" />
|
|
47
|
-
</button>
|
|
48
|
-
)}
|
|
49
|
-
</span>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { Badge, badgeVariants }
|
package/src/Button/Button.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { cn } from '../helpers/cn/cn'
|
|
4
|
-
import { Button as ButtonPrimitive } from '@base-ui/react/button'
|
|
5
|
-
import { cva } from 'class-variance-authority'
|
|
6
|
-
import { Loader2 } from 'lucide-react'
|
|
7
|
-
import type { ButtonProps } from './Button.types'
|
|
8
|
-
|
|
9
|
-
const buttonVariants = cva(
|
|
10
|
-
"group/button inline-flex shrink-0 items-center justify-center gap-2 rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-hidden select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
11
|
-
{
|
|
12
|
-
variants: {
|
|
13
|
-
variant: {
|
|
14
|
-
ghost: 'hover:bg-accent aria-expanded:bg-accent dark:hover:bg-accent/50',
|
|
15
|
-
primary: 'bg-primary text-primary-foreground font-medium hover:bg-primary/90',
|
|
16
|
-
outline:
|
|
17
|
-
'border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
|
|
18
|
-
destructive:
|
|
19
|
-
'bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40',
|
|
20
|
-
attention: 'text-attention-foreground hover:bg-attention-muted',
|
|
21
|
-
subtle: 'text-muted-foreground hover:text-primary',
|
|
22
|
-
danger: 'text-muted-foreground hover:text-destructive',
|
|
23
|
-
link: 'text-primary underline-offset-4 hover:underline',
|
|
24
|
-
},
|
|
25
|
-
size: {
|
|
26
|
-
small: 'h-7 gap-1 px-2.5 text-[0.8rem]',
|
|
27
|
-
default: 'h-8 gap-1.5 px-2.5',
|
|
28
|
-
large: 'h-9 gap-1.5 px-3',
|
|
29
|
-
icon: 'size-8 rounded-full',
|
|
30
|
-
smallIcon: 'size-6 rounded-full',
|
|
31
|
-
largeIcon: 'size-12 rounded-full',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
defaultVariants: {
|
|
35
|
-
variant: 'ghost',
|
|
36
|
-
size: 'default',
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
const Button = ({
|
|
42
|
-
className,
|
|
43
|
-
variant,
|
|
44
|
-
size,
|
|
45
|
-
fullWidth,
|
|
46
|
-
loading,
|
|
47
|
-
disabled,
|
|
48
|
-
children,
|
|
49
|
-
...props
|
|
50
|
-
}: ButtonProps) => (
|
|
51
|
-
<ButtonPrimitive
|
|
52
|
-
data-slot="button"
|
|
53
|
-
className={cn(
|
|
54
|
-
buttonVariants({ variant, size }),
|
|
55
|
-
fullWidth && 'w-full justify-start',
|
|
56
|
-
className,
|
|
57
|
-
)}
|
|
58
|
-
disabled={disabled ?? loading}
|
|
59
|
-
aria-busy={loading ?? undefined}
|
|
60
|
-
{...props}
|
|
61
|
-
>
|
|
62
|
-
{loading && <Loader2 className="size-4 animate-spin" aria-hidden="true" />}
|
|
63
|
-
{children}
|
|
64
|
-
</ButtonPrimitive>
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
export { Button, buttonVariants }
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Button as ButtonPrimitive } from '@base-ui/react/button'
|
|
2
|
-
import type { VariantProps } from 'class-variance-authority'
|
|
3
|
-
import type { buttonVariants } from './Button'
|
|
4
|
-
|
|
5
|
-
type ButtonProps = ButtonPrimitive.Props &
|
|
6
|
-
VariantProps<typeof buttonVariants> & {
|
|
7
|
-
fullWidth?: boolean
|
|
8
|
-
loading?: boolean
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type { ButtonProps }
|