@weichenlin/swell-ui 0.1.0 → 0.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/README.md +116 -16
- package/dist/index.js +24 -33
- package/dist/theme.css +105 -235
- package/dist/themes/warm.css +63 -0
- package/dist/typography.css +12 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -38,32 +38,127 @@ export function App() {
|
|
|
38
38
|
}
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
###
|
|
41
|
+
### Change the brand
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
The default look is neutral gray with a near-black primary. A rebrand is two tokens, set once,
|
|
44
|
+
after the import:
|
|
45
45
|
|
|
46
46
|
```css
|
|
47
47
|
@import 'tailwindcss';
|
|
48
48
|
@import '@weichenlin/swell-ui/styles.css';
|
|
49
49
|
|
|
50
50
|
@theme {
|
|
51
|
-
--color-primary: #
|
|
52
|
-
--
|
|
51
|
+
--color-primary: #f7a501; /* buttons, input focus, checked switches and checkboxes, halos */
|
|
52
|
+
--color-primary-fg: #141413; /* text and marks drawn on the primary fill */
|
|
53
53
|
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
That covers light, dark-from-the-system and `data-theme="dark"` alike. Input focus
|
|
57
|
+
(`--color-input-focus`), checked controls (`--color-control-checked`, `-fg`) and the focus halo
|
|
58
|
+
(`--color-input-halo`) are `var()` references to these two, resolved at runtime, so they follow.
|
|
59
|
+
|
|
60
|
+
To use a different value per scheme, write both on one line with `light-dark()`:
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
```css
|
|
63
|
+
@theme {
|
|
64
|
+
--color-primary: light-dark(#2563eb, #60a5fa);
|
|
65
|
+
--color-primary-fg: light-dark(#ffffff, #0b1220);
|
|
57
66
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A light brand color (yellow, lime, cyan) can fall under 3:1 against the light page. The buttons
|
|
70
|
+
stay readable through `--color-primary-fg`, but if the focus border or a checked box needs more
|
|
71
|
+
contrast, override just that one:
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
@theme {
|
|
75
|
+
--color-input-focus: light-dark(#b37700, #f7a501); /* darker amber on light only */
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Set tokens in `@theme` (or a plain `:root` rule) rather than on an inner element: the derived
|
|
80
|
+
tokens are resolved on the root.
|
|
81
|
+
|
|
82
|
+
### Tokens
|
|
83
|
+
|
|
84
|
+
Every color token is a `light-dark(<light>, <dark>)` pair; override it the same way, once. All of
|
|
85
|
+
them are also Tailwind utilities (`bg-surface`, `text-fg-muted`, `border-input-border`, ...).
|
|
86
|
+
|
|
87
|
+
| Token | Used for |
|
|
88
|
+
| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
89
|
+
| `--color-primary`, `--color-primary-fg` | Brand fill and the text on it |
|
|
90
|
+
| `--color-accent` | Secondary brand color; unused by components, defaults to primary |
|
|
91
|
+
| `--color-bg` | Page background |
|
|
92
|
+
| `--color-surface`, `-surface-raised`, `-surface-muted` | Cards and panels; raised (tooltips, sheets); muted (hover fills, secondary button) |
|
|
93
|
+
| `--color-fg`, `-fg-strong`, `-fg-muted`, `-fg-subtle` | Text, from strongest to quietest (`fg-subtle` stays >=4.5:1) |
|
|
94
|
+
| `--color-border`, `-border-strong` | Dividers and hairlines |
|
|
95
|
+
| `--color-ring`, `-ring-deep` | 1px button and tab-list rings |
|
|
96
|
+
| `--color-inverse`, `-inverse-fg` | `dark` button, tooltips |
|
|
97
|
+
| `--color-focus` | Keyboard focus outline (blue) |
|
|
98
|
+
| `--color-input-bg`, `-input-bg-hover` | Text field fill |
|
|
99
|
+
| `--color-input-border`, `-input-border-hover` | Text field border; also the off track of Switch and box of Checkbox |
|
|
100
|
+
| `--color-input-placeholder` | Placeholder text |
|
|
101
|
+
| `--color-input-focus` | Focused field border. Default: `var(--color-primary)` |
|
|
102
|
+
| `--color-input-halo` | 3px ring around a focused field. Default: input-focus at 14% |
|
|
103
|
+
| `--color-input-halo-invalid`, `-invalid-focus` | Same ring for invalid fields (danger at 12% / 22%) |
|
|
104
|
+
| `--color-input-highlight` | Inset top highlight on fields |
|
|
105
|
+
| `--color-control-checked`, `-control-checked-fg` | Checked Switch/Checkbox fill and its mark. Default: primary / primary-fg |
|
|
106
|
+
| `--color-popover-bg`, `-popover-border`, `-popover-highlight` | Select picker, dialogs; highlight is the active row |
|
|
107
|
+
| `--color-dialog-backdrop` | Dimmed page behind Dialog and Drawer |
|
|
108
|
+
| `--color-{success,warning,danger,info,neutral}` | Status tone: solid fill / icon |
|
|
109
|
+
| `--color-{tone}-fg` | Text on the solid fill |
|
|
110
|
+
| `--color-{tone}-subtle-bg`, `-subtle-fg`, `-border` | Tinted badge, its text and hairline |
|
|
111
|
+
| `--shadow-input`, `-input-lift`, `-switch-thumb`, `-popover`, `-dialog`, `-whisper` | Elevation (`shadow-*` utilities) |
|
|
112
|
+
| `--container-page` | `max-w-page` (1200px) |
|
|
113
|
+
|
|
114
|
+
### Optional looks
|
|
115
|
+
|
|
116
|
+
```css
|
|
117
|
+
@import 'tailwindcss';
|
|
118
|
+
@import '@weichenlin/swell-ui/styles.css';
|
|
119
|
+
@import '@weichenlin/swell-ui/themes/warm.css'; /* parchment surfaces, brown primary */
|
|
120
|
+
@import '@weichenlin/swell-ui/typography.css'; /* h1-h3 in the serif stack, weight 500 */
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Both are independent; your own `@theme` overrides go after them.
|
|
124
|
+
|
|
125
|
+
### Fonts
|
|
126
|
+
|
|
127
|
+
swell-ui sets no font names: text uses Tailwind's `--font-sans` (the system UI stack), and
|
|
128
|
+
`font-serif` / `font-mono` are Tailwind's defaults too. To use a web font, ship its files with
|
|
129
|
+
your app, for example with [Fontsource](https://fontsource.org):
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
pnpm add @fontsource-variable/inter
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```css
|
|
136
|
+
@import 'tailwindcss';
|
|
137
|
+
@import '@fontsource-variable/inter';
|
|
138
|
+
@import '@weichenlin/swell-ui/styles.css';
|
|
139
|
+
|
|
140
|
+
@theme {
|
|
141
|
+
--font-sans: 'Inter Variable', ui-sans-serif, system-ui, sans-serif;
|
|
62
142
|
}
|
|
63
143
|
```
|
|
64
144
|
|
|
65
|
-
|
|
66
|
-
|
|
145
|
+
### Radius
|
|
146
|
+
|
|
147
|
+
swell-ui does not touch Tailwind's radius scale; components use the stock `rounded-*` classes, so
|
|
148
|
+
`rounded-md` is still 6px in your app. Changing `--radius-*` in your `@theme` changes the
|
|
149
|
+
components too.
|
|
150
|
+
|
|
151
|
+
Coming from 0.1, which replaced the scale: shift each class one step up to keep the old size.
|
|
152
|
+
|
|
153
|
+
| 0.1 class | px | Now |
|
|
154
|
+
| --------------- | --- | ------------- |
|
|
155
|
+
| `rounded-sharp` | 4 | `rounded-sm` |
|
|
156
|
+
| `rounded-sm` | 6 | `rounded-md` |
|
|
157
|
+
| `rounded-md` | 8 | `rounded-lg` |
|
|
158
|
+
| `rounded-lg` | 12 | `rounded-xl` |
|
|
159
|
+
| `rounded-xl` | 16 | `rounded-2xl` |
|
|
160
|
+
| `rounded-2xl` | 24 | `rounded-3xl` |
|
|
161
|
+
| `rounded-3xl` | 32 | `rounded-4xl` |
|
|
67
162
|
|
|
68
163
|
### Tokens only
|
|
69
164
|
|
|
@@ -78,7 +173,8 @@ instead:
|
|
|
78
173
|
### `cn`
|
|
79
174
|
|
|
80
175
|
`cn` (clsx + tailwind-merge) is exported and knows the custom tokens, so
|
|
81
|
-
`cn('
|
|
176
|
+
`cn('text-fg-muted', 'text-danger')` gives `text-danger` and `cn('shadow-popover', 'shadow-none')`
|
|
177
|
+
gives `shadow-none`.
|
|
82
178
|
|
|
83
179
|
## Dark mode
|
|
84
180
|
|
|
@@ -86,6 +182,10 @@ Without a `data-theme` attribute the theme follows the system (`prefers-color-sc
|
|
|
86
182
|
`data-theme="light"` or `data-theme="dark"` on `<html>` forces one. The `dark:` variant follows the
|
|
87
183
|
same rule.
|
|
88
184
|
|
|
185
|
+
Under the hood the root's `color-scheme` is `light dark`, `light` or `dark` accordingly, and every
|
|
186
|
+
color token picks its side with `light-dark()`. Browsers support it natively from Chrome 123,
|
|
187
|
+
Safari 17.5 and Firefox 120; Tailwind's production build rewrites it for older targets.
|
|
188
|
+
|
|
89
189
|
`useTheme` persists the choice and sets the attribute; `ThemeSwitcher` is the toggle button:
|
|
90
190
|
|
|
91
191
|
```tsx
|
|
@@ -177,11 +277,11 @@ pnpm test # vitest (jsdom)
|
|
|
177
277
|
pnpm typecheck # tsc -b
|
|
178
278
|
pnpm lint # vp check: format, lint, type-aware lint
|
|
179
279
|
pnpm knip # unused files, exports and dependencies
|
|
180
|
-
pnpm build # vp pack -> dist/index.js, index.d.ts
|
|
280
|
+
pnpm build # vp pack -> dist/index.js, index.d.ts and the CSS files
|
|
181
281
|
pnpm build-storybook
|
|
182
282
|
```
|
|
183
283
|
|
|
184
|
-
|
|
284
|
+
The CSS files under `src/styles/` are copied into `dist/` as they are, so the
|
|
185
285
|
`@source "./*.js"` in `styles.css` is relative to `dist/`. Storybook and the lint use
|
|
186
286
|
`src/styles/dev.css`, which adds Tailwind and scans `src/` and `stories/`.
|
|
187
287
|
|
package/dist/index.js
CHANGED
|
@@ -57,15 +57,6 @@ const twMerge = extendTailwindMerge({ extend: { theme: {
|
|
|
57
57
|
"neutral"
|
|
58
58
|
].flatMap(tone)
|
|
59
59
|
],
|
|
60
|
-
radius: [
|
|
61
|
-
"sharp",
|
|
62
|
-
"sm",
|
|
63
|
-
"md",
|
|
64
|
-
"lg",
|
|
65
|
-
"xl",
|
|
66
|
-
"2xl",
|
|
67
|
-
"3xl"
|
|
68
|
-
],
|
|
69
60
|
shadow: [
|
|
70
61
|
"input",
|
|
71
62
|
"input-lift",
|
|
@@ -129,7 +120,7 @@ function Badge({ tone = "neutral", variant = "subtle", size = "md", icon, busy =
|
|
|
129
120
|
return /* @__PURE__ */ jsxs("span", {
|
|
130
121
|
...props,
|
|
131
122
|
"data-slot": "badge",
|
|
132
|
-
className: cn("inline-flex items-center gap-1 rounded-
|
|
123
|
+
className: cn("inline-flex items-center gap-1 rounded-md border text-xs leading-none font-medium whitespace-nowrap", "forced-colors:border-[CanvasText]", sizeClasses$3[size].base, hasIcon && sizeClasses$3[size].withIcon, toneClass, className),
|
|
133
124
|
children: [hasIcon && /* @__PURE__ */ jsx("span", {
|
|
134
125
|
"aria-hidden": "true",
|
|
135
126
|
"data-slot": "badge-icon",
|
|
@@ -374,7 +365,7 @@ function Tooltip({ content, side = "top", describe = true, className, children }
|
|
|
374
365
|
}[placed]),
|
|
375
366
|
children: /* @__PURE__ */ jsx("span", {
|
|
376
367
|
ref: bubbleRef,
|
|
377
|
-
className: cn("block w-max max-w-xs rounded-
|
|
368
|
+
className: cn("block w-max max-w-xs rounded-md border border-border-strong bg-inverse px-2 py-1", "text-xs/snug font-medium text-inverse-fg shadow-whisper", "transition-[opacity,translate] duration-150 ease-out starting:opacity-0", {
|
|
378
369
|
top: "starting:translate-y-0.5",
|
|
379
370
|
bottom: "starting:-translate-y-0.5",
|
|
380
371
|
left: "starting:translate-x-0.5",
|
|
@@ -401,11 +392,11 @@ const buttonVariants = cva([
|
|
|
401
392
|
], {
|
|
402
393
|
variants: {
|
|
403
394
|
intent: {
|
|
404
|
-
primary: "bg-primary text-primary-fg rounded-
|
|
405
|
-
secondary: "bg-surface-muted text-fg-strong rounded-
|
|
406
|
-
surface: "bg-surface-raised text-fg rounded-
|
|
407
|
-
dark: "bg-inverse text-inverse-fg rounded-
|
|
408
|
-
ghost: "bg-transparent text-fg rounded-
|
|
395
|
+
primary: "bg-primary text-primary-fg rounded-lg shadow-[0_0_0_1px_var(--color-primary)] hover:not-aria-disabled:opacity-90",
|
|
396
|
+
secondary: "bg-surface-muted text-fg-strong rounded-lg shadow-[0_0_0_1px_var(--color-ring)] hover:not-aria-disabled:bg-ring",
|
|
397
|
+
surface: "bg-surface-raised text-fg rounded-xl hover:not-aria-disabled:bg-surface-muted",
|
|
398
|
+
dark: "bg-inverse text-inverse-fg rounded-lg shadow-[0_0_0_1px_var(--color-ring)] hover:not-aria-disabled:opacity-90",
|
|
399
|
+
ghost: "bg-transparent text-fg rounded-lg hover:not-aria-disabled:bg-surface-muted"
|
|
409
400
|
},
|
|
410
401
|
size: {
|
|
411
402
|
sm: "h-8.5 px-3 text-sm",
|
|
@@ -616,7 +607,7 @@ function Card({ as: Comp = "div", className, ...props }) {
|
|
|
616
607
|
"aria-labelledby": (Comp === "section" || Comp === "article") && titled && !props["aria-label"] ? titleId : void 0,
|
|
617
608
|
...props,
|
|
618
609
|
"data-slot": "card",
|
|
619
|
-
className: cn("rounded-
|
|
610
|
+
className: cn("rounded-xl border border-border-strong bg-surface", className)
|
|
620
611
|
})
|
|
621
612
|
});
|
|
622
613
|
}
|
|
@@ -738,7 +729,7 @@ function Notch({ ring, size, disabled, children }) {
|
|
|
738
729
|
const halo = ring === "halo";
|
|
739
730
|
return /* @__PURE__ */ jsx("fieldset", {
|
|
740
731
|
"aria-hidden": true,
|
|
741
|
-
className: cn("pointer-events-none absolute m-0 min-w-0 p-0", "transition-[border-color] duration-200 ease-in-out motion-reduce:transition-none", notchPadding[ring][size], halo ? ["-inset-1 rounded-[calc(var(--radius-
|
|
732
|
+
className: cn("pointer-events-none absolute m-0 min-w-0 p-0", "transition-[border-color] duration-200 ease-in-out motion-reduce:transition-none", notchPadding[ring][size], halo ? ["-inset-1 rounded-[calc(var(--radius-lg)+3px)] border-[3px] border-(--field-halo)", "forced-colors:invisible forced-colors:group-has-[:is(input,select):is(:focus,:open)]/control:visible forced-colors:border-[Highlight]"] : ["-inset-px rounded-[inherit] border border-(--field-border)", disabled && "forced-colors:border-[GrayText]"]),
|
|
742
733
|
children: /* @__PURE__ */ jsx("legend", {
|
|
743
734
|
className: cn("invisible float-none block w-auto max-w-[0.01px] overflow-hidden p-0 text-xs whitespace-nowrap", halo ? "h-0.75" : "h-px", "transition-[max-width] duration-50 ease-out motion-reduce:transition-none", "[[data-slot=input-control]:is([data-start],:has(input:focus),:has(input:not(:placeholder-shown)),:has(input:autofill),:has(input:-webkit-autofill),:has(select:focus),:has(select:open),:has(select):not(:has(option[value=\"\"]:checked)))_&]:max-w-full", "[[data-slot=input-control]:is([data-start],:has(input:focus),:has(input:not(:placeholder-shown)),:has(input:autofill),:has(input:-webkit-autofill),:has(select:focus),:has(select:open),:has(select):not(:has(option[value=\"\"]:checked)))_&]:delay-50", "[[data-slot=input-control]:is([data-start],:has(input:focus),:has(input:not(:placeholder-shown)),:has(input:autofill),:has(input:-webkit-autofill),:has(select:focus),:has(select:open),:has(select):not(:has(option[value=\"\"]:checked)))_&]:duration-100"),
|
|
744
735
|
children: /* @__PURE__ */ jsx("span", {
|
|
@@ -805,7 +796,7 @@ function FieldShell({ label, floating = false, helperText, error, size, id: idPr
|
|
|
805
796
|
"data-slot": "input-control",
|
|
806
797
|
"data-start": hasStart ? "" : void 0,
|
|
807
798
|
onMouseDown: forwardClick,
|
|
808
|
-
className: cn("group/control relative isolate flex items-center rounded-
|
|
799
|
+
className: cn("group/control relative isolate flex items-center rounded-lg border", floatingLabel ? "bg-transparent" : "[--field-bg:var(--color-input-bg)] bg-(--field-bg)", "transition-[border-color,box-shadow,background-color] duration-200 ease-in-out motion-reduce:transition-none", controlHeight[size], invalid ? ["[--field-border:var(--color-danger)] [--field-halo:var(--color-input-halo-invalid)]", "has-[:is(input,select):is(:focus,:open)]:[--field-halo:var(--color-input-halo-invalid-focus)]"] : ["[--field-border:var(--color-input-border)] [--field-halo:transparent]", "has-[:is(input,select):is(:focus,:open)]:[--field-border:var(--color-input-focus)] has-[:is(input,select):is(:focus,:open)]:[--field-halo:var(--color-input-halo)]"], "[--field-lift:0_0_#0000]", !invalid && !disabled && !readOnly && [
|
|
809
800
|
"hover:not-has-[:is(input,select):is(:focus,:open)]:[--field-border:var(--color-input-border-hover)]",
|
|
810
801
|
!floatingLabel && "hover:not-has-[:is(input,select):is(:focus,:open)]:[--field-bg:var(--color-input-bg-hover)]",
|
|
811
802
|
"hover:not-has-[:is(input,select):is(:focus,:open)]:[--field-lift:var(--shadow-input-lift)]"
|
|
@@ -1006,7 +997,7 @@ function Checkbox({ label, description, error, indeterminate = false, className,
|
|
|
1006
997
|
"aria-labelledby": props["aria-labelledby"] ?? row.labelledBy,
|
|
1007
998
|
"aria-describedby": row.describedBy,
|
|
1008
999
|
"aria-invalid": row.invalid ? true : ariaInvalid,
|
|
1009
|
-
className: cn("peer col-start-1 row-start-1 m-0 size-4 cursor-pointer appearance-none rounded-
|
|
1000
|
+
className: cn("peer col-start-1 row-start-1 m-0 size-4 cursor-pointer appearance-none rounded-sm border outline-hidden", "disabled:cursor-not-allowed", "border-(--checkbox-border) bg-(--checkbox-bg) [box-shadow:0_0_0_3px_var(--checkbox-halo)]", "transition-[background-color,border-color,box-shadow] duration-200 ease-in-out", "[--checkbox-bg:var(--color-input-bg)] [--checkbox-halo:transparent]", "checked:[--checkbox-bg:var(--color-control-checked)] indeterminate:[--checkbox-bg:var(--color-control-checked)]", row.invalid ? [
|
|
1010
1001
|
"[--checkbox-border:var(--color-danger)] [--checkbox-halo:var(--color-input-halo-invalid)]",
|
|
1011
1002
|
"focus-visible:[--checkbox-halo:var(--color-input-halo-invalid-focus)]",
|
|
1012
1003
|
"not-forced-colors:checked:focus-visible:outline-danger not-forced-colors:indeterminate:focus-visible:outline-danger"
|
|
@@ -1331,7 +1322,7 @@ function Dialog({ size = "md", className, children, ...props }) {
|
|
|
1331
1322
|
className: cn("m-auto w-[calc(100%-2rem)] max-h-[calc(100dvh-64px)] overflow-visible border-0 bg-transparent p-0 text-fg", "overscroll-contain outline-none", sizeClasses$2[size], motionClasses$1, sheetClasses, className),
|
|
1332
1323
|
children: /* @__PURE__ */ jsx("div", {
|
|
1333
1324
|
"data-slot": "dialog-panel",
|
|
1334
|
-
className: cn("flex max-h-[inherit] flex-col rounded-
|
|
1325
|
+
className: cn("flex max-h-[inherit] flex-col rounded-xl border border-popover-border bg-popover-bg shadow-dialog", "forced-colors:border-[CanvasText]", "max-sm:rounded-b-none max-sm:border-b-0 max-sm:pb-[env(safe-area-inset-bottom)]"),
|
|
1335
1326
|
children
|
|
1336
1327
|
})
|
|
1337
1328
|
});
|
|
@@ -1349,7 +1340,7 @@ function DialogHeader({ title, description, closeButton, className, ...props })
|
|
|
1349
1340
|
className: "min-w-0 flex-1",
|
|
1350
1341
|
children: [/* @__PURE__ */ jsx("h2", {
|
|
1351
1342
|
id: context.titleId,
|
|
1352
|
-
className: "m-0 text-xl/tight text-fg",
|
|
1343
|
+
className: "m-0 text-xl/tight font-medium text-fg",
|
|
1353
1344
|
children: title
|
|
1354
1345
|
}), hasDescription && /* @__PURE__ */ jsx("p", {
|
|
1355
1346
|
id: context.descriptionId,
|
|
@@ -1455,7 +1446,7 @@ function DrawerHeader({ title, description, className, ...props }) {
|
|
|
1455
1446
|
className: "min-w-0 flex-1",
|
|
1456
1447
|
children: [/* @__PURE__ */ jsx("h2", {
|
|
1457
1448
|
id: context.titleId,
|
|
1458
|
-
className: "m-0 text-xl/tight wrap-anywhere text-fg",
|
|
1449
|
+
className: "m-0 text-xl/tight font-medium wrap-anywhere text-fg",
|
|
1459
1450
|
children: title
|
|
1460
1451
|
}), hasDescription && /* @__PURE__ */ jsx("p", {
|
|
1461
1452
|
id: context.descriptionId,
|
|
@@ -1708,9 +1699,9 @@ const labelRestingWidth = {
|
|
|
1708
1699
|
lg: "max-w-[calc(100%-50px)]"
|
|
1709
1700
|
};
|
|
1710
1701
|
const isGroup = (item) => "options" in item;
|
|
1711
|
-
const pickerClasses = cn("supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[appearance:base-select]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:[appearance:base-select]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker-icon]:hidden", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:[position-area:block-end_span-inline-end]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:[position-try-fallbacks:flip-block]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:mt-1", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:min-w-[anchor-size(width)]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:max-h-[max(10rem,calc(100%-12px))]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:overflow-y-auto", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:rounded-
|
|
1702
|
+
const pickerClasses = cn("supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[appearance:base-select]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:[appearance:base-select]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker-icon]:hidden", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:[position-area:block-end_span-inline-end]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:[position-try-fallbacks:flip-block]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:mt-1", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:min-w-[anchor-size(width)]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:max-h-[max(10rem,calc(100%-12px))]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:overflow-y-auto", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:rounded-xl", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:border", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:border-popover-border", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:bg-popover-bg", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:p-1", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:shadow-popover", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:text-fg", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:origin-top", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:opacity-0", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:scale-96", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:transition-[opacity,scale,display,overlay]", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:transition-discrete", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:duration-80", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::picker(select)]:ease-out", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:open:[&::picker(select)]:opacity-100", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:open:[&::picker(select)]:scale-100", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:open:[&::picker(select)]:duration-120", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:open:[&::picker(select)]:starting:opacity-0", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:open:[&::picker(select)]:starting:scale-96", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:motion-reduce:[&::picker(select)]:transition-none");
|
|
1712
1703
|
function optionClasses(size, mono) {
|
|
1713
|
-
return cn("bg-popover-bg text-fg checked:font-medium disabled:text-fg-subtle", textSize[size], mono && "font-mono", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:flex", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:cursor-default", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:items-center", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:gap-2", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:rounded-
|
|
1704
|
+
return cn("bg-popover-bg text-fg checked:font-medium disabled:text-fg-subtle", textSize[size], mono && "font-mono", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:flex", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:cursor-default", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:items-center", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:gap-2", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:rounded-lg", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:bg-transparent", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:px-2", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:py-1.5", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:not-disabled:hover:bg-popover-highlight", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:focus-visible:bg-popover-highlight", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:focus-visible:outline-hidden", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:disabled:text-fg", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:disabled:opacity-50", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::checkmark]:order-1", "supports-[appearance:base-select]:[@media(hover:hover)_and_(pointer:fine)]:[&::checkmark]:ms-auto");
|
|
1714
1705
|
}
|
|
1715
1706
|
function openPicker(select) {
|
|
1716
1707
|
select.focus();
|
|
@@ -1882,7 +1873,7 @@ const listVariant = {
|
|
|
1882
1873
|
"before:bottom-0 before:h-0.5 before:bg-fg before:[translate:var(--tabs-indicator-x)_0]",
|
|
1883
1874
|
"before:transition-[translate,width]",
|
|
1884
1875
|
"forced-colors:before:bg-[Highlight] forced-colors:before:[forced-color-adjust:none]",
|
|
1885
|
-
"after:pointer-events-none after:absolute after:top-0 after:left-0 after:-z-1 after:rounded-
|
|
1876
|
+
"after:pointer-events-none after:absolute after:top-0 after:left-0 after:-z-1 after:rounded-lg",
|
|
1886
1877
|
"after:bg-fg/7 after:opacity-0 data-hover:after:opacity-100",
|
|
1887
1878
|
"after:h-(--tabs-hover-h) after:w-(--tabs-hover-w)",
|
|
1888
1879
|
"after:[translate:var(--tabs-hover-x)_var(--tabs-hover-y)]",
|
|
@@ -1891,8 +1882,8 @@ const listVariant = {
|
|
|
1891
1882
|
"data-hover-enter:after:transition-opacity"
|
|
1892
1883
|
],
|
|
1893
1884
|
segmented: [
|
|
1894
|
-
"inline-flex w-fit rounded-
|
|
1895
|
-
"before:top-0 before:h-(--tabs-indicator-h) before:rounded-
|
|
1885
|
+
"inline-flex w-fit rounded-md p-1 shadow-[inset_0_0_0_1px_var(--color-ring)]",
|
|
1886
|
+
"before:top-0 before:h-(--tabs-indicator-h) before:rounded-sm before:bg-fg/8",
|
|
1896
1887
|
"before:[translate:var(--tabs-indicator-x)_var(--tabs-indicator-y)]",
|
|
1897
1888
|
"before:transition-[translate,width,height]",
|
|
1898
1889
|
"forced-colors:before:border-2 forced-colors:before:border-[Highlight]"
|
|
@@ -1930,8 +1921,8 @@ const itemBase = [
|
|
|
1930
1921
|
"forced-colors:aria-disabled:text-[GrayText] forced-colors:aria-disabled:opacity-100"
|
|
1931
1922
|
];
|
|
1932
1923
|
const itemVariant = {
|
|
1933
|
-
line: ["rounded-
|
|
1934
|
-
segmented: ["h-full rounded-
|
|
1924
|
+
line: ["rounded-lg data-selected:font-medium aria-[current=page]:font-medium"],
|
|
1925
|
+
segmented: ["h-full rounded-sm font-medium"]
|
|
1935
1926
|
};
|
|
1936
1927
|
const itemSize = {
|
|
1937
1928
|
line: {
|
|
@@ -2254,7 +2245,7 @@ function useHoverPill() {
|
|
|
2254
2245
|
}
|
|
2255
2246
|
const bodyClasses = [
|
|
2256
2247
|
"relative isolate flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto overscroll-contain px-3 py-2",
|
|
2257
|
-
"after:pointer-events-none after:absolute after:top-0 after:left-0 after:-z-1 after:rounded-
|
|
2248
|
+
"after:pointer-events-none after:absolute after:top-0 after:left-0 after:-z-1 after:rounded-md",
|
|
2258
2249
|
"after:bg-fg/5 after:opacity-0 data-hover:after:opacity-100",
|
|
2259
2250
|
"after:h-(--sidebar-hover-h) after:w-(--sidebar-hover-w)",
|
|
2260
2251
|
"after:[translate:var(--sidebar-hover-x)_var(--sidebar-hover-y)]",
|
|
@@ -2309,7 +2300,7 @@ function SidebarGroup({ label, className, children, ...props }) {
|
|
|
2309
2300
|
});
|
|
2310
2301
|
}
|
|
2311
2302
|
const itemClasses = [
|
|
2312
|
-
"flex h-9 w-full items-center gap-2 rounded-
|
|
2303
|
+
"flex h-9 w-full items-center gap-2 rounded-md ps-2.5 pe-2",
|
|
2313
2304
|
"text-sm leading-5 font-medium text-fg-muted no-underline select-none",
|
|
2314
2305
|
"touch-manipulation [-webkit-tap-highlight-color:transparent]",
|
|
2315
2306
|
"transition-[color,background-color] duration-150 ease-[cubic-bezier(0.4,0,0.2,1)]",
|
|
@@ -3402,7 +3393,7 @@ function ToastCard({ record, layout, total, expanded, paused, frontHeight, ownHe
|
|
|
3402
3393
|
onPointerMove: handlePointerMove,
|
|
3403
3394
|
onPointerUp: (event) => endDrag(event, false),
|
|
3404
3395
|
onPointerCancel: (event) => endDrag(event, true),
|
|
3405
|
-
className: cn("group/toast absolute inset-x-0 rounded-
|
|
3396
|
+
className: cn("group/toast absolute inset-x-0 rounded-xl outline-none select-none", edge === "bottom" ? "bottom-0 origin-top" : "top-0 origin-bottom", "transform-[translateY(var(--toast-y))_scale(var(--toast-scale))]", "[translate:var(--toast-swipe-x,0px)_var(--toast-swipe-y,0px)]", "transition-[transform,translate,scale,opacity] duration-350 ease-[cubic-bezier(0.25,0.75,0.6,0.98)]", enterFrom[edge], "starting:opacity-0", "after:absolute after:inset-x-0 after:h-3.75", edge === "bottom" ? "after:bottom-full" : "after:top-full", "data-[visible=false]:pointer-events-none data-[visible=false]:opacity-0", "data-removed:pointer-events-none data-removed:scale-99 data-removed:opacity-0 data-removed:duration-150", "data-removed:data-swipe-out:duration-200 data-removed:data-swipe-out:ease-out", "data-[swipe-out=right]:[translate:calc(100%+24px)_0]", "data-[swipe-out=left]:[translate:calc(-100%-24px)_0]", "data-[swipe-out=bottom]:[translate:0_calc(100%+24px)]", "data-[swipe-out=top]:[translate:0_calc(-100%-24px)]", "data-swiping:transition-none", "group-data-moving/viewport:data-entered:transition-none", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus", touchAction),
|
|
3406
3397
|
children: /* @__PURE__ */ jsx("div", {
|
|
3407
3398
|
"data-slot": "toast-card",
|
|
3408
3399
|
style: { height },
|
package/dist/theme.css
CHANGED
|
@@ -1,267 +1,145 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* swell-ui design tokens
|
|
2
|
+
* swell-ui design tokens and base styles. Neutral grays; brand color is one knob (--color-primary).
|
|
3
3
|
* Import right after tailwindcss. Contains no utilities and no @source:
|
|
4
4
|
* use styles.css to also have Tailwind scan the components.
|
|
5
|
+
*
|
|
6
|
+
* Every color is written once as light-dark(<light>, <dark>). Which side applies follows
|
|
7
|
+
* `color-scheme` on the root: the system by default, forced by data-theme="light" / "dark".
|
|
8
|
+
* Tailwind's build polyfills light-dark() for browsers that lack it.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
|
-
/* Tokens. Light values live on bare :root (and @theme). */
|
|
8
11
|
@theme {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
--color-
|
|
15
|
-
--color-fg
|
|
16
|
-
|
|
17
|
-
--color-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
--color-
|
|
21
|
-
--color-
|
|
22
|
-
--color-
|
|
23
|
-
--color-
|
|
24
|
-
--color-
|
|
25
|
-
--color-
|
|
26
|
-
--color-
|
|
27
|
-
--color-
|
|
12
|
+
/*
|
|
13
|
+
* Brand. The only two tokens to override for a rebrand: input focus, checked controls and
|
|
14
|
+
* their halos derive from these at runtime (plain var() references, no baked copies).
|
|
15
|
+
* Default is neutral: near-black on light, near-white on dark.
|
|
16
|
+
*/
|
|
17
|
+
--color-primary: light-dark(#141414, #f9f9f9);
|
|
18
|
+
--color-primary-fg: light-dark(#f9f9f9, #141414);
|
|
19
|
+
/* Secondary brand color; no component uses it, so it just follows primary unless set */
|
|
20
|
+
--color-accent: var(--color-primary);
|
|
21
|
+
|
|
22
|
+
/* Surfaces and text: pure grays, contrast-checked (fg-subtle >=4.5:1 on every surface) */
|
|
23
|
+
--color-bg: light-dark(#f4f4f4, #141414);
|
|
24
|
+
--color-surface: light-dark(#f9f9f9, #303030);
|
|
25
|
+
--color-surface-raised: light-dark(#ffffff, #303030);
|
|
26
|
+
--color-surface-muted: light-dark(#e6e6e6, #262626); /* dark: sunk below surface */
|
|
27
|
+
--color-fg: light-dark(#141414, #f9f9f9);
|
|
28
|
+
--color-fg-strong: light-dark(#4c4c4c, #aeaeae);
|
|
29
|
+
--color-fg-muted: light-dark(#5d5d5d, #aeaeae);
|
|
30
|
+
--color-fg-subtle: light-dark(#676767, #aaaaaa);
|
|
31
|
+
--color-border: light-dark(#eeeeee, #383838); /* row dividers */
|
|
32
|
+
--color-border-strong: light-dark(#e6e6e6, #3d3d3d);
|
|
33
|
+
--color-ring: light-dark(#cfcfcf, #3d3d3d);
|
|
34
|
+
--color-ring-deep: light-dark(#c0c0c0, #4c4c4c);
|
|
35
|
+
--color-inverse: light-dark(#303030, #141414);
|
|
36
|
+
--color-inverse-fg: light-dark(#f9f9f9, #aeaeae);
|
|
37
|
+
/* Light red + dark text on dark: one danger token can't pass 4.5:1 both as fill and as text */
|
|
38
|
+
--color-danger: light-dark(#b53333, #ee8a80);
|
|
39
|
+
--color-danger-fg: light-dark(#f9f9f9, #141414);
|
|
28
40
|
--color-focus: #3898ec; /* Focus Blue */
|
|
41
|
+
|
|
29
42
|
/*
|
|
30
|
-
* Text fields: a slightly sunk solid fill.
|
|
31
|
-
* Borders are
|
|
32
|
-
*
|
|
43
|
+
* Text fields: a slightly sunk solid fill (fg 4% over bg in oklab, light; white 6%, dark).
|
|
44
|
+
* Borders are kept >=3:1 against both the page and the fill (WCAG 1.4.11).
|
|
45
|
+
* Hover stacks cues (stronger border, lifted fill, faint drop shadow): a border step alone is too subtle.
|
|
33
46
|
*/
|
|
34
|
-
|
|
35
|
-
--color-input-
|
|
36
|
-
--color-input-
|
|
37
|
-
--color-input-
|
|
38
|
-
|
|
39
|
-
--color-input-
|
|
40
|
-
--color-input-
|
|
41
|
-
--color-input-focus: #8b5e3c; /* Warm Brown (primary) */
|
|
42
|
-
--color-input-highlight: rgb(0 0 0 / 0.03);
|
|
47
|
+
--color-input-bg: light-dark(#eaeaea, #1f1f1f);
|
|
48
|
+
--color-input-border: light-dark(#7e7e7e, #818181);
|
|
49
|
+
--color-input-placeholder: light-dark(#696969, #989898);
|
|
50
|
+
--color-input-border-hover: light-dark(#4c4c4c, #aeaeae);
|
|
51
|
+
--color-input-bg-hover: light-dark(#f2f2f2, #262626);
|
|
52
|
+
--color-input-focus: var(--color-primary);
|
|
53
|
+
--color-input-highlight: light-dark(rgb(0 0 0 / 0.03), rgb(255 255 255 / 0.04));
|
|
43
54
|
--shadow-input: inset 0 1px 0 var(--color-input-highlight);
|
|
44
|
-
|
|
55
|
+
/* A drop shadow doesn't read on near-black, so it fades out on dark */
|
|
56
|
+
--shadow-input-lift: 0 1px 2px light-dark(rgb(0 0 0 / 0.06), transparent);
|
|
45
57
|
/* Focus/error halos: decorative 3px rings outside the border */
|
|
46
58
|
--color-input-halo: color-mix(in oklab, var(--color-input-focus) 14%, transparent);
|
|
47
59
|
--color-input-halo-invalid: color-mix(in oklab, var(--color-danger) 12%, transparent);
|
|
48
60
|
--color-input-halo-invalid-focus: color-mix(in oklab, var(--color-danger) 22%, transparent);
|
|
61
|
+
|
|
49
62
|
/*
|
|
50
63
|
* Selection controls (Switch, Checkbox): the checked fill and the mark drawn on it (thumb, check, dash).
|
|
51
64
|
* The off track/box reuses the input border, which is already >=3:1 on the page and the fill.
|
|
52
65
|
*/
|
|
53
|
-
--color-control-checked:
|
|
54
|
-
--color-control-checked-fg:
|
|
55
|
-
/* Thumb: a hairline plus a soft drop straight down
|
|
56
|
-
* look shifted, so the shadow stays horizontally symmetric. */
|
|
66
|
+
--color-control-checked: var(--color-primary);
|
|
67
|
+
--color-control-checked-fg: var(--color-primary-fg);
|
|
68
|
+
/* Thumb: a hairline plus a soft drop straight down (a sideways offset makes the small thumb look shifted) */
|
|
57
69
|
--shadow-switch-thumb: 0 0 0 0.5px rgb(0 0 0 / 0.08), 0 1px 2px rgb(0 0 0 / 0.2);
|
|
70
|
+
|
|
58
71
|
/*
|
|
59
|
-
* Popovers (the Select picker): raised above the page with a
|
|
72
|
+
* Popovers (the Select picker): raised above the page with a hairline and a soft shadow.
|
|
60
73
|
* Highlight is the hovered/keyboard-active row; same fill for both so they never disagree.
|
|
74
|
+
* Dark shadows are darker and denser: the surface itself already separates from near-black.
|
|
61
75
|
*/
|
|
62
|
-
--color-popover-bg: #ffffff;
|
|
63
|
-
--color-popover-border: #
|
|
64
|
-
--color-popover-highlight: #
|
|
65
|
-
--shadow-popover:
|
|
76
|
+
--color-popover-bg: light-dark(#ffffff, #303030);
|
|
77
|
+
--color-popover-border: light-dark(#dedede, #3d3d3d);
|
|
78
|
+
--color-popover-highlight: light-dark(#ededed, #3d3d3d);
|
|
79
|
+
--shadow-popover:
|
|
80
|
+
0 8px 24px light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.4)),
|
|
81
|
+
0 1px 3px light-dark(rgb(0 0 0 / 0.06), rgb(0 0 0 / 0.3));
|
|
66
82
|
/*
|
|
67
|
-
* Dialogs: same surface and hairline as popovers, one elevation step higher
|
|
68
|
-
*
|
|
83
|
+
* Dialogs: same surface and hairline as popovers, one elevation step higher. The backdrop is
|
|
84
|
+
* never blurred; it is darker on dark because a 40% dim barely reads over a near-black page.
|
|
69
85
|
*/
|
|
70
|
-
--shadow-dialog:
|
|
71
|
-
|
|
86
|
+
--shadow-dialog:
|
|
87
|
+
0 16px 48px light-dark(rgb(0 0 0 / 0.12), rgb(0 0 0 / 0.5)),
|
|
88
|
+
0 2px 6px light-dark(rgb(0 0 0 / 0.06), rgb(0 0 0 / 0.3));
|
|
89
|
+
--color-dialog-backdrop: light-dark(rgb(20 20 20 / 0.4), rgb(0 0 0 / 0.6));
|
|
90
|
+
|
|
72
91
|
/*
|
|
73
|
-
* Status tones (Badge,
|
|
92
|
+
* Status tones (Badge, Toast, ...). Per tone: main = solid fill / standalone icon,
|
|
74
93
|
* -fg = text on a solid fill, -subtle-bg/-subtle-fg = tinted badge, -border = its hairline.
|
|
75
|
-
* subtle-bg is main mixed over bg in oklab (14% light / 16% dark),
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* danger main/-fg already exist above; only its subtle-* are new.
|
|
94
|
+
* subtle-bg is main mixed over bg in oklab (14% light / 16% dark), border likewise (35% / 40%),
|
|
95
|
+
* baked to hex; subtle-fg is >=5:1 on subtle-bg. Meaning is carried by text + icon; hue and
|
|
96
|
+
* lightness are spaced so success vs danger also survives red-green CVD.
|
|
79
97
|
*/
|
|
80
|
-
/* success: olive
|
|
81
|
-
--color-success: #394d00;
|
|
82
|
-
--color-success-fg: #
|
|
83
|
-
--color-success-subtle-bg: #
|
|
84
|
-
--color-success-subtle-fg: #3a4c0e;
|
|
85
|
-
--color-success-border: #
|
|
86
|
-
/* warning:
|
|
87
|
-
--color-warning: #a5761d;
|
|
88
|
-
--color-warning-fg: #
|
|
89
|
-
--color-warning-subtle-bg: #
|
|
90
|
-
--color-warning-subtle-fg: #7e5600;
|
|
91
|
-
--color-warning-border: #
|
|
92
|
-
/* danger:
|
|
93
|
-
--color-danger-subtle-bg: #
|
|
94
|
-
--color-danger-subtle-fg: #a2302f;
|
|
95
|
-
--color-danger-border: #
|
|
96
|
-
/* info:
|
|
97
|
-
--color-info: #3e7087;
|
|
98
|
-
--color-info-fg: #
|
|
99
|
-
--color-info-subtle-bg: #
|
|
100
|
-
--color-info-subtle-fg: #356075;
|
|
101
|
-
--color-info-border: #
|
|
102
|
-
/* neutral:
|
|
103
|
-
--color-neutral: #
|
|
104
|
-
--color-neutral-fg: #
|
|
105
|
-
--color-neutral-subtle-bg: #
|
|
106
|
-
--color-neutral-subtle-fg: #
|
|
107
|
-
--color-neutral-border: #
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
--font-serif: "Anthropic Serif", Georgia, serif;
|
|
111
|
-
--font-mono: "Anthropic Mono", ui-monospace, Menlo, monospace;
|
|
112
|
-
|
|
113
|
-
/* Radius scale */
|
|
114
|
-
--radius-sharp: 4px;
|
|
115
|
-
--radius-sm: 6px;
|
|
116
|
-
--radius-md: 8px;
|
|
117
|
-
--radius-lg: 12px;
|
|
118
|
-
--radius-xl: 16px;
|
|
119
|
-
--radius-2xl: 24px;
|
|
120
|
-
--radius-3xl: 32px;
|
|
121
|
-
|
|
98
|
+
/* success: olive; deepest tone so it separates from danger by lightness */
|
|
99
|
+
--color-success: light-dark(#394d00, #758e48);
|
|
100
|
+
--color-success-fg: light-dark(#f9f9f9, #141414);
|
|
101
|
+
--color-success-subtle-bg: light-dark(#d7dbd2, #22251d);
|
|
102
|
+
--color-success-subtle-fg: light-dark(#3a4c0e, #98b070);
|
|
103
|
+
--color-success-border: light-dark(#adb79f, #384129);
|
|
104
|
+
/* warning: amber; dark solid-fg because light text on amber fails 4.5:1 */
|
|
105
|
+
--color-warning: light-dark(#a5761d, #e6b463);
|
|
106
|
+
--color-warning-fg: #141414;
|
|
107
|
+
--color-warning-subtle-bg: light-dark(#e9e2d8, #302a21);
|
|
108
|
+
--color-warning-subtle-fg: light-dark(#7e5600, #e2b56e);
|
|
109
|
+
--color-warning-border: light-dark(#d9c7ad, #5f4e34);
|
|
110
|
+
/* danger: main/-fg are above */
|
|
111
|
+
--color-danger-subtle-bg: light-dark(#efdad8, #322523);
|
|
112
|
+
--color-danger-subtle-fg: light-dark(#a2302f, #f0958c);
|
|
113
|
+
--color-danger-border: light-dark(#e5b3ae, #63403c);
|
|
114
|
+
/* info: muted teal-blue */
|
|
115
|
+
--color-info: light-dark(#3e7087, #74a6bf);
|
|
116
|
+
--color-info-fg: light-dark(#f9f9f9, #141414);
|
|
117
|
+
--color-info-subtle-bg: light-dark(#dae1e4, #22282b);
|
|
118
|
+
--color-info-subtle-fg: light-dark(#356075, #87b4cb);
|
|
119
|
+
--color-info-border: light-dark(#b3c4cd, #384952);
|
|
120
|
+
/* neutral: gray */
|
|
121
|
+
--color-neutral: light-dark(#636363, #aeaeae);
|
|
122
|
+
--color-neutral-fg: light-dark(#f9f9f9, #141414);
|
|
123
|
+
--color-neutral-subtle-bg: light-dark(#dedede, #292929);
|
|
124
|
+
--color-neutral-subtle-fg: light-dark(#5b5b5b, #b2b2b2);
|
|
125
|
+
--color-neutral-border: light-dark(#bfbfbf, #4c4c4c);
|
|
126
|
+
|
|
127
|
+
/* Fonts and radii are Tailwind's defaults; see the README to change them */
|
|
122
128
|
--shadow-whisper: rgba(0, 0, 0, 0.05) 0 4px 24px;
|
|
123
129
|
--container-page: 1200px;
|
|
124
130
|
}
|
|
125
131
|
|
|
132
|
+
/* Follow the system unless data-theme forces one; light-dark() above reads this */
|
|
126
133
|
:root {
|
|
127
|
-
color-scheme: light;
|
|
134
|
+
color-scheme: light dark;
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
:
|
|
132
|
-
@media (prefers-color-scheme: dark) {
|
|
133
|
-
color-scheme: dark;
|
|
134
|
-
--color-bg: #141413;
|
|
135
|
-
--color-surface: #30302e;
|
|
136
|
-
--color-surface-raised: #30302e;
|
|
137
|
-
--color-surface-muted: #262625; /* sunk below Dark Surface: header rows, hover fills */
|
|
138
|
-
--color-fg: #faf9f5;
|
|
139
|
-
--color-fg-strong: #b0aea5;
|
|
140
|
-
--color-fg-muted: #b0aea5;
|
|
141
|
-
--color-fg-subtle: #a8a79e; /* >=4.5:1 on raised and highlighted surfaces */
|
|
142
|
-
--color-border: #383836; /* between Dark Surface and Dark Warm: row dividers */
|
|
143
|
-
--color-border-strong: #3d3d3a; /* Dark Warm */
|
|
144
|
-
--color-ring: #3d3d3a; /* Dark Warm */
|
|
145
|
-
--color-ring-deep: #4d4c48; /* Charcoal Warm */
|
|
146
|
-
--color-inverse: #141413;
|
|
147
|
-
--color-inverse-fg: #b0aea5;
|
|
148
|
-
--color-danger: #ee8a80;
|
|
149
|
-
--color-danger-fg: #141413;
|
|
150
|
-
--color-input-bg: #1f1f1e; /* white 6% over bg in oklab */
|
|
151
|
-
--color-input-border: #81817e;
|
|
152
|
-
--color-input-placeholder: #9a998f;
|
|
153
|
-
--color-input-border-hover: #b0aea5;
|
|
154
|
-
--color-input-bg-hover: #262625;
|
|
155
|
-
--color-input-focus: #c49a6c; /* Warm Tan: primary is too dark on dark surfaces */
|
|
156
|
-
--color-control-checked: #c49a6c; /* Warm Tan, as the focus color */
|
|
157
|
-
--color-control-checked-fg: #141413; /* white on tan is only ~2.6:1; near-black is 7.2:1 */
|
|
158
|
-
--color-input-highlight: rgb(255 255 255 / 0.04);
|
|
159
|
-
--shadow-input-lift: 0 0 #0000; /* a drop shadow doesn't read on near-black */
|
|
160
|
-
--color-popover-bg: #30302e; /* Dark Surface */
|
|
161
|
-
--color-popover-border: #3d3d3a; /* Dark Warm */
|
|
162
|
-
--color-popover-highlight: #3d3d3a;
|
|
163
|
-
/* Darker and denser than light: the surface itself already separates from near-black */
|
|
164
|
-
--shadow-popover: 0 8px 24px rgb(0 0 0 / 0.4), 0 1px 3px rgb(0 0 0 / 0.3);
|
|
165
|
-
--shadow-dialog: 0 16px 48px rgb(0 0 0 / 0.5), 0 2px 6px rgb(0 0 0 / 0.3);
|
|
166
|
-
/* Darker than light: a 40% dim barely reads over a near-black page */
|
|
167
|
-
--color-dialog-backdrop: rgb(0 0 0 / 0.6);
|
|
168
|
-
/* Status tones: lightened for dark surfaces, solid-fg flips to near-black */
|
|
169
|
-
/* success: olive, lightened for dark; kept darker than danger for CVD */
|
|
170
|
-
--color-success: #758e48;
|
|
171
|
-
--color-success-fg: #141413;
|
|
172
|
-
--color-success-subtle-bg: #22251c;
|
|
173
|
-
--color-success-subtle-fg: #98b070;
|
|
174
|
-
--color-success-border: #384128;
|
|
175
|
-
/* warning: amber */
|
|
176
|
-
--color-warning: #e6b463;
|
|
177
|
-
--color-warning-fg: #141413;
|
|
178
|
-
--color-warning-subtle-bg: #302a20;
|
|
179
|
-
--color-warning-subtle-fg: #e2b56e;
|
|
180
|
-
--color-warning-border: #5f4e33;
|
|
181
|
-
/* danger: existing (unchanged) */
|
|
182
|
-
--color-danger-subtle-bg: #322522;
|
|
183
|
-
--color-danger-subtle-fg: #f0958c;
|
|
184
|
-
--color-danger-border: #63403b;
|
|
185
|
-
/* info: dusty teal-blue */
|
|
186
|
-
--color-info: #74a6bf;
|
|
187
|
-
--color-info-fg: #141413;
|
|
188
|
-
--color-info-subtle-bg: #22282a;
|
|
189
|
-
--color-info-subtle-fg: #87b4cb;
|
|
190
|
-
--color-info-border: #384951;
|
|
191
|
-
/* neutral: Warm Silver */
|
|
192
|
-
--color-neutral: #b0aea5;
|
|
193
|
-
--color-neutral-fg: #141413;
|
|
194
|
-
--color-neutral-subtle-bg: #292927;
|
|
195
|
-
--color-neutral-subtle-fg: #b4b2aa;
|
|
196
|
-
--color-neutral-border: #4c4c48;
|
|
197
|
-
}
|
|
137
|
+
:root[data-theme="light"] {
|
|
138
|
+
color-scheme: light;
|
|
198
139
|
}
|
|
199
140
|
|
|
200
141
|
:root[data-theme="dark"] {
|
|
201
142
|
color-scheme: dark;
|
|
202
|
-
--color-bg: #141413;
|
|
203
|
-
--color-surface: #30302e;
|
|
204
|
-
--color-surface-raised: #30302e;
|
|
205
|
-
--color-surface-muted: #262625; /* sunk below Dark Surface: header rows, hover fills */
|
|
206
|
-
--color-fg: #faf9f5;
|
|
207
|
-
--color-fg-strong: #b0aea5;
|
|
208
|
-
--color-fg-muted: #b0aea5;
|
|
209
|
-
--color-fg-subtle: #a8a79e; /* >=4.5:1 on raised and highlighted surfaces */
|
|
210
|
-
--color-border: #383836; /* between Dark Surface and Dark Warm: row dividers */
|
|
211
|
-
--color-border-strong: #3d3d3a; /* Dark Warm */
|
|
212
|
-
--color-ring: #3d3d3a; /* Dark Warm */
|
|
213
|
-
--color-ring-deep: #4d4c48; /* Charcoal Warm */
|
|
214
|
-
--color-inverse: #141413;
|
|
215
|
-
--color-inverse-fg: #b0aea5;
|
|
216
|
-
/* Light red + dark text: a single danger token can't pass 4.5:1 both as fill and as text on dark */
|
|
217
|
-
--color-danger: #ee8a80;
|
|
218
|
-
--color-danger-fg: #141413;
|
|
219
|
-
--color-input-bg: #1f1f1e; /* white 6% over bg in oklab */
|
|
220
|
-
--color-input-border: #81817e;
|
|
221
|
-
--color-input-placeholder: #9a998f;
|
|
222
|
-
--color-input-border-hover: #b0aea5;
|
|
223
|
-
--color-input-bg-hover: #262625;
|
|
224
|
-
--color-input-focus: #c49a6c; /* Warm Tan: primary is too dark on dark surfaces */
|
|
225
|
-
--color-control-checked: #c49a6c; /* Warm Tan, as the focus color */
|
|
226
|
-
--color-control-checked-fg: #141413; /* white on tan is only ~2.6:1; near-black is 7.2:1 */
|
|
227
|
-
--color-input-highlight: rgb(255 255 255 / 0.04);
|
|
228
|
-
--shadow-input-lift: 0 0 #0000; /* a drop shadow doesn't read on near-black */
|
|
229
|
-
--color-popover-bg: #30302e; /* Dark Surface */
|
|
230
|
-
--color-popover-border: #3d3d3a; /* Dark Warm */
|
|
231
|
-
--color-popover-highlight: #3d3d3a;
|
|
232
|
-
/* Darker and denser than light: the surface itself already separates from near-black */
|
|
233
|
-
--shadow-popover: 0 8px 24px rgb(0 0 0 / 0.4), 0 1px 3px rgb(0 0 0 / 0.3);
|
|
234
|
-
--shadow-dialog: 0 16px 48px rgb(0 0 0 / 0.5), 0 2px 6px rgb(0 0 0 / 0.3);
|
|
235
|
-
--color-dialog-backdrop: rgb(0 0 0 / 0.6);
|
|
236
|
-
/* Status tones: lightened for dark surfaces, solid-fg flips to near-black */
|
|
237
|
-
/* success: olive, lightened for dark; kept darker than danger for CVD */
|
|
238
|
-
--color-success: #758e48;
|
|
239
|
-
--color-success-fg: #141413;
|
|
240
|
-
--color-success-subtle-bg: #22251c;
|
|
241
|
-
--color-success-subtle-fg: #98b070;
|
|
242
|
-
--color-success-border: #384128;
|
|
243
|
-
/* warning: amber */
|
|
244
|
-
--color-warning: #e6b463;
|
|
245
|
-
--color-warning-fg: #141413;
|
|
246
|
-
--color-warning-subtle-bg: #302a20;
|
|
247
|
-
--color-warning-subtle-fg: #e2b56e;
|
|
248
|
-
--color-warning-border: #5f4e33;
|
|
249
|
-
/* danger: existing (unchanged) */
|
|
250
|
-
--color-danger-subtle-bg: #322522;
|
|
251
|
-
--color-danger-subtle-fg: #f0958c;
|
|
252
|
-
--color-danger-border: #63403b;
|
|
253
|
-
/* info: dusty teal-blue */
|
|
254
|
-
--color-info: #74a6bf;
|
|
255
|
-
--color-info-fg: #141413;
|
|
256
|
-
--color-info-subtle-bg: #22282a;
|
|
257
|
-
--color-info-subtle-fg: #87b4cb;
|
|
258
|
-
--color-info-border: #384951;
|
|
259
|
-
/* neutral: Warm Silver */
|
|
260
|
-
--color-neutral: #b0aea5;
|
|
261
|
-
--color-neutral-fg: #141413;
|
|
262
|
-
--color-neutral-subtle-bg: #292927;
|
|
263
|
-
--color-neutral-subtle-fg: #b4b2aa;
|
|
264
|
-
--color-neutral-border: #4c4c48;
|
|
265
143
|
}
|
|
266
144
|
|
|
267
145
|
/* `dark:` follows the tokens: data-theme="dark" on the root, or the system when not forced light */
|
|
@@ -305,7 +183,7 @@
|
|
|
305
183
|
}
|
|
306
184
|
}
|
|
307
185
|
|
|
308
|
-
/* Layered so the app's own utilities (e.g. a body background
|
|
186
|
+
/* Layered so the app's own utilities (e.g. a body background) win */
|
|
309
187
|
@layer base {
|
|
310
188
|
body {
|
|
311
189
|
margin: 0;
|
|
@@ -314,14 +192,6 @@
|
|
|
314
192
|
font-family: var(--font-sans);
|
|
315
193
|
line-height: 1.6;
|
|
316
194
|
}
|
|
317
|
-
|
|
318
|
-
/* Dialog and Drawer titles rely on this */
|
|
319
|
-
h1,
|
|
320
|
-
h2,
|
|
321
|
-
h3 {
|
|
322
|
-
font-family: var(--font-serif);
|
|
323
|
-
font-weight: 500;
|
|
324
|
-
}
|
|
325
195
|
}
|
|
326
196
|
|
|
327
197
|
/* Unlayered: beats Tailwind's layered utilities (e.g. `transition-*`) on cascade order alone */
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Warm theme: parchment surfaces, warm grays and a brown primary (swell-ui's original look).
|
|
3
|
+
* Import after swell-ui's styles.css (or theme.css); add typography.css for the serif headings.
|
|
4
|
+
* Overrides the color tokens only; input focus and checked controls still derive from primary.
|
|
5
|
+
*/
|
|
6
|
+
@theme {
|
|
7
|
+
/* Warm Brown; Warm Tan on dark, where the brown is too dark against the page */
|
|
8
|
+
--color-primary: light-dark(#8b5e3c, #c49a6c);
|
|
9
|
+
--color-primary-fg: light-dark(#faf9f5, #141413);
|
|
10
|
+
--color-accent: #c49a6c; /* Warm Tan */
|
|
11
|
+
|
|
12
|
+
--color-bg: light-dark(#f5f4ed, #141413); /* Parchment */
|
|
13
|
+
--color-surface: light-dark(#faf9f5, #30302e); /* Ivory / Dark Surface */
|
|
14
|
+
--color-surface-raised: light-dark(#ffffff, #30302e);
|
|
15
|
+
--color-surface-muted: light-dark(#e8e6dc, #262625); /* Warm Sand */
|
|
16
|
+
--color-fg: light-dark(#141413, #faf9f5);
|
|
17
|
+
--color-fg-strong: light-dark(#4d4c48, #b0aea5);
|
|
18
|
+
--color-fg-muted: light-dark(#5e5d59, #b0aea5);
|
|
19
|
+
--color-fg-subtle: light-dark(#686761, #a8a79e);
|
|
20
|
+
--color-border: light-dark(#f0eee6, #383836);
|
|
21
|
+
--color-border-strong: light-dark(#e8e6dc, #3d3d3a);
|
|
22
|
+
--color-ring: light-dark(#d1cfc5, #3d3d3a);
|
|
23
|
+
--color-ring-deep: light-dark(#c2c0b6, #4d4c48);
|
|
24
|
+
--color-inverse: light-dark(#30302e, #141413);
|
|
25
|
+
--color-inverse-fg: light-dark(#faf9f5, #b0aea5);
|
|
26
|
+
--color-danger: light-dark(#b53333, #ee8a80); /* Error Crimson */
|
|
27
|
+
--color-danger-fg: light-dark(#faf9f5, #141413);
|
|
28
|
+
|
|
29
|
+
--color-input-bg: light-dark(#ebeae3, #1f1f1e);
|
|
30
|
+
--color-input-border: light-dark(#7f7e7b, #81817e);
|
|
31
|
+
--color-input-placeholder: light-dark(#6a6964, #9a998f);
|
|
32
|
+
--color-input-border-hover: light-dark(#4d4c48, #b0aea5);
|
|
33
|
+
--color-input-bg-hover: light-dark(#f3f2ec, #262625);
|
|
34
|
+
|
|
35
|
+
--color-popover-bg: light-dark(#ffffff, #30302e);
|
|
36
|
+
--color-popover-border: light-dark(#e0ded4, #3d3d3a);
|
|
37
|
+
--color-popover-highlight: light-dark(#efede5, #3d3d3a);
|
|
38
|
+
--color-dialog-backdrop: light-dark(rgb(20 20 19 / 0.4), rgb(0 0 0 / 0.6));
|
|
39
|
+
|
|
40
|
+
--color-success: light-dark(#394d00, #758e48);
|
|
41
|
+
--color-success-fg: light-dark(#faf9f5, #141413);
|
|
42
|
+
--color-success-subtle-bg: light-dark(#d8dbcc, #22251c);
|
|
43
|
+
--color-success-subtle-fg: light-dark(#3a4c0e, #98b070);
|
|
44
|
+
--color-success-border: light-dark(#aeb79b, #384128);
|
|
45
|
+
--color-warning: light-dark(#a5761d, #e6b463);
|
|
46
|
+
--color-warning-fg: #141413;
|
|
47
|
+
--color-warning-subtle-bg: light-dark(#eae2d1, #302a20);
|
|
48
|
+
--color-warning-subtle-fg: light-dark(#7e5600, #e2b56e);
|
|
49
|
+
--color-warning-border: light-dark(#d9c7a9, #5f4e33);
|
|
50
|
+
--color-danger-subtle-bg: light-dark(#f0dad2, #322522);
|
|
51
|
+
--color-danger-subtle-fg: light-dark(#a2302f, #f0958c);
|
|
52
|
+
--color-danger-border: light-dark(#e5b3aa, #63403b);
|
|
53
|
+
--color-info: light-dark(#3e7087, #74a6bf);
|
|
54
|
+
--color-info-fg: light-dark(#faf9f5, #141413);
|
|
55
|
+
--color-info-subtle-bg: light-dark(#dbe1de, #22282a);
|
|
56
|
+
--color-info-subtle-fg: light-dark(#356075, #87b4cb);
|
|
57
|
+
--color-info-border: light-dark(#b4c4c9, #384951);
|
|
58
|
+
--color-neutral: light-dark(#66635c, #b0aea5);
|
|
59
|
+
--color-neutral-fg: light-dark(#faf9f5, #141413);
|
|
60
|
+
--color-neutral-subtle-bg: light-dark(#e0ded7, #292927);
|
|
61
|
+
--color-neutral-subtle-fg: light-dark(#5e5b55, #b4b2aa);
|
|
62
|
+
--color-neutral-border: light-dark(#c0bfb8, #4c4c48);
|
|
63
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Opt-in editorial headings: h1-h3 in the serif stack at weight 500.
|
|
3
|
+
* Import after swell-ui's styles.css (or theme.css). Layered, so utilities on a heading still win.
|
|
4
|
+
*/
|
|
5
|
+
@layer base {
|
|
6
|
+
h1,
|
|
7
|
+
h2,
|
|
8
|
+
h3 {
|
|
9
|
+
font-family: var(--font-serif);
|
|
10
|
+
font-weight: 500;
|
|
11
|
+
}
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weichenlin/swell-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Hand-built React components styled with Tailwind CSS v4.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"./theme.css": "./dist/theme.css",
|
|
18
18
|
"./styles.css": "./dist/styles.css",
|
|
19
|
+
"./typography.css": "./dist/typography.css",
|
|
20
|
+
"./themes/warm.css": "./dist/themes/warm.css",
|
|
19
21
|
"./package.json": "./package.json"
|
|
20
22
|
},
|
|
21
23
|
"publishConfig": {
|