@sigx/zero-daisyui 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.json +3868 -2
- package/dist/components.d.ts +37 -1
- package/dist/components.js +4 -0
- package/dist/css/components/avatar-group.css +168 -0
- package/dist/css/components/breadcrumbs.css +30 -0
- package/dist/css/components/card.css +26 -0
- package/dist/css/components/chat-log.css +172 -0
- package/dist/css/components/divider.css +68 -3
- package/dist/css/components/hover-card.css +174 -0
- package/dist/css/components/menu.css +7 -0
- package/dist/css/components/menubar.css +75 -0
- package/dist/css/components/pagination.css +83 -0
- package/dist/css/components/steps.css +103 -0
- package/dist/css/components/timeline.css +14 -0
- package/dist/css/components/toast.css +65 -0
- package/dist/css/components/tree-view.css +118 -0
- package/dist/css/index.css +1165 -69
- package/dist/lynx/components/accordion.css +3 -1
- package/dist/lynx/components/avatar-group.css +97 -0
- package/dist/lynx/components/breadcrumbs.css +44 -0
- package/dist/lynx/components/button.css +71 -12
- package/dist/lynx/components/card.css +9 -0
- package/dist/lynx/components/chat-log.css +147 -0
- package/dist/lynx/components/dialog.css +9 -3
- package/dist/lynx/components/divider.css +12 -2
- package/dist/lynx/components/hover-card.css +211 -0
- package/dist/lynx/components/menu.css +7 -0
- package/dist/lynx/components/menubar.css +199 -0
- package/dist/lynx/components/pagination.css +107 -0
- package/dist/lynx/components/popover.css +7 -2
- package/dist/lynx/components/select.css +40 -3
- package/dist/lynx/components/slider.css +3 -2
- package/dist/lynx/components/steps.css +103 -0
- package/dist/lynx/components/switch.css +2 -1
- package/dist/lynx/components/tabs.css +7 -1
- package/dist/lynx/components/timeline.css +32 -0
- package/dist/lynx/components/toast.css +70 -2
- package/dist/lynx/components/tree-view.css +28 -0
- package/dist/lynx/index.css +1297 -114
- package/dist/lynx/manifest.json +112 -4
- package/dist/manifest.json +110 -2
- package/dist/recipes.d.ts +20 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +853 -99
- package/dist/recipes.js.map +1 -1
- package/dist/register.d.ts +36 -0
- package/dist/report.json +1320 -213
- package/package.json +4 -4
- package/src/recipes.ts +877 -99
package/dist/recipes.js
CHANGED
|
@@ -28,6 +28,36 @@ const focusRing = {
|
|
|
28
28
|
outlineOffset: '2px',
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* The focus ring on lynx, for `targets.lynx` (signalxjs/lynx#1163): the same
|
|
33
|
+
* 2px ring 2px clear of the part, spelled as spread box-shadows.
|
|
34
|
+
*
|
|
35
|
+
* The web ring is `outline` + `outline-offset`, and neither survives on
|
|
36
|
+
* lynx: its `outline` ignores `border-radius` (a square box around a
|
|
37
|
+
* rounded button, a rectangle around the switch pill) and it has no
|
|
38
|
+
* `outline-offset` property at all, so the ring sat flush on the fill. A
|
|
39
|
+
* zero-blur shadow's spread follows the element's radius. Shadows stack
|
|
40
|
+
* rather than mask, so the offset gap cannot be transparent: the inner
|
|
41
|
+
* shadow paints it in `gap` — the surface the part sits on, which is what
|
|
42
|
+
* the web's gap shows through to. `outline: none` retires the shared ring
|
|
43
|
+
* in the same merged declaration block.
|
|
44
|
+
*
|
|
45
|
+
* `under` appends the part's own lynx shadows (a knob's depth relief), which
|
|
46
|
+
* the focus rule would otherwise replace. `inset` draws the ring inside the
|
|
47
|
+
* part, flush with its edge, for a part whose parent clips an outside ring
|
|
48
|
+
* away (signalxjs/lynx#1164, the accordion trigger inside its card).
|
|
49
|
+
*/
|
|
50
|
+
const lynxFocusRing = (ink, { gap = 'var(--color-base-100)', under, inset = false } = {}) => ({
|
|
51
|
+
outline: 'none',
|
|
52
|
+
boxShadow: [
|
|
53
|
+
...(inset ? [`inset 0 0 0 2px ${ink}`] : [`0 0 0 2px ${gap}`, `0 0 0 4px ${ink}`]),
|
|
54
|
+
...(under ? [under] : []),
|
|
55
|
+
].join(', '),
|
|
56
|
+
});
|
|
57
|
+
/** `focusRing`'s lynx spelling — the base-content ring, as a states entry. */
|
|
58
|
+
const lynxFocus = (opts) => ({
|
|
59
|
+
'focus-visible': lynxFocusRing('var(--color-base-content)', opts),
|
|
60
|
+
});
|
|
31
61
|
/**
|
|
32
62
|
* How far each role survives in the button's INK — the colour a transparent or
|
|
33
63
|
* tinted fill draws its label, border and focus ring with.
|
|
@@ -495,7 +525,7 @@ export const tabs = {
|
|
|
495
525
|
parts: {
|
|
496
526
|
// No hover on a touch platform: the held tab takes the ink
|
|
497
527
|
// daisy's hover gives it.
|
|
498
|
-
tab: { states: { pressed: { color: 'var(--color-base-content)' } } },
|
|
528
|
+
tab: { states: { pressed: { color: 'var(--color-base-content)' }, ...lynxFocus() } },
|
|
499
529
|
// The list is the indicator's containing block.
|
|
500
530
|
list: { base: { position: 'relative' } },
|
|
501
531
|
// The underline's ink (read by the `border` flavor below) —
|
|
@@ -546,6 +576,14 @@ export const tabs = {
|
|
|
546
576
|
base: { '--tab-radius-limit': 'var(--radius-field)' },
|
|
547
577
|
},
|
|
548
578
|
},
|
|
579
|
+
// The box flavor's tabs sit on the list's base-200
|
|
580
|
+
// surface, so the ring's gap is that surface. Stated on
|
|
581
|
+
// the variant (one class more than the part rule) so it
|
|
582
|
+
// also outranks the active pill's `--shadow-sm`, which
|
|
583
|
+
// is a box-shadow too.
|
|
584
|
+
box: {
|
|
585
|
+
tab: { states: lynxFocus({ gap: 'var(--color-base-200)' }) },
|
|
586
|
+
},
|
|
549
587
|
},
|
|
550
588
|
},
|
|
551
589
|
},
|
|
@@ -895,6 +933,9 @@ export const switchRecipe = {
|
|
|
895
933
|
borderColor: 'var(--switch-accent)',
|
|
896
934
|
backgroundColor: 'var(--color-base-100)',
|
|
897
935
|
},
|
|
936
|
+
// The pill ring (signalxjs/lynx#1163), keeping the
|
|
937
|
+
// track's inset depth line under it.
|
|
938
|
+
'focus-visible': lynxFocusRing('var(--switch-accent)', { under: '0 1px var(--depth-shade) inset' }),
|
|
898
939
|
},
|
|
899
940
|
},
|
|
900
941
|
thumb: {
|
|
@@ -1011,6 +1052,36 @@ const lynxBtnPad = (mult) => ({
|
|
|
1011
1052
|
paddingLeft: `calc(var(--size-field) * ${mult})`,
|
|
1012
1053
|
paddingRight: `calc(var(--size-field) * ${mult})`,
|
|
1013
1054
|
});
|
|
1055
|
+
/**
|
|
1056
|
+
* A four-side property spelled as its physical longhands, for `targets.lynx`
|
|
1057
|
+
* sections that must not ship a `var()`-bearing shorthand.
|
|
1058
|
+
*
|
|
1059
|
+
* Lynx keeps a declaration holding `var()` under its own property id,
|
|
1060
|
+
* unexpanded, and expands it only after the cascade has merged every rule
|
|
1061
|
+
* into one insertion-ordered style map — in that map's order, writing over
|
|
1062
|
+
* the longhands already there (`CSSVariableHandler::HandleCSSVariables`). A
|
|
1063
|
+
* static shorthand, by contrast, is expanded into its longhands at parse
|
|
1064
|
+
* time. So a var-bearing `border-color` and a static `border-top-color` never
|
|
1065
|
+
* meet in the cascade at all: whichever key the map saw later wins, whatever
|
|
1066
|
+
* the specificity (signalxjs/lynx#1161, #1162). Longhands share one key per
|
|
1067
|
+
* side, and the cascade decides again.
|
|
1068
|
+
*/
|
|
1069
|
+
const lynxSides = (prop, value, sides = ['Top', 'Right', 'Bottom', 'Left']) => Object.fromEntries(sides.map((side) => [prop.replace('{}', side), value]));
|
|
1070
|
+
/**
|
|
1071
|
+
* daisy's `.btn` is content-sized and never squeezed: `display: inline-flex`
|
|
1072
|
+
* with `flex-shrink: 0; flex-wrap: nowrap`, so a narrow row makes the button
|
|
1073
|
+
* overflow, never break its label. A lynx view has no inline formatting
|
|
1074
|
+
* context: it stretches to a linear or column parent, and a squeezed label
|
|
1075
|
+
* `<text>` then wraps per character ("Bt / n", signalxjs/lynx#1165).
|
|
1076
|
+
* `width: max-content` is the intrinsic keyword starlight implements (an
|
|
1077
|
+
* intrinsic width is also exempt from the cross-axis stretch), so the box
|
|
1078
|
+
* sizes to its content as the web's does; the other two are daisy's own.
|
|
1079
|
+
*/
|
|
1080
|
+
const lynxBtnFit = {
|
|
1081
|
+
width: 'max-content',
|
|
1082
|
+
flexShrink: '0',
|
|
1083
|
+
flexWrap: 'nowrap',
|
|
1084
|
+
};
|
|
1014
1085
|
/** The btn size ramp's physical inline padding, for `targets.lynx.variants`. */
|
|
1015
1086
|
const lynxBtnSizes = (part) => ({
|
|
1016
1087
|
xs: { [part]: { base: lynxBtnPad(2) } },
|
|
@@ -1130,9 +1201,10 @@ export const dialog = {
|
|
|
1130
1201
|
targets: {
|
|
1131
1202
|
lynx: {
|
|
1132
1203
|
parts: {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1204
|
+
// Content-sized like daisy's btn, never broken mid-word (signalxjs/lynx#1165).
|
|
1205
|
+
trigger: { base: { ...lynxBtnPad(4), ...lynxBtnFit }, states: { pressed: lynxBtnPressed, ...lynxFocus() } },
|
|
1206
|
+
close: { base: lynxBtnPad(4), states: { pressed: lynxBtnPressed, ...lynxFocus() } },
|
|
1207
|
+
cancel: { base: lynxBtnPad(4), states: { pressed: lynxBtnPressed, ...lynxFocus() } },
|
|
1136
1208
|
footer: { base: { marginTop: 'var(--space-2xl)', flexDirection: 'row' } },
|
|
1137
1209
|
},
|
|
1138
1210
|
variants: { size: lynxBtnSizes('trigger') },
|
|
@@ -1223,8 +1295,9 @@ export const popover = {
|
|
|
1223
1295
|
web: floatingArrow('popover', panelArrowPaint),
|
|
1224
1296
|
lynx: {
|
|
1225
1297
|
parts: {
|
|
1226
|
-
|
|
1227
|
-
|
|
1298
|
+
// Content-sized like daisy's btn, never broken mid-word (signalxjs/lynx#1165).
|
|
1299
|
+
trigger: { base: { ...lynxBtnPad(4), ...lynxBtnFit }, states: { pressed: lynxBtnPressed, ...lynxFocus() } },
|
|
1300
|
+
close: { base: lynxBtnPad(3), states: { pressed: lynxBtnPressed, ...lynxFocus() } },
|
|
1228
1301
|
},
|
|
1229
1302
|
variants: { size: lynxBtnSizes('trigger') },
|
|
1230
1303
|
},
|
|
@@ -1273,6 +1346,56 @@ export const tooltip = {
|
|
|
1273
1346
|
},
|
|
1274
1347
|
},
|
|
1275
1348
|
};
|
|
1349
|
+
/**
|
|
1350
|
+
* Hover card — daisy's `link` opening a `dropdown-content` card. The trigger
|
|
1351
|
+
* is running text, not a btn: underlined in the ink it sits in (daisy's bare
|
|
1352
|
+
* `link`), and a colour is `link-<role>` in the role's readable ink
|
|
1353
|
+
* (`roleInk`). The underline thickens on hover and while the card is up,
|
|
1354
|
+
* rather than the ink dimming — the `btn-link` reasoning. The card is the
|
|
1355
|
+
* floating panel popover and menu wear.
|
|
1356
|
+
*/
|
|
1357
|
+
export const hoverCard = {
|
|
1358
|
+
component: 'hover-card',
|
|
1359
|
+
parts: {
|
|
1360
|
+
trigger: {
|
|
1361
|
+
base: {
|
|
1362
|
+
color: 'inherit',
|
|
1363
|
+
textDecorationLine: 'underline',
|
|
1364
|
+
textUnderlineOffset: '2px',
|
|
1365
|
+
cursor: 'pointer',
|
|
1366
|
+
borderRadius: 'var(--radius-selector)',
|
|
1367
|
+
},
|
|
1368
|
+
states: {
|
|
1369
|
+
hover: { textDecorationThickness: '2px' },
|
|
1370
|
+
open: { textDecorationThickness: '2px' },
|
|
1371
|
+
closed: {},
|
|
1372
|
+
...focusRing,
|
|
1373
|
+
},
|
|
1374
|
+
},
|
|
1375
|
+
popup: withPresence(popupPresence('translateY(-4px)'), {
|
|
1376
|
+
base: {
|
|
1377
|
+
...floatingPanel,
|
|
1378
|
+
padding: 'var(--space-xl)',
|
|
1379
|
+
maxWidth: '20rem',
|
|
1380
|
+
fontSize: 'var(--text-sm)',
|
|
1381
|
+
},
|
|
1382
|
+
states: { open: {}, closed: {} },
|
|
1383
|
+
}),
|
|
1384
|
+
},
|
|
1385
|
+
variants: {
|
|
1386
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { trigger: { base: { color: roleInk(c) } } }])),
|
|
1387
|
+
size: {
|
|
1388
|
+
xs: { trigger: { base: { fontSize: 'var(--text-xs)' } } },
|
|
1389
|
+
sm: { trigger: { base: { fontSize: 'var(--text-sm)' } } },
|
|
1390
|
+
md: {},
|
|
1391
|
+
lg: { trigger: { base: { fontSize: 'var(--text-lg)' } } },
|
|
1392
|
+
xl: { trigger: { base: { fontSize: 'var(--text-xl)' } } },
|
|
1393
|
+
},
|
|
1394
|
+
},
|
|
1395
|
+
// Web-only: the popover top layer and the runtime's arrow geometry have
|
|
1396
|
+
// no lynx counterpart.
|
|
1397
|
+
targets: { web: floatingArrow('hover-card', panelArrowPaint) },
|
|
1398
|
+
};
|
|
1276
1399
|
// daisy "menu in a dropdown" look.
|
|
1277
1400
|
export const menu = {
|
|
1278
1401
|
component: 'menu',
|
|
@@ -1406,6 +1529,17 @@ export const menu = {
|
|
|
1406
1529
|
opacity: '0.6',
|
|
1407
1530
|
},
|
|
1408
1531
|
},
|
|
1532
|
+
// daisy's menu hint: the group label's faded ink, pushed to the
|
|
1533
|
+
// row's reading end — faded less, since 0.6 leaves nord's paper
|
|
1534
|
+
// under the 4.5:1 a line read by eye owes.
|
|
1535
|
+
shortcut: {
|
|
1536
|
+
base: {
|
|
1537
|
+
marginInlineStart: 'auto',
|
|
1538
|
+
paddingInlineStart: 'var(--space-lg)',
|
|
1539
|
+
fontSize: 'var(--text-xs)',
|
|
1540
|
+
opacity: '0.75',
|
|
1541
|
+
},
|
|
1542
|
+
},
|
|
1409
1543
|
separator: {
|
|
1410
1544
|
base: {
|
|
1411
1545
|
height: 'var(--border)',
|
|
@@ -1421,11 +1555,53 @@ export const menu = {
|
|
|
1421
1555
|
targets: {
|
|
1422
1556
|
web: floatingArrow('menu', panelArrowPaint),
|
|
1423
1557
|
lynx: {
|
|
1424
|
-
parts: {
|
|
1558
|
+
parts: {
|
|
1559
|
+
trigger: { base: lynxBtnPad(4) },
|
|
1560
|
+
// The shortcut's reading-end push, restated physically (#1084).
|
|
1561
|
+
shortcut: { base: { marginLeft: 'auto', paddingLeft: 'var(--space-lg)' } },
|
|
1562
|
+
},
|
|
1425
1563
|
variants: { size: lynxBtnSizes('trigger') },
|
|
1426
1564
|
},
|
|
1427
1565
|
},
|
|
1428
1566
|
};
|
|
1567
|
+
// The menubar row: daisy's `menu-horizontal` strip — a base-200 box the
|
|
1568
|
+
// menu triggers sit in. The triggers are ordinary `menu` triggers.
|
|
1569
|
+
export const menubar = {
|
|
1570
|
+
component: 'menubar',
|
|
1571
|
+
tokens: { '--menubar-surface': 'var(--color-base-200)' },
|
|
1572
|
+
parts: {
|
|
1573
|
+
root: {
|
|
1574
|
+
base: {
|
|
1575
|
+
display: 'flex',
|
|
1576
|
+
alignItems: 'center',
|
|
1577
|
+
gap: 'var(--space-xs)',
|
|
1578
|
+
padding: 'var(--space-xs)',
|
|
1579
|
+
width: 'max-content',
|
|
1580
|
+
maxWidth: '100%',
|
|
1581
|
+
flexWrap: 'wrap',
|
|
1582
|
+
background: 'var(--menubar-surface)',
|
|
1583
|
+
borderRadius: 'var(--radius-box)',
|
|
1584
|
+
},
|
|
1585
|
+
// The triggers fade themselves — see basic.
|
|
1586
|
+
states: { disabled: {} },
|
|
1587
|
+
selectors: {
|
|
1588
|
+
'&[data-orientation="vertical"]': { flexDirection: 'column', alignItems: 'stretch' },
|
|
1589
|
+
},
|
|
1590
|
+
},
|
|
1591
|
+
},
|
|
1592
|
+
variants: {
|
|
1593
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
1594
|
+
'--menubar-surface': `color-mix(in oklch, var(--color-${c}) 12%, var(--color-base-200))`,
|
|
1595
|
+
} } }])),
|
|
1596
|
+
size: {
|
|
1597
|
+
xs: { root: { base: { gap: '0', padding: '0' } } },
|
|
1598
|
+
sm: { root: { base: { padding: 'var(--space-2xs)' } } },
|
|
1599
|
+
md: {},
|
|
1600
|
+
lg: { root: { base: { gap: 'var(--space-sm)', padding: 'var(--space-sm)' } } },
|
|
1601
|
+
xl: { root: { base: { gap: 'var(--space-md)', padding: 'var(--space-sm)' } } },
|
|
1602
|
+
},
|
|
1603
|
+
},
|
|
1604
|
+
};
|
|
1429
1605
|
export const field = {
|
|
1430
1606
|
component: 'field',
|
|
1431
1607
|
// The label's accent ink — base-content by default, so the un-attributed
|
|
@@ -2428,6 +2604,12 @@ export const slider = {
|
|
|
2428
2604
|
control: {
|
|
2429
2605
|
states: {
|
|
2430
2606
|
invalid: { '--slider-fill': sliderFill('var(--color-error)') },
|
|
2607
|
+
// The control's ring is the NATIVE input's on the web;
|
|
2608
|
+
// lynx always renders the composed parts, where the
|
|
2609
|
+
// thumb is what takes focus and wears the ring below
|
|
2610
|
+
// (signalxjs/lynx#1163 — both at once read as two
|
|
2611
|
+
// nested boxes).
|
|
2612
|
+
'focus-visible': { outline: 'none' },
|
|
2431
2613
|
},
|
|
2432
2614
|
selectors: {
|
|
2433
2615
|
'&[data-orientation="vertical"]': {
|
|
@@ -2525,6 +2707,13 @@ export const slider = {
|
|
|
2525
2707
|
transform: 'translateY(-50%)',
|
|
2526
2708
|
marginLeft: 'calc(var(--slider-thumb-size) / -2)',
|
|
2527
2709
|
},
|
|
2710
|
+
// The round ring (signalxjs/lynx#1163), over the knob's
|
|
2711
|
+
// depth relief.
|
|
2712
|
+
states: {
|
|
2713
|
+
'focus-visible': lynxFocusRing('var(--slider-accent)', {
|
|
2714
|
+
under: '0 -1px var(--depth-shade) inset, 0 8px 0 -4px var(--depth-sheen) inset, 0 1px var(--depth-shade)',
|
|
2715
|
+
}),
|
|
2716
|
+
},
|
|
2528
2717
|
},
|
|
2529
2718
|
},
|
|
2530
2719
|
},
|
|
@@ -2540,7 +2729,20 @@ export const accordion = {
|
|
|
2540
2729
|
// hover fill. The ::after chevron has no lynx counterpart (no
|
|
2541
2730
|
// pseudo-elements, and the anatomy declares no part for it) —
|
|
2542
2731
|
// an app draws its own glyph in the trigger.
|
|
2543
|
-
|
|
2732
|
+
//
|
|
2733
|
+
// The item card clips (`overflow: hidden`) and the trigger
|
|
2734
|
+
// fills it, so an outside ring is clipped away entirely
|
|
2735
|
+
// (signalxjs/lynx#1164): the ring is drawn inset, rounded to
|
|
2736
|
+
// the card's inner corners.
|
|
2737
|
+
trigger: {
|
|
2738
|
+
states: {
|
|
2739
|
+
pressed: { background: 'var(--color-base-200)' },
|
|
2740
|
+
'focus-visible': {
|
|
2741
|
+
...lynxFocusRing('var(--color-base-content)', { inset: true }),
|
|
2742
|
+
borderRadius: 'calc(var(--radius-box) - var(--border))',
|
|
2743
|
+
},
|
|
2744
|
+
},
|
|
2745
|
+
},
|
|
2544
2746
|
},
|
|
2545
2747
|
},
|
|
2546
2748
|
},
|
|
@@ -2828,22 +3030,43 @@ export const select = {
|
|
|
2828
3030
|
// never wider than its container. lynx has no `clamp()`, so the
|
|
2829
3031
|
// same bounds are spelled out — and the base's 13rem floor
|
|
2830
3032
|
// comes down to daisy's 3rem, or the trigger overflowed any
|
|
2831
|
-
// cell narrower than 208px (signalxjs/lynx#1146).
|
|
2832
|
-
//
|
|
2833
|
-
|
|
3033
|
+
// cell narrower than 208px (signalxjs/lynx#1146).
|
|
3034
|
+
//
|
|
3035
|
+
// The bounds sit on the ROOT, the box daisy's `.select` is,
|
|
3036
|
+
// and the trigger fills it. The web root is `inline-flex`, so
|
|
3037
|
+
// it shrink-wraps the trigger; lynx rewrites that to `flex`,
|
|
3038
|
+
// and a root stretched by its parent while the trigger stayed
|
|
3039
|
+
// capped left the clear-trigger — positioned against the root
|
|
3040
|
+
// — floating past the trigger's end (signalxjs/lynx#1166).
|
|
3041
|
+
root: { base: { width: '20rem', maxWidth: '100%', minWidth: '3rem' } },
|
|
2834
3042
|
trigger: {
|
|
2835
|
-
base: { ...lynxBtnPad(4), width: '
|
|
3043
|
+
base: { ...lynxBtnPad(4), width: '100%', maxWidth: '100%', minWidth: '3rem' },
|
|
2836
3044
|
// The web's `:hover` border is dropped on a touch
|
|
2837
3045
|
// platform; a press gets a wash instead — a background,
|
|
2838
3046
|
// so it never fights the open/invalid border inks.
|
|
2839
|
-
states: { pressed: { background: 'var(--color-base-200)' } },
|
|
2840
|
-
},
|
|
3047
|
+
states: { pressed: { background: 'var(--color-base-200)' }, ...lynxFocus() },
|
|
3048
|
+
},
|
|
3049
|
+
// The popup renders in the overlay outlet, outside the root,
|
|
3050
|
+
// so it cannot inherit the root's `--select-accent`, and an
|
|
3051
|
+
// unresolved `var()` on lynx has no fallback: the selected
|
|
3052
|
+
// item and its tick fell back to base-content
|
|
3053
|
+
// (signalxjs/lynx#1168). The popup restates the accent; its
|
|
3054
|
+
// colour-axis twins are in `variants.color` below.
|
|
3055
|
+
popup: { base: { '--select-accent': 'var(--color-primary)' } },
|
|
2841
3056
|
// A touch list has no highlight: the pressed item takes the
|
|
2842
3057
|
// highlighted item's wash.
|
|
2843
3058
|
item: { states: { pressed: { background: 'var(--color-base-200)' } } },
|
|
2844
|
-
'clear-trigger': {
|
|
3059
|
+
'clear-trigger': {
|
|
3060
|
+
base: { top: 'var(--space-xs)', bottom: 'var(--space-xs)', right: 'calc(var(--size-field) * 4 + 1em + var(--space-xs))', minWidth: '1.5rem' },
|
|
3061
|
+
states: lynxFocus(),
|
|
3062
|
+
},
|
|
2845
3063
|
},
|
|
2846
3064
|
variants: {
|
|
3065
|
+
// The popup's accent follows the root's colour axis — the
|
|
3066
|
+
// popup carries the axis class itself (lynx stamps every part).
|
|
3067
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { popup: { base: {
|
|
3068
|
+
'--select-accent': `var(--color-${c})`,
|
|
3069
|
+
} } }])),
|
|
2847
3070
|
size: { ...lynxBtnSizes('trigger'), md: { trigger: { base: lynxBtnPad(4) } } },
|
|
2848
3071
|
},
|
|
2849
3072
|
},
|
|
@@ -2864,6 +3087,22 @@ export const select = {
|
|
|
2864
3087
|
*/
|
|
2865
3088
|
const BTN_STEPS = { xs: 6, sm: 8, md: 10, lg: 12, xl: 14 };
|
|
2866
3089
|
const btnHeight = (step) => `calc(var(--size-field) * ${BTN_STEPS[step]})`;
|
|
3090
|
+
/**
|
|
3091
|
+
* The btn padding ramp — `[size, block space, inline space]` per step. One
|
|
3092
|
+
* table for the web's `padding` shorthand and the lynx section's longhands,
|
|
3093
|
+
* so the two spellings cannot drift apart.
|
|
3094
|
+
*/
|
|
3095
|
+
const BTN_PAD = [
|
|
3096
|
+
['xs', '2xs', 'xs'],
|
|
3097
|
+
['sm', 'xs', 'sm'],
|
|
3098
|
+
['md', 'sm', 'lg'],
|
|
3099
|
+
['lg', 'md', 'xl'],
|
|
3100
|
+
['xl', 'lg', '2xl'],
|
|
3101
|
+
];
|
|
3102
|
+
const btnPadding = (step) => {
|
|
3103
|
+
const [, block, inline] = BTN_PAD.find(([size]) => size === step);
|
|
3104
|
+
return `var(--space-${block}) var(--space-${inline})`;
|
|
3105
|
+
};
|
|
2867
3106
|
/**
|
|
2868
3107
|
* daisy 5's disabled btn, for the lynx target (signalxjs/lynx#1143). The web
|
|
2869
3108
|
* fades the whole button (`opacity: var(--disabled-opacity)`, 0.3 on the
|
|
@@ -3058,11 +3297,11 @@ export const button = {
|
|
|
3058
3297
|
// The `<button>` element is UA `border-box`, so the stated height is
|
|
3059
3298
|
// the rendered height, border included — as in daisy.
|
|
3060
3299
|
size: {
|
|
3061
|
-
xs: { root: { base: { height: btnHeight('xs'), padding: '
|
|
3062
|
-
sm: { root: { base: { height: btnHeight('sm'), padding: '
|
|
3063
|
-
md: { root: { base: { height: btnHeight('md'), padding: '
|
|
3064
|
-
lg: { root: { base: { height: btnHeight('lg'), padding: '
|
|
3065
|
-
xl: { root: { base: { height: btnHeight('xl'), padding: '
|
|
3300
|
+
xs: { root: { base: { height: btnHeight('xs'), padding: btnPadding('xs'), fontSize: 'var(--text-xs)' } } },
|
|
3301
|
+
sm: { root: { base: { height: btnHeight('sm'), padding: btnPadding('sm'), fontSize: 'var(--text-sm)' } } },
|
|
3302
|
+
md: { root: { base: { height: btnHeight('md'), padding: btnPadding('md'), fontSize: 'var(--text-md)' } } },
|
|
3303
|
+
lg: { root: { base: { height: btnHeight('lg'), padding: btnPadding('lg'), fontSize: 'var(--text-lg)' } } },
|
|
3304
|
+
xl: { root: { base: { height: btnHeight('xl'), padding: btnPadding('xl'), fontSize: 'var(--text-xl)' } } },
|
|
3066
3305
|
},
|
|
3067
3306
|
},
|
|
3068
3307
|
/**
|
|
@@ -3099,36 +3338,90 @@ export const button = {
|
|
|
3099
3338
|
// The ring, restated: `currentColor` never resolves on lynx
|
|
3100
3339
|
// (signalxjs/lynx#1079) and the logical sizes/edge are
|
|
3101
3340
|
// unproven there, so the lynx ring is physical longhands in
|
|
3102
|
-
// the variant's own ink (the variants below pick it).
|
|
3341
|
+
// the variant's own ink (the variants below pick it). Every
|
|
3342
|
+
// var-bearing edge is a longhand (`lynxSides`): a var-bearing
|
|
3343
|
+
// `border` expanded after the transparent top and closed the
|
|
3344
|
+
// ring (signalxjs/lynx#1161). The shorthand stays, static, so
|
|
3345
|
+
// the shared `currentColor` spelling never ships.
|
|
3103
3346
|
spinner: {
|
|
3104
3347
|
base: {
|
|
3105
3348
|
width: '1em',
|
|
3106
3349
|
height: '1em',
|
|
3107
|
-
border: '
|
|
3350
|
+
border: '0 solid transparent',
|
|
3351
|
+
...lynxSides('border{}Width', 'calc(var(--border) * 2)'),
|
|
3352
|
+
...lynxSides('border{}Color', 'var(--btn-ink)', ['Right', 'Bottom', 'Left']),
|
|
3108
3353
|
borderTopColor: 'transparent',
|
|
3109
3354
|
},
|
|
3110
3355
|
},
|
|
3111
|
-
root: {
|
|
3356
|
+
root: {
|
|
3357
|
+
// daisy's content-sized, never-squeezed btn (#1165), and
|
|
3358
|
+
// the hairline as longhands: a var-bearing `border`
|
|
3359
|
+
// shorthand would expand over the variants' and the
|
|
3360
|
+
// disabled state's edge colours (see `lynxSides`).
|
|
3361
|
+
base: {
|
|
3362
|
+
...lynxBtnFit,
|
|
3363
|
+
border: '0 solid transparent',
|
|
3364
|
+
...lynxSides('border{}Width', 'var(--border)'),
|
|
3365
|
+
},
|
|
3366
|
+
// The ring in the variant's own ink (signalxjs/lynx#1163).
|
|
3367
|
+
states: { disabled: lynxBtnDisabled, 'focus-visible': lynxFocusRing('var(--btn-ink)') },
|
|
3368
|
+
},
|
|
3112
3369
|
},
|
|
3113
3370
|
variants: {
|
|
3114
3371
|
// The baked disabled paint above outranks every variant's fill
|
|
3115
3372
|
// (the theme host adds a class), but its plain declarations
|
|
3116
3373
|
// do not: outline/dash restate the border, ghost/link drop
|
|
3117
|
-
// the wash (as in daisy).
|
|
3374
|
+
// the wash (as in daisy). outline/dash draw their ink edge as
|
|
3375
|
+
// longhands, so that transparent restatement wins by
|
|
3376
|
+
// specificity instead of losing to a var-bearing
|
|
3377
|
+
// `border-color` (signalxjs/lynx#1162); the shared shorthand
|
|
3378
|
+
// is neutralised to a static value.
|
|
3118
3379
|
variant: {
|
|
3119
|
-
solid: { spinner: { base: {
|
|
3120
|
-
outline: {
|
|
3121
|
-
|
|
3380
|
+
solid: { spinner: { base: lynxSides('border{}Color', 'var(--btn-on-accent)', ['Right', 'Bottom', 'Left']) } },
|
|
3381
|
+
outline: {
|
|
3382
|
+
root: {
|
|
3383
|
+
base: { borderColor: 'transparent', ...lynxSides('border{}Color', 'var(--btn-ink)') },
|
|
3384
|
+
states: { disabled: { borderColor: 'transparent' } },
|
|
3385
|
+
},
|
|
3386
|
+
},
|
|
3387
|
+
dash: {
|
|
3388
|
+
root: {
|
|
3389
|
+
base: { borderColor: 'transparent', ...lynxSides('border{}Color', 'var(--btn-ink)') },
|
|
3390
|
+
states: { disabled: { borderColor: 'transparent' } },
|
|
3391
|
+
},
|
|
3392
|
+
},
|
|
3122
3393
|
ghost: { root: { states: { disabled: { background: 'transparent' } } } },
|
|
3123
|
-
link
|
|
3124
|
-
|
|
3394
|
+
// `link` zeroes the box-shadow in a variant rule that
|
|
3395
|
+
// lands after the ring, so it restates the ring.
|
|
3396
|
+
link: { root: { states: { disabled: { background: 'transparent' }, 'focus-visible': lynxFocusRing('var(--btn-ink)') } } },
|
|
3397
|
+
},
|
|
3398
|
+
// The size ramp's paddings as longhands, so square/circle's
|
|
3399
|
+
// zeroed sides below win by the cascade rather than by the
|
|
3400
|
+
// order lynx happens to expand a var-bearing `padding` in.
|
|
3401
|
+
size: Object.fromEntries(BTN_PAD.map(([size, block, inline]) => [size, {
|
|
3402
|
+
root: {
|
|
3403
|
+
base: {
|
|
3404
|
+
padding: '0',
|
|
3405
|
+
...lynxSides('padding{}', `var(--space-${block})`, ['Top', 'Bottom']),
|
|
3406
|
+
...lynxSides('padding{}', `var(--space-${inline})`, ['Right', 'Left']),
|
|
3407
|
+
},
|
|
3408
|
+
},
|
|
3409
|
+
}])),
|
|
3125
3410
|
},
|
|
3126
3411
|
// The icon chips' zeroed inline padding, restated physically: logical
|
|
3127
3412
|
// spellings resolve on iOS but not on Android (measured,
|
|
3128
|
-
// signalxjs/lynx#1084), so the emitter refuses them.
|
|
3413
|
+
// signalxjs/lynx#1084), so the emitter refuses them. `width: auto`
|
|
3414
|
+
// undoes the root's `max-content`, which would otherwise switch
|
|
3415
|
+
// off the `aspect-ratio` that squares the chip. `wide`/`block`
|
|
3416
|
+
// restate their width physically: `inline-size` and
|
|
3417
|
+
// `max-inline-size` are not lynx properties at all.
|
|
3129
3418
|
modifiers: {
|
|
3130
|
-
|
|
3131
|
-
|
|
3419
|
+
wide: { root: { base: { width: '100%', maxWidth: '16rem' } } },
|
|
3420
|
+
block: { root: { base: { width: '100%' } } },
|
|
3421
|
+
square: { root: { base: { width: 'auto', paddingLeft: '0', paddingRight: '0' } } },
|
|
3422
|
+
circle: { root: { base: { width: 'auto', paddingLeft: '0', paddingRight: '0' } } },
|
|
3423
|
+
// `active` zeroes the box-shadow too (the held press look).
|
|
3424
|
+
active: { root: { states: { 'focus-visible': lynxFocusRing('var(--btn-ink)') } } },
|
|
3132
3425
|
},
|
|
3133
3426
|
},
|
|
3134
3427
|
},
|
|
@@ -3208,6 +3501,114 @@ export const avatar = {
|
|
|
3208
3501
|
},
|
|
3209
3502
|
},
|
|
3210
3503
|
};
|
|
3504
|
+
/**
|
|
3505
|
+
* Avatar group (#297) — daisy's `avatar-group`: the faces overlap by a
|
|
3506
|
+
* quarter, each already cut out by the avatar's own base-100 gap ring, and the
|
|
3507
|
+
* "+N" chip is daisy's `avatar-placeholder` (neutral fill, neutral-content
|
|
3508
|
+
* ink, a circle) wearing the same ring. The group's `size` borrows the
|
|
3509
|
+
* avatar's own size step (`composes`), so a group sizes its faces without a
|
|
3510
|
+
* prop on each, and an avatar's own `size` still wins. Colour is daisy's
|
|
3511
|
+
* ring colour: it rings the chip, and it is borrowed by the avatars too, so
|
|
3512
|
+
* one `color` on the group rings every face alike.
|
|
3513
|
+
*/
|
|
3514
|
+
const avatarGroupSteps = {
|
|
3515
|
+
xs: ['calc(var(--size-selector) * 6)', 'var(--text-xs)'],
|
|
3516
|
+
sm: ['calc(var(--size-selector) * 8)', 'var(--text-xs)'],
|
|
3517
|
+
lg: ['calc(var(--size-selector) * 12)', 'var(--text-md)'],
|
|
3518
|
+
xl: ['calc(var(--size-selector) * 16)', 'var(--text-lg)'],
|
|
3519
|
+
};
|
|
3520
|
+
export const avatarGroup = {
|
|
3521
|
+
component: 'avatar-group',
|
|
3522
|
+
tokens: {
|
|
3523
|
+
'--avatar-group-size': 'calc(var(--size-selector) * 10)',
|
|
3524
|
+
'--avatar-group-text': 'var(--text-sm)',
|
|
3525
|
+
'--avatar-group-ring': 'var(--color-primary)',
|
|
3526
|
+
},
|
|
3527
|
+
parts: {
|
|
3528
|
+
root: {
|
|
3529
|
+
base: {
|
|
3530
|
+
display: 'inline-flex',
|
|
3531
|
+
alignItems: 'center',
|
|
3532
|
+
verticalAlign: 'middle',
|
|
3533
|
+
isolation: 'isolate',
|
|
3534
|
+
},
|
|
3535
|
+
},
|
|
3536
|
+
overflow: {
|
|
3537
|
+
base: {
|
|
3538
|
+
// Positioned like the avatars, or it would paint under the
|
|
3539
|
+
// face it overlaps.
|
|
3540
|
+
position: 'relative',
|
|
3541
|
+
display: 'inline-grid',
|
|
3542
|
+
placeItems: 'center',
|
|
3543
|
+
boxSizing: 'border-box',
|
|
3544
|
+
minWidth: 'var(--avatar-group-size)',
|
|
3545
|
+
height: 'var(--avatar-group-size)',
|
|
3546
|
+
paddingInline: 'var(--space-2xs)',
|
|
3547
|
+
marginInlineStart: 'calc(var(--avatar-group-size) * -0.25)',
|
|
3548
|
+
borderRadius: '9999px',
|
|
3549
|
+
boxShadow: '0 0 0 2px var(--color-base-100), 0 0 0 4px var(--avatar-group-ring)',
|
|
3550
|
+
background: 'var(--color-neutral)',
|
|
3551
|
+
color: 'var(--color-neutral-content)',
|
|
3552
|
+
fontSize: 'var(--avatar-group-text)',
|
|
3553
|
+
fontWeight: 'var(--weight-semibold)',
|
|
3554
|
+
fontVariantNumeric: 'tabular-nums',
|
|
3555
|
+
whiteSpace: 'nowrap',
|
|
3556
|
+
userSelect: 'none',
|
|
3557
|
+
},
|
|
3558
|
+
},
|
|
3559
|
+
},
|
|
3560
|
+
composes: {
|
|
3561
|
+
avatar: {
|
|
3562
|
+
parts: {
|
|
3563
|
+
root: {
|
|
3564
|
+
selectors: {
|
|
3565
|
+
'&:not(:first-child)': { marginInlineStart: 'calc(var(--avatar-group-size) * -0.25)' },
|
|
3566
|
+
},
|
|
3567
|
+
},
|
|
3568
|
+
},
|
|
3569
|
+
},
|
|
3570
|
+
},
|
|
3571
|
+
compoundVariants: [
|
|
3572
|
+
...Object.keys(avatarGroupSteps).map((size) => ({
|
|
3573
|
+
match: { size },
|
|
3574
|
+
parts: {},
|
|
3575
|
+
composes: { avatar: { axes: { size } } },
|
|
3576
|
+
})),
|
|
3577
|
+
...ROLES.map((color) => ({
|
|
3578
|
+
match: { color },
|
|
3579
|
+
parts: {},
|
|
3580
|
+
composes: { avatar: { axes: { color } } },
|
|
3581
|
+
})),
|
|
3582
|
+
],
|
|
3583
|
+
variants: {
|
|
3584
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
3585
|
+
'--avatar-group-ring': `var(--color-${c})`,
|
|
3586
|
+
} } }])),
|
|
3587
|
+
size: {
|
|
3588
|
+
...Object.fromEntries(Object.entries(avatarGroupSteps).map(([size, [box, text]]) => [size, { root: { base: {
|
|
3589
|
+
'--avatar-group-size': box, '--avatar-group-text': text,
|
|
3590
|
+
} } }])),
|
|
3591
|
+
// `md` is the un-attributed render — the defaults in `tokens:`.
|
|
3592
|
+
md: {},
|
|
3593
|
+
},
|
|
3594
|
+
},
|
|
3595
|
+
};
|
|
3596
|
+
/**
|
|
3597
|
+
* A toast whose indicator is rendered (#292): the grid grows a leading column
|
|
3598
|
+
* for the mark, and the text, action and close step one column along.
|
|
3599
|
+
*/
|
|
3600
|
+
const TOAST_MARKED = '[data-scope="toast"][data-part="root"]:has(> [data-scope="toast"][data-part="indicator"]) > &';
|
|
3601
|
+
/** The indicator's marks, cut from its fill — not mirrored in RTL (a tick is not a direction). */
|
|
3602
|
+
const TOAST_CHECK = 'polygon(15.1% 41.3%, 1% 55%, 37.6% 92.8%, 99% 19.8%, 83.9% 7.2%, 36.6% 63.5%)';
|
|
3603
|
+
const TOAST_CROSS = 'polygon(20% 8%, 50% 38%, 80% 8%, 92% 20%, 62% 50%, 92% 80%, 80% 92%, 50% 62%, 20% 92%, 8% 80%, 38% 50%, 8% 20%)';
|
|
3604
|
+
/**
|
|
3605
|
+
* daisy's success and error are FILLS — light enough in `light` and `nord`
|
|
3606
|
+
* that a bare mark in them reads under 2:1 on the toast's base-200. Deepened
|
|
3607
|
+
* toward base-content (which flips with the theme), the hue survives and the
|
|
3608
|
+
* mark clears the 3:1 non-text floor in every theme.
|
|
3609
|
+
*/
|
|
3610
|
+
const TOAST_SUCCESS_INK = 'color-mix(in oklab, var(--color-success) 55%, var(--color-base-content))';
|
|
3611
|
+
const TOAST_ERROR_INK = 'color-mix(in oklab, var(--color-error) 55%, var(--color-base-content))';
|
|
3211
3612
|
/**
|
|
3212
3613
|
* Toast presence is runtime-managed (see the SKILL's Toast section): plain
|
|
3213
3614
|
* two-state transitions only — no `@starting-style`, no `allow-discrete`.
|
|
@@ -3218,6 +3619,7 @@ export const toast = {
|
|
|
3218
3619
|
'--toast-bg': 'var(--color-base-200)',
|
|
3219
3620
|
'--toast-ink': 'var(--color-base-content)',
|
|
3220
3621
|
'--toast-from': '8px',
|
|
3622
|
+
'--toast-mark': '1rem',
|
|
3221
3623
|
},
|
|
3222
3624
|
parts: {
|
|
3223
3625
|
viewport: {
|
|
@@ -3271,6 +3673,7 @@ export const toast = {
|
|
|
3271
3673
|
},
|
|
3272
3674
|
selectors: {
|
|
3273
3675
|
'&[data-placement^="top"]': { '--toast-from': '-8px' },
|
|
3676
|
+
'&:has(> [data-scope="toast"][data-part="indicator"])': { gridTemplateColumns: 'auto 1fr auto auto' },
|
|
3274
3677
|
},
|
|
3275
3678
|
states: {
|
|
3276
3679
|
open: { opacity: '1', transform: 'none' },
|
|
@@ -3280,8 +3683,39 @@ export const toast = {
|
|
|
3280
3683
|
'reduced-motion': { base: { transition: 'none' }, states: { open: { transform: 'none' } } },
|
|
3281
3684
|
},
|
|
3282
3685
|
},
|
|
3686
|
+
// daisy's `loading-spinner` ring in the alert's own ink, then the
|
|
3687
|
+
// success tick or the error cross — the promise toast's status.
|
|
3688
|
+
indicator: {
|
|
3689
|
+
base: {
|
|
3690
|
+
gridColumn: '1',
|
|
3691
|
+
gridRow: '1 / span 2',
|
|
3692
|
+
inlineSize: 'var(--toast-mark)',
|
|
3693
|
+
blockSize: 'var(--toast-mark)',
|
|
3694
|
+
boxSizing: 'border-box',
|
|
3695
|
+
},
|
|
3696
|
+
states: {
|
|
3697
|
+
loading: {
|
|
3698
|
+
borderRadius: '50%',
|
|
3699
|
+
border: 'calc(var(--border) * 2) solid color-mix(in oklab, var(--toast-ink) 25%, transparent)',
|
|
3700
|
+
borderBlockStartColor: 'var(--toast-ink)',
|
|
3701
|
+
animation: 'zero-daisyui-toast-spin 0.75s linear infinite',
|
|
3702
|
+
},
|
|
3703
|
+
complete: { background: TOAST_SUCCESS_INK, clipPath: TOAST_CHECK },
|
|
3704
|
+
error: { background: TOAST_ERROR_INK, clipPath: TOAST_CROSS },
|
|
3705
|
+
},
|
|
3706
|
+
at: {
|
|
3707
|
+
'reduced-motion': { states: { loading: { animation: 'none' } } },
|
|
3708
|
+
'forced-colors': {
|
|
3709
|
+
states: {
|
|
3710
|
+
complete: { background: 'CanvasText', forcedColorAdjust: 'none' },
|
|
3711
|
+
error: { background: 'CanvasText', forcedColorAdjust: 'none' },
|
|
3712
|
+
},
|
|
3713
|
+
},
|
|
3714
|
+
},
|
|
3715
|
+
},
|
|
3283
3716
|
title: {
|
|
3284
3717
|
base: { gridColumn: '1', fontWeight: 'var(--weight-semibold)' },
|
|
3718
|
+
selectors: { [TOAST_MARKED]: { gridColumn: '2' } },
|
|
3285
3719
|
},
|
|
3286
3720
|
description: {
|
|
3287
3721
|
base: {
|
|
@@ -3289,6 +3723,7 @@ export const toast = {
|
|
|
3289
3723
|
fontSize: 'var(--text-xs)',
|
|
3290
3724
|
color: 'color-mix(in oklab, var(--toast-ink) 75%, transparent)',
|
|
3291
3725
|
},
|
|
3726
|
+
selectors: { [TOAST_MARKED]: { gridColumn: '2' } },
|
|
3292
3727
|
},
|
|
3293
3728
|
action: {
|
|
3294
3729
|
base: {
|
|
@@ -3309,6 +3744,7 @@ export const toast = {
|
|
|
3309
3744
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
3310
3745
|
...focusRing,
|
|
3311
3746
|
},
|
|
3747
|
+
selectors: { [TOAST_MARKED]: { gridColumn: '3' } },
|
|
3312
3748
|
},
|
|
3313
3749
|
close: {
|
|
3314
3750
|
base: {
|
|
@@ -3328,6 +3764,7 @@ export const toast = {
|
|
|
3328
3764
|
disabled: { opacity: 'var(--disabled-opacity)' },
|
|
3329
3765
|
...focusRing,
|
|
3330
3766
|
},
|
|
3767
|
+
selectors: { [TOAST_MARKED]: { gridColumn: '4' } },
|
|
3331
3768
|
},
|
|
3332
3769
|
},
|
|
3333
3770
|
variants: {
|
|
@@ -3364,6 +3801,7 @@ export const toast = {
|
|
|
3364
3801
|
// signalxjs/lynx#1070. No `color` default: the un-attributed toast is a
|
|
3365
3802
|
// neutral base-200 surface, outside the color vocabulary.
|
|
3366
3803
|
defaultVariants: { size: 'md' },
|
|
3804
|
+
keyframes: { 'zero-daisyui-toast-spin': 'to { transform: rotate(360deg); }' },
|
|
3367
3805
|
targets: {
|
|
3368
3806
|
lynx: {
|
|
3369
3807
|
parts: {
|
|
@@ -3417,16 +3855,25 @@ export const toast = {
|
|
|
3417
3855
|
// the same ink at 75% opacity reads the same on the soft fill.
|
|
3418
3856
|
description: { base: { color: 'var(--toast-ink)', opacity: '0.75' } },
|
|
3419
3857
|
// The 25% ink border cannot bake either: it takes the full ink.
|
|
3858
|
+
// Their rings' gap is the card's own fill.
|
|
3420
3859
|
action: {
|
|
3421
3860
|
base: {
|
|
3422
3861
|
marginTop: 'var(--space-xs)',
|
|
3423
3862
|
border: 'var(--border) solid var(--toast-ink)',
|
|
3424
3863
|
},
|
|
3864
|
+
states: lynxFocus({ gap: 'var(--toast-bg)' }),
|
|
3865
|
+
},
|
|
3866
|
+
close: {
|
|
3867
|
+
base: { position: 'absolute', top: 'var(--space-xs)', right: 'var(--space-xs)' },
|
|
3868
|
+
states: lynxFocus({ gap: 'var(--toast-bg)' }),
|
|
3425
3869
|
},
|
|
3426
|
-
close: { base: { position: 'absolute', top: 'var(--space-xs)', right: 'var(--space-xs)' } },
|
|
3427
3870
|
},
|
|
3428
3871
|
},
|
|
3429
3872
|
},
|
|
3873
|
+
// The viewport's `open` (the stack expanded) is a fact this skin has no
|
|
3874
|
+
// use for: its toasts are always a plain column, so both states look the
|
|
3875
|
+
// same by design (#292).
|
|
3876
|
+
skipStates: { viewport: ['open', 'closed'] },
|
|
3430
3877
|
};
|
|
3431
3878
|
export const combobox = {
|
|
3432
3879
|
component: 'combobox',
|
|
@@ -4226,12 +4673,28 @@ const treeRowSelectors = {
|
|
|
4226
4673
|
background: 'var(--color-base-300)',
|
|
4227
4674
|
},
|
|
4228
4675
|
};
|
|
4676
|
+
/**
|
|
4677
|
+
* `tickGlyphFallback` for the node check box, whose tick is its `::after`:
|
|
4678
|
+
* the geometry drops and the same pseudo carries the glyph, in the medium's
|
|
4679
|
+
* own ink (the #233 divergence, restated).
|
|
4680
|
+
*/
|
|
4681
|
+
const nodeTickFallback = (ink) => ({
|
|
4682
|
+
selectors: {
|
|
4683
|
+
'&::after': { clipPath: 'none', backgroundColor: '#0000', rotate: '0deg', inset: '0', display: 'grid', placeItems: 'center', color: ink, fontSize: '0.85em', lineHeight: '1' },
|
|
4684
|
+
'&[data-state="checked"]::after': { content: '"✔︎"', clipPath: 'none', rotate: '0deg' },
|
|
4685
|
+
'&[data-state="indeterminate"]::after': { content: '"−"', clipPath: 'none', rotate: '0deg', translate: 'none' },
|
|
4686
|
+
},
|
|
4687
|
+
});
|
|
4229
4688
|
export const treeView = {
|
|
4230
4689
|
component: 'tree-view',
|
|
4231
4690
|
tokens: {
|
|
4232
4691
|
'--tree-accent': 'var(--color-primary)',
|
|
4233
4692
|
'--tree-text': 'var(--text-sm)',
|
|
4234
4693
|
'--tree-on-accent': 'var(--color-primary-content)',
|
|
4694
|
+
// The node check box's pair — the tree's own accent, so a colour
|
|
4695
|
+
// variant retints the box with the row.
|
|
4696
|
+
'--tree-check': 'var(--tree-accent)',
|
|
4697
|
+
'--tree-on-check': 'var(--tree-on-accent)',
|
|
4235
4698
|
},
|
|
4236
4699
|
parts: {
|
|
4237
4700
|
root: {
|
|
@@ -4280,6 +4743,58 @@ export const treeView = {
|
|
|
4280
4743
|
'reduced-motion': { base: { transition: 'none' } },
|
|
4281
4744
|
},
|
|
4282
4745
|
},
|
|
4746
|
+
// daisy's `.checkbox` at row scale: the accent outline, the recessed
|
|
4747
|
+
// depth shade, the fill on check — and daisy's own tick, the six
|
|
4748
|
+
// clip-path points rotated 45°, drawn on the box's `::after` since
|
|
4749
|
+
// the part is one span. On a selected row (the accent fill) the pair
|
|
4750
|
+
// swaps, so the box never vanishes into the row it sits on.
|
|
4751
|
+
'node-checkbox': {
|
|
4752
|
+
base: {
|
|
4753
|
+
display: 'inline-block',
|
|
4754
|
+
position: 'relative',
|
|
4755
|
+
flexShrink: '0',
|
|
4756
|
+
boxSizing: 'border-box',
|
|
4757
|
+
width: '1.25em',
|
|
4758
|
+
height: '1.25em',
|
|
4759
|
+
border: 'var(--border) solid var(--tree-check)',
|
|
4760
|
+
borderRadius: 'var(--radius-selector)',
|
|
4761
|
+
boxShadow: '0 1px var(--depth-shade) inset',
|
|
4762
|
+
cursor: 'pointer',
|
|
4763
|
+
transition: 'background-color var(--duration-normal) var(--ease-standard)',
|
|
4764
|
+
},
|
|
4765
|
+
states: {
|
|
4766
|
+
checked: { backgroundColor: 'var(--tree-check)' },
|
|
4767
|
+
indeterminate: { backgroundColor: 'var(--tree-check)' },
|
|
4768
|
+
unchecked: {},
|
|
4769
|
+
disabled: { cursor: 'not-allowed' },
|
|
4770
|
+
},
|
|
4771
|
+
selectors: {
|
|
4772
|
+
'&::after': {
|
|
4773
|
+
content: '""',
|
|
4774
|
+
position: 'absolute',
|
|
4775
|
+
inset: '15%',
|
|
4776
|
+
opacity: '0',
|
|
4777
|
+
rotate: '45deg',
|
|
4778
|
+
backgroundColor: 'var(--tree-on-check)',
|
|
4779
|
+
clipPath: TICK_COLLAPSED,
|
|
4780
|
+
transition: 'clip-path var(--duration-slow) var(--ease-standard) var(--duration-instant), '
|
|
4781
|
+
+ 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
|
|
4782
|
+
+ 'rotate var(--duration-slow) var(--ease-standard) var(--duration-instant), '
|
|
4783
|
+
+ 'translate var(--duration-slow) var(--ease-standard) var(--duration-instant)',
|
|
4784
|
+
},
|
|
4785
|
+
'&[data-state="checked"]::after': { clipPath: TICK_DRAWN, opacity: '1' },
|
|
4786
|
+
'&[data-state="indeterminate"]::after': { clipPath: DASH_DRAWN, opacity: '1', translate: '0 -35%', rotate: '0deg' },
|
|
4787
|
+
'[data-scope="tree-view"][data-selected] &': {
|
|
4788
|
+
'--tree-check': 'var(--tree-on-accent)',
|
|
4789
|
+
'--tree-on-check': 'var(--tree-accent)',
|
|
4790
|
+
},
|
|
4791
|
+
},
|
|
4792
|
+
at: {
|
|
4793
|
+
'reduced-motion': { base: { transition: 'none' }, selectors: { '&::after': { transition: 'none' } } },
|
|
4794
|
+
'forced-colors': nodeTickFallback('CanvasText'),
|
|
4795
|
+
print: nodeTickFallback('var(--print-ink)'),
|
|
4796
|
+
},
|
|
4797
|
+
},
|
|
4283
4798
|
// Indentation comes from this inline padding — depth is the DOM
|
|
4284
4799
|
// nesting, no per-level rules needed.
|
|
4285
4800
|
'branch-content': {
|
|
@@ -4560,6 +5075,36 @@ export const card = {
|
|
|
4560
5075
|
overflow: 'hidden',
|
|
4561
5076
|
},
|
|
4562
5077
|
},
|
|
5078
|
+
/**
|
|
5079
|
+
* Media (#302) — daisy's `card > figure`: a centred flex box, flush
|
|
5080
|
+
* to the card's edges, the image filling it.
|
|
5081
|
+
*/
|
|
5082
|
+
media: {
|
|
5083
|
+
base: {
|
|
5084
|
+
display: 'flex',
|
|
5085
|
+
alignItems: 'center',
|
|
5086
|
+
justifyContent: 'center',
|
|
5087
|
+
margin: '0',
|
|
5088
|
+
inlineSize: '100%',
|
|
5089
|
+
objectFit: 'cover',
|
|
5090
|
+
},
|
|
5091
|
+
selectors: {
|
|
5092
|
+
// The media's own image, when the band wraps one (a div or a
|
|
5093
|
+
// figure): block-level, the band's full width, cropped rather
|
|
5094
|
+
// than stretched when the band is given a height.
|
|
5095
|
+
'& > :is(img, picture, video, svg, canvas)': {
|
|
5096
|
+
display: 'block',
|
|
5097
|
+
inlineSize: '100%',
|
|
5098
|
+
blockSize: '100%',
|
|
5099
|
+
objectFit: 'cover',
|
|
5100
|
+
},
|
|
5101
|
+
// The corners it shares with the card (#302): the root clips
|
|
5102
|
+
// too, but an asChild <img> is the band itself and rounds on
|
|
5103
|
+
// its own.
|
|
5104
|
+
'&:first-child': { borderStartStartRadius: 'var(--radius-box)', borderStartEndRadius: 'var(--radius-box)' },
|
|
5105
|
+
'&:last-child': { borderEndStartRadius: 'var(--radius-box)', borderEndEndRadius: 'var(--radius-box)' },
|
|
5106
|
+
},
|
|
5107
|
+
},
|
|
4563
5108
|
header: {
|
|
4564
5109
|
base: {
|
|
4565
5110
|
display: 'flex',
|
|
@@ -4889,16 +5434,51 @@ export const divider = {
|
|
|
4889
5434
|
tokens: { '--divider-ink': 'var(--color-base-300)', '--divider-thickness': 'var(--border)' },
|
|
4890
5435
|
parts: {
|
|
4891
5436
|
root: {
|
|
4892
|
-
|
|
5437
|
+
// The rule is two flex segments (#298), drawn as borders: with no
|
|
5438
|
+
// label they meet and read as one line, and a label sits between
|
|
5439
|
+
// them. Borders rather than a background, so forced colours keep
|
|
5440
|
+
// the line instead of revaluing it to Canvas.
|
|
5441
|
+
base: {
|
|
5442
|
+
display: 'flex',
|
|
5443
|
+
alignItems: 'center',
|
|
5444
|
+
border: 'none',
|
|
5445
|
+
alignSelf: 'stretch',
|
|
5446
|
+
},
|
|
4893
5447
|
selectors: {
|
|
4894
|
-
'
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
},
|
|
4898
|
-
'&[data-orientation="
|
|
4899
|
-
|
|
4900
|
-
|
|
5448
|
+
'&::before': { content: '""', flex: '1 1 0', minInlineSize: '0' },
|
|
5449
|
+
'&::after': { content: '""', flex: '1 1 0', minInlineSize: '0' },
|
|
5450
|
+
'&[data-orientation="horizontal"]': { inlineSize: '100%' },
|
|
5451
|
+
'&[data-orientation="horizontal"]::before': { borderBlockStart: 'var(--divider-thickness) solid var(--divider-ink)' },
|
|
5452
|
+
'&[data-orientation="horizontal"]::after': { borderBlockStart: 'var(--divider-thickness) solid var(--divider-ink)' },
|
|
5453
|
+
'&[data-orientation="vertical"]': { flexDirection: 'column', minBlockSize: '1em' },
|
|
5454
|
+
'&[data-orientation="vertical"]::before': { borderInlineStart: 'var(--divider-thickness) solid var(--divider-ink)' },
|
|
5455
|
+
'&[data-orientation="vertical"]::after': { borderInlineStart: 'var(--divider-thickness) solid var(--divider-ink)' },
|
|
5456
|
+
// A placed label sits flush at its edge: the segment on
|
|
5457
|
+
// that side goes, and the other one takes the whole run.
|
|
5458
|
+
'&:has(> [data-scope="divider"][data-part="label"][data-placement="start"])::before': { display: 'none' },
|
|
5459
|
+
'&:has(> [data-scope="divider"][data-part="label"][data-placement="end"])::after': { display: 'none' },
|
|
5460
|
+
},
|
|
5461
|
+
},
|
|
5462
|
+
label: {
|
|
5463
|
+
base: {
|
|
5464
|
+
// daisy's `divider` text: the content colour, the rule
|
|
5465
|
+
// stopping a gap short of it on either side.
|
|
5466
|
+
flex: 'none',
|
|
5467
|
+
paddingInline: 'var(--space-md)',
|
|
5468
|
+
fontSize: 'var(--text-sm)',
|
|
5469
|
+
whiteSpace: 'nowrap',
|
|
5470
|
+
color: 'var(--color-base-content)',
|
|
5471
|
+
},
|
|
5472
|
+
selectors: {
|
|
5473
|
+
'[data-scope="divider"][data-part="root"][data-orientation="vertical"] > &': {
|
|
5474
|
+
paddingInline: '0',
|
|
5475
|
+
paddingBlock: 'var(--space-sm)',
|
|
4901
5476
|
},
|
|
5477
|
+
// Flush means flush: no inset on the side the rule gave up.
|
|
5478
|
+
'&[data-placement="start"]': { paddingInlineStart: '0' },
|
|
5479
|
+
'&[data-placement="end"]': { paddingInlineEnd: '0' },
|
|
5480
|
+
'[data-scope="divider"][data-part="root"][data-orientation="vertical"] > &[data-placement="start"]': { paddingBlockStart: '0' },
|
|
5481
|
+
'[data-scope="divider"][data-part="root"][data-orientation="vertical"] > &[data-placement="end"]': { paddingBlockEnd: '0' },
|
|
4902
5482
|
},
|
|
4903
5483
|
},
|
|
4904
5484
|
},
|
|
@@ -4914,6 +5494,25 @@ export const divider = {
|
|
|
4914
5494
|
xl: { root: { base: { '--divider-thickness': 'calc(var(--border) * 3)' } } },
|
|
4915
5495
|
},
|
|
4916
5496
|
},
|
|
5497
|
+
targets: {
|
|
5498
|
+
lynx: {
|
|
5499
|
+
parts: {
|
|
5500
|
+
// Lynx drops the ::before/::after segments the rule is drawn
|
|
5501
|
+
// with above, which left the lynx divider with no line and,
|
|
5502
|
+
// since only those rules read `--divider-thickness`, no size
|
|
5503
|
+
// ramp either (#375). There the root itself is the line, as
|
|
5504
|
+
// it was before the segments (#356): filled with the ink and
|
|
5505
|
+
// one thickness across.
|
|
5506
|
+
root: {
|
|
5507
|
+
base: { background: 'var(--divider-ink)' },
|
|
5508
|
+
selectors: {
|
|
5509
|
+
'&[data-orientation="horizontal"]': { blockSize: 'var(--divider-thickness)' },
|
|
5510
|
+
'&[data-orientation="vertical"]': { inlineSize: 'var(--divider-thickness)' },
|
|
5511
|
+
},
|
|
5512
|
+
},
|
|
5513
|
+
},
|
|
5514
|
+
},
|
|
5515
|
+
},
|
|
4917
5516
|
};
|
|
4918
5517
|
// ── Loading (#314) ────────────────────────────────────────────────────────
|
|
4919
5518
|
/**
|
|
@@ -5441,6 +6040,10 @@ export const timeline = {
|
|
|
5441
6040
|
},
|
|
5442
6041
|
},
|
|
5443
6042
|
},
|
|
6043
|
+
// Title and description (#302): daisy's timeline-box leads with a
|
|
6044
|
+
// bold line; the detail under it in the card description's ink.
|
|
6045
|
+
title: { base: { display: 'block', margin: '0', fontWeight: 'var(--weight-semibold)', lineHeight: 'var(--leading-tight)' } },
|
|
6046
|
+
description: { base: { display: 'block', margin: '0', marginBlockStart: 'var(--space-2xs)', color: 'color-mix(in oklab, var(--color-base-content) 70%, transparent)' } },
|
|
5444
6047
|
},
|
|
5445
6048
|
variants: {
|
|
5446
6049
|
color: Object.fromEntries(ROLES.map((c) => [c, { marker: { base: {
|
|
@@ -5645,6 +6248,95 @@ export const chat = {
|
|
|
5645
6248
|
},
|
|
5646
6249
|
},
|
|
5647
6250
|
};
|
|
6251
|
+
/**
|
|
6252
|
+
* ChatLog — daisy's `bg-base-200 rounded-box` well that the chat bubbles
|
|
6253
|
+
* already sit in, scrolling; the jump trigger is a `btn btn-sm` pill in the
|
|
6254
|
+
* role's solid pair with daisy's `shadow-xs` and its 1px press sink, floated
|
|
6255
|
+
* over the rows by `position: sticky` at the box's foot. Its negative block
|
|
6256
|
+
* margin gives back the line it would take, so appearing moves no row.
|
|
6257
|
+
*/
|
|
6258
|
+
export const chatLog = {
|
|
6259
|
+
component: 'chat-log',
|
|
6260
|
+
tokens: {
|
|
6261
|
+
'--chat-log-fill': 'var(--color-primary)',
|
|
6262
|
+
'--chat-log-on-fill': 'var(--color-primary-content)',
|
|
6263
|
+
'--chat-log-jump-size': 'calc(var(--size-field) * 8)',
|
|
6264
|
+
},
|
|
6265
|
+
parts: {
|
|
6266
|
+
root: {
|
|
6267
|
+
base: {
|
|
6268
|
+
display: 'flex',
|
|
6269
|
+
flexDirection: 'column',
|
|
6270
|
+
overflowY: 'auto',
|
|
6271
|
+
overscrollBehaviorY: 'contain',
|
|
6272
|
+
borderRadius: 'var(--radius-box)',
|
|
6273
|
+
background: 'var(--color-base-100)',
|
|
6274
|
+
color: 'var(--color-base-content)',
|
|
6275
|
+
border: 'var(--border) solid var(--color-base-300)',
|
|
6276
|
+
padding: 'var(--space-sm) var(--space-md)',
|
|
6277
|
+
},
|
|
6278
|
+
// Drawn inside the box (negative offset): an outward ring would
|
|
6279
|
+
// be clipped by whatever scrolls around the transcript.
|
|
6280
|
+
states: { 'focus-visible': { outline: '2px solid var(--color-base-content)', outlineOffset: '-2px' } },
|
|
6281
|
+
},
|
|
6282
|
+
content: {
|
|
6283
|
+
base: {
|
|
6284
|
+
// `auto` gathers a short transcript at the foot; a long one
|
|
6285
|
+
// overflows and the margin resolves to nothing.
|
|
6286
|
+
marginBlockStart: 'auto',
|
|
6287
|
+
flex: 'none',
|
|
6288
|
+
display: 'grid',
|
|
6289
|
+
rowGap: 'var(--space-xs)',
|
|
6290
|
+
},
|
|
6291
|
+
},
|
|
6292
|
+
'jump-trigger': {
|
|
6293
|
+
base: {
|
|
6294
|
+
position: 'sticky',
|
|
6295
|
+
insetBlockEnd: 'var(--space-sm)',
|
|
6296
|
+
alignSelf: 'center',
|
|
6297
|
+
flex: 'none',
|
|
6298
|
+
marginBlockStart: 'calc(var(--chat-log-jump-size) * -1)',
|
|
6299
|
+
blockSize: 'var(--chat-log-jump-size)',
|
|
6300
|
+
appearance: 'none',
|
|
6301
|
+
display: 'inline-flex',
|
|
6302
|
+
alignItems: 'center',
|
|
6303
|
+
gap: 'var(--space-2xs)',
|
|
6304
|
+
paddingInline: 'var(--space-md)',
|
|
6305
|
+
border: 'var(--border) solid transparent',
|
|
6306
|
+
borderRadius: '9999px',
|
|
6307
|
+
background: 'var(--chat-log-fill)',
|
|
6308
|
+
color: 'var(--chat-log-on-fill)',
|
|
6309
|
+
boxShadow: 'var(--shadow-xs)',
|
|
6310
|
+
fontFamily: 'inherit',
|
|
6311
|
+
fontSize: 'var(--text-xs)',
|
|
6312
|
+
fontWeight: 'var(--weight-semibold)',
|
|
6313
|
+
lineHeight: 'var(--leading-none)',
|
|
6314
|
+
cursor: 'pointer',
|
|
6315
|
+
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
6316
|
+
},
|
|
6317
|
+
states: {
|
|
6318
|
+
open: {},
|
|
6319
|
+
closed: {},
|
|
6320
|
+
hover: { background: 'color-mix(in oklab, var(--chat-log-fill) 90%, black)' },
|
|
6321
|
+
'focus-visible': { outline: '2px solid var(--color-base-content)', outlineOffset: '2px' },
|
|
6322
|
+
},
|
|
6323
|
+
selectors: { '&[data-pressed]:not([data-disabled])': { transform: 'translateY(1px)', boxShadow: 'none' } },
|
|
6324
|
+
},
|
|
6325
|
+
},
|
|
6326
|
+
variants: {
|
|
6327
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { 'jump-trigger': { base: {
|
|
6328
|
+
'--chat-log-fill': `var(--color-${c})`,
|
|
6329
|
+
'--chat-log-on-fill': `var(--color-${c}-content)`,
|
|
6330
|
+
} } }])),
|
|
6331
|
+
size: {
|
|
6332
|
+
xs: { content: { base: { rowGap: 'var(--space-2xs)' } }, 'jump-trigger': { base: { '--chat-log-jump-size': 'calc(var(--size-field) * 6)', fontSize: 'var(--text-xs)' } } },
|
|
6333
|
+
sm: { content: { base: { rowGap: 'var(--space-2xs)' } }, 'jump-trigger': { base: { '--chat-log-jump-size': 'calc(var(--size-field) * 7)', fontSize: 'var(--text-xs)' } } },
|
|
6334
|
+
md: {},
|
|
6335
|
+
lg: { content: { base: { rowGap: 'var(--space-sm)' } }, 'jump-trigger': { base: { '--chat-log-jump-size': 'calc(var(--size-field) * 9)', fontSize: 'var(--text-sm)' } } },
|
|
6336
|
+
xl: { content: { base: { rowGap: 'var(--space-md)' } }, 'jump-trigger': { base: { '--chat-log-jump-size': 'calc(var(--size-field) * 10)', fontSize: 'var(--text-sm)' } } },
|
|
6337
|
+
},
|
|
6338
|
+
},
|
|
6339
|
+
};
|
|
5648
6340
|
/** daisy radial-progress: --value became --progress-percent; same ring. */
|
|
5649
6341
|
export const radialProgress = {
|
|
5650
6342
|
component: 'radial-progress',
|
|
@@ -6031,6 +6723,8 @@ export const breadcrumbs = {
|
|
|
6031
6723
|
alignItems: 'center',
|
|
6032
6724
|
gap: 'var(--space-xs)',
|
|
6033
6725
|
},
|
|
6726
|
+
// `closed` is presence: the runtime hides the part (#295).
|
|
6727
|
+
states: { open: {}, closed: {} },
|
|
6034
6728
|
},
|
|
6035
6729
|
link: {
|
|
6036
6730
|
base: {
|
|
@@ -6059,6 +6753,37 @@ export const breadcrumbs = {
|
|
|
6059
6753
|
userSelect: 'none',
|
|
6060
6754
|
},
|
|
6061
6755
|
},
|
|
6756
|
+
ellipsis: {
|
|
6757
|
+
base: {
|
|
6758
|
+
display: 'flex',
|
|
6759
|
+
alignItems: 'center',
|
|
6760
|
+
gap: 'var(--space-xs)',
|
|
6761
|
+
},
|
|
6762
|
+
// `closed` is presence: the runtime hides the part (#295).
|
|
6763
|
+
states: { open: {}, closed: {} },
|
|
6764
|
+
},
|
|
6765
|
+
'ellipsis-trigger': {
|
|
6766
|
+
base: {
|
|
6767
|
+
appearance: 'none',
|
|
6768
|
+
background: 'transparent',
|
|
6769
|
+
border: '0',
|
|
6770
|
+
padding: '0 var(--space-2xs)',
|
|
6771
|
+
margin: '0',
|
|
6772
|
+
font: 'inherit',
|
|
6773
|
+
lineHeight: 'inherit',
|
|
6774
|
+
color: 'color-mix(in oklch, var(--color-base-content) 75%, transparent)',
|
|
6775
|
+
borderRadius: 'var(--radius-selector)',
|
|
6776
|
+
cursor: 'pointer',
|
|
6777
|
+
transition: 'color var(--duration-fast) var(--ease-standard)',
|
|
6778
|
+
},
|
|
6779
|
+
states: {
|
|
6780
|
+
hover: { color: 'var(--color-base-content)', textDecoration: 'underline' },
|
|
6781
|
+
'focus-visible': {
|
|
6782
|
+
outline: '2px solid var(--color-primary)',
|
|
6783
|
+
outlineOffset: '2px',
|
|
6784
|
+
},
|
|
6785
|
+
},
|
|
6786
|
+
},
|
|
6062
6787
|
},
|
|
6063
6788
|
variants: {
|
|
6064
6789
|
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
@@ -6073,11 +6798,46 @@ export const breadcrumbs = {
|
|
|
6073
6798
|
},
|
|
6074
6799
|
},
|
|
6075
6800
|
};
|
|
6801
|
+
/**
|
|
6802
|
+
* The four pagination triggers — prev/next and, with `withEdges`, the
|
|
6803
|
+
* first/last jumps (#294) — are one cell: the same box, the same
|
|
6804
|
+
* glyph-sized type, and the same flip under the rtl guard, since every
|
|
6805
|
+
* glyph (`‹ › « »`) is physical ink pointing at a reading edge.
|
|
6806
|
+
*/
|
|
6807
|
+
const pageTrigger = {
|
|
6808
|
+
base: {
|
|
6809
|
+
display: 'inline-flex',
|
|
6810
|
+
alignItems: 'center',
|
|
6811
|
+
justifyContent: 'center',
|
|
6812
|
+
minInlineSize: 'var(--pg-size)',
|
|
6813
|
+
blockSize: 'var(--pg-size)',
|
|
6814
|
+
background: 'var(--color-base-200)',
|
|
6815
|
+
color: 'var(--color-base-content)',
|
|
6816
|
+
border: 'none',
|
|
6817
|
+
borderRadius: 'var(--radius-field)',
|
|
6818
|
+
fontSize: 'calc(var(--pg-font) * 1.2)',
|
|
6819
|
+
lineHeight: 'var(--leading-none)',
|
|
6820
|
+
appearance: 'none',
|
|
6821
|
+
// Link mode renders an <a> (#294): no UA underline.
|
|
6822
|
+
textDecoration: 'none',
|
|
6823
|
+
cursor: 'pointer',
|
|
6824
|
+
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
6825
|
+
},
|
|
6826
|
+
states: {
|
|
6827
|
+
hover: { background: 'var(--color-base-300)' },
|
|
6828
|
+
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6829
|
+
...focusRing,
|
|
6830
|
+
},
|
|
6831
|
+
selectors: {
|
|
6832
|
+
'&[data-pressed]:not([data-disabled])': { transform: 'translateY(1px)' },
|
|
6833
|
+
[`&${rtl}`]: { scale: '-1 1' },
|
|
6834
|
+
},
|
|
6835
|
+
};
|
|
6076
6836
|
/**
|
|
6077
6837
|
* Pagination — daisy's join-of-buttons made of standalone btn cells: the
|
|
6078
6838
|
* quiet base-200 fill, base-300 hover, the current page inverted into the
|
|
6079
6839
|
* accent pair (primary by default). Pressed is the daisy 1px sink. The
|
|
6080
|
-
*
|
|
6840
|
+
* `‹ › « »` glyphs flip under the shared rtl guard.
|
|
6081
6841
|
*/
|
|
6082
6842
|
export const pagination = {
|
|
6083
6843
|
component: 'pagination',
|
|
@@ -6123,6 +6883,7 @@ export const pagination = {
|
|
|
6123
6883
|
fontWeight: 'var(--weight-semibold)',
|
|
6124
6884
|
fontVariantNumeric: 'tabular-nums',
|
|
6125
6885
|
appearance: 'none',
|
|
6886
|
+
textDecoration: 'none',
|
|
6126
6887
|
cursor: 'pointer',
|
|
6127
6888
|
transition: 'background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard)',
|
|
6128
6889
|
},
|
|
@@ -6150,60 +6911,10 @@ export const pagination = {
|
|
|
6150
6911
|
userSelect: 'none',
|
|
6151
6912
|
},
|
|
6152
6913
|
},
|
|
6153
|
-
'
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
justifyContent: 'center',
|
|
6158
|
-
minInlineSize: 'var(--pg-size)',
|
|
6159
|
-
blockSize: 'var(--pg-size)',
|
|
6160
|
-
background: 'var(--color-base-200)',
|
|
6161
|
-
color: 'var(--color-base-content)',
|
|
6162
|
-
border: 'none',
|
|
6163
|
-
borderRadius: 'var(--radius-field)',
|
|
6164
|
-
fontSize: 'calc(var(--pg-font) * 1.2)',
|
|
6165
|
-
lineHeight: 'var(--leading-none)',
|
|
6166
|
-
appearance: 'none',
|
|
6167
|
-
cursor: 'pointer',
|
|
6168
|
-
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
6169
|
-
},
|
|
6170
|
-
states: {
|
|
6171
|
-
hover: { background: 'var(--color-base-300)' },
|
|
6172
|
-
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6173
|
-
...focusRing,
|
|
6174
|
-
},
|
|
6175
|
-
selectors: {
|
|
6176
|
-
'&[data-pressed]:not([data-disabled])': { transform: 'translateY(1px)' },
|
|
6177
|
-
[`&${rtl}`]: { scale: '-1 1' },
|
|
6178
|
-
},
|
|
6179
|
-
},
|
|
6180
|
-
'next-trigger': {
|
|
6181
|
-
base: {
|
|
6182
|
-
display: 'inline-flex',
|
|
6183
|
-
alignItems: 'center',
|
|
6184
|
-
justifyContent: 'center',
|
|
6185
|
-
minInlineSize: 'var(--pg-size)',
|
|
6186
|
-
blockSize: 'var(--pg-size)',
|
|
6187
|
-
background: 'var(--color-base-200)',
|
|
6188
|
-
color: 'var(--color-base-content)',
|
|
6189
|
-
border: 'none',
|
|
6190
|
-
borderRadius: 'var(--radius-field)',
|
|
6191
|
-
fontSize: 'calc(var(--pg-font) * 1.2)',
|
|
6192
|
-
lineHeight: 'var(--leading-none)',
|
|
6193
|
-
appearance: 'none',
|
|
6194
|
-
cursor: 'pointer',
|
|
6195
|
-
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
6196
|
-
},
|
|
6197
|
-
states: {
|
|
6198
|
-
hover: { background: 'var(--color-base-300)' },
|
|
6199
|
-
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6200
|
-
...focusRing,
|
|
6201
|
-
},
|
|
6202
|
-
selectors: {
|
|
6203
|
-
'&[data-pressed]:not([data-disabled])': { transform: 'translateY(1px)' },
|
|
6204
|
-
[`&${rtl}`]: { scale: '-1 1' },
|
|
6205
|
-
},
|
|
6206
|
-
},
|
|
6914
|
+
'first-trigger': pageTrigger,
|
|
6915
|
+
'prev-trigger': pageTrigger,
|
|
6916
|
+
'next-trigger': pageTrigger,
|
|
6917
|
+
'last-trigger': pageTrigger,
|
|
6207
6918
|
},
|
|
6208
6919
|
variants: {
|
|
6209
6920
|
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
@@ -6241,6 +6952,20 @@ export const pagination = {
|
|
|
6241
6952
|
*/
|
|
6242
6953
|
const stepInk = (accent, role) => `color-mix(in oklab, ${accent} `
|
|
6243
6954
|
+ `${Math.min(ROLE_INK_KEEP[role] ?? 55, 70)}%, var(--color-base-content))`;
|
|
6955
|
+
/** A Steps root holding wizard parts (#296) — the panel or the triggers. */
|
|
6956
|
+
const STEPS_WIZARD = '&:has(> [data-scope="steps"]:is([data-part="content"], [data-part="prev-trigger"], [data-part="next-trigger"]))';
|
|
6957
|
+
/** Steps' Back/Next (#296): the neutral daisy btn, with its 1px sink. */
|
|
6958
|
+
const stepsTrigger = {
|
|
6959
|
+
base: { ...btn, flex: '0 0 auto', alignSelf: 'flex-start' },
|
|
6960
|
+
states: {
|
|
6961
|
+
hover: { background: 'var(--color-base-300)' },
|
|
6962
|
+
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6963
|
+
...focusRing,
|
|
6964
|
+
},
|
|
6965
|
+
selectors: {
|
|
6966
|
+
'&[data-pressed]:not([data-disabled])': { transform: 'translateY(1px)' },
|
|
6967
|
+
},
|
|
6968
|
+
};
|
|
6244
6969
|
/**
|
|
6245
6970
|
* Steps — daisy's steps translated to the richer rail: bold numbered discs
|
|
6246
6971
|
* on base-200, the walked disc and line refilled with the accent pair
|
|
@@ -6267,6 +6992,10 @@ export const steps = {
|
|
|
6267
6992
|
},
|
|
6268
6993
|
selectors: {
|
|
6269
6994
|
'&[data-orientation="vertical"]': { flexDirection: 'column' },
|
|
6995
|
+
// A wizard (#296): the rail keeps its own line and the panel
|
|
6996
|
+
// and triggers wrap below it. Only then — a bare rail never
|
|
6997
|
+
// wraps, so its steps stay one row at any width.
|
|
6998
|
+
[STEPS_WIZARD]: { flexWrap: 'wrap', rowGap: 'var(--space-md)' },
|
|
6270
6999
|
},
|
|
6271
7000
|
},
|
|
6272
7001
|
/**
|
|
@@ -6305,6 +7034,10 @@ export const steps = {
|
|
|
6305
7034
|
active: { color: 'var(--steps-accent-ink)', fontWeight: 'var(--weight-semibold)' },
|
|
6306
7035
|
complete: { color: 'var(--color-base-content)' },
|
|
6307
7036
|
inactive: { color: 'color-mix(in oklch, var(--color-base-content) 65%, transparent)' },
|
|
7037
|
+
// daisy's `step-error`, whatever the phase: after the phases,
|
|
7038
|
+
// so it wins at equal weight. `stepInk`, not `roleInk`: the
|
|
7039
|
+
// 85% error ink measured 3.79:1 on light's page as text.
|
|
7040
|
+
invalid: { color: stepInk('var(--color-error)', 'error') },
|
|
6308
7041
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6309
7042
|
...focusRing,
|
|
6310
7043
|
},
|
|
@@ -6336,6 +7069,7 @@ export const steps = {
|
|
|
6336
7069
|
// the disc (#112).
|
|
6337
7070
|
complete: { background: 'color-mix(in oklab, var(--steps-accent) 20%, var(--color-base-100))', color: 'var(--steps-complete-ink)' },
|
|
6338
7071
|
inactive: { background: 'var(--color-base-200)', color: 'var(--color-base-content)' },
|
|
7072
|
+
invalid: { background: 'var(--color-error)', color: 'var(--color-error-content)' },
|
|
6339
7073
|
},
|
|
6340
7074
|
},
|
|
6341
7075
|
/**
|
|
@@ -6367,6 +7101,7 @@ export const steps = {
|
|
|
6367
7101
|
states: {
|
|
6368
7102
|
complete: { background: 'var(--steps-accent)' },
|
|
6369
7103
|
inactive: { background: 'var(--color-base-300)' },
|
|
7104
|
+
invalid: { background: 'var(--color-error)' },
|
|
6370
7105
|
},
|
|
6371
7106
|
},
|
|
6372
7107
|
title: {
|
|
@@ -6381,6 +7116,25 @@ export const steps = {
|
|
|
6381
7116
|
fontWeight: 'var(--weight-normal)',
|
|
6382
7117
|
},
|
|
6383
7118
|
},
|
|
7119
|
+
/**
|
|
7120
|
+
* The active step's panel: a full-width line under the rail (the
|
|
7121
|
+
* wizard wrap above). Inactive panels are `hidden`, so they need no
|
|
7122
|
+
* rule.
|
|
7123
|
+
*/
|
|
7124
|
+
content: {
|
|
7125
|
+
base: {
|
|
7126
|
+
flex: '1 0 100%',
|
|
7127
|
+
fontSize: 'var(--text-md)',
|
|
7128
|
+
color: 'var(--color-base-content)',
|
|
7129
|
+
},
|
|
7130
|
+
states: { active: {}, inactive: {} },
|
|
7131
|
+
},
|
|
7132
|
+
// Back/Next are daisy btns; Next takes the far end of their line.
|
|
7133
|
+
'prev-trigger': stepsTrigger,
|
|
7134
|
+
'next-trigger': {
|
|
7135
|
+
...stepsTrigger,
|
|
7136
|
+
base: { ...stepsTrigger.base, marginInlineStart: 'auto' },
|
|
7137
|
+
},
|
|
6384
7138
|
},
|
|
6385
7139
|
variants: {
|
|
6386
7140
|
// Keyed on the ITEM, not the root (#112): the item re-carries
|
|
@@ -7348,11 +8102,11 @@ export const diff = {
|
|
|
7348
8102
|
},
|
|
7349
8103
|
};
|
|
7350
8104
|
export const recipes = [
|
|
7351
|
-
tabs, collapsible, switchRecipe, dialog, popover, tooltip, menu,
|
|
7352
|
-
field, fieldset, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
|
|
8105
|
+
tabs, collapsible, switchRecipe, dialog, popover, tooltip, hoverCard, menu, menubar,
|
|
8106
|
+
field, fieldset, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, avatarGroup, toast, combobox,
|
|
7353
8107
|
toggle, toggleGroup, numberInput, ratingGroup, treeView, input, textarea,
|
|
7354
8108
|
card, alert, emptyState, badge, divider, skeleton, spinner,
|
|
7355
|
-
kbd, status, indicator, stats, timeline, chat, radialProgress, join,
|
|
8109
|
+
kbd, status, indicator, stats, timeline, chat, chatLog, radialProgress, join,
|
|
7356
8110
|
navbar, navList, breadcrumbs, pagination, steps, drawer,
|
|
7357
8111
|
table,
|
|
7358
8112
|
fileUpload,
|