@carbonid1/design-system 5.0.2 → 5.1.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/README.md +3 -0
- package/package.json +1 -1
- package/skills/design-system/SKILL.md +19 -2
- package/skills/design-system/references/theming.md +53 -14
- package/src/Toaster/Toaster.tsx +1 -1
- package/themes/dashboard.css +22 -4
- package/themes/reader.css +51 -6
package/README.md
CHANGED
|
@@ -66,13 +66,16 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
66
66
|
|
|
67
67
|
| Component | Use for |
|
|
68
68
|
| --- | --- |
|
|
69
|
+
| `Badge` | Compact status/label chip with variants |
|
|
69
70
|
| `Button` | Standard button primitive with variants (`ghost`, `primary`, `outline`, `destructive`, `attention`, `subtle`, `danger`, `link`) |
|
|
70
71
|
| `Kbd` | Keyboard key display (`⌘K`, `⇧B`) |
|
|
71
72
|
| `ProgressRing` | Circular progress indicator |
|
|
72
73
|
| `Slider` | Range slider |
|
|
74
|
+
| `ContextMenu` | Right-click / long-press menu (compound API) |
|
|
73
75
|
| `Select` | Custom dropdown — never use native `<select>` |
|
|
74
76
|
| `Tooltip` | Hover/focus tooltip (wraps a single child) |
|
|
75
77
|
| `Toaster` | Toast notification root (renders once in layout) |
|
|
78
|
+
| `toast` | Imperative toast trigger (re-export from `sonner`) |
|
|
76
79
|
| `ThemeProvider` | Wraps `next-themes` — provides dark/light mode |
|
|
77
80
|
| `ThemeCycler` | `shift+t` hotkey cycles `system → light → dark` with toast |
|
|
78
81
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: design-system
|
|
3
|
-
description: 'Shared UI components and design patterns for carbonid1 apps built on @carbonid1/design-system. ALWAYS use when building UI, creating components, adding dropdowns, selects, tooltips, icons, toasts, context menus, or any interactive element. Use when choosing between native HTML elements and custom components. Also
|
|
3
|
+
description: 'Shared UI components and design patterns for carbonid1 apps built on @carbonid1/design-system. ALWAYS use when building UI, creating components, adding dropdowns, selects, tooltips, icons, toasts, context menus, badges, or any interactive element. Use when choosing between native HTML elements and custom components. Also covers visual conventions for layout stability when wiring async UI (skeletons are consumer-owned, but the rules for them live here). Triggers on: dropdown, select, tooltip, tag, badge, icon, component, UI, design system, shared component, layout shift, context menu, right-click menu, button, theme, palette.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Design System
|
|
@@ -9,7 +9,7 @@ Primitives live in `@carbonid1/design-system`. **Check the package's exports and
|
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
11
|
import {
|
|
12
|
-
Button, Kbd, ProgressRing, Slider,
|
|
12
|
+
Badge, Button, Kbd, ProgressRing, Slider,
|
|
13
13
|
ContextMenu, Select, Tooltip,
|
|
14
14
|
Toaster, toast,
|
|
15
15
|
ThemeProvider, ThemeCycler, useTheme,
|
|
@@ -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/src/Toaster/Toaster.tsx
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
import { Toaster as Sonner, type ToasterProps } from 'sonner'
|
|
4
4
|
|
|
5
5
|
export const Toaster = (props: ToasterProps) => (
|
|
6
|
-
<Sonner theme="system"
|
|
6
|
+
<Sonner theme="system" position="bottom-right" {...props} />
|
|
7
7
|
)
|
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);
|