@sigx/zero-daisyui 0.2.0-beta.6 → 0.4.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 +78 -5
- package/dist/audit.json +271915 -0
- package/dist/components.d.ts +75 -18
- package/dist/components.js +15 -3
- package/dist/css/breakpoints.css +7 -0
- package/dist/css/components/avatar.css +12 -0
- package/dist/css/components/badge.css +130 -1
- package/dist/css/components/box.css +58 -0
- package/dist/css/components/button.css +17 -17
- package/dist/css/components/center.css +17 -0
- package/dist/css/components/checkbox.css +1 -1
- package/dist/css/components/combobox.css +57 -6
- package/dist/css/components/container.css +16 -0
- package/dist/css/components/countdown.css +5 -0
- package/dist/css/components/dialog.css +3 -2
- package/dist/css/components/drawer.css +102 -6
- package/dist/css/components/empty-state.css +164 -0
- package/dist/css/components/grid.css +32 -0
- package/dist/css/components/kbd.css +1 -1
- package/dist/css/components/menu.css +4 -4
- package/dist/css/components/nav-list.css +165 -0
- package/dist/css/components/radio-group.css +2 -2
- package/dist/css/components/select.css +1 -1
- package/dist/css/components/spacer.css +13 -0
- package/dist/css/components/stack.css +39 -0
- package/dist/css/components/steps.css +109 -28
- package/dist/css/components/table.css +276 -2
- package/dist/css/components/timeline.css +64 -1
- package/dist/css/components/tree-view.css +2 -2
- package/dist/css/index.css +2220 -279
- package/dist/css/index.d.ts +2 -0
- package/dist/css/tokens.css +9 -0
- package/dist/design-system.d.ts.map +1 -1
- package/dist/design-system.js +15 -8
- package/dist/design-system.js.map +1 -1
- package/dist/lynx/components/avatar.css +12 -0
- package/dist/lynx/components/badge.css +56 -1
- package/dist/lynx/components/box.css +54 -0
- package/dist/lynx/components/breadcrumbs.css +10 -10
- package/dist/lynx/components/button.css +15 -4
- package/dist/lynx/components/card.css +5 -5
- package/dist/lynx/components/center.css +13 -0
- package/dist/lynx/components/chat.css +10 -10
- package/dist/lynx/components/checkbox.css +1 -1
- package/dist/lynx/components/combobox.css +49 -6
- package/dist/lynx/components/container.css +11 -0
- package/dist/lynx/components/countdown.css +5 -0
- package/dist/lynx/components/dialog.css +8 -7
- package/dist/lynx/components/drawer.css +18 -0
- package/dist/lynx/components/empty-state.css +342 -0
- package/dist/lynx/components/file-upload.css +10 -10
- package/dist/lynx/components/grid.css +28 -0
- package/dist/lynx/components/kbd.css +1 -1
- package/dist/lynx/components/menu.css +4 -4
- package/dist/lynx/components/nav-list.css +302 -0
- package/dist/lynx/components/pagination.css +5 -5
- package/dist/lynx/components/radial-progress.css +5 -5
- package/dist/lynx/components/radio-group.css +2 -2
- package/dist/lynx/components/rating-group.css +5 -5
- package/dist/lynx/components/select.css +6 -6
- package/dist/lynx/components/spacer.css +11 -0
- package/dist/lynx/components/stack.css +35 -0
- package/dist/lynx/components/stats.css +10 -10
- package/dist/lynx/components/steps.css +99 -59
- package/dist/lynx/components/switch.css +5 -5
- package/dist/lynx/components/table.css +18 -12
- package/dist/lynx/components/tabs.css +5 -5
- package/dist/lynx/components/timeline.css +14 -5
- package/dist/lynx/components/tree-view.css +2 -2
- package/dist/lynx/index.css +1254 -345
- package/dist/lynx/manifest.json +354 -37
- package/dist/lynx/tokens.css +36 -0
- package/dist/manifest.json +351 -34
- package/dist/recipes.d.ts +17 -10
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +582 -173
- package/dist/recipes.js.map +1 -1
- package/dist/register.d.ts +89 -14
- package/dist/report.json +2476 -564
- package/dist/tokens.d.ts +22 -11
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +57 -7
- package/dist/tokens.js.map +1 -1
- package/package.json +28 -10
- package/src/design-system.ts +15 -8
- package/src/recipes.ts +594 -177
- package/src/tokens.ts +54 -7
- package/dist/css/components/native-select.css +0 -128
- package/dist/lynx/components/native-select.css +0 -130
package/src/recipes.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* package: a design system is data, and "looks like daisy" is one possible
|
|
5
5
|
* value of that data.
|
|
6
6
|
*/
|
|
7
|
-
import type { CssProps, PartStyles, RecipeInput
|
|
8
|
-
import {
|
|
7
|
+
import type { CssProps, PartStyles, RecipeInput } from '@sigx/zero-kit';
|
|
8
|
+
import { axisRoles, tableStackAt } from '@sigx/zero-kit/define';
|
|
9
|
+
import { roles, tokens } from './tokens.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Every role a consumer can pass as `color`, derived from the declaration
|
|
@@ -14,9 +15,7 @@ import { roles } from './tokens.js';
|
|
|
14
15
|
* `-content` or `-soft` are fills or hairlines, not action colours; this
|
|
15
16
|
* design system declares none.
|
|
16
17
|
*/
|
|
17
|
-
const ROLES =
|
|
18
|
-
.filter(([, decl]) => decl.content !== false && decl.soft !== false)
|
|
19
|
-
.map(([name]) => name);
|
|
18
|
+
const ROLES = axisRoles(roles);
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* "…and the reading direction is right-to-left" — appended to a selector, never
|
|
@@ -537,6 +536,13 @@ const disclosureSizes: Record<string, Record<string, PartStyles>> = {
|
|
|
537
536
|
// daisy "collapse collapse-arrow" flavor.
|
|
538
537
|
export const collapsible: RecipeInput = {
|
|
539
538
|
component: 'collapsible',
|
|
539
|
+
// Public to a design system derived from this one (#73).
|
|
540
|
+
hooks: {
|
|
541
|
+
properties: {
|
|
542
|
+
'--collapsible-accent': 'The accent of the open trigger.',
|
|
543
|
+
},
|
|
544
|
+
pseudo: { trigger: ['::after'] },
|
|
545
|
+
},
|
|
540
546
|
// The accent is the open heading's INK, so the default is base-content —
|
|
541
547
|
// the un-attributed render stays exactly daisy's neutral collapse and a
|
|
542
548
|
// role only arrives through `data-color`.
|
|
@@ -611,6 +617,14 @@ export const collapsible: RecipeInput = {
|
|
|
611
617
|
// daisy "toggle" flavor.
|
|
612
618
|
export const switchRecipe: RecipeInput = {
|
|
613
619
|
component: 'switch',
|
|
620
|
+
// Public to a design system derived from this one (#73).
|
|
621
|
+
hooks: {
|
|
622
|
+
properties: {
|
|
623
|
+
'--switch-size': 'The height; width, padding and knob travel derive from it.',
|
|
624
|
+
'--switch-accent': 'The checked track and knob accent.',
|
|
625
|
+
'--switch-ink': 'The unchecked border, knob and ring ink (daisy --input-color).',
|
|
626
|
+
},
|
|
627
|
+
},
|
|
614
628
|
/**
|
|
615
629
|
* daisy drives the whole toggle off ONE size, exactly as it drives the
|
|
616
630
|
* checkbox: `--size` is the height, the padding is a fixed eighth of it,
|
|
@@ -941,6 +955,10 @@ const lynxBtnSizes = (part: string): Record<string, Record<string, PartStyles>>
|
|
|
941
955
|
|
|
942
956
|
export const dialog: RecipeInput = {
|
|
943
957
|
component: 'dialog',
|
|
958
|
+
// Public to a design system derived from this one (#73).
|
|
959
|
+
hooks: {
|
|
960
|
+
keyframes: ['zero-daisy-pop'],
|
|
961
|
+
},
|
|
944
962
|
keyframes: {
|
|
945
963
|
'zero-daisy-pop': 'from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); }',
|
|
946
964
|
},
|
|
@@ -957,7 +975,12 @@ export const dialog: RecipeInput = {
|
|
|
957
975
|
},
|
|
958
976
|
popup: withPresence(popupPresence('translateY(8px) scale(0.97)'), {
|
|
959
977
|
base: {
|
|
960
|
-
|
|
978
|
+
// A <dialog> keeps the UA's `content-box`, and zero ships no
|
|
979
|
+
// reset — so a width or max-width meant to leave a gutter
|
|
980
|
+
// grew by the padding, and at phone width the popup ran past
|
|
981
|
+
// both edges (#101). Its box is the border box.
|
|
982
|
+
boxSizing: 'border-box',
|
|
983
|
+
padding: 'var(--space-2xl)',
|
|
961
984
|
maxWidth: '32rem',
|
|
962
985
|
width: 'calc(100% - 2rem)',
|
|
963
986
|
background: 'var(--color-base-100)',
|
|
@@ -987,14 +1010,15 @@ export const dialog: RecipeInput = {
|
|
|
987
1010
|
},
|
|
988
1011
|
// daisy's `modal-action`: the actions sit at the trailing edge, one
|
|
989
1012
|
// step of the button gap apart, pushed down by the modal's own
|
|
990
|
-
// padding
|
|
991
|
-
//
|
|
1013
|
+
// padding — daisy writes 1.5rem for both, and both now read the top
|
|
1014
|
+
// of the ramp (`--space-2xl`, 1.25rem) so a density override moves
|
|
1015
|
+
// the popup's inset and the footer's push-down together.
|
|
992
1016
|
footer: {
|
|
993
1017
|
base: {
|
|
994
1018
|
display: 'flex',
|
|
995
1019
|
justifyContent: 'flex-end',
|
|
996
1020
|
gap: 'var(--space-md)',
|
|
997
|
-
marginBlockStart: '
|
|
1021
|
+
marginBlockStart: 'var(--space-2xl)',
|
|
998
1022
|
},
|
|
999
1023
|
},
|
|
1000
1024
|
close: {
|
|
@@ -1027,7 +1051,7 @@ export const dialog: RecipeInput = {
|
|
|
1027
1051
|
trigger: { base: lynxBtnPad(4) },
|
|
1028
1052
|
close: { base: lynxBtnPad(4) },
|
|
1029
1053
|
cancel: { base: lynxBtnPad(4) },
|
|
1030
|
-
footer: { base: { marginTop: '
|
|
1054
|
+
footer: { base: { marginTop: 'var(--space-2xl)' } },
|
|
1031
1055
|
},
|
|
1032
1056
|
variants: { size: lynxBtnSizes('trigger') },
|
|
1033
1057
|
},
|
|
@@ -1151,7 +1175,7 @@ export const menu: RecipeInput = {
|
|
|
1151
1175
|
base: {
|
|
1152
1176
|
display: 'flex',
|
|
1153
1177
|
alignItems: 'center',
|
|
1154
|
-
gap: '
|
|
1178
|
+
gap: 'var(--space-md)',
|
|
1155
1179
|
padding: 'var(--space-md) var(--space-lg)',
|
|
1156
1180
|
fontSize: 'var(--text-sm)',
|
|
1157
1181
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -1171,7 +1195,7 @@ export const menu: RecipeInput = {
|
|
|
1171
1195
|
base: {
|
|
1172
1196
|
display: 'flex',
|
|
1173
1197
|
alignItems: 'center',
|
|
1174
|
-
gap: '
|
|
1198
|
+
gap: 'var(--space-md)',
|
|
1175
1199
|
padding: 'var(--space-md) var(--space-lg)',
|
|
1176
1200
|
fontSize: 'var(--text-sm)',
|
|
1177
1201
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -1190,7 +1214,7 @@ export const menu: RecipeInput = {
|
|
|
1190
1214
|
base: {
|
|
1191
1215
|
display: 'flex',
|
|
1192
1216
|
alignItems: 'center',
|
|
1193
|
-
gap: '
|
|
1217
|
+
gap: 'var(--space-md)',
|
|
1194
1218
|
padding: 'var(--space-md) var(--space-lg)',
|
|
1195
1219
|
fontSize: 'var(--text-sm)',
|
|
1196
1220
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -1225,7 +1249,7 @@ export const menu: RecipeInput = {
|
|
|
1225
1249
|
base: {
|
|
1226
1250
|
display: 'flex',
|
|
1227
1251
|
alignItems: 'center',
|
|
1228
|
-
gap: '
|
|
1252
|
+
gap: 'var(--space-md)',
|
|
1229
1253
|
padding: 'var(--space-md) var(--space-lg)',
|
|
1230
1254
|
fontSize: 'var(--text-sm)',
|
|
1231
1255
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -1406,7 +1430,7 @@ export const checkbox: RecipeInput = {
|
|
|
1406
1430
|
},
|
|
1407
1431
|
parts: {
|
|
1408
1432
|
root: {
|
|
1409
|
-
base: { display: 'inline-flex', alignItems: 'center', gap: '
|
|
1433
|
+
base: { display: 'inline-flex', alignItems: 'center', gap: 'var(--space-md)', cursor: 'pointer' },
|
|
1410
1434
|
states: {
|
|
1411
1435
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
1412
1436
|
checked: {}, unchecked: {}, indeterminate: {},
|
|
@@ -1605,7 +1629,7 @@ export const radioGroup: RecipeInput = {
|
|
|
1605
1629
|
},
|
|
1606
1630
|
parts: {
|
|
1607
1631
|
root: {
|
|
1608
|
-
base: { display: 'flex', flexDirection: 'column', gap: '
|
|
1632
|
+
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-md)' },
|
|
1609
1633
|
states: { invalid: {}, required: {} },
|
|
1610
1634
|
selectors: {
|
|
1611
1635
|
// `invalid` is a fact about the GROUP — `item-control` carries
|
|
@@ -1619,7 +1643,7 @@ export const radioGroup: RecipeInput = {
|
|
|
1619
1643
|
states: { disabled: { opacity: 'var(--disabled-opacity)' } },
|
|
1620
1644
|
},
|
|
1621
1645
|
item: {
|
|
1622
|
-
base: { display: 'inline-flex', alignItems: 'center', gap: '
|
|
1646
|
+
base: { display: 'inline-flex', alignItems: 'center', gap: 'var(--space-md)', cursor: 'pointer' },
|
|
1623
1647
|
states: {
|
|
1624
1648
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
1625
1649
|
checked: {}, unchecked: {},
|
|
@@ -2209,7 +2233,7 @@ export const select: RecipeInput = {
|
|
|
2209
2233
|
display: 'flex',
|
|
2210
2234
|
alignItems: 'center',
|
|
2211
2235
|
justifyContent: 'space-between',
|
|
2212
|
-
gap: '
|
|
2236
|
+
gap: 'var(--space-md)',
|
|
2213
2237
|
padding: 'var(--space-md) var(--space-lg)',
|
|
2214
2238
|
fontSize: 'var(--text-sm)',
|
|
2215
2239
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -2278,6 +2302,15 @@ const btnHeight = (step: keyof typeof BTN_STEPS): string =>
|
|
|
2278
2302
|
|
|
2279
2303
|
export const button: RecipeInput = {
|
|
2280
2304
|
component: 'button',
|
|
2305
|
+
// Public to a design system derived from this one (#73).
|
|
2306
|
+
hooks: {
|
|
2307
|
+
properties: {
|
|
2308
|
+
'--btn-accent': 'The fill of the solid variant.',
|
|
2309
|
+
'--btn-on-accent': 'The ink on --btn-accent.',
|
|
2310
|
+
'--btn-soft': 'The soft fill (soft variant, unfilled hover).',
|
|
2311
|
+
'--btn-ink': 'The ink of the unfilled variants.',
|
|
2312
|
+
},
|
|
2313
|
+
},
|
|
2281
2314
|
// The two axes meet here instead of multiplying. `color` sets the accent
|
|
2282
2315
|
// pair; `variant` decides how the accent is used. 8 + 4 + 5 rules rather
|
|
2283
2316
|
// than 8 × 4.
|
|
@@ -2288,9 +2321,34 @@ export const button: RecipeInput = {
|
|
|
2288
2321
|
'--btn-ink': roleInk('primary'),
|
|
2289
2322
|
},
|
|
2290
2323
|
parts: {
|
|
2324
|
+
// The loading spinner (#50): a real part zero renders before the
|
|
2325
|
+
// label while `loading`, drawn as a ring in `currentColor` with one
|
|
2326
|
+
// transparent quadrant — it takes whatever ink the variant chose.
|
|
2327
|
+
// A literal duration and an explicit reduced-motion `none`: the kit
|
|
2328
|
+
// collapses `--duration-*` under reduced motion, and an infinite
|
|
2329
|
+
// loop at ~0s strobes rather than stops.
|
|
2330
|
+
// daisy marks a loading button with a `loading loading-spinner` span;
|
|
2331
|
+
// this is that span, zero's own now (it was a `loading` modifier's
|
|
2332
|
+
// `::before` until #50 made loading a Button state).
|
|
2333
|
+
spinner: {
|
|
2334
|
+
base: {
|
|
2335
|
+
boxSizing: 'border-box',
|
|
2336
|
+
inlineSize: '1em',
|
|
2337
|
+
blockSize: '1em',
|
|
2338
|
+
flex: 'none',
|
|
2339
|
+
borderRadius: '9999px',
|
|
2340
|
+
border: 'calc(var(--border) * 2) solid currentColor',
|
|
2341
|
+
borderBlockStartColor: 'transparent',
|
|
2342
|
+
animation: 'zero-daisyui-btn-spin 0.7s linear infinite',
|
|
2343
|
+
},
|
|
2344
|
+
at: { 'reduced-motion': { base: { animation: 'none' } } },
|
|
2345
|
+
},
|
|
2291
2346
|
root: {
|
|
2292
2347
|
base: {
|
|
2293
2348
|
appearance: 'none',
|
|
2349
|
+
// An asChild `<a>` gets no UA underline (see the README's
|
|
2350
|
+
// link-button note for the unlayered `a { color }` case).
|
|
2351
|
+
textDecoration: 'none',
|
|
2294
2352
|
display: 'inline-flex',
|
|
2295
2353
|
alignItems: 'center',
|
|
2296
2354
|
justifyContent: 'center',
|
|
@@ -2306,6 +2364,9 @@ export const button: RecipeInput = {
|
|
|
2306
2364
|
+ 'border-color var(--duration-fast) var(--ease-standard)',
|
|
2307
2365
|
},
|
|
2308
2366
|
states: {
|
|
2367
|
+
// Work in flight (`loading`, #50): still focusable, still legible —
|
|
2368
|
+
// the label is what the reader is waiting on — so no fade here.
|
|
2369
|
+
loading: { cursor: 'progress' },
|
|
2309
2370
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
2310
2371
|
// The ring is drawn on the page, offset clear of the button —
|
|
2311
2372
|
// ink, not fill, and the same 3:1 floor. On `--btn-accent` it
|
|
@@ -2451,37 +2512,6 @@ export const button: RecipeInput = {
|
|
|
2451
2512
|
active: {
|
|
2452
2513
|
root: { base: { transform: 'translateY(1px)', boxShadow: 'none', filter: 'brightness(0.92)' } },
|
|
2453
2514
|
},
|
|
2454
|
-
/**
|
|
2455
|
-
* daisy's loading spinner, drawn by the recipe (#332): daisy marks
|
|
2456
|
-
* the button with a `loading loading-spinner` span; zero changes no
|
|
2457
|
-
* DOM, so the ring is a `::before` in `currentColor` with one
|
|
2458
|
-
* transparent quadrant — it inherits whatever ink the variant chose,
|
|
2459
|
-
* and the root's existing `gap` spaces it from the label. Literal
|
|
2460
|
-
* duration + explicit reduced-motion `none`, like the spinner
|
|
2461
|
-
* recipe: the kit collapses `--duration-*` under reduced motion, and
|
|
2462
|
-
* an infinite loop at ~0s strobes rather than stops.
|
|
2463
|
-
*/
|
|
2464
|
-
loading: {
|
|
2465
|
-
root: {
|
|
2466
|
-
base: { cursor: 'progress' },
|
|
2467
|
-
selectors: {
|
|
2468
|
-
'&::before': {
|
|
2469
|
-
content: '""',
|
|
2470
|
-
boxSizing: 'border-box',
|
|
2471
|
-
inlineSize: '1em',
|
|
2472
|
-
blockSize: '1em',
|
|
2473
|
-
flex: 'none',
|
|
2474
|
-
borderRadius: '9999px',
|
|
2475
|
-
border: 'calc(var(--border) * 2) solid currentColor',
|
|
2476
|
-
borderBlockStartColor: 'transparent',
|
|
2477
|
-
animation: 'zero-daisyui-btn-spin 0.7s linear infinite',
|
|
2478
|
-
},
|
|
2479
|
-
},
|
|
2480
|
-
at: {
|
|
2481
|
-
'reduced-motion': { selectors: { '&::before': { animation: 'none' } } },
|
|
2482
|
-
},
|
|
2483
|
-
},
|
|
2484
|
-
},
|
|
2485
2515
|
},
|
|
2486
2516
|
defaultVariants: { color: 'primary', variant: 'solid', size: 'md' },
|
|
2487
2517
|
keyframes: { 'zero-daisyui-btn-spin': 'to { transform: rotate(360deg) }' },
|
|
@@ -2549,6 +2579,13 @@ export const avatar: RecipeInput = {
|
|
|
2549
2579
|
},
|
|
2550
2580
|
},
|
|
2551
2581
|
variants: {
|
|
2582
|
+
// The shape axis (zero#129) — one declaration on the root, whose
|
|
2583
|
+
// `overflow: hidden` clips the image and the fallback alike. `rounded` is daisy's selector radius; the default is the circle.
|
|
2584
|
+
shape: {
|
|
2585
|
+
circle: { root: { base: { borderRadius: '9999px' } } },
|
|
2586
|
+
square: { root: { base: { borderRadius: '0' } } },
|
|
2587
|
+
rounded: { root: { base: { borderRadius: 'var(--radius-selector)' } } },
|
|
2588
|
+
},
|
|
2552
2589
|
// daisy colours the RING, not the fallback — that is what its avatar
|
|
2553
2590
|
// does, and the fallback stays neutral so initials read the same
|
|
2554
2591
|
// whichever role is chosen.
|
|
@@ -2760,6 +2797,7 @@ export const combobox: RecipeInput = {
|
|
|
2760
2797
|
base: {
|
|
2761
2798
|
display: 'inline-flex',
|
|
2762
2799
|
alignItems: 'stretch',
|
|
2800
|
+
flexWrap: 'wrap',
|
|
2763
2801
|
minWidth: '13rem',
|
|
2764
2802
|
...fieldControl,
|
|
2765
2803
|
transition: 'border-color var(--duration-fast) var(--ease-standard)',
|
|
@@ -2805,6 +2843,52 @@ export const combobox: RecipeInput = {
|
|
|
2805
2843
|
'&::placeholder': { color: 'color-mix(in oklab, var(--color-base-content) 60%, transparent)' },
|
|
2806
2844
|
},
|
|
2807
2845
|
},
|
|
2846
|
+
// A chosen value under `multiple` (#39): a chip in the control, before
|
|
2847
|
+
// the input. Symmetric metrics only — the physical-direction lint and
|
|
2848
|
+
// the lynx emitter both refuse a one-sided inset.
|
|
2849
|
+
// daisy's badge: a base-200 pill, centred in the stretched row.
|
|
2850
|
+
tag: {
|
|
2851
|
+
base: {
|
|
2852
|
+
display: 'inline-flex',
|
|
2853
|
+
alignItems: 'center',
|
|
2854
|
+
alignSelf: 'center',
|
|
2855
|
+
gap: 'var(--space-2xs)',
|
|
2856
|
+
margin: 'var(--space-2xs)',
|
|
2857
|
+
padding: 'var(--space-2xs) var(--space-sm)',
|
|
2858
|
+
background: 'var(--color-base-200)',
|
|
2859
|
+
color: 'var(--color-base-content)',
|
|
2860
|
+
borderRadius: 'var(--radius-selector)',
|
|
2861
|
+
fontSize: 'var(--text-xs)',
|
|
2862
|
+
fontWeight: 'var(--weight-medium)',
|
|
2863
|
+
lineHeight: 'var(--leading-none)',
|
|
2864
|
+
},
|
|
2865
|
+
states: { disabled: {} },
|
|
2866
|
+
},
|
|
2867
|
+
'tag-label': { base: { whiteSpace: 'nowrap' } },
|
|
2868
|
+
'tag-remove': {
|
|
2869
|
+
base: {
|
|
2870
|
+
appearance: 'none',
|
|
2871
|
+
border: 'none',
|
|
2872
|
+
background: 'transparent',
|
|
2873
|
+
color: 'inherit',
|
|
2874
|
+
font: 'inherit',
|
|
2875
|
+
borderRadius: 'var(--radius-selector)',
|
|
2876
|
+
padding: '0 var(--space-2xs)',
|
|
2877
|
+
lineHeight: 'var(--leading-none)',
|
|
2878
|
+
cursor: 'pointer',
|
|
2879
|
+
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
2880
|
+
},
|
|
2881
|
+
states: {
|
|
2882
|
+
hover: { background: 'color-mix(in oklab, var(--color-base-content) 10%, transparent)' },
|
|
2883
|
+
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
2884
|
+
...focusRing,
|
|
2885
|
+
},
|
|
2886
|
+
selectors: {
|
|
2887
|
+
'&[data-pressed]:not([data-disabled])': {
|
|
2888
|
+
background: 'color-mix(in oklab, var(--color-base-content) 18%, transparent)',
|
|
2889
|
+
},
|
|
2890
|
+
},
|
|
2891
|
+
},
|
|
2808
2892
|
trigger: {
|
|
2809
2893
|
base: {
|
|
2810
2894
|
appearance: 'none',
|
|
@@ -2841,7 +2925,7 @@ export const combobox: RecipeInput = {
|
|
|
2841
2925
|
display: 'flex',
|
|
2842
2926
|
alignItems: 'center',
|
|
2843
2927
|
justifyContent: 'space-between',
|
|
2844
|
-
gap: '
|
|
2928
|
+
gap: 'var(--space-md)',
|
|
2845
2929
|
padding: 'var(--space-md) var(--space-lg)',
|
|
2846
2930
|
fontSize: 'var(--text-sm)',
|
|
2847
2931
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -2873,15 +2957,15 @@ export const combobox: RecipeInput = {
|
|
|
2873
2957
|
'--combobox-accent': `var(--color-${c})`,
|
|
2874
2958
|
} } }])),
|
|
2875
2959
|
// The control carries the height — the shared field ramp, the same one
|
|
2876
|
-
// `select`'s trigger steps on
|
|
2877
|
-
// type. `md` restates exactly the resting values so the union stays
|
|
2960
|
+
// `select`'s trigger steps on, as a floor so wrapped tags can grow it
|
|
2961
|
+
// — and the input carries the inset and the type. `md` restates exactly the resting values so the union stays
|
|
2878
2962
|
// complete.
|
|
2879
2963
|
size: {
|
|
2880
|
-
xs: { control: { base: {
|
|
2881
|
-
sm: { control: { base: {
|
|
2882
|
-
md: { control: { base: {
|
|
2883
|
-
lg: { control: { base: {
|
|
2884
|
-
xl: { control: { base: {
|
|
2964
|
+
xs: { control: { base: { minHeight: fieldHeight('xs') } }, input: { base: { padding: 'var(--space-2xs) var(--space-md)', fontSize: 'var(--text-xs)' } } },
|
|
2965
|
+
sm: { control: { base: { minHeight: fieldHeight('sm') } }, input: { base: { padding: 'var(--space-xs) var(--space-md)', fontSize: 'var(--text-sm)' } } },
|
|
2966
|
+
md: { control: { base: { minHeight: fieldHeight('md') } }, input: { base: { padding: 'var(--space-sm) var(--space-lg)', fontSize: 'var(--text-sm)' } } },
|
|
2967
|
+
lg: { control: { base: { minHeight: fieldHeight('lg') } }, input: { base: { padding: 'var(--space-md) var(--space-xl)', fontSize: 'var(--text-md)' } } },
|
|
2968
|
+
xl: { control: { base: { minHeight: fieldHeight('xl') } }, input: { base: { padding: 'var(--space-lg) var(--space-2xl)', fontSize: 'var(--text-lg)' } } },
|
|
2885
2969
|
},
|
|
2886
2970
|
},
|
|
2887
2971
|
// The visible ring lives on `control`; input and trigger delegate.
|
|
@@ -2981,6 +3065,13 @@ export const toggle: RecipeInput = {
|
|
|
2981
3065
|
// the on item filled with the accent.
|
|
2982
3066
|
export const toggleGroup: RecipeInput = {
|
|
2983
3067
|
component: 'toggle-group',
|
|
3068
|
+
// Public to a design system derived from this one (#73).
|
|
3069
|
+
hooks: {
|
|
3070
|
+
properties: {
|
|
3071
|
+
'--toggle-group-accent': 'The fill of an on item.',
|
|
3072
|
+
'--toggle-group-on-accent': 'The ink on --toggle-group-accent.',
|
|
3073
|
+
},
|
|
3074
|
+
},
|
|
2984
3075
|
tokens: {
|
|
2985
3076
|
'--toggle-group-accent': 'var(--color-primary)',
|
|
2986
3077
|
'--toggle-group-on-accent': 'var(--color-primary-content)',
|
|
@@ -3417,7 +3508,7 @@ export const ratingGroup: RecipeInput = {
|
|
|
3417
3508
|
const treeRow: NonNullable<PartStyles['base']> = {
|
|
3418
3509
|
display: 'flex',
|
|
3419
3510
|
alignItems: 'center',
|
|
3420
|
-
gap: '
|
|
3511
|
+
gap: 'var(--space-md)',
|
|
3421
3512
|
padding: 'var(--space-sm) var(--space-md)',
|
|
3422
3513
|
fontSize: 'var(--tree-text)',
|
|
3423
3514
|
fontWeight: 'var(--weight-medium)',
|
|
@@ -3696,95 +3787,6 @@ export const textarea: RecipeInput = {
|
|
|
3696
3787
|
},
|
|
3697
3788
|
};
|
|
3698
3789
|
|
|
3699
|
-
/**
|
|
3700
|
-
* NativeSelect (#333) — daisy "select" flavor on the platform's own picker:
|
|
3701
|
-
* the `fieldControl` box (same metrics as every field, so mixed rows line
|
|
3702
|
-
* up), `appearance: none`, and the recipe-drawn chevron at the inline end.
|
|
3703
|
-
* No open state — the platform owns the popup — so colour surfaces on the
|
|
3704
|
-
* focus ring, exactly like input's.
|
|
3705
|
-
*/
|
|
3706
|
-
export const nativeSelect: RecipeInput = {
|
|
3707
|
-
component: 'native-select',
|
|
3708
|
-
tokens: { '--native-select-accent': 'var(--color-base-content)' },
|
|
3709
|
-
parts: {
|
|
3710
|
-
root: {
|
|
3711
|
-
base: { position: 'relative', display: 'inline-flex', alignItems: 'center' },
|
|
3712
|
-
states: { disabled: {}, invalid: {}, required: {}, placeholder: {} },
|
|
3713
|
-
},
|
|
3714
|
-
control: {
|
|
3715
|
-
base: {
|
|
3716
|
-
appearance: 'none',
|
|
3717
|
-
width: '100%',
|
|
3718
|
-
minWidth: '12rem',
|
|
3719
|
-
...fieldControl,
|
|
3720
|
-
color: 'var(--color-base-content)',
|
|
3721
|
-
font: 'inherit',
|
|
3722
|
-
fontSize: 'var(--text-sm)',
|
|
3723
|
-
padding: 'var(--space-sm) var(--space-md)',
|
|
3724
|
-
// Room for the chevron the platform no longer draws.
|
|
3725
|
-
paddingInlineEnd: 'calc(var(--space-md) + 1.25em)',
|
|
3726
|
-
cursor: 'pointer',
|
|
3727
|
-
transition: 'border-color var(--duration-fast) var(--ease-standard)',
|
|
3728
|
-
},
|
|
3729
|
-
states: {
|
|
3730
|
-
hover: { borderColor: 'var(--color-base-content)' },
|
|
3731
|
-
invalid: { borderColor: 'var(--color-error)' },
|
|
3732
|
-
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
3733
|
-
required: {},
|
|
3734
|
-
// Same muting as the fields' placeholders (#264).
|
|
3735
|
-
placeholder: { color: 'color-mix(in oklab, var(--color-base-content) 60%, transparent)' },
|
|
3736
|
-
'focus-visible': { ...focusRing['focus-visible'], outline: '2px solid var(--native-select-accent)' },
|
|
3737
|
-
},
|
|
3738
|
-
},
|
|
3739
|
-
indicator: {
|
|
3740
|
-
base: {
|
|
3741
|
-
position: 'absolute',
|
|
3742
|
-
insetInlineEnd: 'var(--space-md)',
|
|
3743
|
-
pointerEvents: 'none',
|
|
3744
|
-
opacity: '0.6',
|
|
3745
|
-
fontSize: 'var(--text-sm)',
|
|
3746
|
-
},
|
|
3747
|
-
},
|
|
3748
|
-
},
|
|
3749
|
-
// The visible ring lives on the <select> itself; the wrapper delegates.
|
|
3750
|
-
skipStates: { root: ['focus-visible'] },
|
|
3751
|
-
variants: {
|
|
3752
|
-
// The ring carries the role — daisy's field chrome is neutral, so
|
|
3753
|
-
// focus is the only place colour surfaces (input's exact move).
|
|
3754
|
-
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
3755
|
-
'--native-select-accent': `var(--color-${c})`,
|
|
3756
|
-
} } }])),
|
|
3757
|
-
// The shared field-height ramp, on the control itself.
|
|
3758
|
-
size: {
|
|
3759
|
-
xs: { control: { base: { height: fieldHeight('xs'), fontSize: 'var(--text-xs)', padding: 'var(--space-2xs) var(--space-xs)', paddingInlineEnd: 'calc(var(--space-xs) + 1.25em)' } } },
|
|
3760
|
-
sm: { control: { base: { height: fieldHeight('sm'), fontSize: 'var(--text-xs)', padding: 'var(--space-xs) var(--space-sm)', paddingInlineEnd: 'calc(var(--space-sm) + 1.25em)' } } },
|
|
3761
|
-
md: {},
|
|
3762
|
-
lg: { control: { base: { height: fieldHeight('lg'), fontSize: 'var(--text-md)', padding: 'var(--space-md) var(--space-lg)', paddingInlineEnd: 'calc(var(--space-lg) + 1.25em)' } } },
|
|
3763
|
-
xl: { control: { base: { height: fieldHeight('xl'), fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)', paddingInlineEnd: 'calc(var(--space-xl) + 1.25em)' } } },
|
|
3764
|
-
},
|
|
3765
|
-
},
|
|
3766
|
-
// The chevron gutter and the indicator anchor, restated physically:
|
|
3767
|
-
// logical spellings resolve on iOS but not on Android (measured,
|
|
3768
|
-
// signalxjs/lynx#1084), so the emitter refuses them. Physical is the
|
|
3769
|
-
// lynx target's norm — no RTL flow there, so `end` IS right.
|
|
3770
|
-
targets: {
|
|
3771
|
-
lynx: {
|
|
3772
|
-
parts: {
|
|
3773
|
-
control: { base: { paddingRight: 'calc(var(--space-md) + 1.25em)' } },
|
|
3774
|
-
indicator: { base: { right: 'var(--space-md)' } },
|
|
3775
|
-
},
|
|
3776
|
-
variants: {
|
|
3777
|
-
size: {
|
|
3778
|
-
xs: { control: { base: { paddingRight: 'calc(var(--space-xs) + 1.25em)' } } },
|
|
3779
|
-
sm: { control: { base: { paddingRight: 'calc(var(--space-sm) + 1.25em)' } } },
|
|
3780
|
-
lg: { control: { base: { paddingRight: 'calc(var(--space-lg) + 1.25em)' } } },
|
|
3781
|
-
xl: { control: { base: { paddingRight: 'calc(var(--space-xl) + 1.25em)' } } },
|
|
3782
|
-
},
|
|
3783
|
-
},
|
|
3784
|
-
},
|
|
3785
|
-
},
|
|
3786
|
-
};
|
|
3787
|
-
|
|
3788
3790
|
// ── Content tier (#311) ───────────────────────────────────────────────────
|
|
3789
3791
|
/**
|
|
3790
3792
|
* daisy "card" flavor: `bg-base-100` on a `rounded-box` with a real shadow —
|
|
@@ -3793,6 +3795,12 @@ export const nativeSelect: RecipeInput = {
|
|
|
3793
3795
|
*/
|
|
3794
3796
|
export const card: RecipeInput = {
|
|
3795
3797
|
component: 'card',
|
|
3798
|
+
// Public to a design system derived from this one (#73).
|
|
3799
|
+
hooks: {
|
|
3800
|
+
properties: {
|
|
3801
|
+
'--card-pad': 'The card padding.',
|
|
3802
|
+
},
|
|
3803
|
+
},
|
|
3796
3804
|
tokens: { '--card-pad': 'var(--space-xl)' },
|
|
3797
3805
|
parts: {
|
|
3798
3806
|
root: {
|
|
@@ -3949,6 +3957,88 @@ export const alert: RecipeInput = {
|
|
|
3949
3957
|
},
|
|
3950
3958
|
};
|
|
3951
3959
|
|
|
3960
|
+
/**
|
|
3961
|
+
* EmptyState (zero#131) — daisy has no empty-state component; this is the
|
|
3962
|
+
* `hero`-content idiom at card scale: a centred column on the base-200
|
|
3963
|
+
* surface with the box radius, the icon in the role's ink and the surface
|
|
3964
|
+
* in its soft tint when a tone is given.
|
|
3965
|
+
*/
|
|
3966
|
+
export const emptyState: RecipeInput = {
|
|
3967
|
+
component: 'empty-state',
|
|
3968
|
+
hooks: {
|
|
3969
|
+
properties: {
|
|
3970
|
+
'--empty-accent': 'The icon ink.',
|
|
3971
|
+
'--empty-tint': 'The surface fill.',
|
|
3972
|
+
},
|
|
3973
|
+
},
|
|
3974
|
+
tokens: {
|
|
3975
|
+
'--empty-accent': 'color-mix(in oklab, var(--color-base-content) 60%, transparent)',
|
|
3976
|
+
'--empty-tint': 'var(--color-base-200)',
|
|
3977
|
+
},
|
|
3978
|
+
parts: {
|
|
3979
|
+
root: {
|
|
3980
|
+
base: {
|
|
3981
|
+
display: 'flex',
|
|
3982
|
+
flexDirection: 'column',
|
|
3983
|
+
alignItems: 'center',
|
|
3984
|
+
textAlign: 'center',
|
|
3985
|
+
gap: 'var(--space-sm)',
|
|
3986
|
+
padding: 'var(--space-2xl) var(--space-xl)',
|
|
3987
|
+
background: 'var(--empty-tint)',
|
|
3988
|
+
color: 'var(--color-base-content)',
|
|
3989
|
+
borderRadius: 'var(--radius-box)',
|
|
3990
|
+
},
|
|
3991
|
+
},
|
|
3992
|
+
icon: {
|
|
3993
|
+
base: {
|
|
3994
|
+
display: 'inline-flex',
|
|
3995
|
+
color: 'var(--empty-accent)',
|
|
3996
|
+
fontSize: 'var(--text-2xl)',
|
|
3997
|
+
lineHeight: 'var(--leading-none)',
|
|
3998
|
+
marginBlockEnd: 'var(--space-xs)',
|
|
3999
|
+
},
|
|
4000
|
+
},
|
|
4001
|
+
title: {
|
|
4002
|
+
base: {
|
|
4003
|
+
margin: '0',
|
|
4004
|
+
fontSize: 'var(--text-lg)',
|
|
4005
|
+
fontWeight: 'var(--weight-bold)',
|
|
4006
|
+
lineHeight: 'var(--leading-tight)',
|
|
4007
|
+
},
|
|
4008
|
+
},
|
|
4009
|
+
description: {
|
|
4010
|
+
base: {
|
|
4011
|
+
maxInlineSize: '36ch',
|
|
4012
|
+
fontSize: 'var(--text-sm)',
|
|
4013
|
+
lineHeight: 'var(--leading-normal)',
|
|
4014
|
+
color: 'color-mix(in oklab, var(--color-base-content) 70%, transparent)',
|
|
4015
|
+
},
|
|
4016
|
+
},
|
|
4017
|
+
actions: {
|
|
4018
|
+
base: {
|
|
4019
|
+
display: 'flex',
|
|
4020
|
+
flexWrap: 'wrap',
|
|
4021
|
+
justifyContent: 'center',
|
|
4022
|
+
gap: 'var(--space-sm)',
|
|
4023
|
+
marginBlockStart: 'var(--space-sm)',
|
|
4024
|
+
},
|
|
4025
|
+
},
|
|
4026
|
+
},
|
|
4027
|
+
variants: {
|
|
4028
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
4029
|
+
'--empty-accent': roleInk(c),
|
|
4030
|
+
'--empty-tint': `var(--color-${c}-soft)`,
|
|
4031
|
+
} } }])),
|
|
4032
|
+
size: {
|
|
4033
|
+
xs: { root: { base: { padding: 'var(--space-md)', gap: 'var(--space-2xs)' } }, icon: { base: { fontSize: 'var(--text-xl)' } }, title: { base: { fontSize: 'var(--text-sm)' } }, description: { base: { fontSize: 'var(--text-xs)' } } },
|
|
4034
|
+
sm: { root: { base: { padding: 'var(--space-lg)', gap: 'var(--space-xs)' } }, icon: { base: { fontSize: 'var(--text-2xl)' } }, title: { base: { fontSize: 'var(--text-md)' } }, description: { base: { fontSize: 'var(--text-xs)' } } },
|
|
4035
|
+
md: {},
|
|
4036
|
+
lg: { root: { base: { padding: 'calc(var(--space-2xl) * 1.5) var(--space-2xl)', gap: 'var(--space-md)' } }, icon: { base: { fontSize: 'var(--text-2xl)' } }, title: { base: { fontSize: 'var(--text-xl)' } }, description: { base: { fontSize: 'var(--text-md)' } } },
|
|
4037
|
+
xl: { root: { base: { padding: 'calc(var(--space-2xl) * 2) var(--space-2xl)', gap: 'var(--space-lg)' } }, icon: { base: { fontSize: 'var(--text-2xl)' } }, title: { base: { fontSize: 'var(--text-2xl)' } }, description: { base: { fontSize: 'var(--text-md)' } } },
|
|
4038
|
+
},
|
|
4039
|
+
},
|
|
4040
|
+
};
|
|
4041
|
+
|
|
3952
4042
|
/**
|
|
3953
4043
|
* daisy "badge" flavor: a fully-rounded pill with a hairline, filled by the
|
|
3954
4044
|
* role. No `variant` — the repo-wide decision (#175) holds everywhere except
|
|
@@ -3957,7 +4047,18 @@ export const alert: RecipeInput = {
|
|
|
3957
4047
|
*/
|
|
3958
4048
|
export const badge: RecipeInput = {
|
|
3959
4049
|
component: 'badge',
|
|
4050
|
+
// Public to a design system derived from this one (#73).
|
|
4051
|
+
hooks: {
|
|
4052
|
+
properties: {
|
|
4053
|
+
'--badge-dot': 'The status dot\'s fill (zero#130); the pill\'s ink at rest.',
|
|
4054
|
+
'--badge-dot-ring': 'The ring around a coloured status dot.',
|
|
4055
|
+
'--badge-fill': 'The badge fill.',
|
|
4056
|
+
'--badge-ink': 'The badge ink.',
|
|
4057
|
+
},
|
|
4058
|
+
},
|
|
3960
4059
|
tokens: {
|
|
4060
|
+
'--badge-dot': 'currentColor',
|
|
4061
|
+
'--badge-dot-ring': 'currentColor',
|
|
3961
4062
|
'--badge-fill': 'var(--color-base-200)',
|
|
3962
4063
|
'--badge-ink': 'var(--color-base-content)',
|
|
3963
4064
|
},
|
|
@@ -3973,7 +4074,7 @@ export const badge: RecipeInput = {
|
|
|
3973
4074
|
// daisy badges are pills; a large radius rather than 9999px so
|
|
3974
4075
|
// a square-cornered theme can still say so through the token.
|
|
3975
4076
|
borderRadius: 'var(--radius-selector)',
|
|
3976
|
-
padding: '
|
|
4077
|
+
padding: 'var(--space-2xs) var(--space-lg)',
|
|
3977
4078
|
fontSize: 'var(--text-xs)',
|
|
3978
4079
|
fontWeight: 'var(--weight-semibold)',
|
|
3979
4080
|
lineHeight: 'var(--leading-normal)',
|
|
@@ -3981,12 +4082,53 @@ export const badge: RecipeInput = {
|
|
|
3981
4082
|
textDecoration: 'none',
|
|
3982
4083
|
},
|
|
3983
4084
|
},
|
|
4085
|
+
// The status dot (zero#130). On an uncoloured pill it is the pill's
|
|
4086
|
+
// INK — a dot in `currentColor` is legible on whatever fill the pill
|
|
4087
|
+
// has. Coloured — by its own `data-color`, or by the pill's, since the
|
|
4088
|
+
// nearest carrier wins (#94) — it is the role's fill inside a ring in
|
|
4089
|
+
// the role's `-content` ink, the timeline marker's answer: a dot that
|
|
4090
|
+
// follows a solid pill of the same role still has an edge. `running` is a
|
|
4091
|
+
// static halo the pulse breathes; under reduced motion the halo
|
|
4092
|
+
// stays and the breathing stops, so the state never vanishes.
|
|
4093
|
+
dot: {
|
|
4094
|
+
base: {
|
|
4095
|
+
display: 'inline-block',
|
|
4096
|
+
flex: 'none',
|
|
4097
|
+
inlineSize: '0.5em',
|
|
4098
|
+
blockSize: '0.5em',
|
|
4099
|
+
boxSizing: 'border-box',
|
|
4100
|
+
borderRadius: '50%',
|
|
4101
|
+
background: 'var(--badge-dot)',
|
|
4102
|
+
border: '0.1em solid var(--badge-dot-ring)',
|
|
4103
|
+
},
|
|
4104
|
+
states: {
|
|
4105
|
+
running: {
|
|
4106
|
+
boxShadow: '0 0 0 0.2em color-mix(in oklch, var(--badge-dot) 35%, transparent)',
|
|
4107
|
+
animation: 'zero-daisy-badge-pulse 1.6s ease-out infinite',
|
|
4108
|
+
},
|
|
4109
|
+
},
|
|
4110
|
+
at: {
|
|
4111
|
+
'reduced-motion': { states: { running: { animation: 'none' } } },
|
|
4112
|
+
// Forced colours strip the fill and keep the border, so the
|
|
4113
|
+
// border draws the whole dot there.
|
|
4114
|
+
'forced-colors': { base: { borderWidth: '0.25em' } },
|
|
4115
|
+
},
|
|
4116
|
+
},
|
|
3984
4117
|
},
|
|
3985
4118
|
variants: {
|
|
3986
|
-
color: Object.fromEntries(ROLES.map((c) => [c, {
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
4119
|
+
color: Object.fromEntries(ROLES.map((c) => [c, {
|
|
4120
|
+
root: { base: {
|
|
4121
|
+
'--badge-fill': `var(--color-${c})`,
|
|
4122
|
+
'--badge-ink': `var(--color-${c}-content)`,
|
|
4123
|
+
} },
|
|
4124
|
+
// The dot's colour (zero#130): keyed under `variants.color` so the
|
|
4125
|
+
// compiler anchors it on the pill's `data-color` AND on the dot's
|
|
4126
|
+
// own re-carried one — the nearest carrier wins (#94).
|
|
4127
|
+
dot: { base: {
|
|
4128
|
+
'--badge-dot': `var(--color-${c})`,
|
|
4129
|
+
'--badge-dot-ring': `var(--color-${c}-content)`,
|
|
4130
|
+
} },
|
|
4131
|
+
}])),
|
|
3990
4132
|
size: {
|
|
3991
4133
|
xs: { root: { base: { fontSize: 'var(--text-xs)', padding: '0 var(--space-sm)' } } },
|
|
3992
4134
|
sm: { root: { base: { fontSize: 'var(--text-xs)', padding: '0 var(--space-md)' } } },
|
|
@@ -3994,6 +4136,8 @@ export const badge: RecipeInput = {
|
|
|
3994
4136
|
lg: { root: { base: { fontSize: 'var(--text-sm)', padding: 'var(--space-2xs) var(--space-lg)' } } },
|
|
3995
4137
|
xl: { root: { base: { fontSize: 'var(--text-md)', padding: 'var(--space-xs) var(--space-xl)' } } },
|
|
3996
4138
|
},
|
|
4139
|
+
}, keyframes: {
|
|
4140
|
+
'zero-daisy-badge-pulse': 'from { box-shadow: 0 0 0 0 color-mix(in oklch, var(--badge-dot) 45%, transparent); } to { box-shadow: 0 0 0 0.5em transparent; }',
|
|
3997
4141
|
},
|
|
3998
4142
|
};
|
|
3999
4143
|
|
|
@@ -4039,6 +4183,12 @@ export const divider: RecipeInput = {
|
|
|
4039
4183
|
*/
|
|
4040
4184
|
export const skeleton: RecipeInput = {
|
|
4041
4185
|
component: 'skeleton',
|
|
4186
|
+
// Public to a design system derived from this one (#73).
|
|
4187
|
+
hooks: {
|
|
4188
|
+
properties: {
|
|
4189
|
+
'--skeleton-fill': 'The placeholder fill.',
|
|
4190
|
+
},
|
|
4191
|
+
},
|
|
4042
4192
|
tokens: { '--skeleton-fill': 'var(--color-base-300)' },
|
|
4043
4193
|
parts: {
|
|
4044
4194
|
root: {
|
|
@@ -4128,7 +4278,11 @@ export const kbd: RecipeInput = {
|
|
|
4128
4278
|
alignItems: 'center',
|
|
4129
4279
|
justifyContent: 'center',
|
|
4130
4280
|
minInlineSize: '1.75em',
|
|
4131
|
-
|
|
4281
|
+
// daisy's .0625rem/.4375rem, restated as the ramp steps they
|
|
4282
|
+
// already are once the cap's own border is absorbed: the OUTER
|
|
4283
|
+
// box lands on 2xs/md, so the cap rides the ramp under a density
|
|
4284
|
+
// override while keeping daisy's painted size exactly at 1px.
|
|
4285
|
+
padding: 'calc(var(--space-2xs) - var(--border)) calc(var(--space-md) - var(--border))',
|
|
4132
4286
|
background: 'var(--kbd-fill)',
|
|
4133
4287
|
color: 'var(--kbd-ink)',
|
|
4134
4288
|
border: 'var(--border) solid var(--color-base-300)',
|
|
@@ -4361,10 +4515,32 @@ export const stats: RecipeInput = {
|
|
|
4361
4515
|
},
|
|
4362
4516
|
};
|
|
4363
4517
|
|
|
4518
|
+
/**
|
|
4519
|
+
* A timeline none of whose content sits on the start side: the root carries
|
|
4520
|
+
* no start-placed content among its own items. Child combinators all the way
|
|
4521
|
+
* down, so a timeline nested inside another's content answers only for
|
|
4522
|
+
* itself.
|
|
4523
|
+
*/
|
|
4524
|
+
const TIMELINE_NO_START = '[data-scope="timeline"][data-part="root"]'
|
|
4525
|
+
+ ':not(:has(> [data-scope="timeline"][data-part="item"] > [data-scope="timeline"][data-part="content"][data-placement="start"])) > &';
|
|
4526
|
+
|
|
4364
4527
|
/** daisy timeline: the boxed content beside a dotted axis. */
|
|
4365
4528
|
export const timeline: RecipeInput = {
|
|
4366
4529
|
component: 'timeline',
|
|
4367
|
-
|
|
4530
|
+
// Public to a design system derived from this one (#73).
|
|
4531
|
+
hooks: {
|
|
4532
|
+
properties: {
|
|
4533
|
+
'--timeline-accent': 'The marker and connector accent.',
|
|
4534
|
+
'--timeline-ring': 'The ring around a coloured marker — the role\'s -content ink (#94).',
|
|
4535
|
+
'--timeline-marker-size': 'The marker diameter.',
|
|
4536
|
+
},
|
|
4537
|
+
},
|
|
4538
|
+
tokens: {
|
|
4539
|
+
'--timeline-accent': 'var(--color-base-content)',
|
|
4540
|
+
// The coloured dot's ring (#94); uncoloured, it IS the dot.
|
|
4541
|
+
'--timeline-ring': 'var(--timeline-accent)',
|
|
4542
|
+
'--timeline-marker-size': 'calc(var(--size-selector) * 3)',
|
|
4543
|
+
},
|
|
4368
4544
|
parts: {
|
|
4369
4545
|
root: {
|
|
4370
4546
|
base: {
|
|
@@ -4400,6 +4576,11 @@ export const timeline: RecipeInput = {
|
|
|
4400
4576
|
gridTemplateColumns: 'auto 1fr',
|
|
4401
4577
|
flex: '1 1 0%',
|
|
4402
4578
|
},
|
|
4579
|
+
// No content on the start side anywhere in this timeline →
|
|
4580
|
+
// the start track collapses instead of holding half the item
|
|
4581
|
+
// empty (#57). Automatic, so there is no prop to forget.
|
|
4582
|
+
[`${TIMELINE_NO_START}[data-orientation="vertical"]`]: { gridTemplateColumns: '0 auto 1fr' },
|
|
4583
|
+
[`${TIMELINE_NO_START}[data-orientation="horizontal"]`]: { gridTemplateRows: '0 auto 1fr' },
|
|
4403
4584
|
},
|
|
4404
4585
|
},
|
|
4405
4586
|
marker: {
|
|
@@ -4409,12 +4590,24 @@ export const timeline: RecipeInput = {
|
|
|
4409
4590
|
boxSizing: 'border-box',
|
|
4410
4591
|
borderRadius: '9999px',
|
|
4411
4592
|
background: 'var(--timeline-accent)',
|
|
4412
|
-
|
|
4593
|
+
// A coloured dot is the role fill inside a RING in the role's
|
|
4594
|
+
// `-content` ink (#94): a light role on a light page, or a dark
|
|
4595
|
+
// one on a dark page, has no edge of its own, and a per-entry
|
|
4596
|
+
// tone is exactly the mark a reader has to find. One half of
|
|
4597
|
+
// the role's own pair clears 3:1 against the page (the contrast
|
|
4598
|
+
// audit measures every colour). With no colour the ring is the
|
|
4599
|
+
// fill — the same solid dot as before.
|
|
4600
|
+
border: 'calc(var(--timeline-marker-size) / 6) solid var(--timeline-ring)',
|
|
4413
4601
|
display: 'flex',
|
|
4414
4602
|
alignItems: 'center',
|
|
4415
4603
|
justifyContent: 'center',
|
|
4416
4604
|
fontSize: '0',
|
|
4417
4605
|
},
|
|
4606
|
+
at: {
|
|
4607
|
+
// Forced colours strip the fill and keep the border, so the
|
|
4608
|
+
// border draws the whole dot there, as it always did.
|
|
4609
|
+
'forced-colors': { base: { borderWidth: 'calc(var(--timeline-marker-size) / 2)' } },
|
|
4610
|
+
},
|
|
4418
4611
|
selectors: {
|
|
4419
4612
|
// The axis cell, both orientations. `place-self` centres the
|
|
4420
4613
|
// dot on the line in the cross axis.
|
|
@@ -4492,6 +4685,7 @@ export const timeline: RecipeInput = {
|
|
|
4492
4685
|
variants: {
|
|
4493
4686
|
color: Object.fromEntries(ROLES.map((c) => [c, { marker: { base: {
|
|
4494
4687
|
'--timeline-accent': `var(--color-${c})`,
|
|
4688
|
+
'--timeline-ring': `var(--color-${c}-content)`,
|
|
4495
4689
|
} } }])),
|
|
4496
4690
|
size: {
|
|
4497
4691
|
xs: { marker: { base: { '--timeline-marker-size': 'calc(var(--size-selector) * 2)' } }, content: { base: { fontSize: 'var(--text-xs)' } } },
|
|
@@ -4863,6 +5057,112 @@ export const navbar: RecipeInput = {
|
|
|
4863
5057
|
},
|
|
4864
5058
|
};
|
|
4865
5059
|
|
|
5060
|
+
/**
|
|
5061
|
+
* NavList (zero#132) — daisy's `menu` idiom for a sidebar: rounded rows, a
|
|
5062
|
+
* base-200 wash on hover, and the current page as the role's soft fill in
|
|
5063
|
+
* the role's ink. `meta` is where daisy's menu puts its badge.
|
|
5064
|
+
*/
|
|
5065
|
+
export const navList: RecipeInput = {
|
|
5066
|
+
component: 'nav-list',
|
|
5067
|
+
hooks: {
|
|
5068
|
+
properties: {
|
|
5069
|
+
'--nav-accent': 'The current link\'s ink.',
|
|
5070
|
+
'--nav-tint': 'The current link\'s fill.',
|
|
5071
|
+
'--nav-ink': 'The resting link ink.',
|
|
5072
|
+
},
|
|
5073
|
+
},
|
|
5074
|
+
tokens: {
|
|
5075
|
+
'--nav-accent': 'var(--color-base-content)',
|
|
5076
|
+
'--nav-tint': 'var(--color-base-300)',
|
|
5077
|
+
'--nav-ink': 'var(--color-base-content)',
|
|
5078
|
+
},
|
|
5079
|
+
parts: {
|
|
5080
|
+
root: {
|
|
5081
|
+
base: {
|
|
5082
|
+
display: 'flex',
|
|
5083
|
+
flexDirection: 'column',
|
|
5084
|
+
gap: 'var(--space-lg)',
|
|
5085
|
+
fontSize: 'var(--text-sm)',
|
|
5086
|
+
color: 'var(--color-base-content)',
|
|
5087
|
+
},
|
|
5088
|
+
},
|
|
5089
|
+
group: {
|
|
5090
|
+
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-xs)' },
|
|
5091
|
+
},
|
|
5092
|
+
heading: {
|
|
5093
|
+
base: {
|
|
5094
|
+
padding: '0 var(--space-lg)',
|
|
5095
|
+
fontSize: 'var(--text-xs)',
|
|
5096
|
+
fontWeight: 'var(--weight-bold)',
|
|
5097
|
+
opacity: '0.6',
|
|
5098
|
+
},
|
|
5099
|
+
},
|
|
5100
|
+
list: {
|
|
5101
|
+
base: {
|
|
5102
|
+
display: 'flex',
|
|
5103
|
+
flexDirection: 'column',
|
|
5104
|
+
gap: 'var(--space-2xs)',
|
|
5105
|
+
listStyle: 'none',
|
|
5106
|
+
margin: '0',
|
|
5107
|
+
padding: '0',
|
|
5108
|
+
},
|
|
5109
|
+
},
|
|
5110
|
+
item: { base: { display: 'block' } },
|
|
5111
|
+
link: {
|
|
5112
|
+
base: {
|
|
5113
|
+
display: 'flex',
|
|
5114
|
+
alignItems: 'center',
|
|
5115
|
+
gap: 'var(--space-sm)',
|
|
5116
|
+
textDecoration: 'none',
|
|
5117
|
+
color: 'var(--nav-ink)',
|
|
5118
|
+
padding: 'var(--space-sm) var(--space-lg)',
|
|
5119
|
+
borderRadius: 'var(--radius-field)',
|
|
5120
|
+
fontWeight: 'var(--weight-medium)',
|
|
5121
|
+
lineHeight: 'var(--leading-tight)',
|
|
5122
|
+
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
5123
|
+
},
|
|
5124
|
+
states: {
|
|
5125
|
+
hover: { background: 'var(--color-base-200)' },
|
|
5126
|
+
active: { background: 'var(--nav-tint)', color: 'var(--nav-accent)' },
|
|
5127
|
+
inactive: {},
|
|
5128
|
+
...focusRing,
|
|
5129
|
+
},
|
|
5130
|
+
},
|
|
5131
|
+
icon: {
|
|
5132
|
+
base: {
|
|
5133
|
+
display: 'inline-flex',
|
|
5134
|
+
inlineSize: '1.25em',
|
|
5135
|
+
justifyContent: 'center',
|
|
5136
|
+
flex: 'none',
|
|
5137
|
+
lineHeight: 'var(--leading-none)',
|
|
5138
|
+
},
|
|
5139
|
+
},
|
|
5140
|
+
meta: {
|
|
5141
|
+
base: {
|
|
5142
|
+
marginInlineStart: 'auto',
|
|
5143
|
+
display: 'inline-flex',
|
|
5144
|
+
alignItems: 'center',
|
|
5145
|
+
fontSize: 'var(--text-xs)',
|
|
5146
|
+
fontVariantNumeric: 'tabular-nums',
|
|
5147
|
+
opacity: '0.7',
|
|
5148
|
+
},
|
|
5149
|
+
},
|
|
5150
|
+
},
|
|
5151
|
+
variants: {
|
|
5152
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
5153
|
+
'--nav-accent': roleInk(c),
|
|
5154
|
+
'--nav-tint': `var(--color-${c}-soft)`,
|
|
5155
|
+
} } }])),
|
|
5156
|
+
size: {
|
|
5157
|
+
xs: { root: { base: { fontSize: 'var(--text-xs)' } }, link: { base: { padding: 'var(--space-2xs) var(--space-md)' } } },
|
|
5158
|
+
sm: { root: { base: { fontSize: 'var(--text-xs)' } }, link: { base: { padding: 'var(--space-xs) var(--space-md)' } } },
|
|
5159
|
+
md: {},
|
|
5160
|
+
lg: { root: { base: { fontSize: 'var(--text-md)' } }, link: { base: { padding: 'var(--space-md) var(--space-lg)' } } },
|
|
5161
|
+
xl: { root: { base: { fontSize: 'var(--text-lg)' } }, link: { base: { padding: 'var(--space-lg) var(--space-xl)' } } },
|
|
5162
|
+
},
|
|
5163
|
+
},
|
|
5164
|
+
};
|
|
5165
|
+
|
|
4866
5166
|
/**
|
|
4867
5167
|
* Breadcrumbs — daisy's breadcrumbs: a compact text-sm trail, links
|
|
4868
5168
|
* underlining on hover, the current crumb plain full ink. Colour rebinds
|
|
@@ -5078,6 +5378,20 @@ export const pagination: RecipeInput = {
|
|
|
5078
5378
|
},
|
|
5079
5379
|
};
|
|
5080
5380
|
|
|
5381
|
+
/**
|
|
5382
|
+
* The complete step's digit: the role hue deepened toward base-content, on
|
|
5383
|
+
* the disc's 20% tint of that role — `roleInk`'s per-role keep, capped at
|
|
5384
|
+
* 70%. The cap is the primary story: 95% on the tint measured 2.96:1 under
|
|
5385
|
+
* nord's muted primary (the static contrast matrix, #403). The per-role part
|
|
5386
|
+
* arrived with per-step colour (#112), when the matrix first measured every
|
|
5387
|
+
* role here: a flat 70% put nord's muted accent/success/warning at 2.2–2.6:1
|
|
5388
|
+
* and the dark themes' neutral (darker than the page) at 1.7–2.0:1, and
|
|
5389
|
+
* `ROLE_INK_KEEP` already knew how much hue each of those can keep.
|
|
5390
|
+
*/
|
|
5391
|
+
const stepInk = (accent: string, role: string): string =>
|
|
5392
|
+
`color-mix(in oklab, ${accent} `
|
|
5393
|
+
+ `${Math.min(ROLE_INK_KEEP[role as keyof typeof roles] ?? 55, 70)}%, var(--color-base-content))`;
|
|
5394
|
+
|
|
5081
5395
|
/**
|
|
5082
5396
|
* Steps — daisy's steps translated to the richer rail: bold numbered discs
|
|
5083
5397
|
* on base-200, the walked disc and line refilled with the accent pair
|
|
@@ -5090,6 +5404,9 @@ export const steps: RecipeInput = {
|
|
|
5090
5404
|
'--steps-accent': 'var(--color-primary)',
|
|
5091
5405
|
'--steps-accent-content': 'var(--color-primary-content)',
|
|
5092
5406
|
'--steps-accent-ink': roleInk('primary'),
|
|
5407
|
+
// Read through the accent, so it resolves on the element that sets
|
|
5408
|
+
// it; a colour on the item restates it for that role.
|
|
5409
|
+
'--steps-complete-ink': stepInk('var(--steps-accent)', 'primary'),
|
|
5093
5410
|
'--steps-ind': 'calc(var(--size-selector) * 7)',
|
|
5094
5411
|
'--steps-font': 'var(--text-sm)',
|
|
5095
5412
|
},
|
|
@@ -5161,7 +5478,14 @@ export const steps: RecipeInput = {
|
|
|
5161
5478
|
},
|
|
5162
5479
|
states: {
|
|
5163
5480
|
active: { background: 'var(--steps-accent)', color: 'var(--steps-accent-content)' },
|
|
5164
|
-
|
|
5481
|
+
// Ink deepened toward base-content, per role (`stepInk`). The
|
|
5482
|
+
// tint mixes in OKLAB: daisy's light base-100 is
|
|
5483
|
+
// `oklch(100% 0 0)`, whose 0 hue the browser interpolates as
|
|
5484
|
+
// written, so an OKLCH mix swung every role's tint toward red
|
|
5485
|
+
// (warning's yellow came out pink) — which the browser parity
|
|
5486
|
+
// check caught once per-step colour put all eight roles on
|
|
5487
|
+
// the disc (#112).
|
|
5488
|
+
complete: { background: 'color-mix(in oklab, var(--steps-accent) 20%, var(--color-base-100))', color: 'var(--steps-complete-ink)' },
|
|
5165
5489
|
inactive: { background: 'var(--color-base-200)', color: 'var(--color-base-content)' },
|
|
5166
5490
|
},
|
|
5167
5491
|
},
|
|
@@ -5210,10 +5534,15 @@ export const steps: RecipeInput = {
|
|
|
5210
5534
|
},
|
|
5211
5535
|
},
|
|
5212
5536
|
variants: {
|
|
5213
|
-
|
|
5537
|
+
// Keyed on the ITEM, not the root (#112): the item re-carries
|
|
5538
|
+
// `color`, so a step's own value outranks the rail's, and the
|
|
5539
|
+
// disc, bridge and title inside it inherit whichever won. A root
|
|
5540
|
+
// colour still reaches every item through the carrier's donut.
|
|
5541
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { item: { base: {
|
|
5214
5542
|
'--steps-accent': `var(--color-${c})`,
|
|
5215
5543
|
'--steps-accent-content': `var(--color-${c}-content)`,
|
|
5216
5544
|
'--steps-accent-ink': roleInk(c),
|
|
5545
|
+
'--steps-complete-ink': stepInk(`var(--color-${c})`, c),
|
|
5217
5546
|
} } }])),
|
|
5218
5547
|
size: {
|
|
5219
5548
|
xs: { root: { base: { '--steps-ind': 'calc(var(--size-selector) * 5)', '--steps-font': 'var(--text-xs)' } } },
|
|
@@ -5253,10 +5582,46 @@ export const steps: RecipeInput = {
|
|
|
5253
5582
|
},
|
|
5254
5583
|
};
|
|
5255
5584
|
|
|
5585
|
+
/**
|
|
5586
|
+
* The modal sheet's slide (#83): in from its edge, back out to it.
|
|
5587
|
+
*
|
|
5588
|
+
* `translate` is physical, so the travel is a custom property that flips with
|
|
5589
|
+
* the placement AND the direction — off the reading start is leftward in LTR
|
|
5590
|
+
* and rightward in RTL (the `rtl` hedge, as everywhere here). Keyed on
|
|
5591
|
+
* `data-l-dock="sheet"`, the regime, which holds through the exit where
|
|
5592
|
+
* `:modal` does not, so the slide-out leaves from the sheet's own box.
|
|
5593
|
+
* Opacity stays at 1: the travel is the transition. Each direction rides the
|
|
5594
|
+
* transition declared at its destination; reduced motion drops both.
|
|
5595
|
+
*/
|
|
5596
|
+
const sheetSlide = (enter: string, exit: string): PartStyles => {
|
|
5597
|
+
const sheet = '&[data-l-dock="sheet"]';
|
|
5598
|
+
const open = `${sheet}[data-state="open"]`;
|
|
5599
|
+
const travel = (tempo: string): string => {
|
|
5600
|
+
const duration = tempo.split(' ')[0];
|
|
5601
|
+
return `translate ${tempo}, display ${duration} allow-discrete, overlay ${duration} allow-discrete`;
|
|
5602
|
+
};
|
|
5603
|
+
return {
|
|
5604
|
+
base: { '--drawer-travel': '-100%' },
|
|
5605
|
+
selectors: {
|
|
5606
|
+
'&[data-placement="end"]': { '--drawer-travel': '100%' },
|
|
5607
|
+
[`&[data-placement="start"]${rtl}`]: { '--drawer-travel': '100%' },
|
|
5608
|
+
[`&[data-placement="end"]${rtl}`]: { '--drawer-travel': '-100%' },
|
|
5609
|
+
[sheet]: { opacity: '1', translate: 'var(--drawer-travel) 0', transition: travel(exit) },
|
|
5610
|
+
[open]: { translate: 'none', transition: travel(enter) },
|
|
5611
|
+
},
|
|
5612
|
+
at: {
|
|
5613
|
+
'starting-style': { selectors: { [open]: { opacity: '1', translate: 'var(--drawer-travel) 0' } } },
|
|
5614
|
+
'reduced-motion': { selectors: { [sheet]: { transition: 'none' }, [open]: { transition: 'none' } } },
|
|
5615
|
+
},
|
|
5616
|
+
};
|
|
5617
|
+
};
|
|
5618
|
+
|
|
5256
5619
|
/**
|
|
5257
5620
|
* Drawer — daisy's drawer-side as a native <dialog> edge sheet: base-100
|
|
5258
|
-
* paper, no border (daisy separates the drawer with the scrim),
|
|
5259
|
-
*
|
|
5621
|
+
* paper, no border (daisy separates the drawer with the scrim), sliding in
|
|
5622
|
+
* from its edge the way daisy's drawer-side does (`translate` over 0.3s,
|
|
5623
|
+
* #83). Base render is the inline mode; `data-l-dock="sheet"` is the
|
|
5624
|
+
* top-layer edge sheet.
|
|
5260
5625
|
*/
|
|
5261
5626
|
export const drawer: RecipeInput = {
|
|
5262
5627
|
component: 'drawer',
|
|
@@ -5271,7 +5636,10 @@ export const drawer: RecipeInput = {
|
|
|
5271
5636
|
...focusRing,
|
|
5272
5637
|
},
|
|
5273
5638
|
},
|
|
5274
|
-
panel: withPresence(popupPresence('none'),
|
|
5639
|
+
panel: withPresence(withPresence(popupPresence('none'), sheetSlide(
|
|
5640
|
+
'var(--duration-slow) var(--ease-standard)',
|
|
5641
|
+
'var(--duration-slow) var(--ease-standard)',
|
|
5642
|
+
)), {
|
|
5275
5643
|
base: {
|
|
5276
5644
|
padding: 'var(--space-lg)',
|
|
5277
5645
|
background: 'var(--color-base-100)',
|
|
@@ -5279,30 +5647,39 @@ export const drawer: RecipeInput = {
|
|
|
5279
5647
|
border: 'none',
|
|
5280
5648
|
borderRadius: 'var(--radius-box)',
|
|
5281
5649
|
boxShadow: 'var(--shadow-xl)',
|
|
5282
|
-
|
|
5650
|
+
// The width is capped by `--l-measure` (Drawer.Panel's
|
|
5651
|
+
// `measure`, via the layout step table); this is the default
|
|
5652
|
+
// an unset `measure` leaves. A cap, not a width, because the
|
|
5653
|
+
// table spells `full` as `none` — valid only for a max. In
|
|
5654
|
+
// flow and as a sheet alike, the panel fills what it is given
|
|
5655
|
+
// up to the measure, as its border box, so `full` never
|
|
5656
|
+
// overflows by its own padding.
|
|
5657
|
+
boxSizing: 'border-box',
|
|
5658
|
+
'--l-measure': 'min(20rem, 85vw)',
|
|
5659
|
+
inlineSize: '100%',
|
|
5660
|
+
maxInlineSize: 'var(--l-measure)',
|
|
5283
5661
|
},
|
|
5284
5662
|
states: { open: {}, closed: {} },
|
|
5285
5663
|
selectors: {
|
|
5286
5664
|
/**
|
|
5287
|
-
* The
|
|
5288
|
-
*
|
|
5289
|
-
*
|
|
5290
|
-
* the
|
|
5291
|
-
* RTL mirrors
|
|
5665
|
+
* The top-layer edge sheet, keyed on the regime
|
|
5666
|
+
* (`data-l-dock="sheet"`) rather than `:modal`, which stops
|
|
5667
|
+
* matching the moment `close()` runs — so the sheet keeps its
|
|
5668
|
+
* box through the exit (#83). The base styles above are the
|
|
5669
|
+
* INLINE render. Logical insets pin the edge, so RTL mirrors
|
|
5670
|
+
* free.
|
|
5292
5671
|
*/
|
|
5293
|
-
'
|
|
5672
|
+
'&[data-l-dock="sheet"]': {
|
|
5294
5673
|
position: 'fixed',
|
|
5295
5674
|
insetBlockStart: '0',
|
|
5296
5675
|
insetBlockEnd: '0',
|
|
5297
5676
|
blockSize: '100dvh',
|
|
5298
5677
|
maxBlockSize: '100dvh',
|
|
5299
|
-
inlineSize: 'min(20rem, 85vw)',
|
|
5300
|
-
maxInlineSize: 'none',
|
|
5301
5678
|
margin: '0',
|
|
5302
5679
|
borderRadius: '0',
|
|
5303
5680
|
},
|
|
5304
|
-
'&[data-placement="start"]
|
|
5305
|
-
'&[data-placement="end"]
|
|
5681
|
+
'&[data-placement="start"][data-l-dock="sheet"]': { insetInlineStart: '0', insetInlineEnd: 'auto' },
|
|
5682
|
+
'&[data-placement="end"][data-l-dock="sheet"]': { insetInlineStart: 'auto', insetInlineEnd: '0' },
|
|
5306
5683
|
},
|
|
5307
5684
|
}),
|
|
5308
5685
|
backdrop: {
|
|
@@ -5344,7 +5721,20 @@ export const drawer: RecipeInput = {
|
|
|
5344
5721
|
*/
|
|
5345
5722
|
export const table: RecipeInput = {
|
|
5346
5723
|
component: 'table',
|
|
5724
|
+
// Public to a design system derived from this one (#73).
|
|
5725
|
+
hooks: {
|
|
5726
|
+
properties: {
|
|
5727
|
+
'--table-cell-align': 'Cell text alignment; zero writes it inline from the column spec.',
|
|
5728
|
+
'--table-accent': 'The header ink accent.',
|
|
5729
|
+
'--table-pad-block': 'Cell block padding.',
|
|
5730
|
+
'--table-pad-inline': 'Cell inline padding.',
|
|
5731
|
+
'--table-font': 'The table font size.',
|
|
5732
|
+
},
|
|
5733
|
+
},
|
|
5347
5734
|
tokens: {
|
|
5735
|
+
// The column spec's alignment (#55): a cell that names an aligned
|
|
5736
|
+
// column overrides it inline; everything else reads `start`.
|
|
5737
|
+
'--table-cell-align': 'start',
|
|
5348
5738
|
'--table-accent': 'var(--color-base-content)',
|
|
5349
5739
|
'--table-pad-block': 'var(--space-sm)',
|
|
5350
5740
|
'--table-pad-inline': 'var(--space-md)',
|
|
@@ -5358,6 +5748,9 @@ export const table: RecipeInput = {
|
|
|
5358
5748
|
borderRadius: 'var(--radius-box)',
|
|
5359
5749
|
background: 'var(--color-base-100)',
|
|
5360
5750
|
},
|
|
5751
|
+
// Stacked (Table.Root stack, #55): zero lays each row out as a block
|
|
5752
|
+
// below the table's breakpoint; the card it becomes is drawn here.
|
|
5753
|
+
at: tableStackAt(tokens, 'root', { border: '0', borderRadius: '0', background: 'transparent' }),
|
|
5361
5754
|
},
|
|
5362
5755
|
table: {
|
|
5363
5756
|
base: {
|
|
@@ -5375,34 +5768,52 @@ export const table: RecipeInput = {
|
|
|
5375
5768
|
fontSize: 'var(--text-xs)',
|
|
5376
5769
|
color: 'color-mix(in oklab, var(--color-base-content) 60%, transparent)',
|
|
5377
5770
|
},
|
|
5771
|
+
at: tableStackAt(tokens, 'caption', { paddingInline: '0' }),
|
|
5378
5772
|
},
|
|
5379
5773
|
head: {},
|
|
5380
|
-
body: {},
|
|
5774
|
+
body: { at: tableStackAt(tokens, 'body', { rowGap: 'var(--space-md)' }) },
|
|
5381
5775
|
foot: {
|
|
5382
5776
|
base: {
|
|
5383
5777
|
fontSize: 'var(--text-xs)',
|
|
5384
5778
|
color: 'color-mix(in oklab, var(--color-base-content) 60%, transparent)',
|
|
5385
5779
|
},
|
|
5780
|
+
at: tableStackAt(tokens, 'foot', { rowGap: 'var(--space-md)', marginBlockStart: 'var(--space-md)' }),
|
|
5386
5781
|
},
|
|
5387
5782
|
row: {
|
|
5388
5783
|
base: { borderBlockEnd: 'var(--border) solid var(--color-base-200)' },
|
|
5389
5784
|
states: {
|
|
5390
5785
|
selected: { background: 'var(--color-base-300)' },
|
|
5391
5786
|
},
|
|
5787
|
+
at: tableStackAt(tokens, 'row', {
|
|
5788
|
+
border: 'var(--border) solid var(--color-base-200)',
|
|
5789
|
+
borderRadius: 'var(--radius-box)',
|
|
5790
|
+
background: 'var(--color-base-100)',
|
|
5791
|
+
padding: 'var(--table-pad-block) var(--table-pad-inline)',
|
|
5792
|
+
}),
|
|
5392
5793
|
},
|
|
5393
5794
|
'header-cell': {
|
|
5394
5795
|
base: {
|
|
5395
5796
|
padding: 'var(--table-pad-block) var(--table-pad-inline)',
|
|
5396
|
-
textAlign: '
|
|
5797
|
+
textAlign: 'var(--table-cell-align)',
|
|
5397
5798
|
fontWeight: 'var(--weight-semibold)',
|
|
5398
5799
|
fontSize: 'var(--text-xs)',
|
|
5399
5800
|
color: 'color-mix(in oklab, var(--table-accent) 60%, transparent)',
|
|
5400
5801
|
},
|
|
5802
|
+
at: tableStackAt(tokens, 'header-cell', { paddingInline: '0' }),
|
|
5401
5803
|
},
|
|
5402
5804
|
cell: {
|
|
5403
5805
|
base: {
|
|
5404
5806
|
padding: 'var(--table-pad-block) var(--table-pad-inline)',
|
|
5405
|
-
textAlign: '
|
|
5807
|
+
textAlign: 'var(--table-cell-align)',
|
|
5808
|
+
},
|
|
5809
|
+
at: tableStackAt(tokens, 'cell', { paddingInline: '0', paddingBlock: 'var(--space-xs)' }),
|
|
5810
|
+
},
|
|
5811
|
+
// A stacked cell's column label, in the header's muted semibold.
|
|
5812
|
+
'cell-label': {
|
|
5813
|
+
base: {
|
|
5814
|
+
fontWeight: 'var(--weight-semibold)',
|
|
5815
|
+
fontSize: 'var(--text-xs)',
|
|
5816
|
+
color: 'color-mix(in oklab, var(--table-accent) 60%, transparent)',
|
|
5406
5817
|
},
|
|
5407
5818
|
},
|
|
5408
5819
|
},
|
|
@@ -5879,6 +6290,12 @@ export const countdown: RecipeInput = {
|
|
|
5879
6290
|
xl: { root: { base: { '--countdown-font': 'var(--text-3xl)' } } },
|
|
5880
6291
|
},
|
|
5881
6292
|
},
|
|
6293
|
+
// One line inside a sentence (#57): the surrounding text's size and
|
|
6294
|
+
// weight instead of the display step. What stays is what makes
|
|
6295
|
+
// it a countdown — tabular digits, its ink, the per-tick entry.
|
|
6296
|
+
modifiers: {
|
|
6297
|
+
inline: { root: { base: { fontSize: 'inherit', fontWeight: 'inherit' } } },
|
|
6298
|
+
},
|
|
5882
6299
|
keyframes: { 'zero-daisyui-countdown-in': 'from { transform: translateY(0.6em); opacity: 0; }' },
|
|
5883
6300
|
};
|
|
5884
6301
|
|
|
@@ -6005,10 +6422,10 @@ export const diff: RecipeInput = {
|
|
|
6005
6422
|
export const recipes: RecipeInput[] = [
|
|
6006
6423
|
tabs, collapsible, switchRecipe, dialog, popover, tooltip, menu,
|
|
6007
6424
|
field, checkbox, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
|
|
6008
|
-
toggle, toggleGroup, numberInput, ratingGroup, treeView, input, textarea,
|
|
6009
|
-
card, alert, badge, divider, skeleton, spinner,
|
|
6425
|
+
toggle, toggleGroup, numberInput, ratingGroup, treeView, input, textarea,
|
|
6426
|
+
card, alert, emptyState, badge, divider, skeleton, spinner,
|
|
6010
6427
|
kbd, status, indicator, stats, timeline, chat, radialProgress, join,
|
|
6011
|
-
navbar, breadcrumbs, pagination, steps, drawer,
|
|
6428
|
+
navbar, navList, breadcrumbs, pagination, steps, drawer,
|
|
6012
6429
|
table,
|
|
6013
6430
|
fileUpload,
|
|
6014
6431
|
carousel,
|