@sigx/zero-daisyui 0.10.0 → 0.12.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 +5 -0
- package/dist/audit.json +458 -3
- package/dist/css/components/drawer.css +19 -2
- package/dist/css/components/radio-group.css +6 -0
- package/dist/css/components/select.css +1 -1
- package/dist/css/components/toast.css +9 -3
- package/dist/css/index.css +35 -6
- package/dist/lynx/components/checkbox.css +4 -2
- package/dist/lynx/components/drawer.css +2 -2
- package/dist/lynx/components/fieldset.css +10 -1
- package/dist/lynx/components/input.css +40 -2
- package/dist/lynx/components/number-input.css +18 -3
- package/dist/lynx/components/radio-group.css +10 -2
- package/dist/lynx/components/select.css +12 -0
- package/dist/lynx/components/textarea.css +23 -2
- package/dist/lynx/components/toggle-group.css +41 -2
- package/dist/lynx/components/toggle.css +27 -2
- package/dist/lynx/index.css +187 -18
- package/dist/lynx/manifest.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +350 -19
- package/dist/recipes.js.map +1 -1
- package/dist/report.json +34 -37
- package/package.json +4 -4
- package/src/recipes.ts +352 -21
package/src/recipes.ts
CHANGED
|
@@ -148,6 +148,14 @@ const roleInk = (role: string): string =>
|
|
|
148
148
|
+ `${ROLE_INK_KEEP[role as keyof typeof roles] ?? 55}%, var(--color-base-content))`;
|
|
149
149
|
|
|
150
150
|
// daisy "tabs-box" flavor: a rounded container, lifted active tab.
|
|
151
|
+
/**
|
|
152
|
+
* The swipe-to-dismiss offset (#293): `createSwipe` publishes the drag as
|
|
153
|
+
* `--swipe-x` / `--swipe-y` on a toast root or a drawer sheet, and this is
|
|
154
|
+
* the one place it is read — composed first into the part's `transform`, so
|
|
155
|
+
* an enter/exit offset or a slide rides on top of it. Unset, it is nothing.
|
|
156
|
+
*/
|
|
157
|
+
const SWIPE = 'translate(var(--swipe-x, 0px), var(--swipe-y, 0px))';
|
|
158
|
+
|
|
151
159
|
/**
|
|
152
160
|
* Enter/exit presence for a top-layer popup.
|
|
153
161
|
*
|
|
@@ -1682,6 +1690,10 @@ export const field: RecipeInput = {
|
|
|
1682
1690
|
label: {
|
|
1683
1691
|
base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)', color: 'var(--field-accent)' },
|
|
1684
1692
|
states: { disabled: { opacity: 'var(--disabled-opacity)' } },
|
|
1693
|
+
// lynx has no pseudo-elements and the anatomy no part to carry
|
|
1694
|
+
// the asterisk, so it stays a web-only mark: on lynx a required
|
|
1695
|
+
// field still stamps `required` on its root and label, and the
|
|
1696
|
+
// control inside adopts it (signalxjs/lynx#1205).
|
|
1685
1697
|
selectors: {
|
|
1686
1698
|
'&[data-required]::after': { content: '" *"', color: roleInk('error') },
|
|
1687
1699
|
},
|
|
@@ -1773,6 +1785,33 @@ export const fieldset: RecipeInput = {
|
|
|
1773
1785
|
xl: { legend: { base: { fontSize: 'var(--text-lg)' } } },
|
|
1774
1786
|
},
|
|
1775
1787
|
},
|
|
1788
|
+
// Lynx has no grid: the one-column track becomes a flex column (the gap
|
|
1789
|
+
// survives), and the block/inline paddings are restated physically —
|
|
1790
|
+
// logical spellings resolve on iOS but not on Android (signalxjs/lynx#1084).
|
|
1791
|
+
targets: {
|
|
1792
|
+
lynx: {
|
|
1793
|
+
parts: {
|
|
1794
|
+
root: {
|
|
1795
|
+
base: {
|
|
1796
|
+
display: 'flex',
|
|
1797
|
+
flexDirection: 'column',
|
|
1798
|
+
paddingTop: 'var(--space-xs)',
|
|
1799
|
+
paddingBottom: 'var(--space-xs)',
|
|
1800
|
+
paddingLeft: '0',
|
|
1801
|
+
paddingRight: '0',
|
|
1802
|
+
},
|
|
1803
|
+
},
|
|
1804
|
+
legend: {
|
|
1805
|
+
base: {
|
|
1806
|
+
paddingTop: 'var(--space-sm)',
|
|
1807
|
+
paddingBottom: 'var(--space-sm)',
|
|
1808
|
+
paddingLeft: '0',
|
|
1809
|
+
paddingRight: '0',
|
|
1810
|
+
},
|
|
1811
|
+
},
|
|
1812
|
+
},
|
|
1813
|
+
},
|
|
1814
|
+
},
|
|
1776
1815
|
};
|
|
1777
1816
|
|
|
1778
1817
|
/**
|
|
@@ -2004,11 +2043,25 @@ export const checkbox: RecipeInput = {
|
|
|
2004
2043
|
targets: {
|
|
2005
2044
|
lynx: {
|
|
2006
2045
|
parts: {
|
|
2046
|
+
// The box/label row, pinned: `inline-flex` is rewritten to
|
|
2047
|
+
// `flex`, and the row must not depend on the engine's
|
|
2048
|
+
// default direction (signalxjs/lynx#1203).
|
|
2049
|
+
root: { base: { display: 'flex', flexDirection: 'row' } },
|
|
2007
2050
|
control: {
|
|
2008
2051
|
// The web fallback (`color-mix(in oklab, … currentColor …)`)
|
|
2009
2052
|
// exists for consumers that unset the accent; this package
|
|
2010
2053
|
// always defines it, so the plain accent IS the ring.
|
|
2011
|
-
|
|
2054
|
+
//
|
|
2055
|
+
// `inline-block` is not a lynx display value (no inline
|
|
2056
|
+
// formatting context); a flex box keeps daisy's geometry —
|
|
2057
|
+
// the padding insets the 100%-sized indicator either way.
|
|
2058
|
+
base: { display: 'flex', border: 'var(--border) solid var(--checkbox-accent)' },
|
|
2059
|
+
// The ring as spread shadows that follow the box's radius
|
|
2060
|
+
// (signalxjs/lynx#1163 — lynx's `outline` ignores it and
|
|
2061
|
+
// has no offset), keeping the box's inset depth line.
|
|
2062
|
+
states: {
|
|
2063
|
+
'focus-visible': lynxFocusRing('var(--checkbox-accent)', { under: '0 1px var(--depth-shade) inset' }),
|
|
2064
|
+
},
|
|
2012
2065
|
},
|
|
2013
2066
|
indicator: {
|
|
2014
2067
|
// The tick's fill: the control's `color:` names
|
|
@@ -2056,6 +2109,11 @@ export const radioGroup: RecipeInput = {
|
|
|
2056
2109
|
// `invalid` paints on each `item-control`, which carries the flag
|
|
2057
2110
|
// itself (#267) — the root only groups the items.
|
|
2058
2111
|
states: { invalid: {}, required: {} },
|
|
2112
|
+
// `orientation="horizontal"` lays the items in a wrapping row —
|
|
2113
|
+
// checkbox-group's rule (signalxjs/zero#393).
|
|
2114
|
+
selectors: {
|
|
2115
|
+
'&[data-orientation="horizontal"]': { flexDirection: 'row', flexWrap: 'wrap', columnGap: 'var(--space-lg)' },
|
|
2116
|
+
},
|
|
2059
2117
|
},
|
|
2060
2118
|
label: {
|
|
2061
2119
|
base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)' },
|
|
@@ -2163,9 +2221,20 @@ export const radioGroup: RecipeInput = {
|
|
|
2163
2221
|
targets: {
|
|
2164
2222
|
lynx: {
|
|
2165
2223
|
parts: {
|
|
2224
|
+
// The control/label row, pinned — as checkbox's root
|
|
2225
|
+
// (signalxjs/lynx#1203).
|
|
2226
|
+
item: { base: { display: 'flex', flexDirection: 'row' } },
|
|
2166
2227
|
'item-control': {
|
|
2167
|
-
|
|
2168
|
-
|
|
2228
|
+
// `display: flex` in place of `inline-block`, which lynx
|
|
2229
|
+
// does not have — the padding still insets the dot.
|
|
2230
|
+
base: { display: 'flex', border: 'var(--border) solid var(--radio-accent)' },
|
|
2231
|
+
states: {
|
|
2232
|
+
checked: { borderColor: 'var(--radio-accent)' },
|
|
2233
|
+
// The round ring (signalxjs/lynx#1163): spread
|
|
2234
|
+
// shadows follow the 9999px radius where `outline`
|
|
2235
|
+
// drew a square.
|
|
2236
|
+
'focus-visible': lynxFocusRing('var(--radio-accent)', { under: '0 1px var(--depth-shade) inset' }),
|
|
2237
|
+
},
|
|
2169
2238
|
},
|
|
2170
2239
|
'item-indicator': {
|
|
2171
2240
|
states: { checked: { backgroundColor: 'var(--radio-accent)' } },
|
|
@@ -2933,12 +3002,6 @@ const fieldControl: NonNullable<PartStyles['base']> = {
|
|
|
2933
3002
|
boxShadow: 'var(--shadow-xs)',
|
|
2934
3003
|
};
|
|
2935
3004
|
|
|
2936
|
-
/**
|
|
2937
|
-
* A select whose clear-trigger is rendered (#280) — anchored on the root, so
|
|
2938
|
-
* a part inside the trigger can make room for the button laid over it.
|
|
2939
|
-
*/
|
|
2940
|
-
const SELECT_CLEARABLE = '[data-scope="select"][data-part="root"]:has(> [data-scope="select"][data-part="clear-trigger"]) &';
|
|
2941
|
-
|
|
2942
3005
|
/**
|
|
2943
3006
|
* The select trigger's lynx steps — `[inline padding in --size-field units,
|
|
2944
3007
|
* font-size step]` — as the size variants set them (`lynxBtnPad` and the
|
|
@@ -3029,9 +3092,13 @@ export const select: RecipeInput = {
|
|
|
3029
3092
|
},
|
|
3030
3093
|
indicator: {
|
|
3031
3094
|
base: { opacity: '0.6', transition: 'transform var(--duration-normal) var(--ease-standard)' },
|
|
3032
|
-
states: {
|
|
3033
|
-
|
|
3034
|
-
|
|
3095
|
+
states: {
|
|
3096
|
+
open: { transform: 'rotate(180deg)' },
|
|
3097
|
+
closed: {},
|
|
3098
|
+
// Room for the clear-trigger laid over the field (#280) — the
|
|
3099
|
+
// `clearable` flag the runtime stamps while it renders (#387).
|
|
3100
|
+
clearable: { marginInlineStart: 'calc(var(--space-2xl) + var(--space-sm))' },
|
|
3101
|
+
},
|
|
3035
3102
|
},
|
|
3036
3103
|
// Clears the selection (#280): daisy's ghost-btn over the field's
|
|
3037
3104
|
// inline end, just before the chevron — muted like it, the base-200
|
|
@@ -3189,7 +3256,25 @@ export const select: RecipeInput = {
|
|
|
3189
3256
|
// focus ring was a tall pill. The ring is inset, so it hugs
|
|
3190
3257
|
// the chip and can never reach the chevron, and no gap shadow
|
|
3191
3258
|
// paints past the chip's rounded corners.
|
|
3192
|
-
indicator: {
|
|
3259
|
+
indicator: {
|
|
3260
|
+
base: { width: '1em', textAlign: 'right' },
|
|
3261
|
+
// The web's room for the chip (`margin-inline-start`) is a
|
|
3262
|
+
// logical spelling Android never resolves, and it is
|
|
3263
|
+
// sized off the web's own offset. Here the chip is a
|
|
3264
|
+
// 1.5rem square one `--space-xs` short of the chevron
|
|
3265
|
+
// at every size, so the room is exactly that — and the
|
|
3266
|
+
// trigger's `gap` stays between the value and the chip.
|
|
3267
|
+
// Before the `clearable` flag the web reached this with
|
|
3268
|
+
// `:has()`, which lynx cannot express: at lg/xl the value
|
|
3269
|
+
// ran under the chip and its focus ring
|
|
3270
|
+
// (signalxjs/lynx#1191).
|
|
3271
|
+
states: { clearable: { marginLeft: 'calc(1.5rem + var(--space-xs))' } },
|
|
3272
|
+
},
|
|
3273
|
+
// The value gives way to the chip instead of running under
|
|
3274
|
+
// it: one line, clipped with an ellipsis — the web's
|
|
3275
|
+
// `nowrap` button text, with a flex item's `min-width: auto`
|
|
3276
|
+
// lifted so it can shrink below its content at all.
|
|
3277
|
+
value: { base: { flexShrink: '1', minWidth: '0', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' } },
|
|
3193
3278
|
'clear-trigger': {
|
|
3194
3279
|
base: { ...lynxSelectClear('md'), width: '1.5rem', height: '1.5rem', minWidth: '1.5rem', boxSizing: 'border-box' },
|
|
3195
3280
|
states: lynxFocus({ inset: true }),
|
|
@@ -3210,7 +3295,6 @@ export const select: RecipeInput = {
|
|
|
3210
3295
|
},
|
|
3211
3296
|
};
|
|
3212
3297
|
|
|
3213
|
-
|
|
3214
3298
|
/**
|
|
3215
3299
|
* daisy's BUTTON ramp — `.btn`'s fixed height per size step, in `--size-field`
|
|
3216
3300
|
* units, exactly as daisyUI 5 ships it (`components/button.css`: `.btn-xs`
|
|
@@ -3829,6 +3913,8 @@ export const toast: RecipeInput = {
|
|
|
3829
3913
|
states: {
|
|
3830
3914
|
open: { opacity: '1', transform: 'none' },
|
|
3831
3915
|
closed: {},
|
|
3916
|
+
// Styled in `targets.web` — the swipe is web-only (#293).
|
|
3917
|
+
swiping: {},
|
|
3832
3918
|
},
|
|
3833
3919
|
at: {
|
|
3834
3920
|
'reduced-motion': { base: { transition: 'none' }, states: { open: { transform: 'none' } } },
|
|
@@ -3954,6 +4040,22 @@ export const toast: RecipeInput = {
|
|
|
3954
4040
|
defaultVariants: { size: 'md' },
|
|
3955
4041
|
keyframes: { 'zero-daisyui-toast-spin': 'to { transform: rotate(360deg); }' },
|
|
3956
4042
|
targets: {
|
|
4043
|
+
// The swipe (#293) — web only, since the runtime's `--swipe-*` offset
|
|
4044
|
+
// is: composed ahead of the enter/exit offset, and no pan or pinch
|
|
4045
|
+
// starts on a toast, so a touch drag is the swipe's.
|
|
4046
|
+
web: {
|
|
4047
|
+
parts: {
|
|
4048
|
+
root: {
|
|
4049
|
+
base: { transform: `${SWIPE} translateY(var(--toast-from))`, touchAction: 'none' },
|
|
4050
|
+
states: { open: { transform: SWIPE } },
|
|
4051
|
+
selectors: {
|
|
4052
|
+
// Mid-swipe the toast tracks the pointer, not a transition.
|
|
4053
|
+
'&[data-swiping]': { transition: 'none', userSelect: 'none' },
|
|
4054
|
+
},
|
|
4055
|
+
at: { 'reduced-motion': { states: { open: { transform: SWIPE } } } },
|
|
4056
|
+
},
|
|
4057
|
+
},
|
|
4058
|
+
},
|
|
3957
4059
|
lynx: {
|
|
3958
4060
|
parts: {
|
|
3959
4061
|
// The viewport is a full-width strip on lynx (the runtime pins
|
|
@@ -4353,6 +4455,45 @@ export const toggle: RecipeInput = {
|
|
|
4353
4455
|
},
|
|
4354
4456
|
},
|
|
4355
4457
|
defaultVariants: { color: 'primary', size: 'md' },
|
|
4458
|
+
// signalxjs/lynx#1204 — the lynx replacements for what this recipe's
|
|
4459
|
+
// shared sections cannot say there.
|
|
4460
|
+
targets: {
|
|
4461
|
+
lynx: {
|
|
4462
|
+
parts: {
|
|
4463
|
+
root: {
|
|
4464
|
+
// daisy's content-sized, never-squeezed btn (lynx#1165):
|
|
4465
|
+
// a lynx view has no inline formatting context, so the
|
|
4466
|
+
// toggle stretched to its column and a squeezed label
|
|
4467
|
+
// wrapped per character. The glyph + label sit in a row.
|
|
4468
|
+
// The hairline as longhands: a var-bearing `border`
|
|
4469
|
+
// shorthand expands after the cascade and would beat
|
|
4470
|
+
// any edge a later rule restates (lynx#1161/#1162).
|
|
4471
|
+
base: {
|
|
4472
|
+
...lynxBtnFit,
|
|
4473
|
+
flexDirection: 'row',
|
|
4474
|
+
border: '0 solid transparent',
|
|
4475
|
+
...lynxSides('border{}Width', 'var(--border)'),
|
|
4476
|
+
...lynxSides('border{}Color', 'var(--color-base-300)'),
|
|
4477
|
+
},
|
|
4478
|
+
states: {
|
|
4479
|
+
// No hover on a touch platform: the held toggle
|
|
4480
|
+
// takes the wash daisy's hover gives it. The shared
|
|
4481
|
+
// pressed rule adds the btn's 1px sink.
|
|
4482
|
+
pressed: { background: 'var(--color-base-300)' },
|
|
4483
|
+
on: lynxSides('border{}Color', 'var(--toggle-accent)'),
|
|
4484
|
+
// lynx's `outline` ignores the radius and has no
|
|
4485
|
+
// offset (lynx#1163): the ring as spread shadows.
|
|
4486
|
+
'focus-visible': lynxFocusRing('var(--toggle-accent)'),
|
|
4487
|
+
},
|
|
4488
|
+
selectors: {
|
|
4489
|
+
// A held on toggle keeps its fill (the web's
|
|
4490
|
+
// `[data-state=on]:hover` stays on the accent too).
|
|
4491
|
+
'&[data-state="on"][data-pressed]': { background: 'var(--toggle-accent)' },
|
|
4492
|
+
},
|
|
4493
|
+
},
|
|
4494
|
+
},
|
|
4495
|
+
},
|
|
4496
|
+
},
|
|
4356
4497
|
};
|
|
4357
4498
|
|
|
4358
4499
|
// daisy "join" of btns: one bordered capsule, hairline seams between items,
|
|
@@ -4464,6 +4605,65 @@ export const toggleGroup: RecipeInput = {
|
|
|
4464
4605
|
),
|
|
4465
4606
|
},
|
|
4466
4607
|
defaultVariants: { color: 'primary' },
|
|
4608
|
+
// signalxjs/lynx#1204 — the lynx replacements for what this recipe's
|
|
4609
|
+
// shared sections cannot say there.
|
|
4610
|
+
targets: {
|
|
4611
|
+
lynx: {
|
|
4612
|
+
parts: {
|
|
4613
|
+
root: {
|
|
4614
|
+
// Content-sized like the web's inline-flex capsule (a
|
|
4615
|
+
// lynx view stretches to its column otherwise), a row
|
|
4616
|
+
// unless vertical, and the frame as longhands so the
|
|
4617
|
+
// orientation rules below can drop one edge by
|
|
4618
|
+
// specificity (a var-bearing `border` shorthand would
|
|
4619
|
+
// expand over them, lynx#1161/#1162).
|
|
4620
|
+
base: {
|
|
4621
|
+
width: 'max-content',
|
|
4622
|
+
flexShrink: '0',
|
|
4623
|
+
flexDirection: 'row',
|
|
4624
|
+
border: '0 solid transparent',
|
|
4625
|
+
...lynxSides('border{}Width', 'var(--border)'),
|
|
4626
|
+
...lynxSides('border{}Color', 'var(--color-base-300)'),
|
|
4627
|
+
},
|
|
4628
|
+
selectors: {
|
|
4629
|
+
// The join seams: lynx has no sibling combinator, so
|
|
4630
|
+
// the web's `item + item` edge cannot be selected.
|
|
4631
|
+
// Every item draws its leading seam instead (below),
|
|
4632
|
+
// and the frame gives up that edge — the first item's
|
|
4633
|
+
// seam stands in for it, inside the frame's clip.
|
|
4634
|
+
'&[data-orientation="horizontal"]': { borderLeftWidth: '0' },
|
|
4635
|
+
'&[data-orientation="vertical"]': { borderTopWidth: '0' },
|
|
4636
|
+
},
|
|
4637
|
+
},
|
|
4638
|
+
item: {
|
|
4639
|
+
base: { flexShrink: '0', flexWrap: 'nowrap' },
|
|
4640
|
+
states: {
|
|
4641
|
+
// No hover on a touch platform: a held item takes the
|
|
4642
|
+
// wash daisy's hover gives it; an on item keeps its
|
|
4643
|
+
// fill (the selector below).
|
|
4644
|
+
pressed: { background: 'var(--color-base-300)' },
|
|
4645
|
+
// The frame clips its items, so the ring is drawn
|
|
4646
|
+
// inside the item (lynx's `outline` has no offset
|
|
4647
|
+
// and ignores the radius, lynx#1163).
|
|
4648
|
+
'focus-visible': lynxFocusRing('var(--toggle-group-accent)', { inset: true }),
|
|
4649
|
+
},
|
|
4650
|
+
selectors: {
|
|
4651
|
+
'&[data-state="on"][data-pressed]': { background: 'var(--toggle-group-accent)' },
|
|
4652
|
+
'&[data-orientation="horizontal"]': {
|
|
4653
|
+
borderLeftWidth: 'var(--border)',
|
|
4654
|
+
borderLeftStyle: 'solid',
|
|
4655
|
+
borderLeftColor: 'var(--color-base-300)',
|
|
4656
|
+
},
|
|
4657
|
+
'&[data-orientation="vertical"]': {
|
|
4658
|
+
borderTopWidth: 'var(--border)',
|
|
4659
|
+
borderTopStyle: 'solid',
|
|
4660
|
+
borderTopColor: 'var(--color-base-300)',
|
|
4661
|
+
},
|
|
4662
|
+
},
|
|
4663
|
+
},
|
|
4664
|
+
},
|
|
4665
|
+
},
|
|
4666
|
+
},
|
|
4467
4667
|
};
|
|
4468
4668
|
|
|
4469
4669
|
// daisy "join" of an input and two btns: one bordered capsule (the control),
|
|
@@ -4608,14 +4808,53 @@ export const numberInput: RecipeInput = {
|
|
|
4608
4808
|
xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } } },
|
|
4609
4809
|
},
|
|
4610
4810
|
},
|
|
4611
|
-
//
|
|
4612
|
-
//
|
|
4613
|
-
//
|
|
4811
|
+
// Lynx restatements:
|
|
4812
|
+
// - the steppers' inline padding and their seams, physically: logical
|
|
4813
|
+
// spellings resolve on iOS but not on Android (measured,
|
|
4814
|
+
// signalxjs/lynx#1084);
|
|
4815
|
+
// - the steppers are views around a text glyph, with none of a
|
|
4816
|
+
// button's content centering;
|
|
4817
|
+
// - the native input takes no `inherit`: its ink is spelled out, and it
|
|
4818
|
+
// grows into any room a stretched control leaves between the steppers;
|
|
4819
|
+
// - the web root is inline-flex and hugs its content; lynx's flex column
|
|
4820
|
+
// stretches, so the children align to the start instead;
|
|
4821
|
+
// - the focus ring is `lynxFocusRing` (lynx's outline ignores the radius
|
|
4822
|
+
// and has no offset, signalxjs/lynx#1163). A shadow must not be
|
|
4823
|
+
// clipped by the box's own `overflow: hidden`, so the control stops
|
|
4824
|
+
// clipping and the steppers round their own outer corners instead.
|
|
4614
4825
|
targets: {
|
|
4615
4826
|
lynx: {
|
|
4616
4827
|
parts: {
|
|
4617
|
-
|
|
4618
|
-
|
|
4828
|
+
root: { base: { alignItems: 'flex-start' } },
|
|
4829
|
+
control: {
|
|
4830
|
+
base: { overflow: 'visible' },
|
|
4831
|
+
states: { 'focus-visible': lynxFocusRing('var(--number-input-accent)') },
|
|
4832
|
+
},
|
|
4833
|
+
input: { base: { color: 'var(--color-base-content)', flexGrow: '1' } },
|
|
4834
|
+
'increment-trigger': {
|
|
4835
|
+
base: {
|
|
4836
|
+
display: 'flex',
|
|
4837
|
+
alignItems: 'center',
|
|
4838
|
+
justifyContent: 'center',
|
|
4839
|
+
paddingLeft: 'var(--space-lg)',
|
|
4840
|
+
paddingRight: 'var(--space-lg)',
|
|
4841
|
+
borderLeft: 'var(--border) solid var(--color-base-300)',
|
|
4842
|
+
borderTopRightRadius: 'var(--radius-field)',
|
|
4843
|
+
borderBottomRightRadius: 'var(--radius-field)',
|
|
4844
|
+
},
|
|
4845
|
+
},
|
|
4846
|
+
'decrement-trigger': {
|
|
4847
|
+
base: {
|
|
4848
|
+
display: 'flex',
|
|
4849
|
+
alignItems: 'center',
|
|
4850
|
+
justifyContent: 'center',
|
|
4851
|
+
paddingLeft: 'var(--space-lg)',
|
|
4852
|
+
paddingRight: 'var(--space-lg)',
|
|
4853
|
+
borderRight: 'var(--border) solid var(--color-base-300)',
|
|
4854
|
+
borderTopLeftRadius: 'var(--radius-field)',
|
|
4855
|
+
borderBottomLeftRadius: 'var(--radius-field)',
|
|
4856
|
+
},
|
|
4857
|
+
},
|
|
4619
4858
|
},
|
|
4620
4859
|
},
|
|
4621
4860
|
},
|
|
@@ -5029,6 +5268,15 @@ const affixSize = (fontSize: string) => ({
|
|
|
5029
5268
|
'visibility-trigger': { base: { fontSize } },
|
|
5030
5269
|
});
|
|
5031
5270
|
|
|
5271
|
+
/**
|
|
5272
|
+
* A text field's placeholder ink on lynx (signalxjs/lynx#1205): the web's
|
|
5273
|
+
* `::placeholder` mix, for lynx's `-x-placeholder-color` property — the
|
|
5274
|
+
* emitter bakes the `color-mix()` to one literal per theme.
|
|
5275
|
+
*/
|
|
5276
|
+
const textFieldPlaceholder = 'color-mix(in oklab, var(--color-base-content) 60%, transparent)';
|
|
5277
|
+
/** The ghost field button's held look on lynx — the web's hover wash, on a press. */
|
|
5278
|
+
const fieldButtonHeld: CssProps = { color: 'var(--color-base-content)', background: 'var(--color-base-200)' };
|
|
5279
|
+
|
|
5032
5280
|
/**
|
|
5033
5281
|
* daisy "input" flavor: the shared field box with nothing in it but the text.
|
|
5034
5282
|
* Same `fieldControl` metrics as select's trigger, combobox's control and the
|
|
@@ -5150,6 +5398,53 @@ export const input: RecipeInput = {
|
|
|
5150
5398
|
xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } }, ...affixSize('var(--text-lg)') },
|
|
5151
5399
|
},
|
|
5152
5400
|
},
|
|
5401
|
+
// lynx (signalxjs/lynx#1205). The field is a native text widget inside
|
|
5402
|
+
// a view `control`, so what the web gets from the cascade is restated:
|
|
5403
|
+
targets: {
|
|
5404
|
+
lynx: {
|
|
5405
|
+
parts: {
|
|
5406
|
+
// The web control is an `inline-flex` row; the emitter's
|
|
5407
|
+
// `flex` rewrite keeps no direction, so the row is explicit.
|
|
5408
|
+
// No `:hover` border on a touch platform — the ring below
|
|
5409
|
+
// (box-shadow spread, signalxjs/lynx#1163) is the focus cue,
|
|
5410
|
+
// in the root's accent like the web outline.
|
|
5411
|
+
control: {
|
|
5412
|
+
base: { flexDirection: 'row' },
|
|
5413
|
+
states: { 'focus-visible': lynxFocusRing('var(--input-accent)') },
|
|
5414
|
+
},
|
|
5415
|
+
// `color: inherit` never reaches a native text field, and
|
|
5416
|
+
// `::placeholder` does not exist: the ink is explicit and the
|
|
5417
|
+
// placeholder takes lynx's own `-x-placeholder-color`, the
|
|
5418
|
+
// web's 60% mix baked per theme.
|
|
5419
|
+
input: {
|
|
5420
|
+
base: {
|
|
5421
|
+
color: 'var(--color-base-content)',
|
|
5422
|
+
'-x-placeholder-color': textFieldPlaceholder,
|
|
5423
|
+
flexGrow: '1',
|
|
5424
|
+
flexShrink: '1',
|
|
5425
|
+
},
|
|
5426
|
+
},
|
|
5427
|
+
// The adornment's edge padding, physical: Android never
|
|
5428
|
+
// resolves the logical spellings (signalxjs/lynx#1084).
|
|
5429
|
+
adornment: {
|
|
5430
|
+
selectors: {
|
|
5431
|
+
'&[data-placement="start"]': { paddingLeft: 'var(--space-md)' },
|
|
5432
|
+
'&[data-placement="end"]': { paddingRight: 'var(--space-md)' },
|
|
5433
|
+
},
|
|
5434
|
+
},
|
|
5435
|
+
// Same for the ghost buttons' end margin; the web's `:hover`
|
|
5436
|
+
// wash answers a press instead.
|
|
5437
|
+
'clear-trigger': {
|
|
5438
|
+
base: { marginRight: 'var(--space-xs)' },
|
|
5439
|
+
states: { pressed: fieldButtonHeld },
|
|
5440
|
+
},
|
|
5441
|
+
'visibility-trigger': {
|
|
5442
|
+
base: { marginRight: 'var(--space-xs)' },
|
|
5443
|
+
states: { pressed: fieldButtonHeld },
|
|
5444
|
+
},
|
|
5445
|
+
},
|
|
5446
|
+
},
|
|
5447
|
+
},
|
|
5153
5448
|
};
|
|
5154
5449
|
|
|
5155
5450
|
/**
|
|
@@ -5218,6 +5513,22 @@ export const textarea: RecipeInput = {
|
|
|
5218
5513
|
xl: { textarea: { base: { fontSize: 'var(--text-lg)', minHeight: `calc(${fieldHeight('xl')} * 2)`, padding: 'var(--space-lg) var(--space-xl)' } } },
|
|
5219
5514
|
},
|
|
5220
5515
|
},
|
|
5516
|
+
// lynx (signalxjs/lynx#1205): lynx-zero renders the part as a view (the
|
|
5517
|
+
// box, whose state classes repaint) around the native field, which wears
|
|
5518
|
+
// the part's base class as its text face. So the ink and the placeholder
|
|
5519
|
+
// are explicit here — `color: inherit` and `::placeholder` never reach a
|
|
5520
|
+
// native widget — and the web's `:hover` border and outline ring become
|
|
5521
|
+
// the lynx focus ring in the root's accent (signalxjs/lynx#1163).
|
|
5522
|
+
targets: {
|
|
5523
|
+
lynx: {
|
|
5524
|
+
parts: {
|
|
5525
|
+
textarea: {
|
|
5526
|
+
base: { color: 'var(--color-base-content)', '-x-placeholder-color': textFieldPlaceholder },
|
|
5527
|
+
states: { 'focus-visible': lynxFocusRing('var(--textarea-accent)') },
|
|
5528
|
+
},
|
|
5529
|
+
},
|
|
5530
|
+
},
|
|
5531
|
+
},
|
|
5221
5532
|
};
|
|
5222
5533
|
|
|
5223
5534
|
// ── Content tier (#311) ───────────────────────────────────────────────────
|
|
@@ -7401,7 +7712,8 @@ const sheetSlide = (enter: string, exit: string): PartStyles => {
|
|
|
7401
7712
|
const open = `${sheet}[data-state="open"]`;
|
|
7402
7713
|
const travel = (tempo: string): string => {
|
|
7403
7714
|
const duration = tempo.split(' ')[0];
|
|
7404
|
-
|
|
7715
|
+
// `transform` rides along for the swipe's spring back (#293).
|
|
7716
|
+
return `translate ${tempo}, transform ${tempo}, display ${duration} allow-discrete, overlay ${duration} allow-discrete`;
|
|
7405
7717
|
};
|
|
7406
7718
|
return {
|
|
7407
7719
|
base: { '--drawer-travel': '-100% 0' },
|
|
@@ -7539,8 +7851,27 @@ export const drawer: RecipeInput = {
|
|
|
7539
7851
|
// Trigger-carried axes — see `btnColors` for why the panel is out of
|
|
7540
7852
|
// reach and the trigger is the whole story here.
|
|
7541
7853
|
variants: { color: btnColors(), size: btnSizes },
|
|
7542
|
-
// The btn paddings, restated physically — see `lynxBtnPad` (#1084).
|
|
7543
7854
|
targets: {
|
|
7855
|
+
// Swipe to dismiss (#293) — web only, since the runtime's `--swipe-*`
|
|
7856
|
+
// offset is: the sheet wears the drag offset (`SWIPE`) in every
|
|
7857
|
+
// state — so a swiped sheet leaves from where it was let go — at a
|
|
7858
|
+
// weight above the presence's own open `transform`. Mid-swipe no
|
|
7859
|
+
// transition runs. A touch pan across the dismiss axis stays the
|
|
7860
|
+
// page's; along it is the gesture's, so content taller than a
|
|
7861
|
+
// block-edge sheet scrolls in a box of its own inside the panel.
|
|
7862
|
+
web: {
|
|
7863
|
+
parts: {
|
|
7864
|
+
panel: {
|
|
7865
|
+
selectors: {
|
|
7866
|
+
'&[data-l-dock="sheet"][data-state]': { transform: SWIPE },
|
|
7867
|
+
'&[data-l-dock="sheet"][data-state="open"][data-swiping]': { transition: 'none', userSelect: 'none' },
|
|
7868
|
+
'&[data-l-dock="sheet"]:where([data-placement="start"], [data-placement="end"])': { touchAction: 'pan-y' },
|
|
7869
|
+
'&[data-l-dock="sheet"]:where([data-placement="top"], [data-placement="bottom"])': { touchAction: 'pan-x' },
|
|
7870
|
+
},
|
|
7871
|
+
},
|
|
7872
|
+
},
|
|
7873
|
+
},
|
|
7874
|
+
// The btn paddings, restated physically — see `lynxBtnPad` (#1084).
|
|
7544
7875
|
lynx: {
|
|
7545
7876
|
parts: {
|
|
7546
7877
|
trigger: { base: lynxBtnPad(4) },
|