@sigx/zero-daisyui 0.6.0 → 0.7.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 +1 -2
- package/dist/audit.json +1805 -70
- package/dist/components.d.ts +9 -0
- package/dist/components.js +1 -0
- package/dist/css/components/fieldset.css +91 -0
- package/dist/css/components/menu.css +66 -0
- package/dist/css/components/popover.css +72 -0
- package/dist/css/components/progress.css +0 -4
- package/dist/css/components/table.css +70 -0
- package/dist/css/components/tooltip.css +65 -0
- package/dist/css/index.css +363 -4
- package/dist/lynx/components/accordion.css +4 -0
- package/dist/lynx/components/button.css +53 -1
- package/dist/lynx/components/dialog.css +19 -0
- package/dist/lynx/components/fieldset.css +219 -0
- package/dist/lynx/components/popover.css +37 -0
- package/dist/lynx/components/progress.css +0 -20
- package/dist/lynx/components/select.css +12 -1
- package/dist/lynx/components/slider.css +292 -10
- package/dist/lynx/components/switch.css +2 -0
- package/dist/lynx/components/table.css +52 -0
- package/dist/lynx/components/tabs.css +102 -93
- package/dist/lynx/components/timeline.css +58 -1
- package/dist/lynx/components/toast.css +41 -6
- package/dist/lynx/index.css +894 -134
- package/dist/lynx/manifest.json +30 -3
- package/dist/manifest.json +28 -1
- package/dist/recipes.d.ts +10 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +508 -55
- package/dist/recipes.js.map +1 -1
- package/dist/register.d.ts +9 -0
- package/dist/report.json +303 -57
- package/package.json +4 -4
- package/src/recipes.ts +514 -55
package/dist/recipes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { axisRoles, tableStackAt } from '@sigx/zero-kit/define';
|
|
1
|
+
import { axisRoles, popupArrow, popupArrowHost, tableStackAt } from '@sigx/zero-kit/define';
|
|
2
2
|
import { roles, tokens } from './tokens.js';
|
|
3
3
|
/**
|
|
4
4
|
* Every role a consumer can pass as `color`, derived from the declaration
|
|
@@ -289,7 +289,9 @@ export const tabs = {
|
|
|
289
289
|
},
|
|
290
290
|
// daisy draws each flavor's active mark on the tab itself (the
|
|
291
291
|
// underline, the lift, the pill) and none of them slides, so a
|
|
292
|
-
// `Tabs.Indicator` renders nothing here (#283).
|
|
292
|
+
// `Tabs.Indicator` renders nothing here (#283). On lynx, which has
|
|
293
|
+
// no pseudo-elements, the `border` flavor's underline is drawn on
|
|
294
|
+
// this part instead (see `targets.lynx`).
|
|
293
295
|
indicator: { base: { display: 'none' } },
|
|
294
296
|
panel: {
|
|
295
297
|
base: { fontSize: 'var(--text-md)' },
|
|
@@ -491,11 +493,17 @@ export const tabs = {
|
|
|
491
493
|
targets: {
|
|
492
494
|
lynx: {
|
|
493
495
|
parts: {
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
+
// No hover on a touch platform: the held tab takes the ink
|
|
497
|
+
// daisy's hover gives it.
|
|
498
|
+
tab: { states: { pressed: { color: 'var(--color-base-content)' } } },
|
|
499
|
+
// The list is the indicator's containing block.
|
|
500
|
+
list: { base: { position: 'relative' } },
|
|
501
|
+
// The underline's ink (read by the `border` flavor below) —
|
|
502
|
+
// base-content by default, exactly what the shared
|
|
503
|
+
// `states.active.color` paints on the tab. One class, so any
|
|
496
504
|
// color-axis definition below (host + two classes, baked per
|
|
497
505
|
// theme) wins on specificity.
|
|
498
|
-
|
|
506
|
+
indicator: { base: { '--tab-active-ink': 'var(--color-base-content)' } },
|
|
499
507
|
},
|
|
500
508
|
variants: {
|
|
501
509
|
// `currentColor` never resolves on lynx (measured on device
|
|
@@ -505,18 +513,28 @@ export const tabs = {
|
|
|
505
513
|
// theme colors through color-mix(), which the emitter bakes
|
|
506
514
|
// once per theme — the same per-color/per-theme literal the
|
|
507
515
|
// active `color:` rules already land as.
|
|
508
|
-
color: Object.fromEntries(ROLES.map((c) => [c, {
|
|
516
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { indicator: { base: { '--tab-active-ink': roleInk(c) } } }])),
|
|
509
517
|
variant: {
|
|
510
518
|
// The underline is a ::before on the web, which lynx
|
|
511
|
-
// drops
|
|
512
|
-
//
|
|
513
|
-
// the
|
|
514
|
-
//
|
|
515
|
-
//
|
|
519
|
+
// drops. It is redrawn on the `indicator` part (zero#324),
|
|
520
|
+
// which lynx-zero renders in every list and positions
|
|
521
|
+
// over the active tab from measured geometry (the web's
|
|
522
|
+
// `--tabs-indicator-*` properties are web-only). The part
|
|
523
|
+
// IS the tab's box, so daisy's bar is its bottom edge:
|
|
524
|
+
// 3px, 80% wide and centred, like `left: 10%; width: 80%`
|
|
525
|
+
// on the web. The ink rides `--tab-active-ink` — a plain
|
|
526
|
+
// var() chain, device-proven — instead of `currentColor`,
|
|
527
|
+
// which lynx never resolves.
|
|
516
528
|
border: {
|
|
517
|
-
|
|
518
|
-
base: {
|
|
519
|
-
|
|
529
|
+
indicator: {
|
|
530
|
+
base: {
|
|
531
|
+
display: 'flex',
|
|
532
|
+
boxSizing: 'border-box',
|
|
533
|
+
borderBottomWidth: '3px',
|
|
534
|
+
borderBottomStyle: 'solid',
|
|
535
|
+
borderBottomColor: 'var(--tab-active-ink)',
|
|
536
|
+
transform: 'scaleX(0.8)',
|
|
537
|
+
},
|
|
520
538
|
},
|
|
521
539
|
},
|
|
522
540
|
// `min()` does not exist on lynx (signalxjs/lynx#1066) and
|
|
@@ -864,6 +882,10 @@ export const switchRecipe = {
|
|
|
864
882
|
// No currentColor on this target — the ink is named
|
|
865
883
|
// directly, and `checked` moves border + fill below.
|
|
866
884
|
border: 'var(--border) solid var(--switch-ink)',
|
|
885
|
+
// daisy's 1px inset depth line along the track's top
|
|
886
|
+
// edge — `currentColor` at `--depth` × 10% on the
|
|
887
|
+
// web, the theme's own depth shade here.
|
|
888
|
+
boxShadow: '0 1px var(--depth-shade) inset',
|
|
867
889
|
userSelect: 'none',
|
|
868
890
|
transition: 'border-color var(--duration-slow) var(--ease-standard), '
|
|
869
891
|
+ 'background-color var(--duration-normal) var(--ease-standard)',
|
|
@@ -884,6 +906,12 @@ export const switchRecipe = {
|
|
|
884
906
|
height: 'calc(var(--switch-size) - (var(--border) + var(--switch-p)) * 2)',
|
|
885
907
|
borderRadius: 'var(--radius-selector)',
|
|
886
908
|
backgroundColor: 'var(--switch-ink)',
|
|
909
|
+
// daisy's knob relief (`.toggle:before`): a shade
|
|
910
|
+
// along the bottom edge, a sheen across the top, and
|
|
911
|
+
// a 1px drop — the web's `currentColor` drop spelled
|
|
912
|
+
// as the theme's depth shade. The noise layer has no
|
|
913
|
+
// lynx spelling and stays web-only.
|
|
914
|
+
boxShadow: '0 -1px var(--depth-shade) inset, 0 8px 0 -4px var(--depth-sheen) inset, 0 1px var(--depth-shade)',
|
|
887
915
|
transition: 'transform var(--duration-normal) var(--ease-standard), '
|
|
888
916
|
+ 'background-color var(--duration-instant) var(--ease-standard)',
|
|
889
917
|
},
|
|
@@ -990,6 +1018,21 @@ const lynxBtnSizes = (part) => ({
|
|
|
990
1018
|
lg: { [part]: { base: lynxBtnPad(5) } },
|
|
991
1019
|
xl: { [part]: { base: lynxBtnPad(6) } },
|
|
992
1020
|
});
|
|
1021
|
+
/**
|
|
1022
|
+
* The press rendering of the overlay btns (dialog, popover), for their
|
|
1023
|
+
* `targets.lynx` sections. The web answers a pointer with `:hover`, which
|
|
1024
|
+
* the lynx emitter drops (a touch platform), and these parts' `pressed`
|
|
1025
|
+
* flag — stamped by the lynx runtime on every touch — had no rule, so a
|
|
1026
|
+
* tap showed nothing (signalxjs/lynx#1146). daisy's `:hover` wash plus the
|
|
1027
|
+
* btn's 1px sink, the same pair `button` paints for its own `pressed`. A
|
|
1028
|
+
* colour variant restates the background after the states at equal
|
|
1029
|
+
* specificity, so a coloured trigger keeps its fill and sinks.
|
|
1030
|
+
*/
|
|
1031
|
+
const lynxBtnPressed = {
|
|
1032
|
+
background: 'var(--color-base-300)',
|
|
1033
|
+
transform: 'translateY(1px)',
|
|
1034
|
+
boxShadow: 'none',
|
|
1035
|
+
};
|
|
993
1036
|
export const dialog = {
|
|
994
1037
|
component: 'dialog',
|
|
995
1038
|
// Public to a design system derived from this one (#73).
|
|
@@ -1081,14 +1124,16 @@ export const dialog = {
|
|
|
1081
1124
|
// reach and the trigger is the whole story here.
|
|
1082
1125
|
variants: { color: btnColors(), size: btnSizes },
|
|
1083
1126
|
// The btn paddings and the footer's push-down, restated physically —
|
|
1084
|
-
// see `lynxBtnPad` for the #1084 verdict this answers
|
|
1127
|
+
// see `lynxBtnPad` for the #1084 verdict this answers — and the btns'
|
|
1128
|
+
// press rendering (`lynxBtnPressed`). The footer states its row: on lynx
|
|
1129
|
+
// a row container says so next to `display: flex`.
|
|
1085
1130
|
targets: {
|
|
1086
1131
|
lynx: {
|
|
1087
1132
|
parts: {
|
|
1088
|
-
trigger: { base: lynxBtnPad(4) },
|
|
1089
|
-
close: { base: lynxBtnPad(4) },
|
|
1090
|
-
cancel: { base: lynxBtnPad(4) },
|
|
1091
|
-
footer: { base: { marginTop: 'var(--space-2xl)' } },
|
|
1133
|
+
trigger: { base: lynxBtnPad(4), states: { pressed: lynxBtnPressed } },
|
|
1134
|
+
close: { base: lynxBtnPad(4), states: { pressed: lynxBtnPressed } },
|
|
1135
|
+
cancel: { base: lynxBtnPad(4), states: { pressed: lynxBtnPressed } },
|
|
1136
|
+
footer: { base: { marginTop: 'var(--space-2xl)', flexDirection: 'row' } },
|
|
1092
1137
|
},
|
|
1093
1138
|
variants: { size: lynxBtnSizes('trigger') },
|
|
1094
1139
|
},
|
|
@@ -1102,6 +1147,21 @@ const floatingPanel = {
|
|
|
1102
1147
|
borderRadius: 'var(--radius-box)',
|
|
1103
1148
|
boxShadow: 'var(--shadow-lg)',
|
|
1104
1149
|
};
|
|
1150
|
+
/**
|
|
1151
|
+
* The arrow a floating surface grows toward its anchor (#279), in the
|
|
1152
|
+
* surface's own paint: the dropdown's base-100 and border for popover and
|
|
1153
|
+
* menu, the tooltip's borderless neutral for the tooltip — daisy's
|
|
1154
|
+
* `tooltip` draws exactly that tail under its bubble. Web-only (placed by
|
|
1155
|
+
* the runtime's `--arrow-x`/`--arrow-y`), so it rides `targets.web` with
|
|
1156
|
+
* the popup's permission to let it out.
|
|
1157
|
+
*/
|
|
1158
|
+
const floatingArrow = (scope, paint, size = '0.625rem') => ({
|
|
1159
|
+
parts: {
|
|
1160
|
+
popup: { selectors: popupArrowHost(scope) },
|
|
1161
|
+
arrow: popupArrow(scope, { size, paint }),
|
|
1162
|
+
},
|
|
1163
|
+
});
|
|
1164
|
+
const panelArrowPaint = { background: 'var(--color-base-100)', border: 'var(--border) solid var(--color-base-300)' };
|
|
1105
1165
|
/**
|
|
1106
1166
|
* daisy's dropdown menu for a listbox, sized by the geometry the
|
|
1107
1167
|
* anchored-position strategy publishes (#278): at least the width of the
|
|
@@ -1138,6 +1198,14 @@ export const popover = {
|
|
|
1138
1198
|
title: {
|
|
1139
1199
|
base: { margin: '0 0 var(--space-md)', fontSize: 'var(--text-md)', fontWeight: 'var(--weight-bold)' },
|
|
1140
1200
|
},
|
|
1201
|
+
// The modal's muted body line, at the dropdown's tighter rhythm.
|
|
1202
|
+
description: {
|
|
1203
|
+
base: {
|
|
1204
|
+
margin: '0 0 var(--space-lg)',
|
|
1205
|
+
fontSize: 'var(--text-sm)',
|
|
1206
|
+
color: 'color-mix(in oklab, var(--color-base-content) 75%, transparent)',
|
|
1207
|
+
},
|
|
1208
|
+
},
|
|
1141
1209
|
close: {
|
|
1142
1210
|
base: { ...btn, height: 'calc(var(--size-field) * 8)', paddingInline: 'calc(var(--size-field) * 3)', fontSize: 'var(--text-xs)' },
|
|
1143
1211
|
states: {
|
|
@@ -1149,12 +1217,14 @@ export const popover = {
|
|
|
1149
1217
|
},
|
|
1150
1218
|
// Trigger-carried axes — same wiring as dialog, same reason.
|
|
1151
1219
|
variants: { color: btnColors(), size: btnSizes },
|
|
1152
|
-
// The btn paddings, restated physically — see `lynxBtnPad` (#1084)
|
|
1220
|
+
// The btn paddings, restated physically — see `lynxBtnPad` (#1084) —
|
|
1221
|
+
// and the btns' press rendering (`lynxBtnPressed`).
|
|
1153
1222
|
targets: {
|
|
1223
|
+
web: floatingArrow('popover', panelArrowPaint),
|
|
1154
1224
|
lynx: {
|
|
1155
1225
|
parts: {
|
|
1156
|
-
trigger: { base: lynxBtnPad(4) },
|
|
1157
|
-
close: { base: lynxBtnPad(3) },
|
|
1226
|
+
trigger: { base: lynxBtnPad(4), states: { pressed: lynxBtnPressed } },
|
|
1227
|
+
close: { base: lynxBtnPad(3), states: { pressed: lynxBtnPressed } },
|
|
1158
1228
|
},
|
|
1159
1229
|
variants: { size: lynxBtnSizes('trigger') },
|
|
1160
1230
|
},
|
|
@@ -1196,6 +1266,7 @@ export const tooltip = {
|
|
|
1196
1266
|
variants: { color: btnColors(), size: btnSizes },
|
|
1197
1267
|
// The btn paddings, restated physically — see `lynxBtnPad` (#1084).
|
|
1198
1268
|
targets: {
|
|
1269
|
+
web: floatingArrow('tooltip', { background: 'var(--color-neutral)' }, '0.5rem'),
|
|
1199
1270
|
lynx: {
|
|
1200
1271
|
parts: { trigger: { base: lynxBtnPad(4) } },
|
|
1201
1272
|
variants: { size: lynxBtnSizes('trigger') },
|
|
@@ -1348,6 +1419,7 @@ export const menu = {
|
|
|
1348
1419
|
variants: { color: btnColors(), size: btnSizes },
|
|
1349
1420
|
// The btn paddings, restated physically — see `lynxBtnPad` (#1084).
|
|
1350
1421
|
targets: {
|
|
1422
|
+
web: floatingArrow('menu', panelArrowPaint),
|
|
1351
1423
|
lynx: {
|
|
1352
1424
|
parts: { trigger: { base: lynxBtnPad(4) } },
|
|
1353
1425
|
variants: { size: lynxBtnSizes('trigger') },
|
|
@@ -1403,6 +1475,60 @@ export const field = {
|
|
|
1403
1475
|
},
|
|
1404
1476
|
},
|
|
1405
1477
|
};
|
|
1478
|
+
/**
|
|
1479
|
+
* Fieldset (#285), daisyUI 5's `.fieldset` / `.fieldset-legend`: a
|
|
1480
|
+
* one-column grid with a tight row gap and a little block padding, no frame
|
|
1481
|
+
* of its own (daisy draws one only when the app adds `border`), and a
|
|
1482
|
+
* semibold legend with block padding standing in for its margin. The UA
|
|
1483
|
+
* `min-inline-size: min-content` is undone so a wide control cannot push the
|
|
1484
|
+
* group past its column. Colour accents the legend through `roleInk`, the
|
|
1485
|
+
* Field way.
|
|
1486
|
+
*/
|
|
1487
|
+
export const fieldset = {
|
|
1488
|
+
component: 'fieldset',
|
|
1489
|
+
tokens: { '--fieldset-accent': 'var(--color-base-content)' },
|
|
1490
|
+
parts: {
|
|
1491
|
+
root: {
|
|
1492
|
+
base: {
|
|
1493
|
+
display: 'grid',
|
|
1494
|
+
gridTemplateColumns: '1fr',
|
|
1495
|
+
gridAutoRows: 'max-content',
|
|
1496
|
+
gap: 'var(--space-sm)',
|
|
1497
|
+
minInlineSize: '0',
|
|
1498
|
+
margin: '0',
|
|
1499
|
+
paddingBlock: 'var(--space-xs)',
|
|
1500
|
+
paddingInline: '0',
|
|
1501
|
+
border: 'none',
|
|
1502
|
+
},
|
|
1503
|
+
},
|
|
1504
|
+
legend: {
|
|
1505
|
+
base: {
|
|
1506
|
+
paddingBlock: 'var(--space-sm)',
|
|
1507
|
+
paddingInline: '0',
|
|
1508
|
+
fontSize: 'var(--text-sm)',
|
|
1509
|
+
fontWeight: 'var(--weight-semibold)',
|
|
1510
|
+
color: 'var(--fieldset-accent)',
|
|
1511
|
+
},
|
|
1512
|
+
states: {
|
|
1513
|
+
disabled: { opacity: 'var(--disabled-opacity)' },
|
|
1514
|
+
invalid: { color: roleInk('error') },
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
},
|
|
1518
|
+
variants: {
|
|
1519
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
1520
|
+
'--fieldset-accent': roleInk(c),
|
|
1521
|
+
} } }])),
|
|
1522
|
+
size: {
|
|
1523
|
+
xs: { legend: { base: { fontSize: 'var(--text-xs)' } } },
|
|
1524
|
+
sm: { legend: { base: { fontSize: 'var(--text-xs)' } } },
|
|
1525
|
+
// `md` is the un-attributed render.
|
|
1526
|
+
md: {},
|
|
1527
|
+
lg: { legend: { base: { fontSize: 'var(--text-md)' } } },
|
|
1528
|
+
xl: { legend: { base: { fontSize: 'var(--text-lg)' } } },
|
|
1529
|
+
},
|
|
1530
|
+
},
|
|
1531
|
+
};
|
|
1406
1532
|
/**
|
|
1407
1533
|
* daisy's tick, drawn by a clip-path that GROWS.
|
|
1408
1534
|
*
|
|
@@ -1900,9 +2026,13 @@ export const progress = {
|
|
|
1900
2026
|
transition: 'width var(--duration-slow) var(--ease-standard)',
|
|
1901
2027
|
},
|
|
1902
2028
|
states: {
|
|
1903
|
-
// `complete`
|
|
1904
|
-
//
|
|
1905
|
-
|
|
2029
|
+
// `complete` keeps the colour axis. daisyUI 5 has no complete
|
|
2030
|
+
// tint — a full `progress-primary` stays primary — and a
|
|
2031
|
+
// `color="secondary"` bar that turns green at 100% reads as a
|
|
2032
|
+
// bug, not as meaning (signalxjs/lynx#1144). A caller who wants
|
|
2033
|
+
// "done" to look like success says so: `color="success"`.
|
|
2034
|
+
// Declared equal to `loading` in `sameAs` below.
|
|
2035
|
+
complete: {},
|
|
1906
2036
|
loading: {},
|
|
1907
2037
|
indeterminate: { width: '40%', animation: 'zero-daisy-indeterminate 1.2s ease-in-out infinite' },
|
|
1908
2038
|
},
|
|
@@ -1938,6 +2068,8 @@ export const progress = {
|
|
|
1938
2068
|
// so the manifest states the default for runtimes with no `:not()`
|
|
1939
2069
|
// fallback (the lynx target; signalxjs/lynx#1070).
|
|
1940
2070
|
defaultVariants: { color: 'primary', size: 'md' },
|
|
2071
|
+
// A finished bar is a full bar of the same ink (see `complete` above).
|
|
2072
|
+
sameAs: { root: { complete: 'loading' }, range: { complete: 'loading' } },
|
|
1941
2073
|
keyframes: {
|
|
1942
2074
|
// Logical, so the sweep runs the way the bar fills — `margin-inline-start`
|
|
1943
2075
|
// is animatable and direction-aware on its own. The determinate `width`,
|
|
@@ -1958,6 +2090,8 @@ export const progress = {
|
|
|
1958
2090
|
},
|
|
1959
2091
|
},
|
|
1960
2092
|
};
|
|
2093
|
+
/** The slider's deepened fill — the web's `--slider-fill` mix, for a given accent. */
|
|
2094
|
+
const sliderFill = (accent) => `color-mix(in oklab, ${accent} 90%, var(--color-base-content))`;
|
|
1961
2095
|
export const slider = {
|
|
1962
2096
|
component: 'slider',
|
|
1963
2097
|
// daisy's `.range` metrics: the thumb is `--range-thumb-size`
|
|
@@ -2266,29 +2400,94 @@ export const slider = {
|
|
|
2266
2400
|
},
|
|
2267
2401
|
},
|
|
2268
2402
|
lynx: {
|
|
2403
|
+
// The web's deepened fill, theme-baked: `color-mix()` over theme
|
|
2404
|
+
// colours cannot resolve on device, but the emitter restates a
|
|
2405
|
+
// colour function per theme as literals (as `progress` does for
|
|
2406
|
+
// its accent), so each colour step gets the same 90/10 mix the
|
|
2407
|
+
// web track and knob ring wear. Bound on the root like
|
|
2408
|
+
// `--slider-accent`, rebound by `color` below and by `invalid`
|
|
2409
|
+
// on the control (which wraps the rail on lynx).
|
|
2410
|
+
tokens: { '--slider-fill': sliderFill('var(--color-primary)') },
|
|
2411
|
+
variants: {
|
|
2412
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
2413
|
+
'--slider-fill': sliderFill(`var(--color-${c})`),
|
|
2414
|
+
} } }])),
|
|
2415
|
+
},
|
|
2269
2416
|
parts: {
|
|
2417
|
+
// Vertical (lynx#1144): the lynx runtime positions the moving
|
|
2418
|
+
// parts itself — `top` percentages, the knob centred by
|
|
2419
|
+
// `left: 50%` plus the base's `translateY(-50%)` and
|
|
2420
|
+
// negative `margin-left` — so the class rules only turn the
|
|
2421
|
+
// channel upright: the control is a thumb-wide column of
|
|
2422
|
+
// daisy's rail length, the track fills it.
|
|
2423
|
+
root: {
|
|
2424
|
+
selectors: {
|
|
2425
|
+
'&[data-orientation="vertical"]': { width: 'auto', alignItems: 'center' },
|
|
2426
|
+
},
|
|
2427
|
+
},
|
|
2428
|
+
control: {
|
|
2429
|
+
states: {
|
|
2430
|
+
invalid: { '--slider-fill': sliderFill('var(--color-error)') },
|
|
2431
|
+
},
|
|
2432
|
+
selectors: {
|
|
2433
|
+
'&[data-orientation="vertical"]': {
|
|
2434
|
+
display: 'flex',
|
|
2435
|
+
flexDirection: 'row',
|
|
2436
|
+
justifyContent: 'center',
|
|
2437
|
+
width: 'var(--slider-thumb-size)',
|
|
2438
|
+
height: 'calc(var(--size-selector) * 40)',
|
|
2439
|
+
},
|
|
2440
|
+
},
|
|
2441
|
+
},
|
|
2270
2442
|
track: {
|
|
2271
2443
|
// The web track's `margin-block` breathing room,
|
|
2272
2444
|
// restated physically: logical spellings resolve on iOS
|
|
2273
2445
|
// but not on Android (measured, signalxjs/lynx#1084), so
|
|
2274
2446
|
// the emitter refuses them. Physical is the lynx
|
|
2275
2447
|
// target's norm — no RTL flow there.
|
|
2448
|
+
//
|
|
2449
|
+
// daisy's channel radius is the selector radius (the
|
|
2450
|
+
// native control's runnable-track), not a pill.
|
|
2276
2451
|
base: {
|
|
2277
2452
|
marginTop: 'calc((var(--slider-thumb-size) - var(--slider-track-size)) / 2)',
|
|
2278
2453
|
marginBottom: 'calc((var(--slider-thumb-size) - var(--slider-track-size)) / 2)',
|
|
2454
|
+
borderRadius: 'var(--radius-selector)',
|
|
2455
|
+
},
|
|
2456
|
+
selectors: {
|
|
2457
|
+
'&[data-orientation="vertical"]': {
|
|
2458
|
+
width: 'var(--slider-track-size)',
|
|
2459
|
+
height: '100%',
|
|
2460
|
+
marginTop: '0',
|
|
2461
|
+
marginBottom: '0',
|
|
2462
|
+
},
|
|
2279
2463
|
},
|
|
2280
2464
|
},
|
|
2281
2465
|
mark: {
|
|
2282
|
-
//
|
|
2283
|
-
//
|
|
2284
|
-
|
|
2466
|
+
// The web draws the tick with `::before`, which lynx has
|
|
2467
|
+
// no projection for — the mark was an empty, invisible
|
|
2468
|
+
// box. Here the mark IS the tick: a 2px rule across the
|
|
2469
|
+
// channel, centred on its runtime-set `left` percent.
|
|
2470
|
+
// (Lynx marks carry no label text.)
|
|
2471
|
+
base: {
|
|
2472
|
+
top: '0',
|
|
2473
|
+
width: '2px',
|
|
2474
|
+
height: 'var(--slider-track-size)',
|
|
2475
|
+
marginLeft: '-1px',
|
|
2476
|
+
paddingTop: '0',
|
|
2477
|
+
backgroundColor: 'var(--color-base-content)',
|
|
2478
|
+
},
|
|
2479
|
+
selectors: {
|
|
2480
|
+
'&[data-orientation="vertical"]': {
|
|
2481
|
+
left: '0',
|
|
2482
|
+
width: 'var(--slider-track-size)',
|
|
2483
|
+
height: '2px',
|
|
2484
|
+
marginLeft: '0',
|
|
2485
|
+
marginTop: '-1px',
|
|
2486
|
+
},
|
|
2487
|
+
},
|
|
2285
2488
|
},
|
|
2286
2489
|
range: {
|
|
2287
|
-
|
|
2288
|
-
// base-content (`progressFill`'s recipe); the plain
|
|
2289
|
-
// accent — a var() chain, proven working on device — is
|
|
2290
|
-
// the closest lynx-expressible equivalent.
|
|
2291
|
-
base: { background: 'var(--slider-accent)' },
|
|
2490
|
+
base: { background: 'var(--slider-fill)', borderRadius: 'var(--radius-selector)' },
|
|
2292
2491
|
},
|
|
2293
2492
|
thumb: {
|
|
2294
2493
|
// daisy's real range thumb, ported from daisyUI 5
|
|
@@ -2299,13 +2498,16 @@ export const slider = {
|
|
|
2299
2498
|
// `--range-thumb` defaults to `var(--color-base-100)`,
|
|
2300
2499
|
// `--range-p` is daisy's literal `.25rem` (deliberately
|
|
2301
2500
|
// not the theme's size token), and the border ink is the
|
|
2302
|
-
//
|
|
2303
|
-
//
|
|
2304
|
-
//
|
|
2305
|
-
//
|
|
2306
|
-
//
|
|
2307
|
-
//
|
|
2308
|
-
//
|
|
2501
|
+
// deepened fill the web's native thumb ring reads. daisy
|
|
2502
|
+
// paints the progress fill itself as a 100cqw box-shadow
|
|
2503
|
+
// thrown off this thumb — a native-input trick with no
|
|
2504
|
+
// lynx equivalent; the composed `.zx-slider__range` box
|
|
2505
|
+
// above IS that fill here, so the knob only needs its own
|
|
2506
|
+
// paints. Its radius is daisy's formula minus the min()
|
|
2507
|
+
// clamp lynx cannot evaluate — exact for every shipped
|
|
2508
|
+
// theme (all set `--radius-selector` ≥ .25rem / 3) — and
|
|
2509
|
+
// it wears daisy's depth shading (`currentColor`'s 10%
|
|
2510
|
+
// outer shadow spelled as the theme's shade).
|
|
2309
2511
|
//
|
|
2310
2512
|
// The centering geometry is restated physically beside
|
|
2311
2513
|
// the paint: `inset-block-start`/`translate`/
|
|
@@ -2316,7 +2518,9 @@ export const slider = {
|
|
|
2316
2518
|
// proven on both platforms.
|
|
2317
2519
|
base: {
|
|
2318
2520
|
background: 'var(--color-base-100)',
|
|
2319
|
-
border: '0.25rem solid var(--slider-
|
|
2521
|
+
border: '0.25rem solid var(--slider-fill)',
|
|
2522
|
+
borderRadius: 'calc(var(--radius-selector) + 0.25rem)',
|
|
2523
|
+
boxShadow: '0 -1px var(--depth-shade) inset, 0 8px 0 -4px var(--depth-sheen) inset, 0 1px var(--depth-shade)',
|
|
2320
2524
|
top: '50%',
|
|
2321
2525
|
transform: 'translateY(-50%)',
|
|
2322
2526
|
marginLeft: 'calc(var(--slider-thumb-size) / -2)',
|
|
@@ -2328,7 +2532,18 @@ export const slider = {
|
|
|
2328
2532
|
};
|
|
2329
2533
|
export const accordion = {
|
|
2330
2534
|
component: 'accordion',
|
|
2331
|
-
targets:
|
|
2535
|
+
targets: {
|
|
2536
|
+
...disclosureExit('accordion'),
|
|
2537
|
+
lynx: {
|
|
2538
|
+
parts: {
|
|
2539
|
+
// No hover on a touch platform: the held trigger takes daisy's
|
|
2540
|
+
// hover fill. The ::after chevron has no lynx counterpart (no
|
|
2541
|
+
// pseudo-elements, and the anatomy declares no part for it) —
|
|
2542
|
+
// an app draws its own glyph in the trigger.
|
|
2543
|
+
trigger: { states: { pressed: { background: 'var(--color-base-200)' } } },
|
|
2544
|
+
},
|
|
2545
|
+
},
|
|
2546
|
+
},
|
|
2332
2547
|
// Same accent grammar as collapsible: the open heading's ink, defaulting
|
|
2333
2548
|
// to base-content so the un-attributed render is unchanged.
|
|
2334
2549
|
tokens: { '--accordion-accent': 'var(--color-base-content)' },
|
|
@@ -2609,7 +2824,23 @@ export const select = {
|
|
|
2609
2824
|
web: { parts: { popup: { base: anchoredListbox } } },
|
|
2610
2825
|
lynx: {
|
|
2611
2826
|
parts: {
|
|
2612
|
-
|
|
2827
|
+
// daisy's `.select` is `width: clamp(3rem, 20rem, 100%)`: 20rem,
|
|
2828
|
+
// never wider than its container. lynx has no `clamp()`, so the
|
|
2829
|
+
// same bounds are spelled out — and the base's 13rem floor
|
|
2830
|
+
// comes down to daisy's 3rem, or the trigger overflowed any
|
|
2831
|
+
// cell narrower than 208px (signalxjs/lynx#1146). The root
|
|
2832
|
+
// caps too, so a shrink-wrapped root cannot push it back out.
|
|
2833
|
+
root: { base: { maxWidth: '100%' } },
|
|
2834
|
+
trigger: {
|
|
2835
|
+
base: { ...lynxBtnPad(4), width: '20rem', maxWidth: '100%', minWidth: '3rem' },
|
|
2836
|
+
// The web's `:hover` border is dropped on a touch
|
|
2837
|
+
// platform; a press gets a wash instead — a background,
|
|
2838
|
+
// so it never fights the open/invalid border inks.
|
|
2839
|
+
states: { pressed: { background: 'var(--color-base-200)' } },
|
|
2840
|
+
},
|
|
2841
|
+
// A touch list has no highlight: the pressed item takes the
|
|
2842
|
+
// highlighted item's wash.
|
|
2843
|
+
item: { states: { pressed: { background: 'var(--color-base-200)' } } },
|
|
2613
2844
|
'clear-trigger': { base: { top: 'var(--space-xs)', bottom: 'var(--space-xs)', right: 'calc(var(--size-field) * 4 + 1em + var(--space-xs))', minWidth: '1.5rem' } },
|
|
2614
2845
|
},
|
|
2615
2846
|
variants: {
|
|
@@ -2633,6 +2864,22 @@ export const select = {
|
|
|
2633
2864
|
*/
|
|
2634
2865
|
const BTN_STEPS = { xs: 6, sm: 8, md: 10, lg: 12, xl: 14 };
|
|
2635
2866
|
const btnHeight = (step) => `calc(var(--size-field) * ${BTN_STEPS[step]})`;
|
|
2867
|
+
/**
|
|
2868
|
+
* daisy 5's disabled btn, for the lynx target (signalxjs/lynx#1143). The web
|
|
2869
|
+
* fades the whole button (`opacity: var(--disabled-opacity)`, 0.3 on the
|
|
2870
|
+
* dark themes), which on device left a solid button's label barely legible.
|
|
2871
|
+
* daisy's own `.btn:disabled` paints instead: a base-content wash (not on
|
|
2872
|
+
* ghost/link), a transparent border, no shadow, and a muted base-content
|
|
2873
|
+
* label. daisy mutes that label to 20%; this keeps it at 50%, still plainly
|
|
2874
|
+
* inert but readable on a phone at arm's length.
|
|
2875
|
+
*/
|
|
2876
|
+
const lynxBtnDisabled = {
|
|
2877
|
+
opacity: '1',
|
|
2878
|
+
background: 'color-mix(in oklab, var(--color-base-content) 10%, transparent)',
|
|
2879
|
+
color: 'color-mix(in oklab, var(--color-base-content) 50%, transparent)',
|
|
2880
|
+
borderColor: 'transparent',
|
|
2881
|
+
boxShadow: 'none',
|
|
2882
|
+
};
|
|
2636
2883
|
export const button = {
|
|
2637
2884
|
component: 'button',
|
|
2638
2885
|
// Public to a design system derived from this one (#73).
|
|
@@ -2846,11 +3093,39 @@ export const button = {
|
|
|
2846
3093
|
},
|
|
2847
3094
|
defaultVariants: { color: 'primary', variant: 'solid', size: 'md' },
|
|
2848
3095
|
keyframes: { 'zero-daisyui-btn-spin': 'to { transform: rotate(360deg) }' },
|
|
2849
|
-
// The icon chips' zeroed inline padding, restated physically: logical
|
|
2850
|
-
// spellings resolve on iOS but not on Android (measured,
|
|
2851
|
-
// signalxjs/lynx#1084), so the emitter refuses them.
|
|
2852
3096
|
targets: {
|
|
2853
3097
|
lynx: {
|
|
3098
|
+
parts: {
|
|
3099
|
+
// The ring, restated: `currentColor` never resolves on lynx
|
|
3100
|
+
// (signalxjs/lynx#1079) and the logical sizes/edge are
|
|
3101
|
+
// unproven there, so the lynx ring is physical longhands in
|
|
3102
|
+
// the variant's own ink (the variants below pick it).
|
|
3103
|
+
spinner: {
|
|
3104
|
+
base: {
|
|
3105
|
+
width: '1em',
|
|
3106
|
+
height: '1em',
|
|
3107
|
+
border: 'calc(var(--border) * 2) solid var(--btn-ink)',
|
|
3108
|
+
borderTopColor: 'transparent',
|
|
3109
|
+
},
|
|
3110
|
+
},
|
|
3111
|
+
root: { states: { disabled: lynxBtnDisabled } },
|
|
3112
|
+
},
|
|
3113
|
+
variants: {
|
|
3114
|
+
// The baked disabled paint above outranks every variant's fill
|
|
3115
|
+
// (the theme host adds a class), but its plain declarations
|
|
3116
|
+
// do not: outline/dash restate the border, ghost/link drop
|
|
3117
|
+
// the wash (as in daisy).
|
|
3118
|
+
variant: {
|
|
3119
|
+
solid: { spinner: { base: { borderColor: 'var(--btn-on-accent)', borderTopColor: 'transparent' } } },
|
|
3120
|
+
outline: { root: { states: { disabled: { borderColor: 'transparent' } } } },
|
|
3121
|
+
dash: { root: { states: { disabled: { borderColor: 'transparent' } } } },
|
|
3122
|
+
ghost: { root: { states: { disabled: { background: 'transparent' } } } },
|
|
3123
|
+
link: { root: { states: { disabled: { background: 'transparent' } } } },
|
|
3124
|
+
},
|
|
3125
|
+
},
|
|
3126
|
+
// The icon chips' zeroed inline padding, restated physically: logical
|
|
3127
|
+
// spellings resolve on iOS but not on Android (measured,
|
|
3128
|
+
// signalxjs/lynx#1084), so the emitter refuses them.
|
|
2854
3129
|
modifiers: {
|
|
2855
3130
|
square: { root: { base: { paddingLeft: '0', paddingRight: '0' } } },
|
|
2856
3131
|
circle: { root: { base: { paddingLeft: '0', paddingRight: '0' } } },
|
|
@@ -3089,21 +3364,66 @@ export const toast = {
|
|
|
3089
3364
|
// signalxjs/lynx#1070. No `color` default: the un-attributed toast is a
|
|
3090
3365
|
// neutral base-200 surface, outside the color vocabulary.
|
|
3091
3366
|
defaultVariants: { size: 'md' },
|
|
3092
|
-
// The corner placements' physical anchors: logical inset spellings
|
|
3093
|
-
// resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
|
|
3094
|
-
// the emitter refuses them. Physical is the lynx target's norm — no RTL
|
|
3095
|
-
// flow there, so `start` IS left and `end` IS right.
|
|
3096
3367
|
targets: {
|
|
3097
3368
|
lynx: {
|
|
3098
3369
|
parts: {
|
|
3370
|
+
// The viewport is a full-width strip on lynx (the runtime pins
|
|
3371
|
+
// it to the outlet's top or bottom edge). What the web gets
|
|
3372
|
+
// from `:popover-open` and `min(24rem, 100vw)` is restated:
|
|
3373
|
+
// flex (a lynx view defaults to linear layout, which ignores
|
|
3374
|
+
// flex-direction and gap), and the cards aligned to the
|
|
3375
|
+
// placement's edge. The centred pair's `left: 50%` +
|
|
3376
|
+
// `translateX(-50%)` pull-back is for a shrink-wrapped popover;
|
|
3377
|
+
// on the strip it shifted everything half a width left, so it
|
|
3378
|
+
// is cancelled. The corner anchors are physical: logical
|
|
3379
|
+
// insets resolve on iOS but not on Android (measured,
|
|
3380
|
+
// signalxjs/lynx#1084), and lynx has no RTL flow, so `start`
|
|
3381
|
+
// IS left and `end` IS right.
|
|
3099
3382
|
viewport: {
|
|
3383
|
+
base: { display: 'flex', width: 'auto' },
|
|
3100
3384
|
selectors: {
|
|
3101
|
-
'&[data-placement="top-start"]': { left: '0' },
|
|
3102
|
-
'&[data-placement="top
|
|
3103
|
-
'&[data-placement="
|
|
3104
|
-
'&[data-placement="bottom-
|
|
3385
|
+
'&[data-placement="top-start"]': { left: '0', alignItems: 'flex-start' },
|
|
3386
|
+
'&[data-placement="top"]': { left: '0', right: '0', transform: 'none', alignItems: 'center' },
|
|
3387
|
+
'&[data-placement="top-end"]': { right: '0', alignItems: 'flex-end' },
|
|
3388
|
+
'&[data-placement="bottom-start"]': { left: '0', alignItems: 'flex-start' },
|
|
3389
|
+
'&[data-placement="bottom"]': { left: '0', right: '0', transform: 'none', alignItems: 'center' },
|
|
3390
|
+
'&[data-placement="bottom-end"]': { right: '0', alignItems: 'flex-end' },
|
|
3105
3391
|
},
|
|
3106
3392
|
},
|
|
3393
|
+
// No grid on lynx (signalxjs/lynx#1075): the card is a flex
|
|
3394
|
+
// column (title, description, then the action), with the
|
|
3395
|
+
// close button pinned to the top-right corner and the title
|
|
3396
|
+
// kept clear of it. The web's 24rem viewport cap becomes the
|
|
3397
|
+
// card's own cap. The `^="top"` entry offset has no class
|
|
3398
|
+
// form, so each top placement restates it.
|
|
3399
|
+
root: {
|
|
3400
|
+
base: {
|
|
3401
|
+
display: 'flex',
|
|
3402
|
+
flexDirection: 'column',
|
|
3403
|
+
alignItems: 'flex-start',
|
|
3404
|
+
position: 'relative',
|
|
3405
|
+
boxSizing: 'border-box',
|
|
3406
|
+
width: '100%',
|
|
3407
|
+
maxWidth: '24rem',
|
|
3408
|
+
},
|
|
3409
|
+
selectors: {
|
|
3410
|
+
'&[data-placement="top-start"]': { '--toast-from': '-8px' },
|
|
3411
|
+
'&[data-placement="top"]': { '--toast-from': '-8px' },
|
|
3412
|
+
'&[data-placement="top-end"]': { '--toast-from': '-8px' },
|
|
3413
|
+
},
|
|
3414
|
+
},
|
|
3415
|
+
title: { base: { paddingRight: '1.75em' } },
|
|
3416
|
+
// The 75% ink mix over a recipe-local property cannot bake;
|
|
3417
|
+
// the same ink at 75% opacity reads the same on the soft fill.
|
|
3418
|
+
description: { base: { color: 'var(--toast-ink)', opacity: '0.75' } },
|
|
3419
|
+
// The 25% ink border cannot bake either: it takes the full ink.
|
|
3420
|
+
action: {
|
|
3421
|
+
base: {
|
|
3422
|
+
marginTop: 'var(--space-xs)',
|
|
3423
|
+
border: 'var(--border) solid var(--toast-ink)',
|
|
3424
|
+
},
|
|
3425
|
+
},
|
|
3426
|
+
close: { base: { position: 'absolute', top: 'var(--space-xs)', right: 'var(--space-xs)' } },
|
|
3107
3427
|
},
|
|
3108
3428
|
},
|
|
3109
3429
|
},
|
|
@@ -5135,6 +5455,87 @@ export const timeline = {
|
|
|
5135
5455
|
xl: { marker: { base: { '--timeline-marker-size': 'calc(var(--size-selector) * 4)' } }, content: { base: { fontSize: 'var(--text-md)' } } },
|
|
5136
5456
|
},
|
|
5137
5457
|
},
|
|
5458
|
+
/**
|
|
5459
|
+
* Lynx has no grid, and it does not resolve `inline-size`/`block-size`:
|
|
5460
|
+
* the item fell back to the engine's default layout and the marker,
|
|
5461
|
+
* sized only logically, stretched into a pill (signalxjs/lynx#1145).
|
|
5462
|
+
*
|
|
5463
|
+
* The item is a wrapping flex row instead. Vertical: line one is the
|
|
5464
|
+
* marker beside its content (start-side content ordered before the
|
|
5465
|
+
* marker), and the connector takes line two by itself (`flex-basis:
|
|
5466
|
+
* 100%`), drawn as a left border under the marker's centre, which is the
|
|
5467
|
+
* web's row-2 segment. Horizontal: line one is the marker with the
|
|
5468
|
+
* connector running on to the item's end, and the content wraps under it
|
|
5469
|
+
* (start-side content above). Content grows from a zero basis, so long
|
|
5470
|
+
* text never wraps onto its own line.
|
|
5471
|
+
*
|
|
5472
|
+
* The web collapses the start track when no item has start-side content
|
|
5473
|
+
* (`:has()`); lynx has no such test. The vertical connector sits under a
|
|
5474
|
+
* marker at the item's start edge, which is where the marker is unless
|
|
5475
|
+
* that item has start-side content: an alternating vertical timeline is
|
|
5476
|
+
* web-only for now.
|
|
5477
|
+
*/
|
|
5478
|
+
targets: {
|
|
5479
|
+
lynx: {
|
|
5480
|
+
parts: {
|
|
5481
|
+
item: { base: { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center' } },
|
|
5482
|
+
marker: {
|
|
5483
|
+
base: {
|
|
5484
|
+
width: 'var(--timeline-marker-size)',
|
|
5485
|
+
height: 'var(--timeline-marker-size)',
|
|
5486
|
+
flexGrow: '0',
|
|
5487
|
+
flexShrink: '0',
|
|
5488
|
+
},
|
|
5489
|
+
},
|
|
5490
|
+
connector: {
|
|
5491
|
+
selectors: {
|
|
5492
|
+
'&[data-orientation="vertical"]': {
|
|
5493
|
+
flexGrow: '0',
|
|
5494
|
+
flexShrink: '1',
|
|
5495
|
+
flexBasis: '100%',
|
|
5496
|
+
height: 'var(--space-lg)',
|
|
5497
|
+
background: 'transparent',
|
|
5498
|
+
borderLeftWidth: 'var(--border)',
|
|
5499
|
+
borderLeftStyle: 'solid',
|
|
5500
|
+
borderLeftColor: 'var(--color-base-300)',
|
|
5501
|
+
marginLeft: 'calc(var(--timeline-marker-size) / 2 - var(--border) / 2)',
|
|
5502
|
+
},
|
|
5503
|
+
'&[data-orientation="horizontal"]': {
|
|
5504
|
+
order: '1',
|
|
5505
|
+
flexGrow: '1',
|
|
5506
|
+
flexShrink: '1',
|
|
5507
|
+
flexBasis: '0',
|
|
5508
|
+
height: 'var(--border)',
|
|
5509
|
+
minWidth: 'var(--space-lg)',
|
|
5510
|
+
},
|
|
5511
|
+
},
|
|
5512
|
+
},
|
|
5513
|
+
content: {
|
|
5514
|
+
selectors: {
|
|
5515
|
+
'&[data-orientation="vertical"][data-placement="start"]': {
|
|
5516
|
+
order: '-1',
|
|
5517
|
+
flexGrow: '1',
|
|
5518
|
+
flexShrink: '1',
|
|
5519
|
+
flexBasis: '0',
|
|
5520
|
+
},
|
|
5521
|
+
'&[data-orientation="vertical"][data-placement="end"]': {
|
|
5522
|
+
flexGrow: '1',
|
|
5523
|
+
flexShrink: '1',
|
|
5524
|
+
flexBasis: '0',
|
|
5525
|
+
},
|
|
5526
|
+
'&[data-orientation="horizontal"][data-placement="start"]': {
|
|
5527
|
+
order: '-1',
|
|
5528
|
+
flexBasis: '100%',
|
|
5529
|
+
},
|
|
5530
|
+
'&[data-orientation="horizontal"][data-placement="end"]': {
|
|
5531
|
+
order: '2',
|
|
5532
|
+
flexBasis: '100%',
|
|
5533
|
+
},
|
|
5534
|
+
},
|
|
5535
|
+
},
|
|
5536
|
+
},
|
|
5537
|
+
},
|
|
5538
|
+
},
|
|
5138
5539
|
};
|
|
5139
5540
|
/** daisy chat: the classic bubble, colour-refilled per role. */
|
|
5140
5541
|
export const chat = {
|
|
@@ -6163,6 +6564,9 @@ export const drawer = {
|
|
|
6163
6564
|
*/
|
|
6164
6565
|
export const table = {
|
|
6165
6566
|
component: 'table',
|
|
6567
|
+
// A sortable header cell carries the sort state for `aria-sort`'s sake;
|
|
6568
|
+
// its trigger and indicator paint it.
|
|
6569
|
+
skipStates: { 'header-cell': ['ascending', 'descending', 'none'] },
|
|
6166
6570
|
// Public to a design system derived from this one (#73).
|
|
6167
6571
|
hooks: {
|
|
6168
6572
|
properties: {
|
|
@@ -6248,6 +6652,55 @@ export const table = {
|
|
|
6248
6652
|
},
|
|
6249
6653
|
at: tableStackAt(tokens, 'header-cell', { paddingInline: '0' }),
|
|
6250
6654
|
},
|
|
6655
|
+
// Sorting (#286): daisy's header label made a button — the sorted
|
|
6656
|
+
// column's label takes the full accent ink, and the mark beside it
|
|
6657
|
+
// turns with the direction.
|
|
6658
|
+
'sort-trigger': {
|
|
6659
|
+
base: {
|
|
6660
|
+
appearance: 'none',
|
|
6661
|
+
display: 'inline-flex',
|
|
6662
|
+
alignItems: 'center',
|
|
6663
|
+
margin: '0',
|
|
6664
|
+
border: '0',
|
|
6665
|
+
font: 'inherit',
|
|
6666
|
+
letterSpacing: 'inherit',
|
|
6667
|
+
textTransform: 'inherit',
|
|
6668
|
+
textAlign: 'inherit',
|
|
6669
|
+
cursor: 'pointer',
|
|
6670
|
+
gap: 'var(--space-2xs)',
|
|
6671
|
+
padding: '0',
|
|
6672
|
+
borderRadius: 'var(--radius-selector)',
|
|
6673
|
+
background: 'transparent',
|
|
6674
|
+
color: 'inherit',
|
|
6675
|
+
transition: 'color var(--duration-fast) var(--ease-standard)',
|
|
6676
|
+
},
|
|
6677
|
+
states: {
|
|
6678
|
+
ascending: { color: 'var(--table-accent)' },
|
|
6679
|
+
descending: { color: 'var(--table-accent)' },
|
|
6680
|
+
none: {},
|
|
6681
|
+
hover: { color: 'var(--table-accent)' },
|
|
6682
|
+
disabled: { cursor: 'not-allowed', opacity: 'var(--disabled-opacity)' },
|
|
6683
|
+
...focusRing,
|
|
6684
|
+
},
|
|
6685
|
+
at: { 'reduced-motion': { base: { transition: 'none' } } },
|
|
6686
|
+
},
|
|
6687
|
+
// zero's ▲, turned for descending; an unsorted column shows it only
|
|
6688
|
+
// under a hovered or keyboard-focused trigger.
|
|
6689
|
+
'sort-indicator': {
|
|
6690
|
+
base: {
|
|
6691
|
+
display: 'inline-block',
|
|
6692
|
+
fontSize: '0.75em',
|
|
6693
|
+
lineHeight: '1',
|
|
6694
|
+
transition: 'transform var(--duration-fast) var(--ease-standard), opacity var(--duration-fast) var(--ease-standard)',
|
|
6695
|
+
},
|
|
6696
|
+
states: {
|
|
6697
|
+
ascending: {},
|
|
6698
|
+
descending: { transform: 'rotate(180deg)' },
|
|
6699
|
+
none: { opacity: '0' },
|
|
6700
|
+
},
|
|
6701
|
+
selectors: { '[data-scope="table"][data-part="sort-trigger"]:is(:hover, [data-focus-visible]) > &[data-state="none"]': { opacity: '0.6' } },
|
|
6702
|
+
at: { 'reduced-motion': { base: { transition: 'none' } } },
|
|
6703
|
+
},
|
|
6251
6704
|
cell: {
|
|
6252
6705
|
base: {
|
|
6253
6706
|
padding: 'var(--table-pad-block) var(--table-pad-inline)',
|
|
@@ -6896,7 +7349,7 @@ export const diff = {
|
|
|
6896
7349
|
};
|
|
6897
7350
|
export const recipes = [
|
|
6898
7351
|
tabs, collapsible, switchRecipe, dialog, popover, tooltip, menu,
|
|
6899
|
-
field, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
|
|
7352
|
+
field, fieldset, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
|
|
6900
7353
|
toggle, toggleGroup, numberInput, ratingGroup, treeView, input, textarea,
|
|
6901
7354
|
card, alert, emptyState, badge, divider, skeleton, spinner,
|
|
6902
7355
|
kbd, status, indicator, stats, timeline, chat, radialProgress, join,
|