atlas-react-kit 0.5.0 → 0.6.1
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/COMPONENTS.md +63 -52
- package/DESIGN_RULES.md +32 -15
- package/README.md +6 -3
- package/dist/accordion.js +2 -1
- package/dist/badge.js +7 -2
- package/dist/button.d.ts +2 -2
- package/dist/button.js +14 -4
- package/dist/calendar.js +1 -1
- package/dist/checkbox.js +2 -1
- package/dist/color-legend.d.ts +22 -0
- package/dist/color-legend.js +11 -0
- package/dist/combobox.js +2 -1
- package/dist/dropdown-menu.js +2 -1
- package/dist/form/markdown-field.d.ts +17 -0
- package/dist/form/markdown-field.js +27 -0
- package/dist/info-tip.d.ts +27 -0
- package/dist/info-tip.js +64 -0
- package/dist/input.js +2 -1
- package/dist/internal/form-control.d.ts +1 -1
- package/dist/internal/form-control.js +1 -1
- package/dist/internal/styles.d.ts +50 -0
- package/dist/internal/styles.js +50 -0
- package/dist/internal/tabs-styles.js +6 -3
- package/dist/markdown.d.ts +18 -0
- package/dist/markdown.js +62 -0
- package/dist/meter.d.ts +22 -0
- package/dist/meter.js +17 -0
- package/dist/pagination.js +2 -1
- package/dist/radio-group.js +2 -1
- package/dist/search-picker.d.ts +30 -0
- package/dist/search-picker.js +101 -0
- package/dist/select.js +3 -2
- package/dist/slider.d.ts +20 -0
- package/dist/slider.js +21 -0
- package/dist/stat-tile.d.ts +21 -0
- package/dist/stat-tile.js +17 -0
- package/dist/switch.js +2 -1
- package/dist/table.js +3 -2
- package/dist/textarea.js +2 -1
- package/dist/toggle-group.js +2 -2
- package/dist/toggle.d.ts +7 -1
- package/dist/toggle.js +17 -3
- package/package.json +37 -1
- package/skills/atlas-react-kit/SKILL.md +41 -32
- package/theme.css +23 -9
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "./utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Uppercase muted label used on tiles and thin section chrome. Apps that
|
|
5
|
+
* only needed a heading class can import this instead of a wrapper.
|
|
6
|
+
*/
|
|
7
|
+
export const sectionLabel = "text-xs font-semibold tracking-widest text-muted-foreground uppercase";
|
|
8
|
+
/** Row of tiles: stacked on phones, divided columns from `sm`. */
|
|
9
|
+
export const statTileGrid = "grid ring-1 ring-foreground/5 sm:divide-x";
|
|
10
|
+
/**
|
|
11
|
+
* Label, big tabular figure, optional extra (a meter), muted sub line.
|
|
12
|
+
* Domain copy and status colors stay in the app.
|
|
13
|
+
*/
|
|
14
|
+
function StatTile({ label, value, sub, children, className }) {
|
|
15
|
+
return (_jsxs("div", { "data-slot": "stat-tile", className: cn("flex flex-col gap-1 px-3 py-2", className), children: [_jsx("span", { className: sectionLabel, children: label }), _jsx("span", { className: "text-lg font-semibold tracking-tight tabular-nums", children: value }), children, sub ? (_jsx("span", { className: "text-xs leading-snug text-muted-foreground", children: sub })) : null] }));
|
|
16
|
+
}
|
|
17
|
+
export { StatTile };
|
package/dist/switch.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Switch as SwitchPrimitive } from "radix-ui";
|
|
5
|
+
import { disabledState, focusRing, invalidState } from "./internal/styles.js";
|
|
5
6
|
import { cn } from "./utils.js";
|
|
6
7
|
/**
|
|
7
8
|
* On/off setting that applies immediately (notifications, dark mode). Use
|
|
@@ -9,6 +10,6 @@ import { cn } from "./utils.js";
|
|
|
9
10
|
* primitives; checked uses the primary fill, as `Checkbox` does.
|
|
10
11
|
*/
|
|
11
12
|
function Switch({ className, ...props }) {
|
|
12
|
-
return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", className: cn("peer inline-flex h-5 w-9 shrink-0 items-center rounded-none border border-transparent bg-input p-0.5 shadow-xs transition-colors
|
|
13
|
+
return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", className: cn(focusRing, invalidState, disabledState, "peer inline-flex h-5 w-9 shrink-0 items-center rounded-none border border-transparent bg-input p-0.5 shadow-xs transition-colors data-checked:bg-primary dark:data-checked:bg-primary", className), ...props, children: _jsx(SwitchPrimitive.Thumb, { "data-slot": "switch-thumb", className: "pointer-events-none block size-3.5 rounded-none bg-background shadow-sm ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground" }) }));
|
|
13
14
|
}
|
|
14
15
|
export { Switch };
|
package/dist/table.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { focusRing } from "./internal/styles.js";
|
|
4
5
|
import { cn } from "./utils.js";
|
|
5
6
|
/**
|
|
6
7
|
* True while the element's content is wider than its box. Re-checked when
|
|
@@ -32,7 +33,7 @@ function useHorizontalOverflow(ref) {
|
|
|
32
33
|
function Table({ className, ...props }) {
|
|
33
34
|
const containerRef = React.useRef(null);
|
|
34
35
|
const scrollable = useHorizontalOverflow(containerRef);
|
|
35
|
-
return (_jsx("div", { ref: containerRef, "data-slot": "table-container", tabIndex: scrollable ? 0 : undefined, className: "relative w-full min-w-0 max-w-full overflow-x-auto overscroll-x-contain
|
|
36
|
+
return (_jsx("div", { ref: containerRef, "data-slot": "table-container", tabIndex: scrollable ? 0 : undefined, className: cn(focusRing, "relative w-full min-w-0 max-w-full overflow-x-auto overscroll-x-contain"), children: _jsx("table", { "data-slot": "table", className: cn("w-full min-w-full caption-bottom text-sm", className), ...props }) }));
|
|
36
37
|
}
|
|
37
38
|
function TableHeader({ className, ...props }) {
|
|
38
39
|
return (_jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props }));
|
|
@@ -44,7 +45,7 @@ function TableFooter({ className, ...props }) {
|
|
|
44
45
|
return (_jsx("tfoot", { "data-slot": "table-footer", className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props }));
|
|
45
46
|
}
|
|
46
47
|
function TableRow({ className, ...props }) {
|
|
47
|
-
return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-selected", className), ...props }));
|
|
48
|
+
return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-selected data-[state=selected]:shadow-[inset_2px_0_0_var(--color-selected-foreground)]", className), ...props }));
|
|
48
49
|
}
|
|
49
50
|
function TableHead({ className, ...props }) {
|
|
50
51
|
return (_jsx("th", { "data-slot": "table-head", className: cn("h-10 px-2 text-left align-middle text-xs font-medium tracking-wider whitespace-nowrap text-muted-foreground uppercase sm:h-12 sm:px-3 [&:has([role=checkbox])]:pr-0", className), ...props }));
|
package/dist/textarea.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { disabledState, fieldSurface, focusRing, invalidState, } from "./internal/styles.js";
|
|
3
4
|
import { cn } from "./utils.js";
|
|
4
5
|
function Textarea({ className, ...props }) {
|
|
5
|
-
return (_jsx("textarea", { "data-slot": "textarea", className: cn("flex field-sizing-content min-h-16
|
|
6
|
+
return (_jsx("textarea", { "data-slot": "textarea", className: cn(fieldSurface, focusRing, invalidState, disabledState, "flex field-sizing-content min-h-16 resize-none", className), ...props }));
|
|
6
7
|
}
|
|
7
8
|
export { Textarea };
|
package/dist/toggle-group.js
CHANGED
|
@@ -16,9 +16,9 @@ function ToggleGroup({ className, variant, size, spacing = 2, orientation = "hor
|
|
|
16
16
|
}
|
|
17
17
|
function ToggleGroupItem({ className, children, variant = "default", size = "default", ...props }) {
|
|
18
18
|
const context = React.useContext(ToggleGroupContext);
|
|
19
|
-
return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(
|
|
19
|
+
return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(toggleVariants({
|
|
20
20
|
variant: context.variant || variant,
|
|
21
21
|
size: context.size || size,
|
|
22
|
-
}), className), ...props, children: children }));
|
|
22
|
+
}), "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", className), ...props, children: children }));
|
|
23
23
|
}
|
|
24
24
|
export { ToggleGroup, ToggleGroupItem };
|
package/dist/toggle.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
4
|
+
/**
|
|
5
|
+
* Selected chrome shared by Toggle and ToggleGroupItem (see
|
|
6
|
+
* `selectedState.toggle`): the wash, selected text, and a selected border
|
|
7
|
+
* for `data-state=on`, `aria-pressed`, and `aria-checked`.
|
|
8
|
+
*/
|
|
9
|
+
declare const selectedToggleChrome: string;
|
|
4
10
|
declare const toggleVariants: (props?: ({
|
|
5
11
|
variant?: "default" | "outline" | null | undefined;
|
|
6
12
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
7
13
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
14
|
declare function Toggle({ className, variant, size, ...props }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>): React.JSX.Element;
|
|
9
|
-
export { Toggle, toggleVariants };
|
|
15
|
+
export { Toggle, toggleVariants, selectedToggleChrome };
|
package/dist/toggle.js
CHANGED
|
@@ -3,11 +3,25 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { Toggle as TogglePrimitive } from "radix-ui";
|
|
6
|
+
import { disabledState, focusRing, invalidState, selectedState, } from "./internal/styles.js";
|
|
6
7
|
import { cn } from "./utils.js";
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Selected chrome shared by Toggle and ToggleGroupItem (see
|
|
10
|
+
* `selectedState.toggle`): the wash, selected text, and a selected border
|
|
11
|
+
* for `data-state=on`, `aria-pressed`, and `aria-checked`.
|
|
12
|
+
*/
|
|
13
|
+
const selectedToggleChrome = selectedState.toggle;
|
|
14
|
+
const toggleVariants = cva([
|
|
15
|
+
focusRing,
|
|
16
|
+
selectedState.hover,
|
|
17
|
+
selectedToggleChrome,
|
|
18
|
+
invalidState,
|
|
19
|
+
disabledState,
|
|
20
|
+
"group/toggle inline-flex items-center justify-center gap-1.5 rounded-none text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-colors focus-visible:text-selected-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
21
|
+
], {
|
|
8
22
|
variants: {
|
|
9
23
|
variant: {
|
|
10
|
-
default: "bg-transparent",
|
|
24
|
+
default: "border border-transparent bg-transparent",
|
|
11
25
|
outline: "border border-input bg-transparent",
|
|
12
26
|
},
|
|
13
27
|
size: {
|
|
@@ -24,4 +38,4 @@ const toggleVariants = cva("group/toggle inline-flex items-center justify-center
|
|
|
24
38
|
function Toggle({ className, variant = "default", size = "default", ...props }) {
|
|
25
39
|
return (_jsx(TogglePrimitive.Root, { "data-slot": "toggle", className: cn(toggleVariants({ variant, size, className })), ...props }));
|
|
26
40
|
}
|
|
27
|
-
export { Toggle, toggleVariants };
|
|
41
|
+
export { Toggle, toggleVariants, selectedToggleChrome };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atlas-react-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Shared React components and Tailwind theme for Atlas",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"types": "./dist/checkbox.d.ts",
|
|
57
57
|
"default": "./dist/checkbox.js"
|
|
58
58
|
},
|
|
59
|
+
"./color-legend": {
|
|
60
|
+
"types": "./dist/color-legend.d.ts",
|
|
61
|
+
"default": "./dist/color-legend.js"
|
|
62
|
+
},
|
|
59
63
|
"./combobox": {
|
|
60
64
|
"types": "./dist/combobox.d.ts",
|
|
61
65
|
"default": "./dist/combobox.js"
|
|
@@ -88,6 +92,10 @@
|
|
|
88
92
|
"types": "./dist/field.d.ts",
|
|
89
93
|
"default": "./dist/field.js"
|
|
90
94
|
},
|
|
95
|
+
"./info-tip": {
|
|
96
|
+
"types": "./dist/info-tip.d.ts",
|
|
97
|
+
"default": "./dist/info-tip.js"
|
|
98
|
+
},
|
|
91
99
|
"./input": {
|
|
92
100
|
"types": "./dist/input.d.ts",
|
|
93
101
|
"default": "./dist/input.js"
|
|
@@ -96,6 +104,14 @@
|
|
|
96
104
|
"types": "./dist/label.d.ts",
|
|
97
105
|
"default": "./dist/label.js"
|
|
98
106
|
},
|
|
107
|
+
"./markdown": {
|
|
108
|
+
"types": "./dist/markdown.d.ts",
|
|
109
|
+
"default": "./dist/markdown.js"
|
|
110
|
+
},
|
|
111
|
+
"./meter": {
|
|
112
|
+
"types": "./dist/meter.d.ts",
|
|
113
|
+
"default": "./dist/meter.js"
|
|
114
|
+
},
|
|
99
115
|
"./pagination": {
|
|
100
116
|
"types": "./dist/pagination.d.ts",
|
|
101
117
|
"default": "./dist/pagination.js"
|
|
@@ -112,6 +128,10 @@
|
|
|
112
128
|
"types": "./dist/radio-group.d.ts",
|
|
113
129
|
"default": "./dist/radio-group.js"
|
|
114
130
|
},
|
|
131
|
+
"./search-picker": {
|
|
132
|
+
"types": "./dist/search-picker.d.ts",
|
|
133
|
+
"default": "./dist/search-picker.js"
|
|
134
|
+
},
|
|
115
135
|
"./select": {
|
|
116
136
|
"types": "./dist/select.d.ts",
|
|
117
137
|
"default": "./dist/select.js"
|
|
@@ -124,6 +144,10 @@
|
|
|
124
144
|
"types": "./dist/skeleton.d.ts",
|
|
125
145
|
"default": "./dist/skeleton.js"
|
|
126
146
|
},
|
|
147
|
+
"./slider": {
|
|
148
|
+
"types": "./dist/slider.d.ts",
|
|
149
|
+
"default": "./dist/slider.js"
|
|
150
|
+
},
|
|
127
151
|
"./sonner": {
|
|
128
152
|
"types": "./dist/sonner.d.ts",
|
|
129
153
|
"default": "./dist/sonner.js"
|
|
@@ -136,6 +160,10 @@
|
|
|
136
160
|
"types": "./dist/spinner.d.ts",
|
|
137
161
|
"default": "./dist/spinner.js"
|
|
138
162
|
},
|
|
163
|
+
"./stat-tile": {
|
|
164
|
+
"types": "./dist/stat-tile.d.ts",
|
|
165
|
+
"default": "./dist/stat-tile.js"
|
|
166
|
+
},
|
|
139
167
|
"./switch": {
|
|
140
168
|
"types": "./dist/switch.d.ts",
|
|
141
169
|
"default": "./dist/switch.js"
|
|
@@ -188,6 +216,10 @@
|
|
|
188
216
|
"types": "./dist/form/feedback-field.d.ts",
|
|
189
217
|
"default": "./dist/form/feedback-field.js"
|
|
190
218
|
},
|
|
219
|
+
"./form/markdown-field": {
|
|
220
|
+
"types": "./dist/form/markdown-field.d.ts",
|
|
221
|
+
"default": "./dist/form/markdown-field.js"
|
|
222
|
+
},
|
|
191
223
|
"./form/select-field": {
|
|
192
224
|
"types": "./dist/form/select-field.d.ts",
|
|
193
225
|
"default": "./dist/form/select-field.js"
|
|
@@ -213,6 +245,7 @@
|
|
|
213
245
|
"build": "tsc",
|
|
214
246
|
"check": "npm run build && prettier --check .",
|
|
215
247
|
"test": "npm run build && vitest run",
|
|
248
|
+
"test:coverage": "npm run build && vitest run --coverage",
|
|
216
249
|
"prepack": "npm run build"
|
|
217
250
|
},
|
|
218
251
|
"engines": {
|
|
@@ -240,6 +273,8 @@
|
|
|
240
273
|
"next-themes": "^0.4.6",
|
|
241
274
|
"radix-ui": "^1.4.3",
|
|
242
275
|
"react-day-picker": "^10.0.1",
|
|
276
|
+
"react-markdown": "^10.1.0",
|
|
277
|
+
"remark-gfm": "^4.0.1",
|
|
243
278
|
"sonner": "^2.0.7",
|
|
244
279
|
"tailwind-merge": "^3.6.0",
|
|
245
280
|
"tw-animate-css": "^1.4.0"
|
|
@@ -252,6 +287,7 @@
|
|
|
252
287
|
"@types/node": "^25.5.0",
|
|
253
288
|
"@types/react": "^19.2.14",
|
|
254
289
|
"@types/react-dom": "^19.2.3",
|
|
290
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
255
291
|
"jsdom": "^29.1.1",
|
|
256
292
|
"next": "16.2.6",
|
|
257
293
|
"prettier": "^3.8.1",
|
|
@@ -49,33 +49,40 @@ Import each component from its subpath, e.g.
|
|
|
49
49
|
`import { Button } from "atlas-react-kit/button"`. There is no root barrel, and
|
|
50
50
|
`dist/` or `internal/` paths are not public API.
|
|
51
51
|
|
|
52
|
-
| Need | Use
|
|
53
|
-
| ----------------------------------------------- |
|
|
54
|
-
| Short fixed list (a handful of options) | `Select` family from `select`
|
|
55
|
-
| Long or searchable list (timezones, currencies) | `Combobox` from `combobox` with `{ value, label }[]` options
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
52
|
+
| Need | Use |
|
|
53
|
+
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| Short fixed list (a handful of options) | `Select` family from `select` |
|
|
55
|
+
| Long or searchable list (timezones, currencies) | `Combobox` from `combobox` with `{ value, label }[]` options |
|
|
56
|
+
| Always-visible searchable list (places) | `SearchPicker` from `search-picker` |
|
|
57
|
+
| One date, or a from/to range | `DatePickerButton` from `date-picker` (`mode="range"` for ranges) |
|
|
58
|
+
| Date and time (not `datetime-local`) | `DateTimePickerButton` from `date-time-picker` |
|
|
59
|
+
| Labelled field with error text | `FormTextField`, `FormSelectField`, `FormDatePickerField`, `FormDateTimePickerField`, `FormMarkdownField` from `form/*` |
|
|
60
|
+
| Form-level error (save failed) | `FormFeedbackField` from `form/feedback-field` |
|
|
61
|
+
| Content panels on one page | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` from `tabs` |
|
|
62
|
+
| View switch that changes the URL | `TabsNav` with links: `TabsNavLink` from `next/tabs-nav-link` in Next, else `tabsTriggerVariants()` |
|
|
63
|
+
| Status chip (paid, pending, failed) | `Badge variant="soft"` with `tone="success"`, `"warning"`, `"destructive"`, or `"neutral"` |
|
|
64
|
+
| Data table | `Table` family from `table`; `SortableTableHead`; `TableBodySkeleton` while loading |
|
|
65
|
+
| Nothing to show yet | `EmptyPanel` from `empty-panel` |
|
|
66
|
+
| Grouped content | `Card` family from `card`; `<Card flush>` for an edge-to-edge table or list |
|
|
67
|
+
| Modal | `Dialog` family from `dialog` |
|
|
68
|
+
| Label for an icon button, or a short hint | `Tooltip` from `tooltip`; keep `aria-label` on icon buttons |
|
|
69
|
+
| Longer help from a glyph | `InfoTip` / `InlineTip` from `info-tip` (not `Tooltip`) |
|
|
70
|
+
| Color scale key | `ColorLegend` from `color-legend` |
|
|
71
|
+
| GitHub-flavoured Markdown | `Markdown` from `markdown`; write/preview with `FormMarkdownField` |
|
|
72
|
+
| Summary figure in a strip | `StatTile` from `stat-tile` (`statTileGrid`, `sectionLabel`) |
|
|
73
|
+
| Value against a scale, optional ticks | `Meter` from `meter` (not `Progress`) |
|
|
74
|
+
| Number on a continuous scale | `Slider` from `slider` |
|
|
75
|
+
| Row actions or an overflow menu of commands | `DropdownMenu` family from `dropdown-menu` |
|
|
76
|
+
| Setting that applies immediately | `Switch` from `switch`; `Checkbox` for choices submitted with a form |
|
|
77
|
+
| One choice from a few visible options | `RadioGroup` and `RadioGroupItem` from `radio-group` |
|
|
78
|
+
| Inline message about a page or section | `Alert` from `alert` (`success`, `warning`, `destructive` variants) |
|
|
79
|
+
| Person or workspace image | `Avatar`, `AvatarImage`, `AvatarFallback` from `avatar` |
|
|
80
|
+
| Sections that expand in place | `Accordion` family from `accordion` |
|
|
81
|
+
| Pages of a long list | `Pagination` family from `pagination` |
|
|
82
|
+
| Where the page sits in a hierarchy | `Breadcrumb` family from `breadcrumb` |
|
|
83
|
+
| Work in progress | `Progress` from `progress` when the percent is known, else `Spinner` from `spinner` |
|
|
84
|
+
| Crash recovery around a section | `ErrorBoundary` from `error-boundary` |
|
|
85
|
+
| Conditional classes | `cn` from `utils` |
|
|
79
86
|
|
|
80
87
|
If nothing fits, build the piece in the app from kit primitives and semantic
|
|
81
88
|
tokens. Do not copy kit source into the app; a missing shared component belongs
|
|
@@ -95,9 +102,10 @@ list; the ones that come up most:
|
|
|
95
102
|
`border-border`, `bg-card`, `text-destructive`. Raw palette classes
|
|
96
103
|
(`bg-purple-600`, `text-gray-500`) and paired `dark:` colors drift from the
|
|
97
104
|
theme; tokens already switch with light and dark.
|
|
98
|
-
- **Selected and active state**: `bg-selected text-selected-foreground`
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
- **Selected and active state**: `bg-selected text-selected-foreground` plus
|
|
106
|
+
a `border-selected-foreground` border (or bar) for the current nav item,
|
|
107
|
+
chip, row, or toggle; the wash alone is too faint to show state. Grey fills
|
|
108
|
+
read as hover, not selection.
|
|
101
109
|
- **Status**: `success`, `warning`, and `destructive` for good, caution, and
|
|
102
110
|
bad. Use them as text or soft tints (the `Badge` tones), which meet AA in
|
|
103
111
|
both themes. Don't override a soft badge's colors. There is no foreground
|
|
@@ -106,8 +114,9 @@ list; the ones that come up most:
|
|
|
106
114
|
which is too dark to read on the dark background.
|
|
107
115
|
- **Accessibility**: the kit targets WCAG 2.2 AA. Give every control a name
|
|
108
116
|
(`Label htmlFor`, or `aria-label` on icon-only buttons and an unlabelled
|
|
109
|
-
`Combobox`), keep the shipped focus rings
|
|
110
|
-
|
|
117
|
+
`Combobox`), keep the shipped focus rings (solid `ring-ring`, never
|
|
118
|
+
`ring-ring/30`), keep field borders on `border-input` (3:1; `border-border`
|
|
119
|
+
is for dividers), and don't lower contrast with raw colors or opacity.
|
|
111
120
|
- **Square corners**: primitives have none. Use `rounded-full` only for real
|
|
112
121
|
circles (avatars, dots). Adding `rounded-md` to a card or button breaks the
|
|
113
122
|
system.
|
package/theme.css
CHANGED
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
@slot;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
@custom-variant data-on {
|
|
23
|
+
&:where([data-state="on"]) {
|
|
24
|
+
@slot;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
22
27
|
@custom-variant data-disabled {
|
|
23
28
|
&:where([data-disabled="true"]),
|
|
24
29
|
&:where([data-disabled]:not([data-disabled="false"])) {
|
|
@@ -100,8 +105,11 @@
|
|
|
100
105
|
--muted-foreground: oklch(0.53 0 0);
|
|
101
106
|
--accent: oklch(0.97 0 0);
|
|
102
107
|
--accent-foreground: oklch(0.205 0 0);
|
|
103
|
-
/* Soft brand-purple wash for selected/active chrome (nav, tabs, chips, rows).
|
|
104
|
-
|
|
108
|
+
/* Soft brand-purple wash for selected/active chrome (nav, tabs, chips, rows).
|
|
109
|
+
Mixed in oklab: in oklch, white's hue (0) pulls the mix toward pink. The
|
|
110
|
+
wash is too faint to mark state alone, so selected chrome also takes a
|
|
111
|
+
`selected-foreground` border or bar (WCAG 1.4.11). */
|
|
112
|
+
--selected: color-mix(in oklab, var(--primary) 12%, var(--background));
|
|
105
113
|
--selected-foreground: var(--primary);
|
|
106
114
|
/* AA as text on the page (6.4:1) and on its own /10 and /15 tints. */
|
|
107
115
|
--destructive: oklch(0.505 0.213 27.518);
|
|
@@ -110,9 +118,13 @@
|
|
|
110
118
|
nothing puts text on a solid fill. See DESIGN_RULES.md. */
|
|
111
119
|
--success: oklch(0.45 0.11 160);
|
|
112
120
|
--warning: oklch(0.48 0.115 70);
|
|
121
|
+
/* Dividers and card edges: decorative, so no contrast minimum. */
|
|
113
122
|
--border: oklch(0.922 0 0);
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
/* Form-control boundaries (fields, checkboxes, radios, switch track): 3:1
|
|
124
|
+
on the page and on cards, as WCAG 1.4.11 requires. */
|
|
125
|
+
--input: oklch(0.62 0 0);
|
|
126
|
+
/* Focus ring: solid brand purple, 7.3:1 on the page. */
|
|
127
|
+
--ring: var(--primary);
|
|
116
128
|
--chart-1: oklch(0.87 0 0);
|
|
117
129
|
--chart-2: oklch(0.556 0 0);
|
|
118
130
|
--chart-3: oklch(0.439 0 0);
|
|
@@ -136,7 +148,9 @@
|
|
|
136
148
|
--card-foreground: oklch(0.985 0 0);
|
|
137
149
|
--popover: oklch(0.205 0 0);
|
|
138
150
|
--popover-foreground: oklch(0.985 0 0);
|
|
139
|
-
|
|
151
|
+
/* Light enough for checked fills and borders to reach 3:1 on the page and
|
|
152
|
+
cards, dark enough for primary-foreground text (5:1). */
|
|
153
|
+
--primary: oklch(0.55 0.25 292.7);
|
|
140
154
|
--primary-foreground: oklch(0.969 0.016 293.756);
|
|
141
155
|
--secondary: oklch(0.274 0.006 286.033);
|
|
142
156
|
--secondary-foreground: oklch(0.985 0 0);
|
|
@@ -144,14 +158,14 @@
|
|
|
144
158
|
--muted-foreground: oklch(0.708 0 0);
|
|
145
159
|
--accent: oklch(0.269 0 0);
|
|
146
160
|
--accent-foreground: oklch(0.985 0 0);
|
|
147
|
-
--selected: color-mix(in
|
|
161
|
+
--selected: color-mix(in oklab, var(--primary) 24%, var(--background));
|
|
148
162
|
--selected-foreground: oklch(0.85 0.06 293);
|
|
149
163
|
--destructive: oklch(0.704 0.191 22.216);
|
|
150
164
|
--success: oklch(0.765 0.177 163.223);
|
|
151
165
|
--warning: oklch(0.828 0.189 84.429);
|
|
152
166
|
--border: oklch(1 0 0 / 10%);
|
|
153
|
-
--input: oklch(
|
|
154
|
-
--ring: oklch(0.
|
|
167
|
+
--input: oklch(0.52 0 0);
|
|
168
|
+
--ring: oklch(0.75 0.14 293);
|
|
155
169
|
--chart-1: oklch(0.87 0 0);
|
|
156
170
|
--chart-2: oklch(0.556 0 0);
|
|
157
171
|
--chart-3: oklch(0.439 0 0);
|
|
@@ -169,7 +183,7 @@
|
|
|
169
183
|
|
|
170
184
|
@layer base {
|
|
171
185
|
* {
|
|
172
|
-
@apply border-border outline-ring
|
|
186
|
+
@apply border-border outline-ring;
|
|
173
187
|
}
|
|
174
188
|
body {
|
|
175
189
|
@apply bg-background text-foreground;
|