@sigx/zero-daisyui 0.4.0 → 0.6.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 +61 -1
- package/dist/audit.json +6963 -3228
- package/dist/components.d.ts +14 -2
- package/dist/components.js +1 -0
- package/dist/css/components/accordion.css +26 -2
- package/dist/css/components/alert.css +2 -0
- package/dist/css/components/avatar.css +2 -0
- package/dist/css/components/badge.css +2 -0
- package/dist/css/components/box.css +2 -0
- package/dist/css/components/breadcrumbs.css +2 -0
- package/dist/css/components/button.css +2 -0
- package/dist/css/components/card.css +2 -0
- package/dist/css/components/carousel.css +2 -0
- package/dist/css/components/center.css +2 -0
- package/dist/css/components/chat.css +2 -0
- package/dist/css/components/checkbox-group.css +98 -0
- package/dist/css/components/checkbox.css +10 -0
- package/dist/css/components/collapsible.css +16 -2
- package/dist/css/components/combobox.css +43 -1
- package/dist/css/components/container.css +2 -0
- package/dist/css/components/countdown.css +2 -0
- package/dist/css/components/dialog.css +2 -0
- package/dist/css/components/diff.css +7 -0
- package/dist/css/components/divider.css +2 -0
- package/dist/css/components/drawer.css +2 -0
- package/dist/css/components/empty-state.css +2 -0
- package/dist/css/components/field.css +2 -0
- package/dist/css/components/file-upload.css +34 -0
- package/dist/css/components/grid.css +2 -0
- package/dist/css/components/indicator.css +2 -0
- package/dist/css/components/input.css +137 -0
- package/dist/css/components/join.css +2 -0
- package/dist/css/components/kbd.css +2 -0
- package/dist/css/components/menu.css +2 -0
- package/dist/css/components/nav-list.css +2 -0
- package/dist/css/components/navbar.css +2 -0
- package/dist/css/components/number-input.css +2 -0
- package/dist/css/components/pagination.css +6 -0
- package/dist/css/components/popover.css +2 -0
- package/dist/css/components/progress.css +2 -0
- package/dist/css/components/radial-progress.css +2 -0
- package/dist/css/components/radio-group.css +10 -4
- package/dist/css/components/rating-group.css +2 -0
- package/dist/css/components/select.css +61 -1
- package/dist/css/components/skeleton.css +2 -0
- package/dist/css/components/slider.css +154 -25
- package/dist/css/components/spacer.css +2 -0
- package/dist/css/components/spinner.css +2 -0
- package/dist/css/components/stack.css +2 -0
- package/dist/css/components/stats.css +68 -16
- package/dist/css/components/status.css +2 -0
- package/dist/css/components/steps.css +2 -0
- package/dist/css/components/swap.css +2 -0
- package/dist/css/components/switch.css +23 -0
- package/dist/css/components/table.css +7 -0
- package/dist/css/components/tabs.css +15 -0
- package/dist/css/components/textarea.css +2 -0
- package/dist/css/components/timeline.css +3 -0
- package/dist/css/components/toast.css +2 -0
- package/dist/css/components/toggle-group.css +2 -0
- package/dist/css/components/toggle.css +2 -0
- package/dist/css/components/tooltip.css +2 -0
- package/dist/css/components/tree-view.css +6 -0
- package/dist/css/index.css +702 -64
- package/dist/css/tokens.css +13 -13
- package/dist/lynx/components/accordion.css +5 -0
- package/dist/lynx/components/alert.css +20 -0
- package/dist/lynx/components/button.css +5 -0
- package/dist/lynx/components/checkbox-group.css +209 -0
- package/dist/lynx/components/checkbox.css +8 -0
- package/dist/lynx/components/combobox.css +53 -0
- package/dist/lynx/components/diff.css +5 -0
- package/dist/lynx/components/drawer.css +4 -0
- package/dist/lynx/components/file-upload.css +47 -0
- package/dist/lynx/components/input.css +178 -0
- package/dist/lynx/components/number-input.css +8 -0
- package/dist/lynx/components/pagination.css +16 -0
- package/dist/lynx/components/progress.css +4 -4
- package/dist/lynx/components/radio-group.css +8 -0
- package/dist/lynx/components/rating-group.css +3 -3
- package/dist/lynx/components/select.css +65 -0
- package/dist/lynx/components/slider.css +106 -25
- package/dist/lynx/components/stats.css +146 -16
- package/dist/lynx/components/steps.css +4 -0
- package/dist/lynx/components/switch.css +4 -0
- package/dist/lynx/components/table.css +5 -0
- package/dist/lynx/components/tabs.css +13 -0
- package/dist/lynx/components/timeline.css +26 -0
- package/dist/lynx/components/toggle.css +5 -0
- package/dist/lynx/components/tree-view.css +4 -0
- package/dist/lynx/index.css +998 -94
- package/dist/lynx/manifest.json +40 -5
- package/dist/lynx/tokens.css +4 -4
- package/dist/manifest.json +38 -3
- package/dist/recipes.d.ts +7 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +658 -60
- package/dist/recipes.js.map +1 -1
- package/dist/register.d.ts +9 -0
- package/dist/report.json +627 -211
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +18 -4
- package/dist/tokens.js.map +1 -1
- package/package.json +5 -5
- package/src/recipes.ts +667 -61
- package/src/tokens.ts +18 -4
package/dist/recipes.js
CHANGED
|
@@ -140,13 +140,45 @@ const disclosurePresence = {
|
|
|
140
140
|
'&::details-content': {
|
|
141
141
|
blockSize: '0',
|
|
142
142
|
overflow: 'hidden',
|
|
143
|
+
},
|
|
144
|
+
// Open only: a close removes `open`, which hides the content before
|
|
145
|
+
// any transition could run — the panel animates that half (#276).
|
|
146
|
+
'&[open]::details-content': {
|
|
147
|
+
blockSize: 'auto',
|
|
143
148
|
transition: 'block-size var(--duration-normal) var(--ease-standard), '
|
|
144
149
|
+ 'content-visibility var(--duration-normal) allow-discrete',
|
|
145
150
|
},
|
|
146
|
-
'&[open]::details-content': { blockSize: 'auto' },
|
|
147
151
|
},
|
|
148
|
-
at: { 'reduced-motion': { selectors: { '
|
|
152
|
+
at: { 'reduced-motion': { selectors: { '&[open]::details-content': { transition: 'none' } } } },
|
|
149
153
|
};
|
|
154
|
+
/**
|
|
155
|
+
* The panel's close (#276). Removing `open` hides a `<details>` at once, so
|
|
156
|
+
* the close cannot live on `::details-content` the way the open does: zero
|
|
157
|
+
* flips the panel to `closed` first and holds the element open until the
|
|
158
|
+
* panel's own animation has played. The runtime publishes the panel's
|
|
159
|
+
* measured height as `--<scope>-panel-height`, which is the one value this
|
|
160
|
+
* needs — `auto` is not an endpoint any engine but Chromium can animate.
|
|
161
|
+
* `border-box` so the measured height (content + padding) is the whole box.
|
|
162
|
+
* Web-only: the property is written by the DOM runtime (`RUNTIME_PROPERTIES`).
|
|
163
|
+
*/
|
|
164
|
+
const disclosureExit = (scope) => ({
|
|
165
|
+
web: {
|
|
166
|
+
parts: {
|
|
167
|
+
panel: {
|
|
168
|
+
states: {
|
|
169
|
+
closed: {
|
|
170
|
+
boxSizing: 'border-box',
|
|
171
|
+
animation: `${scope}-panel-exit var(--duration-normal) var(--ease-standard)`,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
at: { 'reduced-motion': { states: { closed: { animation: 'none' } } } },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
keyframes: {
|
|
178
|
+
[`${scope}-panel-exit`]: `from { block-size: var(--${scope}-panel-height); } to { block-size: 0; padding-block: 0; }`,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
});
|
|
150
182
|
/**
|
|
151
183
|
* Merge presence into a part's own styles per KEY, not per block: a recipe
|
|
152
184
|
* that already writes `states: { open: {} }` — the "deliberately unstyled"
|
|
@@ -191,14 +223,33 @@ const withPresence = (presence, styles) => ({
|
|
|
191
223
|
*/
|
|
192
224
|
export const tabs = {
|
|
193
225
|
component: 'tabs',
|
|
226
|
+
tokens: {
|
|
227
|
+
// How far the focus ring reaches outside a tab (2px + 2px offset):
|
|
228
|
+
// the list's scroll box pads by it so the ring is never clipped (#45).
|
|
229
|
+
'--tabs-ring-room': '4px',
|
|
230
|
+
},
|
|
194
231
|
parts: {
|
|
195
232
|
root: {
|
|
196
233
|
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-lg)' },
|
|
197
234
|
},
|
|
198
235
|
list: {
|
|
236
|
+
// The row's own scroll box (#45), Pagination's and Stats' answer:
|
|
237
|
+
// a row of labels wider than its column (a phone, a wide fallback
|
|
238
|
+
// face) scrolls instead of pushing the page sideways.
|
|
239
|
+
// `max-inline-size` (border-box, so it includes the padding)
|
|
240
|
+
// gives the shrink-to-fit list a width to scroll in. The padding is room for the focus ring, which the
|
|
241
|
+
// scroll box would otherwise clip at the row's edges; the
|
|
242
|
+
// matching scroll padding keeps it clear when focus scrolls a
|
|
243
|
+
// tab into view. The box variant's own padding (space-xs, the
|
|
244
|
+
// same 4px) replaces it there.
|
|
199
245
|
base: {
|
|
200
246
|
display: 'inline-flex',
|
|
201
247
|
alignSelf: 'flex-start',
|
|
248
|
+
boxSizing: 'border-box',
|
|
249
|
+
maxInlineSize: '100%',
|
|
250
|
+
overflowX: 'auto',
|
|
251
|
+
padding: 'var(--tabs-ring-room)',
|
|
252
|
+
scrollPaddingInline: 'var(--tabs-ring-room)',
|
|
202
253
|
},
|
|
203
254
|
},
|
|
204
255
|
tab: {
|
|
@@ -236,6 +287,10 @@ export const tabs = {
|
|
|
236
287
|
...focusRing,
|
|
237
288
|
},
|
|
238
289
|
},
|
|
290
|
+
// daisy draws each flavor's active mark on the tab itself (the
|
|
291
|
+
// underline, the lift, the pill) and none of them slides, so a
|
|
292
|
+
// `Tabs.Indicator` renders nothing here (#283).
|
|
293
|
+
indicator: { base: { display: 'none' } },
|
|
239
294
|
panel: {
|
|
240
295
|
base: { fontSize: 'var(--text-md)' },
|
|
241
296
|
states: { active: {}, inactive: {} },
|
|
@@ -505,6 +560,7 @@ const disclosureSizes = {
|
|
|
505
560
|
// daisy "collapse collapse-arrow" flavor.
|
|
506
561
|
export const collapsible = {
|
|
507
562
|
component: 'collapsible',
|
|
563
|
+
targets: disclosureExit('collapsible'),
|
|
508
564
|
// Public to a design system derived from this one (#73).
|
|
509
565
|
hooks: {
|
|
510
566
|
properties: {
|
|
@@ -621,6 +677,8 @@ export const switchRecipe = {
|
|
|
621
677
|
root: {
|
|
622
678
|
base: { display: 'inline-flex', alignItems: 'center', gap: 'var(--space-lg)', cursor: 'pointer' },
|
|
623
679
|
states: {
|
|
680
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
681
|
+
readonly: { cursor: 'default' },
|
|
624
682
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
625
683
|
checked: {},
|
|
626
684
|
unchecked: {},
|
|
@@ -753,6 +811,29 @@ export const switchRecipe = {
|
|
|
753
811
|
+ '0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)',
|
|
754
812
|
transition: 'background-color var(--duration-instant) var(--ease-standard)',
|
|
755
813
|
},
|
|
814
|
+
at: {
|
|
815
|
+
// The knob is `currentColor` background plus shadows:
|
|
816
|
+
// forced colours revalue the one to Canvas and drop the
|
|
817
|
+
// others, leaving two empty pill outlines that read the
|
|
818
|
+
// same on and off (#189). Opt the knob out of forcing
|
|
819
|
+
// and paint it in system colours — CanvasText at rest,
|
|
820
|
+
// Highlight once checked. The noise and the shadows go
|
|
821
|
+
// too: unforced, they would mix the forced ink back in.
|
|
822
|
+
'forced-colors': {
|
|
823
|
+
base: {
|
|
824
|
+
forcedColorAdjust: 'none',
|
|
825
|
+
backgroundColor: 'CanvasText',
|
|
826
|
+
backgroundImage: 'none',
|
|
827
|
+
boxShadow: 'none',
|
|
828
|
+
},
|
|
829
|
+
states: { checked: { backgroundColor: 'Highlight' } },
|
|
830
|
+
// A disabled switch draws in GrayText, the forced palette's
|
|
831
|
+
// own disabled ink, not full-strength ink faded by an
|
|
832
|
+
// author opacity the user's theme cannot style. Keyed off
|
|
833
|
+
// the control's flag, which outranks `checked`.
|
|
834
|
+
selectors: { '[data-scope="switch"][data-part="control"][data-disabled] &': { backgroundColor: 'GrayText' } },
|
|
835
|
+
},
|
|
836
|
+
},
|
|
756
837
|
},
|
|
757
838
|
},
|
|
758
839
|
},
|
|
@@ -1021,6 +1102,22 @@ const floatingPanel = {
|
|
|
1021
1102
|
borderRadius: 'var(--radius-box)',
|
|
1022
1103
|
boxShadow: 'var(--shadow-lg)',
|
|
1023
1104
|
};
|
|
1105
|
+
/**
|
|
1106
|
+
* daisy's dropdown menu for a listbox, sized by the geometry the
|
|
1107
|
+
* anchored-position strategy publishes (#278): at least the width of the
|
|
1108
|
+
* control that opened it (`--anchor-width`, daisy's 13rem floor otherwise)
|
|
1109
|
+
* and never taller than the room on the side it opened to
|
|
1110
|
+
* (`--available-height`), scrolling past a 20rem cap. Border-box, so both
|
|
1111
|
+
* bounds measure the box the strategy positions. Runtime-published
|
|
1112
|
+
* properties are web-only, so this is each listbox recipe's `targets.web`;
|
|
1113
|
+
* lynx keeps the shared 13rem.
|
|
1114
|
+
*/
|
|
1115
|
+
const anchoredListbox = {
|
|
1116
|
+
boxSizing: 'border-box',
|
|
1117
|
+
minWidth: 'var(--anchor-width, 13rem)',
|
|
1118
|
+
maxHeight: 'min(20rem, var(--available-height, 20rem))',
|
|
1119
|
+
overflowY: 'auto',
|
|
1120
|
+
};
|
|
1024
1121
|
export const popover = {
|
|
1025
1122
|
component: 'popover',
|
|
1026
1123
|
parts: {
|
|
@@ -1380,6 +1477,8 @@ export const checkbox = {
|
|
|
1380
1477
|
root: {
|
|
1381
1478
|
base: { display: 'inline-flex', alignItems: 'center', gap: 'var(--space-md)', cursor: 'pointer' },
|
|
1382
1479
|
states: {
|
|
1480
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
1481
|
+
readonly: { cursor: 'default' },
|
|
1383
1482
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
1384
1483
|
checked: {}, unchecked: {}, indeterminate: {},
|
|
1385
1484
|
},
|
|
@@ -1452,6 +1551,8 @@ export const checkbox = {
|
|
|
1452
1551
|
// `invalid` is semantic, not an accent: it stays error under
|
|
1453
1552
|
// every colour variant, on purpose.
|
|
1454
1553
|
invalid: { borderColor: 'var(--color-error)' },
|
|
1554
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
1555
|
+
readonly: { cursor: 'default' },
|
|
1455
1556
|
disabled: {},
|
|
1456
1557
|
},
|
|
1457
1558
|
},
|
|
@@ -1576,13 +1677,9 @@ export const radioGroup = {
|
|
|
1576
1677
|
parts: {
|
|
1577
1678
|
root: {
|
|
1578
1679
|
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-md)' },
|
|
1680
|
+
// `invalid` paints on each `item-control`, which carries the flag
|
|
1681
|
+
// itself (#267) — the root only groups the items.
|
|
1579
1682
|
states: { invalid: {}, required: {} },
|
|
1580
|
-
selectors: {
|
|
1581
|
-
// `invalid` is a fact about the GROUP — `item-control` carries
|
|
1582
|
-
// no flag of its own — and it is semantic: it stays error
|
|
1583
|
-
// under every colour variant.
|
|
1584
|
-
'&[data-invalid] [data-part="item-control"]': { borderColor: 'var(--color-error)' },
|
|
1585
|
-
},
|
|
1586
1683
|
},
|
|
1587
1684
|
label: {
|
|
1588
1685
|
base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)' },
|
|
@@ -1591,6 +1688,8 @@ export const radioGroup = {
|
|
|
1591
1688
|
item: {
|
|
1592
1689
|
base: { display: 'inline-flex', alignItems: 'center', gap: 'var(--space-md)', cursor: 'pointer' },
|
|
1593
1690
|
states: {
|
|
1691
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
1692
|
+
readonly: { cursor: 'default' },
|
|
1594
1693
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
1595
1694
|
checked: {}, unchecked: {},
|
|
1596
1695
|
},
|
|
@@ -1625,6 +1724,10 @@ export const radioGroup = {
|
|
|
1625
1724
|
animation: 'var(--duration-fast) var(--ease-standard) zero-daisy-radio',
|
|
1626
1725
|
},
|
|
1627
1726
|
unchecked: {},
|
|
1727
|
+
// `invalid` is semantic: it stays error under every colour
|
|
1728
|
+
// variant, and after `checked` so it holds whichever radio is
|
|
1729
|
+
// chosen.
|
|
1730
|
+
invalid: { borderColor: 'var(--color-error)' },
|
|
1628
1731
|
'focus-visible': { outline: '2px solid var(--radio-accent)', outlineOffset: '2px' },
|
|
1629
1732
|
disabled: {},
|
|
1630
1733
|
},
|
|
@@ -1695,6 +1798,47 @@ export const radioGroup = {
|
|
|
1695
1798
|
},
|
|
1696
1799
|
},
|
|
1697
1800
|
};
|
|
1801
|
+
/**
|
|
1802
|
+
* CheckboxGroup (#282) — daisy has no group component; its forms stack
|
|
1803
|
+
* `.checkbox` rows under a `fieldset-legend`. The boxes keep the checkbox
|
|
1804
|
+
* recipe; the group owns the stack and the legend, which speaks Field's
|
|
1805
|
+
* label (colour through `roleInk`, #210).
|
|
1806
|
+
*/
|
|
1807
|
+
export const checkboxGroup = {
|
|
1808
|
+
component: 'checkbox-group',
|
|
1809
|
+
tokens: { '--checkbox-group-accent': 'var(--color-base-content)' },
|
|
1810
|
+
parts: {
|
|
1811
|
+
root: {
|
|
1812
|
+
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-md)' },
|
|
1813
|
+
// `invalid` and `readonly` paint on each box — the root only
|
|
1814
|
+
// lays the boxes out.
|
|
1815
|
+
states: { invalid: {}, required: {}, readonly: {} },
|
|
1816
|
+
selectors: {
|
|
1817
|
+
'&[data-orientation="horizontal"]': { flexDirection: 'row', flexWrap: 'wrap', columnGap: 'var(--space-lg)' },
|
|
1818
|
+
},
|
|
1819
|
+
},
|
|
1820
|
+
label: {
|
|
1821
|
+
base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)', color: 'var(--checkbox-group-accent)' },
|
|
1822
|
+
states: { disabled: { opacity: 'var(--disabled-opacity)' } },
|
|
1823
|
+
selectors: {
|
|
1824
|
+
'&[data-required]::after': { content: '" *"', color: roleInk('error') },
|
|
1825
|
+
},
|
|
1826
|
+
},
|
|
1827
|
+
},
|
|
1828
|
+
variants: {
|
|
1829
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
1830
|
+
'--checkbox-group-accent': roleInk(c),
|
|
1831
|
+
} } }])),
|
|
1832
|
+
size: {
|
|
1833
|
+
xs: { root: { base: { gap: 'var(--space-sm)' } }, label: { base: { fontSize: 'var(--text-xs)' } } },
|
|
1834
|
+
sm: { label: { base: { fontSize: 'var(--text-xs)' } } },
|
|
1835
|
+
md: {},
|
|
1836
|
+
lg: { label: { base: { fontSize: 'var(--text-md)' } } },
|
|
1837
|
+
xl: { root: { base: { gap: 'var(--space-lg)' } }, label: { base: { fontSize: 'var(--text-lg)' } } },
|
|
1838
|
+
},
|
|
1839
|
+
},
|
|
1840
|
+
skipStates: { label: ['invalid'] },
|
|
1841
|
+
};
|
|
1698
1842
|
// --------------------------------------------------------------------------
|
|
1699
1843
|
// 4. progress — replace the whole export (only the two radius lines move)
|
|
1700
1844
|
// --------------------------------------------------------------------------
|
|
@@ -1816,7 +1960,15 @@ export const progress = {
|
|
|
1816
1960
|
};
|
|
1817
1961
|
export const slider = {
|
|
1818
1962
|
component: 'slider',
|
|
1819
|
-
|
|
1963
|
+
// daisy's `.range` metrics: the thumb is `--range-thumb-size`
|
|
1964
|
+
// (×6 of `--size-selector` at md, ×4…×8 across the ramp — the same
|
|
1965
|
+
// selector ramp checkbox rides) and the track is half of it. Both the
|
|
1966
|
+
// native control and the composed parts read these.
|
|
1967
|
+
tokens: {
|
|
1968
|
+
'--slider-accent': 'var(--color-primary)',
|
|
1969
|
+
'--slider-track-size': 'calc(var(--size-selector) * 3)',
|
|
1970
|
+
'--slider-thumb-size': 'calc(var(--size-selector) * 6)',
|
|
1971
|
+
},
|
|
1820
1972
|
parts: {
|
|
1821
1973
|
root: {
|
|
1822
1974
|
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-sm)', width: '100%' },
|
|
@@ -1826,30 +1978,106 @@ export const slider = {
|
|
|
1826
1978
|
base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)' },
|
|
1827
1979
|
states: { disabled: {} },
|
|
1828
1980
|
},
|
|
1981
|
+
// A custom skin (`appearance: none`), ported from daisyUI 5's
|
|
1982
|
+
// `range.css`: a `--radius-selector` track half the thumb's height,
|
|
1983
|
+
// and daisy's real range thumb — a `base-100` knob inside a
|
|
1984
|
+
// `.25rem` ring of the fill ink (daisy's `--range-p`), carrying the
|
|
1985
|
+
// same `--depth` inset shading the checkbox and radio wear. daisy
|
|
1986
|
+
// throws the elapsed fill as a 100cqw box-shadow off the thumb and
|
|
1987
|
+
// clips it with `overflow: hidden`; here the track reads the
|
|
1988
|
+
// runtime-published `--slider-percent` as a gradient stop instead,
|
|
1989
|
+
// as every other skin does. The fill is deepened toward
|
|
1990
|
+
// `base-content` the way `progressFill` deepens daisy's bar — a raw
|
|
1991
|
+
// role on the base-300 rail is exactly the #210/#228 bug class.
|
|
1992
|
+
//
|
|
1993
|
+
// The rebuild is also the correctness move (see the kit skill): Blink
|
|
1994
|
+
// treats range inputs as ALWAYS `:focus-visible`, even on mouse
|
|
1995
|
+
// focus, so the base kills the native outline and the
|
|
1996
|
+
// `focus-visible` state — compiled to the runtime's keyboard-only
|
|
1997
|
+
// `[data-focus-visible]` flag — draws the ring only when it means it.
|
|
1829
1998
|
control: {
|
|
1830
|
-
base: {
|
|
1999
|
+
base: {
|
|
2000
|
+
appearance: 'none',
|
|
2001
|
+
width: '100%',
|
|
2002
|
+
height: 'var(--slider-thumb-size)',
|
|
2003
|
+
margin: '0',
|
|
2004
|
+
background: 'transparent',
|
|
2005
|
+
cursor: 'pointer',
|
|
2006
|
+
outline: 'none',
|
|
2007
|
+
// Kept for the forced-colors fallback below, where native
|
|
2008
|
+
// rendering takes over and still honours the accent.
|
|
2009
|
+
accentColor: 'var(--slider-accent)',
|
|
2010
|
+
// `--slider-fill` and `--slider-track` live in targets.web
|
|
2011
|
+
// below: the fill is a color-mix() the lynx emitter drops,
|
|
2012
|
+
// and the track reads the runtime-published
|
|
2013
|
+
// `--slider-percent`, a web-only mechanism.
|
|
2014
|
+
},
|
|
1831
2015
|
states: {
|
|
2016
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
2017
|
+
readonly: { cursor: 'default' },
|
|
1832
2018
|
disabled: { cursor: 'not-allowed' },
|
|
1833
2019
|
'focus-visible': { outline: '2px solid var(--slider-accent)', outlineOffset: '2px' },
|
|
1834
2020
|
// `invalid` is semantic: it stays error under every colour
|
|
1835
|
-
// variant, on purpose
|
|
1836
|
-
|
|
2021
|
+
// variant, on purpose — the accent indirection swaps the
|
|
2022
|
+
// fill, the thumb ring, the focus ring and the forced-colors
|
|
2023
|
+
// fallback together.
|
|
2024
|
+
invalid: { '--slider-accent': 'var(--color-error)' },
|
|
2025
|
+
},
|
|
2026
|
+
selectors: {
|
|
2027
|
+
// Vendor pseudos cannot share a selector list — one unknown
|
|
2028
|
+
// selector invalidates the whole rule — so each engine gets
|
|
2029
|
+
// its own copy reading the shared custom properties.
|
|
2030
|
+
'&::-webkit-slider-runnable-track': {
|
|
2031
|
+
height: 'var(--slider-track-size)',
|
|
2032
|
+
borderRadius: 'var(--radius-selector)',
|
|
2033
|
+
background: 'var(--slider-track)',
|
|
2034
|
+
},
|
|
2035
|
+
'&::-webkit-slider-thumb': {
|
|
2036
|
+
appearance: 'none',
|
|
2037
|
+
boxSizing: 'border-box',
|
|
2038
|
+
width: 'var(--slider-thumb-size)',
|
|
2039
|
+
height: 'var(--slider-thumb-size)',
|
|
2040
|
+
marginTop: 'calc((var(--slider-track-size) - var(--slider-thumb-size)) / 2)',
|
|
2041
|
+
borderRadius: 'calc(var(--radius-selector) + min(0.25rem, var(--radius-selector) * 3))',
|
|
2042
|
+
border: '0.25rem solid var(--slider-fill)',
|
|
2043
|
+
background: 'var(--color-base-100)',
|
|
2044
|
+
boxShadow: '0 -1px var(--depth-shade) inset, 0 8px 0 -4px var(--depth-sheen) inset',
|
|
2045
|
+
},
|
|
2046
|
+
'&::-moz-range-track': {
|
|
2047
|
+
height: 'var(--slider-track-size)',
|
|
2048
|
+
borderRadius: 'var(--radius-selector)',
|
|
2049
|
+
background: 'var(--slider-track)',
|
|
2050
|
+
},
|
|
2051
|
+
'&::-moz-range-thumb': {
|
|
2052
|
+
boxSizing: 'border-box',
|
|
2053
|
+
width: 'var(--slider-thumb-size)',
|
|
2054
|
+
height: 'var(--slider-thumb-size)',
|
|
2055
|
+
borderRadius: 'calc(var(--radius-selector) + min(0.25rem, var(--radius-selector) * 3))',
|
|
2056
|
+
border: '0.25rem solid var(--slider-fill)',
|
|
2057
|
+
background: 'var(--color-base-100)',
|
|
2058
|
+
boxShadow: '0 -1px var(--depth-shade) inset, 0 8px 0 -4px var(--depth-sheen) inset',
|
|
2059
|
+
},
|
|
2060
|
+
},
|
|
2061
|
+
at: {
|
|
2062
|
+
// Native rendering knows forced colors better than a custom
|
|
2063
|
+
// skin; the retained accentColor keeps the fallback branded.
|
|
2064
|
+
'forced-colors': { base: { appearance: 'auto' } },
|
|
1837
2065
|
},
|
|
1838
2066
|
},
|
|
1839
|
-
// The composed range projection (#325)
|
|
1840
|
-
//
|
|
1841
|
-
//
|
|
1842
|
-
// `base-content` the way `progressFill` deepens daisy's bar — a raw
|
|
1843
|
-
// role on the base-300 rail is exactly the #210/#228 bug class.
|
|
2067
|
+
// The composed range projection (#325): the same metrics as the
|
|
2068
|
+
// native control above, restating its accent as real boxes. The
|
|
2069
|
+
// fill is the same deepened ink the native track paints.
|
|
1844
2070
|
track: {
|
|
1845
2071
|
base: {
|
|
1846
|
-
height: '
|
|
1847
|
-
|
|
2072
|
+
height: 'var(--slider-track-size)',
|
|
2073
|
+
// Reserve the thumb's overhang so the row's box matches the
|
|
2074
|
+
// native control's.
|
|
2075
|
+
marginBlock: 'calc((var(--slider-thumb-size) - var(--slider-track-size)) / 2)',
|
|
1848
2076
|
borderRadius: '9999px',
|
|
1849
2077
|
background: 'var(--color-base-300)',
|
|
1850
2078
|
cursor: 'pointer',
|
|
1851
2079
|
},
|
|
1852
|
-
states: { disabled: { cursor: 'not-allowed' } },
|
|
2080
|
+
states: { readonly: { cursor: 'default' }, disabled: { cursor: 'not-allowed' } },
|
|
1853
2081
|
},
|
|
1854
2082
|
range: {
|
|
1855
2083
|
base: {
|
|
@@ -1862,11 +2090,11 @@ export const slider = {
|
|
|
1862
2090
|
thumb: {
|
|
1863
2091
|
base: {
|
|
1864
2092
|
boxSizing: 'border-box',
|
|
1865
|
-
width: '
|
|
1866
|
-
height: '
|
|
2093
|
+
width: 'var(--slider-thumb-size)',
|
|
2094
|
+
height: 'var(--slider-thumb-size)',
|
|
1867
2095
|
insetBlockStart: '50%',
|
|
1868
2096
|
translate: '0 -50%',
|
|
1869
|
-
marginInlineStart: 'calc(var(--size
|
|
2097
|
+
marginInlineStart: 'calc(var(--slider-thumb-size) / -2)',
|
|
1870
2098
|
borderRadius: '9999px',
|
|
1871
2099
|
background: 'color-mix(in oklab, var(--slider-accent) 90%, var(--color-base-content))',
|
|
1872
2100
|
cursor: 'pointer',
|
|
@@ -1876,12 +2104,14 @@ export const slider = {
|
|
|
1876
2104
|
states: {
|
|
1877
2105
|
'focus-visible': { outline: '2px solid var(--slider-accent)', outlineOffset: '2px' },
|
|
1878
2106
|
pressed: {},
|
|
2107
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
2108
|
+
readonly: { cursor: 'default' },
|
|
1879
2109
|
disabled: { cursor: 'not-allowed' },
|
|
1880
2110
|
},
|
|
1881
2111
|
},
|
|
1882
2112
|
mark: {
|
|
1883
2113
|
base: {
|
|
1884
|
-
paddingBlockStart: 'calc(var(--size
|
|
2114
|
+
paddingBlockStart: 'calc(var(--slider-track-size) + var(--space-2xs))',
|
|
1885
2115
|
fontSize: 'var(--text-xs)',
|
|
1886
2116
|
lineHeight: '1',
|
|
1887
2117
|
whiteSpace: 'nowrap',
|
|
@@ -1895,7 +2125,7 @@ export const slider = {
|
|
|
1895
2125
|
insetBlockStart: '0',
|
|
1896
2126
|
insetInlineStart: '-1px',
|
|
1897
2127
|
width: '2px',
|
|
1898
|
-
height: '
|
|
2128
|
+
height: 'var(--slider-track-size)',
|
|
1899
2129
|
background: 'var(--color-base-content)',
|
|
1900
2130
|
},
|
|
1901
2131
|
},
|
|
@@ -1908,33 +2138,133 @@ export const slider = {
|
|
|
1908
2138
|
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
1909
2139
|
'--slider-accent': `var(--color-${c})`,
|
|
1910
2140
|
} } }])),
|
|
1911
|
-
//
|
|
1912
|
-
//
|
|
2141
|
+
// daisy's `.range-{xs…xl}` ramp: `--range-thumb-size` at ×4…×8 of
|
|
2142
|
+
// `--size-selector` (1 / 1.25 / 1.5 / 1.75 / 2rem), the track half
|
|
2143
|
+
// of it — so every step moves the whole widget; the un-attributed
|
|
2144
|
+
// (and `md`) render takes the middle step from `tokens:`.
|
|
1913
2145
|
size: {
|
|
1914
|
-
xs: {
|
|
1915
|
-
sm: {
|
|
2146
|
+
xs: { root: { base: { '--slider-track-size': 'calc(var(--size-selector) * 2)', '--slider-thumb-size': 'calc(var(--size-selector) * 4)' } }, label: { base: { fontSize: 'var(--text-xs)' } } },
|
|
2147
|
+
sm: { root: { base: { '--slider-track-size': 'calc(var(--size-selector) * 2.5)', '--slider-thumb-size': 'calc(var(--size-selector) * 5)' } }, label: { base: { fontSize: 'var(--text-sm)' } } },
|
|
2148
|
+
// `md`'s metrics ARE the `tokens:` defaults — one source of truth.
|
|
1916
2149
|
md: { label: { base: { fontSize: 'var(--text-sm)' } } },
|
|
1917
|
-
lg: {
|
|
1918
|
-
xl: {
|
|
2150
|
+
lg: { root: { base: { '--slider-track-size': 'calc(var(--size-selector) * 3.5)', '--slider-thumb-size': 'calc(var(--size-selector) * 7)' } }, label: { base: { fontSize: 'var(--text-md)' } } },
|
|
2151
|
+
xl: { root: { base: { '--slider-track-size': 'calc(var(--size-selector) * 4)', '--slider-thumb-size': 'calc(var(--size-selector) * 8)' } }, label: { base: { fontSize: 'var(--text-lg)' } } },
|
|
1919
2152
|
},
|
|
1920
2153
|
},
|
|
1921
|
-
// Base equals the default — `tokens:` binds the primary accent
|
|
1922
|
-
// only restates the label font-size the
|
|
1923
|
-
//
|
|
2154
|
+
// Base equals the default — `tokens:` binds the primary accent and the
|
|
2155
|
+
// middle-step metrics, and `md` only restates the label font-size the
|
|
2156
|
+
// base already declares — so the twins restate it;
|
|
1924
2157
|
// declared for the manifest (signalxjs/lynx#1070).
|
|
1925
2158
|
defaultVariants: { color: 'primary', size: 'md' },
|
|
1926
2159
|
skipStates: { root: ['invalid', 'focus-visible'] },
|
|
1927
2160
|
// ONE design decision, two paints. On the web the accent reaches the
|
|
1928
|
-
// screen twice
|
|
1929
|
-
//
|
|
1930
|
-
// renders neither
|
|
1931
|
-
// `<input type=range
|
|
1932
|
-
//
|
|
1933
|
-
//
|
|
1934
|
-
//
|
|
1935
|
-
//
|
|
1936
|
-
//
|
|
2161
|
+
// screen twice, both times deepened with `color-mix()`: the native
|
|
2162
|
+
// control's gradient track and thumb ring (via `--slider-fill`, below),
|
|
2163
|
+
// and the composed range/thumb. Lynx renders neither — it has no native
|
|
2164
|
+
// `<input type=range>`, and the emitter drops `color-mix()` as
|
|
2165
|
+
// unresolvable — so the compiled range and thumb once shipped with no
|
|
2166
|
+
// paint at all: grey track, invisible fill, invisible knob (measured on
|
|
2167
|
+
// device, signalxjs/lynx#1075). The geometry all survives (the sizes are
|
|
2168
|
+
// direct calc() over the metric tokens); paint is the only gap, so the
|
|
2169
|
+
// lynx section only restates paint, in lynx-expressible spellings, plus
|
|
2170
|
+
// the physical restatement of the logical geometry.
|
|
1937
2171
|
targets: {
|
|
2172
|
+
web: {
|
|
2173
|
+
parts: {
|
|
2174
|
+
control: {
|
|
2175
|
+
base: {
|
|
2176
|
+
// The fill ink, deepened like the composed range.
|
|
2177
|
+
'--slider-fill': 'color-mix(in oklab, var(--slider-accent) 90%, var(--color-base-content))',
|
|
2178
|
+
// The filled track reads the runtime-published
|
|
2179
|
+
// `--slider-percent` — web-only (`RUNTIME_PROPERTIES`);
|
|
2180
|
+
// the lynx runtime paints its own track/range parts.
|
|
2181
|
+
'--slider-track': 'linear-gradient(to right, var(--slider-fill) var(--slider-percent, 50%), var(--color-base-300) 0)',
|
|
2182
|
+
},
|
|
2183
|
+
selectors: {
|
|
2184
|
+
// A vertical native range is spelled through writing
|
|
2185
|
+
// mode by the runtime (#170), so the long axis is the
|
|
2186
|
+
// box's HEIGHT and the channel's WIDTH; the fill
|
|
2187
|
+
// grows from the foot.
|
|
2188
|
+
'&[data-orientation="vertical"]': {
|
|
2189
|
+
width: 'var(--slider-thumb-size)',
|
|
2190
|
+
height: 'var(--slider-length)',
|
|
2191
|
+
'--slider-track': 'linear-gradient(to top, var(--slider-fill) var(--slider-percent, 50%), var(--color-base-300) 0)',
|
|
2192
|
+
},
|
|
2193
|
+
'&[data-orientation="vertical"]::-webkit-slider-runnable-track': {
|
|
2194
|
+
width: 'var(--slider-track-size)',
|
|
2195
|
+
height: 'auto',
|
|
2196
|
+
},
|
|
2197
|
+
// Blink lays the thumb against the channel's block
|
|
2198
|
+
// axis — in vertical-lr that is its left edge.
|
|
2199
|
+
'&[data-orientation="vertical"]::-webkit-slider-thumb': {
|
|
2200
|
+
marginTop: '0',
|
|
2201
|
+
marginBlockStart: 'calc((var(--slider-track-size) - var(--slider-thumb-size)) / 2)',
|
|
2202
|
+
},
|
|
2203
|
+
'&[data-orientation="vertical"]::-moz-range-track': {
|
|
2204
|
+
width: 'var(--slider-track-size)',
|
|
2205
|
+
height: '100%',
|
|
2206
|
+
},
|
|
2207
|
+
},
|
|
2208
|
+
},
|
|
2209
|
+
// Vertical (#170): the runtime positions the moving parts by
|
|
2210
|
+
// physical `bottom` percents; these turn the channel upright and
|
|
2211
|
+
// re-centre the knob on the other axis. Web-only — the lynx
|
|
2212
|
+
// runtime ships no vertical slider.
|
|
2213
|
+
root: {
|
|
2214
|
+
selectors: {
|
|
2215
|
+
'&[data-orientation="vertical"]': {
|
|
2216
|
+
width: 'auto',
|
|
2217
|
+
alignItems: 'center',
|
|
2218
|
+
'--slider-length': 'calc(var(--size-selector) * 40)',
|
|
2219
|
+
},
|
|
2220
|
+
},
|
|
2221
|
+
},
|
|
2222
|
+
track: {
|
|
2223
|
+
selectors: {
|
|
2224
|
+
'&[data-orientation="vertical"]': {
|
|
2225
|
+
width: 'var(--slider-track-size)',
|
|
2226
|
+
height: 'var(--slider-length)',
|
|
2227
|
+
marginBlock: '0',
|
|
2228
|
+
marginInline: 'calc((var(--slider-thumb-size) - var(--slider-track-size)) / 2)',
|
|
2229
|
+
},
|
|
2230
|
+
},
|
|
2231
|
+
},
|
|
2232
|
+
range: {
|
|
2233
|
+
selectors: {
|
|
2234
|
+
'&[data-orientation="vertical"]': { insetInlineStart: '0', width: '100%' },
|
|
2235
|
+
},
|
|
2236
|
+
},
|
|
2237
|
+
thumb: {
|
|
2238
|
+
selectors: {
|
|
2239
|
+
'&[data-orientation="vertical"]': {
|
|
2240
|
+
insetBlockStart: 'auto',
|
|
2241
|
+
insetInlineStart: '50%',
|
|
2242
|
+
translate: 'none',
|
|
2243
|
+
marginInlineStart: 'calc(var(--slider-thumb-size) / -2)',
|
|
2244
|
+
marginBlockEnd: 'calc(var(--slider-thumb-size) / -2)',
|
|
2245
|
+
},
|
|
2246
|
+
},
|
|
2247
|
+
},
|
|
2248
|
+
// The label sits beside the channel, centred on its tick.
|
|
2249
|
+
mark: {
|
|
2250
|
+
selectors: {
|
|
2251
|
+
'&[data-orientation="vertical"]': {
|
|
2252
|
+
insetInlineStart: '0',
|
|
2253
|
+
paddingBlockStart: '0',
|
|
2254
|
+
paddingInlineStart: 'calc(var(--slider-track-size) + var(--space-2xs))',
|
|
2255
|
+
translate: '0 50%',
|
|
2256
|
+
},
|
|
2257
|
+
'&[data-orientation="vertical"]::before': {
|
|
2258
|
+
insetBlockStart: '50%',
|
|
2259
|
+
insetInlineStart: '0',
|
|
2260
|
+
width: 'var(--slider-track-size)',
|
|
2261
|
+
height: '2px',
|
|
2262
|
+
translate: '0 -50%',
|
|
2263
|
+
},
|
|
2264
|
+
},
|
|
2265
|
+
},
|
|
2266
|
+
},
|
|
2267
|
+
},
|
|
1938
2268
|
lynx: {
|
|
1939
2269
|
parts: {
|
|
1940
2270
|
track: {
|
|
@@ -1944,14 +2274,14 @@ export const slider = {
|
|
|
1944
2274
|
// the emitter refuses them. Physical is the lynx
|
|
1945
2275
|
// target's norm — no RTL flow there.
|
|
1946
2276
|
base: {
|
|
1947
|
-
marginTop: 'calc(var(--size-
|
|
1948
|
-
marginBottom: 'calc(var(--size-
|
|
2277
|
+
marginTop: 'calc((var(--slider-thumb-size) - var(--slider-track-size)) / 2)',
|
|
2278
|
+
marginBottom: 'calc((var(--slider-thumb-size) - var(--slider-track-size)) / 2)',
|
|
1949
2279
|
},
|
|
1950
2280
|
},
|
|
1951
2281
|
mark: {
|
|
1952
2282
|
// Same #1084 verdict: the tick labels hang below the
|
|
1953
2283
|
// track off a physical padding.
|
|
1954
|
-
base: { paddingTop: 'calc(var(--size
|
|
2284
|
+
base: { paddingTop: 'calc(var(--slider-track-size) + var(--space-2xs))' },
|
|
1955
2285
|
},
|
|
1956
2286
|
range: {
|
|
1957
2287
|
// The web fill deepens the accent 90/10 toward
|
|
@@ -1989,7 +2319,7 @@ export const slider = {
|
|
|
1989
2319
|
border: '0.25rem solid var(--slider-accent)',
|
|
1990
2320
|
top: '50%',
|
|
1991
2321
|
transform: 'translateY(-50%)',
|
|
1992
|
-
marginLeft: 'calc(var(--size
|
|
2322
|
+
marginLeft: 'calc(var(--slider-thumb-size) / -2)',
|
|
1993
2323
|
},
|
|
1994
2324
|
},
|
|
1995
2325
|
},
|
|
@@ -1998,12 +2328,15 @@ export const slider = {
|
|
|
1998
2328
|
};
|
|
1999
2329
|
export const accordion = {
|
|
2000
2330
|
component: 'accordion',
|
|
2331
|
+
targets: disclosureExit('accordion'),
|
|
2001
2332
|
// Same accent grammar as collapsible: the open heading's ink, defaulting
|
|
2002
2333
|
// to base-content so the un-attributed render is unchanged.
|
|
2003
2334
|
tokens: { '--accordion-accent': 'var(--color-base-content)' },
|
|
2004
2335
|
parts: {
|
|
2005
2336
|
root: {
|
|
2006
2337
|
base: { display: 'flex', flexDirection: 'column', gap: 'var(--space-md)' },
|
|
2338
|
+
// `orientation="horizontal"` (#276): the cards sit in a row.
|
|
2339
|
+
selectors: { '&[data-orientation="horizontal"]': { flexDirection: 'row', alignItems: 'start' } },
|
|
2007
2340
|
},
|
|
2008
2341
|
item: withPresence(disclosurePresence, {
|
|
2009
2342
|
base: {
|
|
@@ -2013,6 +2346,7 @@ export const accordion = {
|
|
|
2013
2346
|
overflow: 'hidden',
|
|
2014
2347
|
},
|
|
2015
2348
|
states: { open: {}, closed: {} },
|
|
2349
|
+
selectors: { '[data-scope="accordion"][data-part="root"][data-orientation="horizontal"] > &': { flex: '1 1 0', minInlineSize: '0' } },
|
|
2016
2350
|
}),
|
|
2017
2351
|
trigger: {
|
|
2018
2352
|
base: {
|
|
@@ -2095,14 +2429,20 @@ const fieldControl = {
|
|
|
2095
2429
|
borderRadius: 'var(--radius-field)',
|
|
2096
2430
|
boxShadow: 'var(--shadow-xs)',
|
|
2097
2431
|
};
|
|
2432
|
+
/**
|
|
2433
|
+
* A select whose clear-trigger is rendered (#280) — anchored on the root, so
|
|
2434
|
+
* a part inside the trigger can make room for the button laid over it.
|
|
2435
|
+
*/
|
|
2436
|
+
const SELECT_CLEARABLE = '[data-scope="select"][data-part="root"]:has(> [data-scope="select"][data-part="clear-trigger"]) &';
|
|
2098
2437
|
export const select = {
|
|
2099
2438
|
component: 'select',
|
|
2100
2439
|
// Accent as text/border ink only — daisy's highlighted item is a neutral
|
|
2101
2440
|
// base-200 wash, so no `-on-accent` content colour is consumed here.
|
|
2102
2441
|
tokens: { '--select-accent': 'var(--color-primary)' },
|
|
2103
2442
|
parts: {
|
|
2443
|
+
// Positioned: the clear-trigger sits over the trigger's inline end.
|
|
2104
2444
|
root: {
|
|
2105
|
-
base: { display: 'inline-flex', flexDirection: 'column' },
|
|
2445
|
+
base: { display: 'inline-flex', flexDirection: 'column', position: 'relative' },
|
|
2106
2446
|
},
|
|
2107
2447
|
trigger: {
|
|
2108
2448
|
base: {
|
|
@@ -2115,6 +2455,8 @@ export const select = {
|
|
|
2115
2455
|
},
|
|
2116
2456
|
states: {
|
|
2117
2457
|
hover: { borderColor: 'var(--color-base-content)' },
|
|
2458
|
+
// Readonly answers to nothing, so it does not invite a click.
|
|
2459
|
+
readonly: { cursor: 'default' },
|
|
2118
2460
|
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
2119
2461
|
open: { borderColor: 'var(--select-accent)' },
|
|
2120
2462
|
closed: {},
|
|
@@ -2151,6 +2493,41 @@ export const select = {
|
|
|
2151
2493
|
indicator: {
|
|
2152
2494
|
base: { opacity: '0.6', transition: 'transform var(--duration-normal) var(--ease-standard)' },
|
|
2153
2495
|
states: { open: { transform: 'rotate(180deg)' }, closed: {} },
|
|
2496
|
+
// Room for the clear-trigger laid over the field (#280).
|
|
2497
|
+
selectors: { [SELECT_CLEARABLE]: { marginInlineStart: 'calc(var(--space-2xl) + var(--space-sm))' } },
|
|
2498
|
+
},
|
|
2499
|
+
// Clears the selection (#280): daisy's ghost-btn over the field's
|
|
2500
|
+
// inline end, just before the chevron — muted like it, the base-200
|
|
2501
|
+
// wash on hover, the base-content ring on focus.
|
|
2502
|
+
'clear-trigger': {
|
|
2503
|
+
base: {
|
|
2504
|
+
appearance: 'none',
|
|
2505
|
+
position: 'absolute',
|
|
2506
|
+
insetBlock: '0',
|
|
2507
|
+
// Centred in the field at WCAG 2.5.8's 24px target, shrinking
|
|
2508
|
+
// only with a field too short to hold it.
|
|
2509
|
+
marginBlock: 'auto',
|
|
2510
|
+
blockSize: 'min(1.5rem, 100%)',
|
|
2511
|
+
minInlineSize: '1.5rem',
|
|
2512
|
+
insetInlineEnd: 'calc(var(--size-field) * 4 + 1em + var(--space-xs))',
|
|
2513
|
+
display: 'inline-flex',
|
|
2514
|
+
alignItems: 'center',
|
|
2515
|
+
justifyContent: 'center',
|
|
2516
|
+
padding: '0 var(--space-xs)',
|
|
2517
|
+
border: 'none',
|
|
2518
|
+
background: 'transparent',
|
|
2519
|
+
color: 'color-mix(in oklab, var(--color-base-content) 70%, transparent)',
|
|
2520
|
+
font: 'inherit',
|
|
2521
|
+
fontSize: 'var(--text-sm)',
|
|
2522
|
+
lineHeight: 'var(--leading-none)',
|
|
2523
|
+
borderRadius: 'var(--radius-field)',
|
|
2524
|
+
cursor: 'pointer',
|
|
2525
|
+
transition: 'background var(--duration-fast) var(--ease-standard)',
|
|
2526
|
+
},
|
|
2527
|
+
states: {
|
|
2528
|
+
hover: { background: 'var(--color-base-200)', color: 'var(--color-base-content)' },
|
|
2529
|
+
...focusRing,
|
|
2530
|
+
},
|
|
2154
2531
|
},
|
|
2155
2532
|
popup: withPresence(popupPresence('translateY(-4px)'), {
|
|
2156
2533
|
base: { ...floatingPanel, padding: 'var(--space-md)', minWidth: '13rem' },
|
|
@@ -2166,6 +2543,16 @@ export const select = {
|
|
|
2166
2543
|
opacity: '0.6',
|
|
2167
2544
|
},
|
|
2168
2545
|
},
|
|
2546
|
+
// A windowed group's heading (#127): the same overline as the label,
|
|
2547
|
+
// as a row of the flat window instead of inside a `group`.
|
|
2548
|
+
'group-heading': {
|
|
2549
|
+
base: {
|
|
2550
|
+
padding: 'var(--space-md) var(--space-lg) var(--space-xs)',
|
|
2551
|
+
fontSize: 'var(--text-xs)',
|
|
2552
|
+
fontWeight: 'var(--weight-bold)',
|
|
2553
|
+
opacity: '0.6',
|
|
2554
|
+
},
|
|
2555
|
+
},
|
|
2169
2556
|
item: {
|
|
2170
2557
|
base: {
|
|
2171
2558
|
display: 'flex',
|
|
@@ -2189,6 +2576,14 @@ export const select = {
|
|
|
2189
2576
|
base: { fontSize: 'var(--text-xs)', color: 'var(--select-accent)' },
|
|
2190
2577
|
states: { selected: {} },
|
|
2191
2578
|
},
|
|
2579
|
+
// The menu's rule between runs of options (#280).
|
|
2580
|
+
separator: {
|
|
2581
|
+
base: {
|
|
2582
|
+
height: 'var(--border)',
|
|
2583
|
+
margin: 'var(--space-sm) var(--space-md)',
|
|
2584
|
+
background: 'var(--color-base-300)',
|
|
2585
|
+
},
|
|
2586
|
+
},
|
|
2192
2587
|
},
|
|
2193
2588
|
variants: {
|
|
2194
2589
|
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
@@ -2211,8 +2606,12 @@ export const select = {
|
|
|
2211
2606
|
// The trigger ramp restates `md` too (unlike `btnSizes`), so its lynx
|
|
2212
2607
|
// counterpart does as well.
|
|
2213
2608
|
targets: {
|
|
2609
|
+
web: { parts: { popup: { base: anchoredListbox } } },
|
|
2214
2610
|
lynx: {
|
|
2215
|
-
parts: {
|
|
2611
|
+
parts: {
|
|
2612
|
+
trigger: { base: lynxBtnPad(4) },
|
|
2613
|
+
'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
|
+
},
|
|
2216
2615
|
variants: {
|
|
2217
2616
|
size: { ...lynxBtnSizes('trigger'), md: { trigger: { base: lynxBtnPad(4) } } },
|
|
2218
2617
|
},
|
|
@@ -2835,6 +3234,23 @@ export const combobox = {
|
|
|
2835
3234
|
disabled: { cursor: 'not-allowed' },
|
|
2836
3235
|
},
|
|
2837
3236
|
},
|
|
3237
|
+
// Clears the value and the text (#280): the chevron's ghost, one
|
|
3238
|
+
// step before it — muted, full ink on hover.
|
|
3239
|
+
'clear-trigger': {
|
|
3240
|
+
base: {
|
|
3241
|
+
appearance: 'none',
|
|
3242
|
+
border: 'none',
|
|
3243
|
+
background: 'transparent',
|
|
3244
|
+
color: 'inherit',
|
|
3245
|
+
font: 'inherit',
|
|
3246
|
+
opacity: '0.6',
|
|
3247
|
+
padding: '0 var(--space-xs)',
|
|
3248
|
+
lineHeight: 'var(--leading-none)',
|
|
3249
|
+
cursor: 'pointer',
|
|
3250
|
+
transition: 'opacity var(--duration-fast) var(--ease-standard)',
|
|
3251
|
+
},
|
|
3252
|
+
states: { hover: { opacity: '1' } },
|
|
3253
|
+
},
|
|
2838
3254
|
popup: withPresence(popupPresence('translateY(-4px)'), {
|
|
2839
3255
|
base: { ...floatingPanel, padding: 'var(--space-md)', minWidth: '13rem' },
|
|
2840
3256
|
states: { open: {}, closed: {} },
|
|
@@ -2849,6 +3265,16 @@ export const combobox = {
|
|
|
2849
3265
|
opacity: '0.6',
|
|
2850
3266
|
},
|
|
2851
3267
|
},
|
|
3268
|
+
// A windowed group's heading (#127): the same overline as the label,
|
|
3269
|
+
// as a row of the flat window instead of inside a `group`.
|
|
3270
|
+
'group-heading': {
|
|
3271
|
+
base: {
|
|
3272
|
+
padding: 'var(--space-md) var(--space-lg) var(--space-xs)',
|
|
3273
|
+
fontSize: 'var(--text-xs)',
|
|
3274
|
+
fontWeight: 'var(--weight-bold)',
|
|
3275
|
+
opacity: '0.6',
|
|
3276
|
+
},
|
|
3277
|
+
},
|
|
2852
3278
|
item: {
|
|
2853
3279
|
base: {
|
|
2854
3280
|
display: 'flex',
|
|
@@ -2880,6 +3306,23 @@ export const combobox = {
|
|
|
2880
3306
|
opacity: '0.6',
|
|
2881
3307
|
},
|
|
2882
3308
|
},
|
|
3309
|
+
// The list still arriving (#280): the empty row's muted grammar.
|
|
3310
|
+
loading: {
|
|
3311
|
+
base: {
|
|
3312
|
+
padding: 'var(--space-lg)',
|
|
3313
|
+
fontSize: 'var(--text-sm)',
|
|
3314
|
+
textAlign: 'center',
|
|
3315
|
+
opacity: '0.6',
|
|
3316
|
+
},
|
|
3317
|
+
},
|
|
3318
|
+
// The menu's rule between runs of options (#280).
|
|
3319
|
+
separator: {
|
|
3320
|
+
base: {
|
|
3321
|
+
height: 'var(--border)',
|
|
3322
|
+
margin: 'var(--space-sm) var(--space-md)',
|
|
3323
|
+
background: 'var(--color-base-300)',
|
|
3324
|
+
},
|
|
3325
|
+
},
|
|
2883
3326
|
},
|
|
2884
3327
|
variants: {
|
|
2885
3328
|
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
@@ -2902,6 +3345,9 @@ export const combobox = {
|
|
|
2902
3345
|
input: ['focus-visible'],
|
|
2903
3346
|
trigger: ['focus-visible'],
|
|
2904
3347
|
},
|
|
3348
|
+
targets: {
|
|
3349
|
+
web: { parts: { popup: { base: anchoredListbox } } },
|
|
3350
|
+
},
|
|
2905
3351
|
};
|
|
2906
3352
|
// daisy "btn" that stays pressed: the off state is the neutral btn, the on
|
|
2907
3353
|
// state is the solid accent fill.
|
|
@@ -3500,7 +3946,9 @@ export const treeView = {
|
|
|
3500
3946
|
opacity: '0.6',
|
|
3501
3947
|
transition: 'transform var(--duration-fast) var(--ease-standard)',
|
|
3502
3948
|
},
|
|
3503
|
-
|
|
3949
|
+
// Loading: the chevron halts at daisy's own collapse-arrow angle —
|
|
3950
|
+
// static (no loop), ink unchanged, so contrast is untouched.
|
|
3951
|
+
states: { open: { transform: 'rotate(90deg)' }, closed: {}, loading: { transform: 'rotate(45deg)' } },
|
|
3504
3952
|
// The glyph is element text the runtime renders (`TreeView.tsx`), not
|
|
3505
3953
|
// `content:`, so the `:dir(rtl)` swap the submenu chevron uses is not
|
|
3506
3954
|
// available here — a mirror is its equivalent. `scale` composes
|
|
@@ -3519,6 +3967,9 @@ export const treeView = {
|
|
|
3519
3967
|
states: { open: {}, closed: {} },
|
|
3520
3968
|
},
|
|
3521
3969
|
},
|
|
3970
|
+
// Open-and-loading content lays out exactly as open content does — the
|
|
3971
|
+
// indicator and the treeitem's aria-busy carry the state.
|
|
3972
|
+
sameAs: { 'branch-content': { loading: 'open' } },
|
|
3522
3973
|
variants: {
|
|
3523
3974
|
// A tree colours one thing: the selected row. Everything else is
|
|
3524
3975
|
// structure, and tinting it would fight the content.
|
|
@@ -3547,6 +3998,32 @@ export const treeView = {
|
|
|
3547
3998
|
},
|
|
3548
3999
|
},
|
|
3549
4000
|
};
|
|
4001
|
+
/**
|
|
4002
|
+
* The affordances inside a text field (#281): daisy's in-field label ink and
|
|
4003
|
+
* the ghost circle button, both following the size step.
|
|
4004
|
+
*/
|
|
4005
|
+
const affixInk = 'color-mix(in oklab, var(--color-base-content) 70%, transparent)';
|
|
4006
|
+
const fieldButton = {
|
|
4007
|
+
appearance: 'none',
|
|
4008
|
+
border: 'none',
|
|
4009
|
+
background: 'transparent',
|
|
4010
|
+
color: affixInk,
|
|
4011
|
+
borderRadius: '9999px',
|
|
4012
|
+
alignSelf: 'center',
|
|
4013
|
+
flex: 'none',
|
|
4014
|
+
order: '1',
|
|
4015
|
+
marginInlineEnd: 'var(--space-xs)',
|
|
4016
|
+
padding: 'var(--space-2xs) var(--space-xs)',
|
|
4017
|
+
fontSize: 'var(--text-sm)',
|
|
4018
|
+
fontWeight: 'var(--weight-semibold)',
|
|
4019
|
+
lineHeight: 'var(--leading-none)',
|
|
4020
|
+
cursor: 'pointer',
|
|
4021
|
+
};
|
|
4022
|
+
const affixSize = (fontSize) => ({
|
|
4023
|
+
adornment: { base: { fontSize } },
|
|
4024
|
+
'clear-trigger': { base: { fontSize } },
|
|
4025
|
+
'visibility-trigger': { base: { fontSize } },
|
|
4026
|
+
});
|
|
3550
4027
|
/**
|
|
3551
4028
|
* daisy "input" flavor: the shared field box with nothing in it but the text.
|
|
3552
4029
|
* Same `fieldControl` metrics as select's trigger, combobox's control and the
|
|
@@ -3573,6 +4050,7 @@ export const input = {
|
|
|
3573
4050
|
base: {
|
|
3574
4051
|
display: 'inline-flex',
|
|
3575
4052
|
alignItems: 'stretch',
|
|
4053
|
+
gap: 'var(--space-2xs)',
|
|
3576
4054
|
...fieldControl,
|
|
3577
4055
|
overflow: 'hidden',
|
|
3578
4056
|
transition: 'border-color var(--duration-fast) var(--ease-standard)',
|
|
@@ -3609,6 +4087,43 @@ export const input = {
|
|
|
3609
4087
|
'&::placeholder': { color: 'color-mix(in oklab, var(--color-base-content) 60%, transparent)' },
|
|
3610
4088
|
},
|
|
3611
4089
|
},
|
|
4090
|
+
// daisy's `label` inside `.input`: a quiet prefix/suffix at one edge,
|
|
4091
|
+
// ordered logically so it flips with the reading direction.
|
|
4092
|
+
adornment: {
|
|
4093
|
+
base: {
|
|
4094
|
+
display: 'inline-flex',
|
|
4095
|
+
alignItems: 'center',
|
|
4096
|
+
flex: 'none',
|
|
4097
|
+
color: affixInk,
|
|
4098
|
+
fontSize: 'var(--text-sm)',
|
|
4099
|
+
lineHeight: 'var(--leading-none)',
|
|
4100
|
+
},
|
|
4101
|
+
states: { disabled: {} },
|
|
4102
|
+
selectors: {
|
|
4103
|
+
'&[data-placement="start"]': { order: '-1', paddingInlineStart: 'var(--space-md)' },
|
|
4104
|
+
'&[data-placement="end"]': { order: '1', paddingInlineEnd: 'var(--space-md)' },
|
|
4105
|
+
},
|
|
4106
|
+
},
|
|
4107
|
+
// `btn btn-ghost btn-circle btn-xs` inside the field.
|
|
4108
|
+
'clear-trigger': {
|
|
4109
|
+
base: fieldButton,
|
|
4110
|
+
states: {
|
|
4111
|
+
hover: { color: 'var(--color-base-content)', background: 'var(--color-base-200)' },
|
|
4112
|
+
disabled: { cursor: 'not-allowed' },
|
|
4113
|
+
...focusRing,
|
|
4114
|
+
},
|
|
4115
|
+
},
|
|
4116
|
+
// Shown: the ghost button's active fill and full ink.
|
|
4117
|
+
'visibility-trigger': {
|
|
4118
|
+
base: fieldButton,
|
|
4119
|
+
states: {
|
|
4120
|
+
on: { color: 'var(--color-base-content)', background: 'var(--color-base-200)' },
|
|
4121
|
+
off: {},
|
|
4122
|
+
hover: { color: 'var(--color-base-content)', background: 'var(--color-base-200)' },
|
|
4123
|
+
disabled: { cursor: 'not-allowed' },
|
|
4124
|
+
...focusRing,
|
|
4125
|
+
},
|
|
4126
|
+
},
|
|
3612
4127
|
},
|
|
3613
4128
|
// The visible ring lives on `control`; the input delegates.
|
|
3614
4129
|
skipStates: { input: ['focus-visible'] },
|
|
@@ -3621,13 +4136,13 @@ export const input = {
|
|
|
3621
4136
|
// The control carries the height off the shared field ramp; the text
|
|
3622
4137
|
// follows so the box stays proportional.
|
|
3623
4138
|
size: {
|
|
3624
|
-
xs: { control: { base: { height: fieldHeight('xs') } }, input: { base: { fontSize: 'var(--text-xs)', padding: 'var(--space-2xs) var(--space-xs)' } } },
|
|
3625
|
-
sm: { control: { base: { height: fieldHeight('sm') } }, input: { base: { fontSize: 'var(--text-xs)', padding: 'var(--space-xs) var(--space-sm)' } } },
|
|
4139
|
+
xs: { control: { base: { height: fieldHeight('xs') } }, input: { base: { fontSize: 'var(--text-xs)', padding: 'var(--space-2xs) var(--space-xs)' } }, ...affixSize('var(--text-xs)') },
|
|
4140
|
+
sm: { control: { base: { height: fieldHeight('sm') } }, input: { base: { fontSize: 'var(--text-xs)', padding: 'var(--space-xs) var(--space-sm)' } }, ...affixSize('var(--text-xs)') },
|
|
3626
4141
|
// `md` is the un-attributed render: the base already IS the
|
|
3627
4142
|
// middle step.
|
|
3628
4143
|
md: {},
|
|
3629
|
-
lg: { control: { base: { height: fieldHeight('lg') } }, input: { base: { fontSize: 'var(--text-md)', padding: 'var(--space-md) var(--space-lg)' } } },
|
|
3630
|
-
xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } } },
|
|
4144
|
+
lg: { control: { base: { height: fieldHeight('lg') } }, input: { base: { fontSize: 'var(--text-md)', padding: 'var(--space-md) var(--space-lg)' } }, ...affixSize('var(--text-md)') },
|
|
4145
|
+
xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } }, ...affixSize('var(--text-lg)') },
|
|
3631
4146
|
},
|
|
3632
4147
|
},
|
|
3633
4148
|
};
|
|
@@ -4343,8 +4858,17 @@ export const stats = {
|
|
|
4343
4858
|
tokens: { '--stats-accent': 'var(--color-base-content)' },
|
|
4344
4859
|
parts: {
|
|
4345
4860
|
root: {
|
|
4861
|
+
// The row's own scroll box (#43): a value never wraps or shrinks
|
|
4862
|
+
// (`nowrap` below), so a row too wide for its container scrolls
|
|
4863
|
+
// inside the root — daisyUI's own `.stats` behaviour, and Table's
|
|
4864
|
+
// and Pagination's answer — instead of pushing the page sideways.
|
|
4865
|
+
// Items keep their flex automatic minimum — their min-content
|
|
4866
|
+
// width — so the overflow lands here rather than in the next
|
|
4867
|
+
// item. No ring room: a stat is not focusable, and anything a
|
|
4868
|
+
// consumer puts in one sits inside the item's own padding.
|
|
4346
4869
|
base: {
|
|
4347
4870
|
display: 'flex',
|
|
4871
|
+
overflowX: 'auto',
|
|
4348
4872
|
border: 'var(--border) solid var(--color-base-200)',
|
|
4349
4873
|
borderRadius: 'var(--radius-box)',
|
|
4350
4874
|
background: 'var(--color-base-100)',
|
|
@@ -4381,6 +4905,8 @@ export const stats = {
|
|
|
4381
4905
|
value: {
|
|
4382
4906
|
base: {
|
|
4383
4907
|
gridColumn: '1',
|
|
4908
|
+
// A figure is read whole: `$12 930` never breaks at its space.
|
|
4909
|
+
whiteSpace: 'nowrap',
|
|
4384
4910
|
fontSize: 'var(--text-2xl)',
|
|
4385
4911
|
fontWeight: 'var(--weight-bold)',
|
|
4386
4912
|
fontVariantNumeric: 'tabular-nums',
|
|
@@ -4403,8 +4929,19 @@ export const stats = {
|
|
|
4403
4929
|
},
|
|
4404
4930
|
},
|
|
4405
4931
|
variants: {
|
|
4406
|
-
|
|
4407
|
-
|
|
4932
|
+
// Keyed on the ITEM, not the root (#161): the item re-carries
|
|
4933
|
+
// `color`, so a stat's own value outranks the row's, and the value
|
|
4934
|
+
// inside it inherits whichever won. A root colour still reaches
|
|
4935
|
+
// every item through the carrier's donut.
|
|
4936
|
+
//
|
|
4937
|
+
// The value is TEXT on the page, so the raw role is not always a
|
|
4938
|
+
// readable ink: measured by the text matrix once the item re-carried
|
|
4939
|
+
// the axis (#161), light warning is 1.76:1 and dark neutral 1.26:1
|
|
4940
|
+
// on base-100. `roleInk` is the shared answer for a role used as ink
|
|
4941
|
+
// (a per-role share of the role over base-content, which flips with
|
|
4942
|
+
// the scheme), the same one the field and button text use.
|
|
4943
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { item: { base: {
|
|
4944
|
+
'--stats-accent': roleInk(c),
|
|
4408
4945
|
} } }])),
|
|
4409
4946
|
size: {
|
|
4410
4947
|
xs: { value: { base: { fontSize: 'var(--text-lg)' } } },
|
|
@@ -4450,7 +4987,11 @@ export const timeline = {
|
|
|
4450
4987
|
padding: '0',
|
|
4451
4988
|
},
|
|
4452
4989
|
selectors: {
|
|
4453
|
-
|
|
4990
|
+
// A horizontal timeline is a row of steps whose width follows
|
|
4991
|
+
// the step count, not the container: at phone width it scrolls
|
|
4992
|
+
// inside its own box, the answer Table and Pagination give
|
|
4993
|
+
// (#45), rather than widening the page.
|
|
4994
|
+
'&[data-orientation="horizontal"]': { flexDirection: 'row', overflowX: 'auto' },
|
|
4454
4995
|
},
|
|
4455
4996
|
},
|
|
4456
4997
|
/**
|
|
@@ -5144,9 +5685,27 @@ export const pagination = {
|
|
|
5144
5685
|
'--pg-accent-content': 'var(--color-primary-content)',
|
|
5145
5686
|
'--pg-size': 'calc(var(--size-field) * 10)',
|
|
5146
5687
|
'--pg-font': 'var(--text-sm)',
|
|
5688
|
+
// How far the focus ring reaches outside a control: the root's
|
|
5689
|
+
// scroll box pads by it so the ring is never clipped (#44).
|
|
5690
|
+
'--pg-ring-room': '4px',
|
|
5147
5691
|
},
|
|
5148
5692
|
parts: {
|
|
5149
|
-
root: {
|
|
5693
|
+
root: {
|
|
5694
|
+
// The row's own scroll box (#44): the window is constant-width by
|
|
5695
|
+
// design, so a narrow container scrolls it rather than clipping
|
|
5696
|
+
// it — Table's answer. The padding is room for the focus ring's
|
|
5697
|
+
// 2px + 2px offset, which the scroll box would otherwise clip at
|
|
5698
|
+
// the row's edges; the matching scroll padding keeps it clear
|
|
5699
|
+
// when focus scrolls a page into view.
|
|
5700
|
+
base: {
|
|
5701
|
+
display: 'flex',
|
|
5702
|
+
alignItems: 'center',
|
|
5703
|
+
gap: 'var(--space-2xs)',
|
|
5704
|
+
overflowX: 'auto',
|
|
5705
|
+
padding: 'var(--pg-ring-room)',
|
|
5706
|
+
scrollPaddingInline: 'var(--pg-ring-room)',
|
|
5707
|
+
},
|
|
5708
|
+
},
|
|
5150
5709
|
item: {
|
|
5151
5710
|
base: {
|
|
5152
5711
|
display: 'inline-flex',
|
|
@@ -5631,6 +6190,11 @@ export const table = {
|
|
|
5631
6190
|
borderRadius: 'var(--radius-box)',
|
|
5632
6191
|
background: 'var(--color-base-100)',
|
|
5633
6192
|
},
|
|
6193
|
+
// The scroll box is a keyboard stop (#270): daisy's ink ring,
|
|
6194
|
+
// drawn inside the box (negative offset) — the root is as wide
|
|
6195
|
+
// as its column, so an outward ring would be clipped by
|
|
6196
|
+
// whatever scrolls around it.
|
|
6197
|
+
states: { 'focus-visible': { outline: '2px solid var(--color-base-content)', outlineOffset: '-2px' } },
|
|
5634
6198
|
// Stacked (Table.Root stack, #55): zero lays each row out as a block
|
|
5635
6199
|
// below the table's breakpoint; the card it becomes is drawn here.
|
|
5636
6200
|
at: tableStackAt(tokens, 'root', { border: '0', borderRadius: '0', background: 'transparent' }),
|
|
@@ -5820,7 +6384,15 @@ export const fileUpload = {
|
|
|
5820
6384
|
borderRadius: 'var(--radius-field)',
|
|
5821
6385
|
background: 'var(--color-base-100)',
|
|
5822
6386
|
},
|
|
5823
|
-
states: {
|
|
6387
|
+
states: {
|
|
6388
|
+
disabled: { opacity: 'var(--disabled-opacity)' },
|
|
6389
|
+
// A rejected file the app renders through Item: daisy's
|
|
6390
|
+
// error border over a faint error wash.
|
|
6391
|
+
invalid: {
|
|
6392
|
+
borderColor: 'var(--color-error)',
|
|
6393
|
+
background: 'color-mix(in oklab, var(--color-error) 8%, var(--color-base-100))',
|
|
6394
|
+
},
|
|
6395
|
+
},
|
|
5824
6396
|
},
|
|
5825
6397
|
'item-name': {
|
|
5826
6398
|
base: {
|
|
@@ -5856,6 +6428,26 @@ export const fileUpload = {
|
|
|
5856
6428
|
...focusRing,
|
|
5857
6429
|
},
|
|
5858
6430
|
},
|
|
6431
|
+
// daisy's btn-ghost btn-xs: the remove button's pill, at text size.
|
|
6432
|
+
'clear-trigger': {
|
|
6433
|
+
base: {
|
|
6434
|
+
appearance: 'none',
|
|
6435
|
+
border: 'none',
|
|
6436
|
+
background: 'transparent',
|
|
6437
|
+
color: 'inherit',
|
|
6438
|
+
borderRadius: '9999px',
|
|
6439
|
+
padding: 'var(--space-2xs) var(--space-sm)',
|
|
6440
|
+
fontSize: 'var(--fu-font)',
|
|
6441
|
+
fontWeight: 'var(--weight-semibold)',
|
|
6442
|
+
lineHeight: 'var(--leading-none)',
|
|
6443
|
+
cursor: 'pointer',
|
|
6444
|
+
},
|
|
6445
|
+
states: {
|
|
6446
|
+
hover: { background: 'var(--color-base-200)' },
|
|
6447
|
+
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6448
|
+
...focusRing,
|
|
6449
|
+
},
|
|
6450
|
+
},
|
|
5859
6451
|
},
|
|
5860
6452
|
variants: {
|
|
5861
6453
|
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
@@ -6196,6 +6788,9 @@ export const diff = {
|
|
|
6196
6788
|
background: 'var(--color-base-100)',
|
|
6197
6789
|
borderRadius: 'var(--radius-box)',
|
|
6198
6790
|
},
|
|
6791
|
+
// The root holds the images, which are content: disabled is the
|
|
6792
|
+
// handle's to show (#272).
|
|
6793
|
+
states: { disabled: {} },
|
|
6199
6794
|
},
|
|
6200
6795
|
before: {
|
|
6201
6796
|
base: { gridArea: '1 / 1', minWidth: '0' },
|
|
@@ -6225,6 +6820,9 @@ export const diff = {
|
|
|
6225
6820
|
},
|
|
6226
6821
|
states: {
|
|
6227
6822
|
...focusRing,
|
|
6823
|
+
// daisy's disabled look (#272): the uniform fade, and no grab
|
|
6824
|
+
// cursor — the images are content and stay unfaded.
|
|
6825
|
+
disabled: { opacity: 'var(--disabled-opacity)', cursor: 'not-allowed' },
|
|
6228
6826
|
pressed: { '--diff-accent': 'color-mix(in oklab, var(--color-base-content) 85%, var(--color-base-100))' },
|
|
6229
6827
|
},
|
|
6230
6828
|
selectors: {
|
|
@@ -6298,7 +6896,7 @@ export const diff = {
|
|
|
6298
6896
|
};
|
|
6299
6897
|
export const recipes = [
|
|
6300
6898
|
tabs, collapsible, switchRecipe, dialog, popover, tooltip, menu,
|
|
6301
|
-
field, checkbox, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
|
|
6899
|
+
field, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
|
|
6302
6900
|
toggle, toggleGroup, numberInput, ratingGroup, treeView, input, textarea,
|
|
6303
6901
|
card, alert, emptyState, badge, divider, skeleton, spinner,
|
|
6304
6902
|
kbd, status, indicator, stats, timeline, chat, radialProgress, join,
|