@sigx/zero-daisyui 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.json +60 -0
- package/dist/css/components/combobox.css +7 -1
- package/dist/css/components/spinner.css +5 -5
- package/dist/css/index.css +12 -6
- package/dist/lynx/components/alert.css +42 -6
- package/dist/lynx/components/avatar-group.css +13 -1
- package/dist/lynx/components/avatar.css +56 -1
- package/dist/lynx/components/badge.css +209 -4
- package/dist/lynx/components/card.css +12 -1
- package/dist/lynx/components/checkbox.css +40 -9
- package/dist/lynx/components/combobox.css +7 -1
- package/dist/lynx/components/divider.css +45 -0
- package/dist/lynx/components/empty-state.css +7 -1
- package/dist/lynx/components/kbd.css +15 -1
- package/dist/lynx/components/number-input.css +20 -0
- package/dist/lynx/components/radio-group.css +0 -1
- package/dist/lynx/components/skeleton.css +1 -1
- package/dist/lynx/components/spinner.css +32 -12
- package/dist/lynx/components/stats.css +39 -3
- package/dist/lynx/components/status.css +197 -1
- package/dist/lynx/components/textarea.css +9 -6
- package/dist/lynx/components/toast.css +328 -15
- package/dist/lynx/components/toggle-group.css +18 -5
- package/dist/lynx/index.css +1095 -80
- package/dist/lynx/manifest.json +3 -3
- package/dist/lynx/tokens.css +0 -6
- package/dist/manifest.json +1 -1
- package/dist/recipes.d.ts +6 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +821 -34
- package/dist/recipes.js.map +1 -1
- package/dist/report.json +92 -61
- package/package.json +4 -4
- package/src/recipes.ts +825 -34
package/dist/recipes.js
CHANGED
|
@@ -1754,6 +1754,12 @@ export const fieldset = {
|
|
|
1754
1754
|
* horizontal bar and lifts it to the middle. All three verbatim from daisyUI
|
|
1755
1755
|
* 5.7.8's `.checkbox:before` / `:checked` / `:indeterminate`.
|
|
1756
1756
|
*/
|
|
1757
|
+
/**
|
|
1758
|
+
* The lynx tick's stroke: 20% of the checkbox's content box — the width of
|
|
1759
|
+
* daisy's polygon bars — as a length, since borders take no percentage.
|
|
1760
|
+
* The content box is the control less its padding and border on both sides.
|
|
1761
|
+
*/
|
|
1762
|
+
const CHECKBOX_LYNX_STROKE = 'calc((var(--checkbox-size) - var(--checkbox-pad) * 2 - var(--border) * 2) * 0.2)';
|
|
1757
1763
|
const TICK_COLLAPSED = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%)';
|
|
1758
1764
|
const TICK_DRAWN = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%)';
|
|
1759
1765
|
const DASH_DRAWN = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%)';
|
|
@@ -1991,26 +1997,62 @@ export const checkbox = {
|
|
|
1991
1997
|
},
|
|
1992
1998
|
},
|
|
1993
1999
|
indicator: {
|
|
1994
|
-
// The tick
|
|
1995
|
-
//
|
|
1996
|
-
//
|
|
2000
|
+
// The tick WITHOUT clip-path, which lynx does not apply
|
|
2001
|
+
// (measured on iOS, signalxjs/lynx#1216 — daisy's
|
|
2002
|
+
// polygon-cut box drew as a solid diamond; the emitter
|
|
2003
|
+
// now drops every clip-path). daisy's drawn polygon is an
|
|
2004
|
+
// L inside the content box: a bar at x 50–70% over the
|
|
2005
|
+
// full height and a foot at y 80–100% from x 20%, both
|
|
2006
|
+
// 20% of the box thick, rotated 45° about the box's
|
|
2007
|
+
// centre. That is exactly the classic two-border check:
|
|
2008
|
+
// a 50%-wide full-height box at x 20% with a right and a
|
|
2009
|
+
// bottom border 20% of the content box thick, turned
|
|
2010
|
+
// about the content box's centre (60% 50% of itself).
|
|
2011
|
+
//
|
|
2012
|
+
// The ink: the control's `color:` names
|
|
2013
|
+
// `--checkbox-on-accent`, and `currentColor` was that ink
|
|
2014
|
+
// by inheritance (it never resolves on lynx,
|
|
2015
|
+
// signalxjs/lynx#1079) — spent directly on the borders.
|
|
1997
2016
|
//
|
|
1998
|
-
// The
|
|
1999
|
-
//
|
|
2000
|
-
//
|
|
2001
|
-
//
|
|
2002
|
-
// proven on both platforms — and the transition follows
|
|
2003
|
-
// the property that now moves.
|
|
2017
|
+
// The motion is `transform`: the standalone `rotate` /
|
|
2018
|
+
// `translate` properties resolve on iOS but not on
|
|
2019
|
+
// Android (signalxjs/lynx#1084). No sheen shadow — on a
|
|
2020
|
+
// hollow box it would band the inside of the L.
|
|
2004
2021
|
base: {
|
|
2005
|
-
|
|
2022
|
+
boxSizing: 'border-box',
|
|
2023
|
+
width: '50%',
|
|
2024
|
+
height: '100%',
|
|
2025
|
+
marginLeft: '20%',
|
|
2026
|
+
backgroundColor: 'transparent',
|
|
2027
|
+
borderStyle: 'solid',
|
|
2028
|
+
borderColor: 'var(--checkbox-on-accent)',
|
|
2029
|
+
borderTopWidth: '0',
|
|
2030
|
+
borderLeftWidth: '0',
|
|
2031
|
+
borderRightWidth: CHECKBOX_LYNX_STROKE,
|
|
2032
|
+
borderBottomWidth: CHECKBOX_LYNX_STROKE,
|
|
2033
|
+
boxShadow: 'none',
|
|
2034
|
+
transformOrigin: '60% 50%',
|
|
2006
2035
|
transform: 'rotate(45deg)',
|
|
2007
|
-
transition: '
|
|
2008
|
-
+ 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
|
|
2036
|
+
transition: 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
|
|
2009
2037
|
+ 'transform var(--duration-slow) var(--ease-standard) var(--duration-instant)',
|
|
2010
2038
|
},
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2039
|
+
states: {
|
|
2040
|
+
// daisy's dash (signalxjs/lynx#1217): the same six
|
|
2041
|
+
// points un-rotated into a bar at x 20–80%, 20% of
|
|
2042
|
+
// the box tall, lifted to the middle. Here a filled,
|
|
2043
|
+
// borderless 60% × 20% box centred on the cross
|
|
2044
|
+
// axis — the unclipped full-height box had painted
|
|
2045
|
+
// the top half of the control.
|
|
2046
|
+
indeterminate: {
|
|
2047
|
+
width: '60%',
|
|
2048
|
+
height: '20%',
|
|
2049
|
+
alignSelf: 'center',
|
|
2050
|
+
borderRightWidth: '0',
|
|
2051
|
+
borderBottomWidth: '0',
|
|
2052
|
+
backgroundColor: 'var(--checkbox-on-accent)',
|
|
2053
|
+
transform: 'rotate(0deg)',
|
|
2054
|
+
},
|
|
2055
|
+
},
|
|
2014
2056
|
},
|
|
2015
2057
|
},
|
|
2016
2058
|
},
|
|
@@ -3632,6 +3674,80 @@ export const avatar = {
|
|
|
3632
3674
|
xl: { root: { base: { '--avatar-size': 'calc(var(--size-selector) * 16)', '--avatar-text': 'var(--text-lg)' } } },
|
|
3633
3675
|
},
|
|
3634
3676
|
},
|
|
3677
|
+
// signalxjs/lynx#1237 — the lynx layout for what the web gets from grid.
|
|
3678
|
+
targets: {
|
|
3679
|
+
lynx: {
|
|
3680
|
+
parts: {
|
|
3681
|
+
// No grid on lynx (signalxjs/lynx#1075): the web stacks the
|
|
3682
|
+
// image and the fallback in one grid cell. Here the root is a
|
|
3683
|
+
// flex box that does not shrink in a row, and both faces are
|
|
3684
|
+
// laid over it absolutely — the fallback, later in the tree,
|
|
3685
|
+
// paints over a pending image exactly as the grid's second
|
|
3686
|
+
// item does. lynx-zero renders neither face while its state
|
|
3687
|
+
// hides it (`hiddenIn`), so the web's `&:not([hidden])`
|
|
3688
|
+
// display rule has nothing to replace.
|
|
3689
|
+
root: {
|
|
3690
|
+
base: {
|
|
3691
|
+
display: 'flex',
|
|
3692
|
+
flexDirection: 'row',
|
|
3693
|
+
alignItems: 'center',
|
|
3694
|
+
justifyContent: 'center',
|
|
3695
|
+
flexShrink: '0',
|
|
3696
|
+
},
|
|
3697
|
+
},
|
|
3698
|
+
// Each face also rounds itself to the root's shape (below),
|
|
3699
|
+
// rather than trusting the root's clip to round an image on
|
|
3700
|
+
// both engines.
|
|
3701
|
+
image: {
|
|
3702
|
+
base: { position: 'absolute', top: '0', left: '0', borderRadius: '9999px' },
|
|
3703
|
+
},
|
|
3704
|
+
fallback: {
|
|
3705
|
+
base: {
|
|
3706
|
+
position: 'absolute',
|
|
3707
|
+
top: '0',
|
|
3708
|
+
left: '0',
|
|
3709
|
+
borderRadius: '9999px',
|
|
3710
|
+
display: 'flex',
|
|
3711
|
+
alignItems: 'center',
|
|
3712
|
+
justifyContent: 'center',
|
|
3713
|
+
textAlign: 'center',
|
|
3714
|
+
},
|
|
3715
|
+
},
|
|
3716
|
+
},
|
|
3717
|
+
variants: {
|
|
3718
|
+
shape: Object.fromEntries([
|
|
3719
|
+
['circle', '9999px'], ['square', '0'], ['rounded', 'var(--radius-selector)'],
|
|
3720
|
+
].map(([shape, radius]) => [shape, {
|
|
3721
|
+
image: { base: { borderRadius: radius } },
|
|
3722
|
+
fallback: { base: { borderRadius: radius } },
|
|
3723
|
+
}])),
|
|
3724
|
+
},
|
|
3725
|
+
// Every avatar after the first in an AvatarGroup overlaps its
|
|
3726
|
+
// neighbour by a quarter of its own box — the web's
|
|
3727
|
+
// `avatar-group` compose, `&:not(:first-child)`. Lynx has neither
|
|
3728
|
+
// the nested-scope compose nor `:first-child`, so lynx-zero stamps
|
|
3729
|
+
// those avatars `stacked` itself (`zx-m-stacked`, in mount order).
|
|
3730
|
+
// A lynx-zero rendering detail, not a modifier an author sets, so
|
|
3731
|
+
// it is styled from raw lynx css, one rule per avatar size.
|
|
3732
|
+
css: `
|
|
3733
|
+
.zx-avatar__root.zx-m-stacked {
|
|
3734
|
+
margin-left: calc(var(--size-selector) * -2.5);
|
|
3735
|
+
}
|
|
3736
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-xs {
|
|
3737
|
+
margin-left: calc(var(--size-selector) * -1.5);
|
|
3738
|
+
}
|
|
3739
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-sm {
|
|
3740
|
+
margin-left: calc(var(--size-selector) * -2);
|
|
3741
|
+
}
|
|
3742
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-lg {
|
|
3743
|
+
margin-left: calc(var(--size-selector) * -3);
|
|
3744
|
+
}
|
|
3745
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-xl {
|
|
3746
|
+
margin-left: calc(var(--size-selector) * -4);
|
|
3747
|
+
}
|
|
3748
|
+
`,
|
|
3749
|
+
},
|
|
3750
|
+
},
|
|
3635
3751
|
};
|
|
3636
3752
|
/**
|
|
3637
3753
|
* Avatar group (#297) — daisy's `avatar-group`: the faces overlap by a
|
|
@@ -3724,6 +3840,33 @@ export const avatarGroup = {
|
|
|
3724
3840
|
md: {},
|
|
3725
3841
|
},
|
|
3726
3842
|
},
|
|
3843
|
+
// signalxjs/lynx#1237. The avatars inside take the group's size and
|
|
3844
|
+
// colour from lynx-zero (the `composes` above has no class form on this
|
|
3845
|
+
// target), and their overlap is the avatar recipe's `stacked` rule.
|
|
3846
|
+
targets: {
|
|
3847
|
+
lynx: {
|
|
3848
|
+
parts: {
|
|
3849
|
+
// A row that sizes to its faces (a lynx view stretches to its
|
|
3850
|
+
// column otherwise).
|
|
3851
|
+
root: { base: { display: 'flex', flexDirection: 'row', width: 'max-content' } },
|
|
3852
|
+
// `inline-grid` centring as flex, and the logical inline
|
|
3853
|
+
// padding and overlap as physical spellings — logical
|
|
3854
|
+
// margins and paddings resolve on iOS but not on Android
|
|
3855
|
+
// (signalxjs/lynx#1084); lynx has no RTL flow, so start IS left.
|
|
3856
|
+
overflow: {
|
|
3857
|
+
base: {
|
|
3858
|
+
display: 'flex',
|
|
3859
|
+
alignItems: 'center',
|
|
3860
|
+
justifyContent: 'center',
|
|
3861
|
+
flexShrink: '0',
|
|
3862
|
+
paddingLeft: 'var(--space-2xs)',
|
|
3863
|
+
paddingRight: 'var(--space-2xs)',
|
|
3864
|
+
marginLeft: 'calc(var(--avatar-group-size) * -0.25)',
|
|
3865
|
+
},
|
|
3866
|
+
},
|
|
3867
|
+
},
|
|
3868
|
+
},
|
|
3869
|
+
},
|
|
3727
3870
|
};
|
|
3728
3871
|
/**
|
|
3729
3872
|
* A toast whose indicator is rendered (#292): the grid grows a leading column
|
|
@@ -4017,7 +4160,99 @@ export const toast = {
|
|
|
4017
4160
|
base: { position: 'absolute', top: 'var(--space-xs)', right: 'var(--space-xs)' },
|
|
4018
4161
|
states: lynxFocus({ gap: 'var(--toast-bg)' }),
|
|
4019
4162
|
},
|
|
4163
|
+
// The promise toast's mark (signalxjs/lynx#1196). No grid and
|
|
4164
|
+
// no `:has()`: the mark is laid over the card's leading
|
|
4165
|
+
// padding, at the root's own padding offsets (per size,
|
|
4166
|
+
// below), and lynx-zero stamps `marked` on every part of a
|
|
4167
|
+
// toast that shows it, so the text steps aside (the raw css
|
|
4168
|
+
// below) — the web's leading grid column.
|
|
4169
|
+
indicator: {
|
|
4170
|
+
base: {
|
|
4171
|
+
position: 'absolute',
|
|
4172
|
+
left: 'var(--space-lg)',
|
|
4173
|
+
top: 'var(--space-md)',
|
|
4174
|
+
width: 'var(--toast-mark)',
|
|
4175
|
+
height: 'var(--toast-mark)',
|
|
4176
|
+
},
|
|
4177
|
+
states: {
|
|
4178
|
+
// daisy's ring in the toast's ink. The track is the
|
|
4179
|
+
// ink at 25%, which cannot bake over the recipe-local
|
|
4180
|
+
// `--toast-ink`: the neutral toast's track mixes the
|
|
4181
|
+
// theme's base-content, and each colour's twin (the
|
|
4182
|
+
// variants below) its role. Longhands, and the head as
|
|
4183
|
+
// `border-top-color`: logical spellings are not lynx's
|
|
4184
|
+
// (signalxjs/lynx#1084), and a var-bearing shorthand
|
|
4185
|
+
// would repaint the head after the cascade (#1161).
|
|
4186
|
+
loading: {
|
|
4187
|
+
// The shared shorthand is dropped on lynx (its
|
|
4188
|
+
// track cannot bake); a static one here keeps it
|
|
4189
|
+
// that way should the shared value ever bake.
|
|
4190
|
+
border: '0 solid transparent',
|
|
4191
|
+
borderStyle: 'solid',
|
|
4192
|
+
...lynxSides('border{}Width', 'calc(var(--border) * 2)'),
|
|
4193
|
+
...lynxSides('border{}Color', 'color-mix(in oklab, var(--color-base-content) 25%, transparent)', ['Right', 'Bottom', 'Left']),
|
|
4194
|
+
borderTopColor: 'var(--toast-ink)',
|
|
4195
|
+
},
|
|
4196
|
+
// The tick without clip-path, which lynx does not apply
|
|
4197
|
+
// (signalxjs/lynx#1216): the checkbox's two-border
|
|
4198
|
+
// check — a box 3/8 of the mark wide and 3/4 tall with
|
|
4199
|
+
// its right and bottom borders drawn, turned 45° —
|
|
4200
|
+
// pulled to the middle of the mark's slot by its
|
|
4201
|
+
// margins. The fill the web cuts the tick from is
|
|
4202
|
+
// cleared.
|
|
4203
|
+
complete: {
|
|
4204
|
+
background: 'transparent',
|
|
4205
|
+
boxSizing: 'border-box',
|
|
4206
|
+
width: 'calc(var(--toast-mark) * 0.375)',
|
|
4207
|
+
height: 'calc(var(--toast-mark) * 0.75)',
|
|
4208
|
+
marginLeft: 'calc(var(--toast-mark) * 0.3125)',
|
|
4209
|
+
borderStyle: 'solid',
|
|
4210
|
+
borderColor: TOAST_SUCCESS_INK,
|
|
4211
|
+
borderTopWidth: '0',
|
|
4212
|
+
borderLeftWidth: '0',
|
|
4213
|
+
borderRightWidth: 'calc(var(--border) * 2)',
|
|
4214
|
+
borderBottomWidth: 'calc(var(--border) * 2)',
|
|
4215
|
+
transform: 'rotate(45deg)',
|
|
4216
|
+
},
|
|
4217
|
+
// The cross without clip-path: its two diagonal bars
|
|
4218
|
+
// as hard-stop gradients over a clear fill.
|
|
4219
|
+
error: {
|
|
4220
|
+
background: `linear-gradient(45deg, transparent 41%, ${TOAST_ERROR_INK} 41%, ${TOAST_ERROR_INK} 59%, transparent 59%), `
|
|
4221
|
+
+ `linear-gradient(-45deg, transparent 41%, ${TOAST_ERROR_INK} 41%, ${TOAST_ERROR_INK} 59%, transparent 59%)`,
|
|
4222
|
+
},
|
|
4223
|
+
},
|
|
4224
|
+
},
|
|
4020
4225
|
},
|
|
4226
|
+
variants: {
|
|
4227
|
+
// Each colour's ring track: its role at 25% (the web's mix
|
|
4228
|
+
// over `--toast-ink`, which is that role).
|
|
4229
|
+
color: Object.fromEntries(ROLES.map((role) => [role, {
|
|
4230
|
+
indicator: {
|
|
4231
|
+
states: {
|
|
4232
|
+
loading: lynxSides('border{}Color', `color-mix(in oklab, var(--color-${role}) 25%, transparent)`, ['Right', 'Bottom', 'Left']),
|
|
4233
|
+
},
|
|
4234
|
+
},
|
|
4235
|
+
}])),
|
|
4236
|
+
// The mark sits at the root's padding offsets for its size.
|
|
4237
|
+
size: {
|
|
4238
|
+
xs: { indicator: { base: { left: 'var(--space-md)', top: 'var(--space-xs)' } } },
|
|
4239
|
+
sm: { indicator: { base: { left: 'var(--space-md)', top: 'var(--space-sm)' } } },
|
|
4240
|
+
lg: { indicator: { base: { left: 'var(--space-xl)', top: 'var(--space-lg)' } } },
|
|
4241
|
+
xl: { indicator: { base: { left: 'var(--space-2xl)', top: 'var(--space-xl)' } } },
|
|
4242
|
+
},
|
|
4243
|
+
},
|
|
4244
|
+
// The text of a marked toast steps past the mark and the web's
|
|
4245
|
+
// column gap. `marked` is a lynx-zero rendering detail (the
|
|
4246
|
+
// spelling of `:has(> indicator)`), not a modifier an author sets,
|
|
4247
|
+
// so it is styled from raw lynx css. The reduced-motion and
|
|
4248
|
+
// forced-colors stops cannot follow: lynx emits no `@media`.
|
|
4249
|
+
css: `
|
|
4250
|
+
.zx-toast__title.zx-m-marked,
|
|
4251
|
+
.zx-toast__description.zx-m-marked,
|
|
4252
|
+
.zx-toast__action.zx-m-marked {
|
|
4253
|
+
margin-left: calc(var(--toast-mark) + var(--space-md));
|
|
4254
|
+
}
|
|
4255
|
+
`,
|
|
4021
4256
|
},
|
|
4022
4257
|
},
|
|
4023
4258
|
// The viewport's `open` (the stack expanded) is a fact this skin has no
|
|
@@ -4045,6 +4280,11 @@ export const combobox = {
|
|
|
4045
4280
|
flexWrap: 'wrap',
|
|
4046
4281
|
minWidth: '13rem',
|
|
4047
4282
|
...fieldControl,
|
|
4283
|
+
// The one field control that wraps: under `multiple` its tags
|
|
4284
|
+
// flow onto new rows, so the field step is a floor, not a cap —
|
|
4285
|
+
// a fixed height spills wrapped tags over the next field (#407).
|
|
4286
|
+
height: 'auto',
|
|
4287
|
+
minHeight: fieldHeight('md'),
|
|
4048
4288
|
transition: 'border-color var(--duration-fast) var(--ease-standard)',
|
|
4049
4289
|
},
|
|
4050
4290
|
states: {
|
|
@@ -4107,7 +4347,8 @@ export const combobox = {
|
|
|
4107
4347
|
fontWeight: 'var(--weight-medium)',
|
|
4108
4348
|
lineHeight: 'var(--leading-none)',
|
|
4109
4349
|
},
|
|
4110
|
-
|
|
4350
|
+
// Arrowed onto from the input (#411).
|
|
4351
|
+
states: { disabled: {}, ...focusRing },
|
|
4111
4352
|
},
|
|
4112
4353
|
'tag-label': { base: { whiteSpace: 'nowrap' } },
|
|
4113
4354
|
'tag-remove': {
|
|
@@ -4388,6 +4629,10 @@ export const toggle = {
|
|
|
4388
4629
|
},
|
|
4389
4630
|
},
|
|
4390
4631
|
};
|
|
4632
|
+
/** A toggle-group end item's outer corner: the frame's radius inside its hairline. */
|
|
4633
|
+
const joinInnerRadius = 'calc(var(--radius-field) - var(--border))';
|
|
4634
|
+
/** A zero seam width that still holds var(), so it can beat the var-bearing seam on lynx. */
|
|
4635
|
+
const noSeam = 'calc(var(--border) * 0)';
|
|
4391
4636
|
// daisy "join" of btns: one bordered capsule, hairline seams between items,
|
|
4392
4637
|
// the on item filled with the accent.
|
|
4393
4638
|
export const toggleGroup = {
|
|
@@ -4515,15 +4760,13 @@ export const toggleGroup = {
|
|
|
4515
4760
|
...lynxSides('border{}Width', 'var(--border)'),
|
|
4516
4761
|
...lynxSides('border{}Color', 'var(--color-base-300)'),
|
|
4517
4762
|
},
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
'&[data-orientation="vertical"]': { borderTopWidth: '0' },
|
|
4526
|
-
},
|
|
4763
|
+
// The frame keeps all four edges (signalxjs/lynx#1218).
|
|
4764
|
+
// It used to give up its leading edge to the first item's
|
|
4765
|
+
// seam and trust its own clip to round that item's
|
|
4766
|
+
// corners, but on iOS the frame's clip does not round the
|
|
4767
|
+
// leading corners: the first item painted square, and its
|
|
4768
|
+
// seam was lost with them. The `first`/`last` modifiers
|
|
4769
|
+
// below carry the join's ends instead.
|
|
4527
4770
|
},
|
|
4528
4771
|
item: {
|
|
4529
4772
|
base: { flexShrink: '0', flexWrap: 'nowrap' },
|
|
@@ -4539,6 +4782,10 @@ export const toggleGroup = {
|
|
|
4539
4782
|
},
|
|
4540
4783
|
selectors: {
|
|
4541
4784
|
'&[data-state="on"][data-pressed]': { background: 'var(--toggle-group-accent)' },
|
|
4785
|
+
// The join seams: lynx has no sibling combinator, so
|
|
4786
|
+
// the web's `item + item` edge cannot be selected.
|
|
4787
|
+
// Every item draws its leading seam, and the `first`
|
|
4788
|
+
// modifier takes it off the item at the frame's edge.
|
|
4542
4789
|
'&[data-orientation="horizontal"]': {
|
|
4543
4790
|
borderLeftWidth: 'var(--border)',
|
|
4544
4791
|
borderLeftStyle: 'solid',
|
|
@@ -4552,6 +4799,40 @@ export const toggleGroup = {
|
|
|
4552
4799
|
},
|
|
4553
4800
|
},
|
|
4554
4801
|
},
|
|
4802
|
+
// The ends of the join (signalxjs/lynx#1218). Lynx has no
|
|
4803
|
+
// `:first-child` or `:last-child`, so lynx-zero stamps the end
|
|
4804
|
+
// items itself, as `data-mod-first` / `data-mod-last` (the
|
|
4805
|
+
// `zx-m-first` / `zx-m-last` classes). They are a lynx-zero
|
|
4806
|
+
// rendering detail, not modifiers an author sets, so they are
|
|
4807
|
+
// not declared in the vocabulary and are styled from raw lynx
|
|
4808
|
+
// css. The end items round their own outer corners to the
|
|
4809
|
+
// frame's inner radius, so an on or held end item follows the
|
|
4810
|
+
// frame's corners without relying on its clip; `first` also
|
|
4811
|
+
// drops the leading seam, which would double the frame's edge.
|
|
4812
|
+
// That zero width is spelled through var() on purpose: a static
|
|
4813
|
+
// longhand loses to the var-bearing seam width whatever its
|
|
4814
|
+
// specificity (lynx expands var() declarations after the
|
|
4815
|
+
// cascade, signalxjs/lynx#1161).
|
|
4816
|
+
css: `
|
|
4817
|
+
.zx-toggle-group__item.zx-m-first.zx-o-horizontal {
|
|
4818
|
+
border-left-width: ${noSeam};
|
|
4819
|
+
border-top-left-radius: ${joinInnerRadius};
|
|
4820
|
+
border-bottom-left-radius: ${joinInnerRadius};
|
|
4821
|
+
}
|
|
4822
|
+
.zx-toggle-group__item.zx-m-first.zx-o-vertical {
|
|
4823
|
+
border-top-width: ${noSeam};
|
|
4824
|
+
border-top-left-radius: ${joinInnerRadius};
|
|
4825
|
+
border-top-right-radius: ${joinInnerRadius};
|
|
4826
|
+
}
|
|
4827
|
+
.zx-toggle-group__item.zx-m-last.zx-o-horizontal {
|
|
4828
|
+
border-top-right-radius: ${joinInnerRadius};
|
|
4829
|
+
border-bottom-right-radius: ${joinInnerRadius};
|
|
4830
|
+
}
|
|
4831
|
+
.zx-toggle-group__item.zx-m-last.zx-o-vertical {
|
|
4832
|
+
border-bottom-left-radius: ${joinInnerRadius};
|
|
4833
|
+
border-bottom-right-radius: ${joinInnerRadius};
|
|
4834
|
+
}
|
|
4835
|
+
`,
|
|
4555
4836
|
},
|
|
4556
4837
|
},
|
|
4557
4838
|
};
|
|
@@ -4719,7 +5000,19 @@ export const numberInput = {
|
|
|
4719
5000
|
base: { overflow: 'visible' },
|
|
4720
5001
|
states: { 'focus-visible': lynxFocusRing('var(--number-input-accent)') },
|
|
4721
5002
|
},
|
|
4722
|
-
|
|
5003
|
+
// `::placeholder` does not exist on lynx, so the web's 60%
|
|
5004
|
+
// mix rides lynx's own `-x-placeholder-color` (baked per
|
|
5005
|
+
// theme) — without it the platform default reads dark on
|
|
5006
|
+
// dark (signalxjs/lynx#1222). Spelled out, not the
|
|
5007
|
+
// `textFieldPlaceholder` const: that one is declared further
|
|
5008
|
+
// down the module, after this literal is evaluated.
|
|
5009
|
+
input: {
|
|
5010
|
+
base: {
|
|
5011
|
+
color: 'var(--color-base-content)',
|
|
5012
|
+
'-x-placeholder-color': 'color-mix(in oklab, var(--color-base-content) 60%, transparent)',
|
|
5013
|
+
flexGrow: '1',
|
|
5014
|
+
},
|
|
5015
|
+
},
|
|
4723
5016
|
'increment-trigger': {
|
|
4724
5017
|
base: {
|
|
4725
5018
|
display: 'flex',
|
|
@@ -5323,6 +5616,8 @@ export const input = {
|
|
|
5323
5616
|
},
|
|
5324
5617
|
},
|
|
5325
5618
|
};
|
|
5619
|
+
/** The lynx textarea floor: two field heights, as ONE calc() (signalxjs/lynx#1219). */
|
|
5620
|
+
const textareaFloor = (step) => `calc(var(--size-field) * ${FIELD_STEPS[step] * 2})`;
|
|
5326
5621
|
/**
|
|
5327
5622
|
* daisy "textarea" flavor. The `fieldControl` box minus its `height`: the
|
|
5328
5623
|
* whole point of a textarea is that its height is the content's (and the
|
|
@@ -5395,12 +5690,64 @@ export const textarea = {
|
|
|
5395
5690
|
// are explicit here — `color: inherit` and `::placeholder` never reach a
|
|
5396
5691
|
// native widget — and the web's `:hover` border and outline ring become
|
|
5397
5692
|
// the lynx focus ring in the root's accent (signalxjs/lynx#1163).
|
|
5693
|
+
//
|
|
5694
|
+
// The floor is one flat `calc(var(--size-field) * n)` per size
|
|
5695
|
+
// (signalxjs/lynx#1219): the shared `calc(fieldHeight * 2)` nests a
|
|
5696
|
+
// calc() in a calc(), and on device the box never took it — an empty
|
|
5697
|
+
// textarea sat one line tall.
|
|
5698
|
+
//
|
|
5699
|
+
// The focus ring is NOT `lynxFocusRing`'s two stacked outset shadows
|
|
5700
|
+
// (signalxjs/lynx#1220). Lynx iOS re-inserts a view's outset shadow
|
|
5701
|
+
// layers at a stale index whenever it re-lays the view out
|
|
5702
|
+
// (`LynxBackgroundManager.updateShadow` reads the sublayer count before
|
|
5703
|
+
// removing the old layers), which reverses their stacking: the 4px ring
|
|
5704
|
+
// lands on top of the 2px gap and paints one solid band. A fixed-height
|
|
5705
|
+
// box never re-lays out; this auto-height one does, as its native field
|
|
5706
|
+
// measures and grows. So the ring is spelled with one outset shadow and
|
|
5707
|
+
// inset shadows, which paint areas the outset never reaches: the ring
|
|
5708
|
+
// just outside the box, the box's own border repainted as the base-100
|
|
5709
|
+
// gap, and the hairline redrawn inside it by the edge inset's spread, in the
|
|
5710
|
+
// edge colour (`--textarea-edge`, the error colour while invalid). The
|
|
5711
|
+
// gap is the border's width instead of the web's 2px.
|
|
5712
|
+
//
|
|
5713
|
+
// Android paints inset shadows OVER the border, where iOS paints them
|
|
5714
|
+
// under it (signalxjs/lynx#1230): the base-100 border vanished and the
|
|
5715
|
+
// whole 2px inset showed as a grey band against the ring. So the gap is
|
|
5716
|
+
// also drawn as a 1px base-100 inset stacked above the edge inset. On
|
|
5717
|
+
// Android that inset is the gap. On iOS the border already covers it,
|
|
5718
|
+
// whichever order the layers end up in, so iOS paints as before.
|
|
5398
5719
|
targets: {
|
|
5399
5720
|
lynx: {
|
|
5400
5721
|
parts: {
|
|
5401
5722
|
textarea: {
|
|
5402
|
-
base: {
|
|
5403
|
-
|
|
5723
|
+
base: {
|
|
5724
|
+
color: 'var(--color-base-content)',
|
|
5725
|
+
'-x-placeholder-color': textFieldPlaceholder,
|
|
5726
|
+
'--textarea-edge': 'var(--color-base-300)',
|
|
5727
|
+
minHeight: textareaFloor('md'),
|
|
5728
|
+
},
|
|
5729
|
+
states: {
|
|
5730
|
+
invalid: { '--textarea-edge': 'var(--color-error)' },
|
|
5731
|
+
'focus-visible': {
|
|
5732
|
+
outline: 'none',
|
|
5733
|
+
borderColor: 'var(--color-base-100)',
|
|
5734
|
+
boxShadow: '0 0 0 2px var(--textarea-accent), '
|
|
5735
|
+
// The gap: the border's width in base-100,
|
|
5736
|
+
// for Android, which paints this over the border.
|
|
5737
|
+
+ 'inset 0 0 0 1px var(--color-base-100), '
|
|
5738
|
+
// Twice the border (1px in every shipped
|
|
5739
|
+
// theme): the inner half shows as the hairline.
|
|
5740
|
+
+ 'inset 0 0 0 2px var(--textarea-edge)',
|
|
5741
|
+
},
|
|
5742
|
+
},
|
|
5743
|
+
},
|
|
5744
|
+
},
|
|
5745
|
+
variants: {
|
|
5746
|
+
size: {
|
|
5747
|
+
xs: { textarea: { base: { minHeight: textareaFloor('xs') } } },
|
|
5748
|
+
sm: { textarea: { base: { minHeight: textareaFloor('sm') } } },
|
|
5749
|
+
lg: { textarea: { base: { minHeight: textareaFloor('lg') } } },
|
|
5750
|
+
xl: { textarea: { base: { minHeight: textareaFloor('xl') } } },
|
|
5404
5751
|
},
|
|
5405
5752
|
},
|
|
5406
5753
|
},
|
|
@@ -5513,6 +5860,42 @@ export const card = {
|
|
|
5513
5860
|
xl: { root: { base: { '--card-pad': 'var(--space-2xl)' } } },
|
|
5514
5861
|
},
|
|
5515
5862
|
},
|
|
5863
|
+
targets: {
|
|
5864
|
+
lynx: {
|
|
5865
|
+
parts: {
|
|
5866
|
+
// The root does not clip on lynx: a clipping layer takes its
|
|
5867
|
+
// own shadow with it on iOS, and the card's elevation is the
|
|
5868
|
+
// daisy look. The media band rounds its own corners instead
|
|
5869
|
+
// (below), which is all the clip was for.
|
|
5870
|
+
root: { base: { overflow: 'visible' } },
|
|
5871
|
+
// `inline-size` has no lynx spelling; the image selector and
|
|
5872
|
+
// the `:first-child` / `:last-child` corners are dropped (no
|
|
5873
|
+
// child or structural pseudo-classes). The band clips its
|
|
5874
|
+
// own content, so an `<image>` inside it takes the corners.
|
|
5875
|
+
media: { base: { width: '100%', overflow: 'hidden' } },
|
|
5876
|
+
// A lynx view is a column unless told otherwise.
|
|
5877
|
+
footer: { base: { flexDirection: 'row' } },
|
|
5878
|
+
},
|
|
5879
|
+
// The corners the media band shares with the card
|
|
5880
|
+
// (signalxjs/lynx#1235). Lynx has no `:first-child` or
|
|
5881
|
+
// `:last-child`, so lynx-zero stamps the band itself from its
|
|
5882
|
+
// mount order among the card's bands, as `data-mod-first` /
|
|
5883
|
+
// `data-mod-last` (the `zx-m-first` / `zx-m-last` classes). They
|
|
5884
|
+
// are a lynx-zero rendering detail, not modifiers an author sets,
|
|
5885
|
+
// so they are not declared in the vocabulary and are styled from
|
|
5886
|
+
// raw lynx css (toggle-group's join ends are the precedent).
|
|
5887
|
+
css: `
|
|
5888
|
+
.zx-card__media.zx-m-first {
|
|
5889
|
+
border-top-left-radius: var(--radius-box);
|
|
5890
|
+
border-top-right-radius: var(--radius-box);
|
|
5891
|
+
}
|
|
5892
|
+
.zx-card__media.zx-m-last {
|
|
5893
|
+
border-bottom-left-radius: var(--radius-box);
|
|
5894
|
+
border-bottom-right-radius: var(--radius-box);
|
|
5895
|
+
}
|
|
5896
|
+
`,
|
|
5897
|
+
},
|
|
5898
|
+
},
|
|
5516
5899
|
};
|
|
5517
5900
|
/**
|
|
5518
5901
|
* daisy "alert" flavor: the role's soft tint edge to edge, its ink for the
|
|
@@ -5603,6 +5986,87 @@ export const alert = {
|
|
|
5603
5986
|
xl: { root: { base: { padding: 'var(--space-xl) var(--space-2xl)' } } },
|
|
5604
5987
|
},
|
|
5605
5988
|
},
|
|
5989
|
+
targets: {
|
|
5990
|
+
// No grid on lynx (signalxjs/lynx#1075, #1235): the web's three
|
|
5991
|
+
// columns (the icon over both rows | the title over the description
|
|
5992
|
+
// | the close button) are rebuilt as a flex column with the icon and
|
|
5993
|
+
// the close button placed absolutely — the toast's close pin is the
|
|
5994
|
+
// precedent. The padding moves onto two properties so the icon and
|
|
5995
|
+
// the close button can follow it per size.
|
|
5996
|
+
lynx: {
|
|
5997
|
+
tokens: {
|
|
5998
|
+
'--alert-pad-y': 'var(--space-md)',
|
|
5999
|
+
'--alert-pad-x': 'var(--space-lg)',
|
|
6000
|
+
},
|
|
6001
|
+
parts: {
|
|
6002
|
+
root: {
|
|
6003
|
+
base: {
|
|
6004
|
+
display: 'flex',
|
|
6005
|
+
flexDirection: 'column',
|
|
6006
|
+
alignItems: 'stretch',
|
|
6007
|
+
gap: 'var(--space-sm)',
|
|
6008
|
+
position: 'relative',
|
|
6009
|
+
boxSizing: 'border-box',
|
|
6010
|
+
padding: 'var(--alert-pad-y) var(--alert-pad-x)',
|
|
6011
|
+
},
|
|
6012
|
+
},
|
|
6013
|
+
// The icon column: a --text-xl slot at the padding edge,
|
|
6014
|
+
// centred over the alert's height (the web's two-row span).
|
|
6015
|
+
icon: {
|
|
6016
|
+
base: {
|
|
6017
|
+
position: 'absolute',
|
|
6018
|
+
left: 'var(--alert-pad-x)',
|
|
6019
|
+
top: '0',
|
|
6020
|
+
bottom: '0',
|
|
6021
|
+
width: 'var(--text-xl)',
|
|
6022
|
+
display: 'flex',
|
|
6023
|
+
justifyContent: 'center',
|
|
6024
|
+
},
|
|
6025
|
+
},
|
|
6026
|
+
// Pinned top-right, its glyph on the padding edge. `inherit`
|
|
6027
|
+
// is spelled as the root's own ink.
|
|
6028
|
+
close: {
|
|
6029
|
+
base: {
|
|
6030
|
+
position: 'absolute',
|
|
6031
|
+
top: 'calc(var(--alert-pad-y) - var(--space-2xs))',
|
|
6032
|
+
right: 'calc(var(--alert-pad-x) - var(--space-2xs))',
|
|
6033
|
+
color: 'var(--color-base-content)',
|
|
6034
|
+
fontSize: 'var(--text-md)',
|
|
6035
|
+
},
|
|
6036
|
+
states: lynxFocus({ gap: 'var(--alert-tint)' }),
|
|
6037
|
+
},
|
|
6038
|
+
},
|
|
6039
|
+
variants: {
|
|
6040
|
+
size: {
|
|
6041
|
+
xs: { root: { base: { '--alert-pad-y': 'var(--space-2xs)', '--alert-pad-x': 'var(--space-sm)' } } },
|
|
6042
|
+
sm: { root: { base: { '--alert-pad-y': 'var(--space-xs)', '--alert-pad-x': 'var(--space-md)' } } },
|
|
6043
|
+
lg: { root: { base: { '--alert-pad-y': 'var(--space-lg)', '--alert-pad-x': 'var(--space-xl)' } } },
|
|
6044
|
+
xl: { root: { base: { '--alert-pad-y': 'var(--space-xl)', '--alert-pad-x': 'var(--space-2xl)' } } },
|
|
6045
|
+
},
|
|
6046
|
+
},
|
|
6047
|
+
// The title and description keep clear of the icon column and
|
|
6048
|
+
// the close button's corner. Lynx has no `:has()`, so lynx-zero
|
|
6049
|
+
// stamps what the alert renders onto both text parts, as
|
|
6050
|
+
// `data-mod-with-icon` / `data-mod-with-close` (the
|
|
6051
|
+
// `zx-m-with-icon` / `zx-m-with-close` classes) — a lynx-zero
|
|
6052
|
+
// rendering detail like toggle-group's join ends, styled from raw
|
|
6053
|
+
// lynx css.
|
|
6054
|
+
css: `
|
|
6055
|
+
.zx-alert__title.zx-m-with-icon {
|
|
6056
|
+
margin-left: calc(var(--text-xl) + var(--space-md));
|
|
6057
|
+
}
|
|
6058
|
+
.zx-alert__description.zx-m-with-icon {
|
|
6059
|
+
margin-left: calc(var(--text-xl) + var(--space-md));
|
|
6060
|
+
}
|
|
6061
|
+
.zx-alert__title.zx-m-with-close {
|
|
6062
|
+
margin-right: calc(var(--text-md) + var(--space-md));
|
|
6063
|
+
}
|
|
6064
|
+
.zx-alert__description.zx-m-with-close {
|
|
6065
|
+
margin-right: calc(var(--text-md) + var(--space-md));
|
|
6066
|
+
}
|
|
6067
|
+
`,
|
|
6068
|
+
},
|
|
6069
|
+
},
|
|
5606
6070
|
};
|
|
5607
6071
|
/**
|
|
5608
6072
|
* EmptyState (zero#131) — daisy has no empty-state component; this is the
|
|
@@ -5684,6 +6148,25 @@ export const emptyState = {
|
|
|
5684
6148
|
xl: { root: { base: { padding: 'calc(var(--space-2xl) * 2) var(--space-2xl)', gap: 'var(--space-lg)' } }, icon: { base: { fontSize: 'var(--text-2xl)' } }, title: { base: { fontSize: 'var(--text-2xl)' } }, description: { base: { fontSize: 'var(--text-md)' } } },
|
|
5685
6149
|
},
|
|
5686
6150
|
},
|
|
6151
|
+
// signalxjs/lynx#1236 — the lynx replacements for what the shared
|
|
6152
|
+
// sections cannot say there.
|
|
6153
|
+
targets: {
|
|
6154
|
+
lynx: {
|
|
6155
|
+
parts: {
|
|
6156
|
+
// Physical margins: Android resolves no logical margin
|
|
6157
|
+
// (signalxjs/lynx#1084).
|
|
6158
|
+
icon: { base: { marginBottom: 'var(--space-xs)' } },
|
|
6159
|
+
// Title and description are lynx `<text>`: a view's
|
|
6160
|
+
// `text-align` and `color` do not reach one unless the app
|
|
6161
|
+
// turned CSS inheritance on, so each states its own ink and
|
|
6162
|
+
// centring. The web's 36ch measure becomes the root's width:
|
|
6163
|
+
// `ch` is unmeasured on lynx.
|
|
6164
|
+
title: { base: { color: 'var(--color-base-content)', textAlign: 'center' } },
|
|
6165
|
+
description: { base: { textAlign: 'center', maxInlineSize: '100%' } },
|
|
6166
|
+
actions: { base: { flexDirection: 'row', marginTop: 'var(--space-sm)' } },
|
|
6167
|
+
},
|
|
6168
|
+
},
|
|
6169
|
+
},
|
|
5687
6170
|
};
|
|
5688
6171
|
/**
|
|
5689
6172
|
* daisy "badge" flavor: a fully-rounded pill with a hairline, filled by the
|
|
@@ -5785,6 +6268,62 @@ export const badge = {
|
|
|
5785
6268
|
}, keyframes: {
|
|
5786
6269
|
'zero-daisy-badge-pulse': 'from { box-shadow: 0 0 0 0 color-mix(in oklch, var(--badge-dot) 45%, transparent); } to { box-shadow: 0 0 0 0.5em transparent; }',
|
|
5787
6270
|
},
|
|
6271
|
+
/**
|
|
6272
|
+
* Lynx (signalxjs/lynx#1234). The dot's `currentColor` never resolves
|
|
6273
|
+
* there (lynx#1079), so the uncoloured dot and its ring take the pill's
|
|
6274
|
+
* ink token, the value `currentColor` names on the web. The running halo
|
|
6275
|
+
* and pulse mix the recipe-local `--badge-dot`, which cannot bake, so the
|
|
6276
|
+
* halo is its own token mixed from each role's `--color-*` (baked per
|
|
6277
|
+
* theme) and the pulse breathes opacity instead of the halo's spread.
|
|
6278
|
+
* `inline-size`/`block-size` do not resolve on lynx (lynx#1145): the dot
|
|
6279
|
+
* is sized physically. A lynx view has no inline formatting context, so
|
|
6280
|
+
* the pill is content-sized with an explicit row (lynx#1165), and its
|
|
6281
|
+
* hairline and the dot's ring are longhands (lynx#1161/#1162).
|
|
6282
|
+
*/
|
|
6283
|
+
targets: {
|
|
6284
|
+
lynx: {
|
|
6285
|
+
tokens: {
|
|
6286
|
+
'--badge-dot': 'var(--badge-ink)',
|
|
6287
|
+
'--badge-dot-ring': 'var(--badge-ink)',
|
|
6288
|
+
'--badge-dot-halo': 'color-mix(in oklch, var(--color-base-content) 35%, transparent)',
|
|
6289
|
+
},
|
|
6290
|
+
parts: {
|
|
6291
|
+
root: {
|
|
6292
|
+
base: {
|
|
6293
|
+
...lynxBtnFit,
|
|
6294
|
+
flexDirection: 'row',
|
|
6295
|
+
border: '0 solid transparent',
|
|
6296
|
+
...lynxSides('border{}Width', 'var(--border)'),
|
|
6297
|
+
},
|
|
6298
|
+
},
|
|
6299
|
+
dot: {
|
|
6300
|
+
base: {
|
|
6301
|
+
display: 'flex',
|
|
6302
|
+
width: '0.5em',
|
|
6303
|
+
height: '0.5em',
|
|
6304
|
+
flexShrink: '0',
|
|
6305
|
+
border: '0 solid transparent',
|
|
6306
|
+
...lynxSides('border{}Width', '0.1em'),
|
|
6307
|
+
...lynxSides('border{}Color', 'var(--badge-dot-ring)'),
|
|
6308
|
+
},
|
|
6309
|
+
states: {
|
|
6310
|
+
running: {
|
|
6311
|
+
boxShadow: '0 0 0 0.2em var(--badge-dot-halo)',
|
|
6312
|
+
animation: 'zero-daisy-badge-pulse 1.6s ease-in-out infinite',
|
|
6313
|
+
},
|
|
6314
|
+
},
|
|
6315
|
+
},
|
|
6316
|
+
},
|
|
6317
|
+
variants: {
|
|
6318
|
+
color: Object.fromEntries(ROLES.map((c) => [c, {
|
|
6319
|
+
dot: { base: { '--badge-dot-halo': `color-mix(in oklch, var(--color-${c}) 35%, transparent)` } },
|
|
6320
|
+
}])),
|
|
6321
|
+
},
|
|
6322
|
+
keyframes: {
|
|
6323
|
+
'zero-daisy-badge-pulse': 'from, to { opacity: 1; } 50% { opacity: 0.45; }',
|
|
6324
|
+
},
|
|
6325
|
+
},
|
|
6326
|
+
},
|
|
5788
6327
|
};
|
|
5789
6328
|
/** daisy "divider": the base-300 rule, thickening with the ramp. */
|
|
5790
6329
|
export const divider = {
|
|
@@ -5854,6 +6393,11 @@ export const divider = {
|
|
|
5854
6393
|
},
|
|
5855
6394
|
targets: {
|
|
5856
6395
|
lynx: {
|
|
6396
|
+
// `auto` behind a custom property, so the labelled root's size
|
|
6397
|
+
// is var-bearing too and can beat the var-bearing line thickness
|
|
6398
|
+
// below (lynx expands var() declarations after the cascade,
|
|
6399
|
+
// signalxjs/lynx#1161).
|
|
6400
|
+
tokens: { '--divider-fit': 'auto' },
|
|
5857
6401
|
parts: {
|
|
5858
6402
|
// Lynx drops the ::before/::after segments the rule is drawn
|
|
5859
6403
|
// with above, which left the lynx divider with no line and,
|
|
@@ -5868,7 +6412,64 @@ export const divider = {
|
|
|
5868
6412
|
'&[data-orientation="vertical"]': { inlineSize: 'var(--divider-thickness)' },
|
|
5869
6413
|
},
|
|
5870
6414
|
},
|
|
6415
|
+
// The label's inset, physical (Android resolves no logical
|
|
6416
|
+
// padding, signalxjs/lynx#1084), and flush on the side its
|
|
6417
|
+
// placement gave up. Lynx has no descendant combinator from
|
|
6418
|
+
// the root, so lynx-zero stamps the root's orientation on the
|
|
6419
|
+
// label itself; vertical, the inset turns to the block axis.
|
|
6420
|
+
// Every zero is spelled through var() so it beats the
|
|
6421
|
+
// var-bearing inset (signalxjs/lynx#1161).
|
|
6422
|
+
label: {
|
|
6423
|
+
base: {
|
|
6424
|
+
paddingLeft: 'var(--space-md)',
|
|
6425
|
+
paddingRight: 'var(--space-md)',
|
|
6426
|
+
},
|
|
6427
|
+
selectors: {
|
|
6428
|
+
'&[data-placement="start"]': { paddingLeft: 'calc(var(--space-md) * 0)' },
|
|
6429
|
+
'&[data-placement="end"]': { paddingRight: 'calc(var(--space-md) * 0)' },
|
|
6430
|
+
'&[data-orientation="vertical"]': {
|
|
6431
|
+
paddingLeft: 'calc(var(--space-md) * 0)',
|
|
6432
|
+
paddingRight: 'calc(var(--space-md) * 0)',
|
|
6433
|
+
paddingTop: 'var(--space-sm)',
|
|
6434
|
+
paddingBottom: 'var(--space-sm)',
|
|
6435
|
+
},
|
|
6436
|
+
'&[data-orientation="vertical"][data-placement="start"]': { paddingTop: 'calc(var(--space-sm) * 0)' },
|
|
6437
|
+
'&[data-orientation="vertical"][data-placement="end"]': { paddingBottom: 'calc(var(--space-sm) * 0)' },
|
|
6438
|
+
},
|
|
6439
|
+
},
|
|
5871
6440
|
},
|
|
6441
|
+
// A labelled divider (signalxjs/lynx#1236). With no pseudo-
|
|
6442
|
+
// elements, lynx-zero draws the rule's two segments as views
|
|
6443
|
+
// carrying the root's own line classes plus `zx-m-segment` —
|
|
6444
|
+
// so each paints this recipe's line, ink and thickness ramp — and
|
|
6445
|
+
// leaves out the one on the label's placed side (the web's
|
|
6446
|
+
// `:has(> [data-placement])::before { display: none }`). The root
|
|
6447
|
+
// holding them is stamped `zx-m-labelled` and stops being a line:
|
|
6448
|
+
// no fill, its cross size following the label, a row when
|
|
6449
|
+
// horizontal. Both are lynx-zero rendering details, not
|
|
6450
|
+
// modifiers an author sets, so they are styled from raw lynx css
|
|
6451
|
+
// (the toggle-group join ends' reasoning).
|
|
6452
|
+
css: `
|
|
6453
|
+
.zx-divider__root.zx-m-labelled {
|
|
6454
|
+
--divider-ink: transparent;
|
|
6455
|
+
}
|
|
6456
|
+
.zx-divider__root.zx-m-labelled.zx-o-horizontal {
|
|
6457
|
+
flex-direction: row;
|
|
6458
|
+
block-size: var(--divider-fit);
|
|
6459
|
+
}
|
|
6460
|
+
.zx-divider__root.zx-m-labelled.zx-o-vertical {
|
|
6461
|
+
inline-size: var(--divider-fit);
|
|
6462
|
+
}
|
|
6463
|
+
.zx-divider__root.zx-m-segment {
|
|
6464
|
+
flex: 1 1 0;
|
|
6465
|
+
}
|
|
6466
|
+
.zx-divider__root.zx-m-segment.zx-o-horizontal {
|
|
6467
|
+
min-width: 0;
|
|
6468
|
+
}
|
|
6469
|
+
.zx-divider__root.zx-m-segment.zx-o-vertical {
|
|
6470
|
+
min-height: 0;
|
|
6471
|
+
}
|
|
6472
|
+
`,
|
|
5872
6473
|
},
|
|
5873
6474
|
},
|
|
5874
6475
|
};
|
|
@@ -5924,12 +6525,28 @@ export const skeleton = {
|
|
|
5924
6525
|
keyframes: {
|
|
5925
6526
|
'zero-daisyui-skeleton': 'from, to { opacity: 1; } 50% { opacity: 0.55; }',
|
|
5926
6527
|
},
|
|
6528
|
+
// signalxjs/lynx#1237. The pulse is an opacity loop on the part itself —
|
|
6529
|
+
// no pseudo-element, so it carries as is; only its keyframes are
|
|
6530
|
+
// restated as three plain stops, not the `from, to` selector list. The
|
|
6531
|
+
// reduced-motion stop cannot follow: lynx emits no `@media`.
|
|
6532
|
+
targets: {
|
|
6533
|
+
lynx: {
|
|
6534
|
+
keyframes: {
|
|
6535
|
+
'zero-daisyui-skeleton': '0% { opacity: 1; } 50% { opacity: 0.55; } 100% { opacity: 1; }',
|
|
6536
|
+
},
|
|
6537
|
+
},
|
|
6538
|
+
},
|
|
5927
6539
|
};
|
|
5928
|
-
/**
|
|
6540
|
+
/**
|
|
6541
|
+
* daisy "loading-spinner": the ring, with one quadrant in the role's ink.
|
|
6542
|
+
* Sized on daisy's `loading-*` ramp — `--size-selector * 4…8` (16–32px), md
|
|
6543
|
+
* the `loading` default of `* 6` (signalxjs/lynx#1237: the ramp was
|
|
6544
|
+
* `--size-field * 0.35…0.9`, a 2px box that the 2px border swallowed).
|
|
6545
|
+
*/
|
|
5929
6546
|
export const spinner = {
|
|
5930
6547
|
component: 'spinner',
|
|
5931
6548
|
tokens: {
|
|
5932
|
-
'--spinner-size': 'calc(var(--size-
|
|
6549
|
+
'--spinner-size': 'calc(var(--size-selector) * 6)',
|
|
5933
6550
|
'--spinner-ink': 'var(--color-primary)',
|
|
5934
6551
|
'--spinner-track': 'var(--color-base-300)',
|
|
5935
6552
|
},
|
|
@@ -5953,14 +6570,40 @@ export const spinner = {
|
|
|
5953
6570
|
'--spinner-ink': `var(--color-${c})`,
|
|
5954
6571
|
} } }])),
|
|
5955
6572
|
size: {
|
|
5956
|
-
xs: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
5957
|
-
sm: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
6573
|
+
xs: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 4)' } } },
|
|
6574
|
+
sm: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 5)' } } },
|
|
5958
6575
|
md: {},
|
|
5959
|
-
lg: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
5960
|
-
xl: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
6576
|
+
lg: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 7)' } } },
|
|
6577
|
+
xl: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 8)' } } },
|
|
5961
6578
|
},
|
|
5962
6579
|
},
|
|
5963
6580
|
keyframes: { 'zero-daisyui-spin': 'to { transform: rotate(360deg); }' },
|
|
6581
|
+
// signalxjs/lynx#1237 — the ring in physical spellings. The logical box
|
|
6582
|
+
// size and `border-block-start-color` are not lynx's; the ring is
|
|
6583
|
+
// restated as longhands, because a var-bearing `border` shorthand
|
|
6584
|
+
// expands after the cascade and would repaint the head in the track
|
|
6585
|
+
// colour (signalxjs/lynx#1161). A flex box, since `inline-block` is not a
|
|
6586
|
+
// lynx display value, that keeps its size in a row. The reduced-motion
|
|
6587
|
+
// stop cannot follow: lynx emits no `@media`.
|
|
6588
|
+
targets: {
|
|
6589
|
+
lynx: {
|
|
6590
|
+
parts: {
|
|
6591
|
+
root: {
|
|
6592
|
+
base: {
|
|
6593
|
+
display: 'flex',
|
|
6594
|
+
flexShrink: '0',
|
|
6595
|
+
width: 'var(--spinner-size)',
|
|
6596
|
+
height: 'var(--spinner-size)',
|
|
6597
|
+
border: '0 solid transparent',
|
|
6598
|
+
borderStyle: 'solid',
|
|
6599
|
+
...lynxSides('border{}Width', 'calc(var(--border) * 2)'),
|
|
6600
|
+
...lynxSides('border{}Color', 'var(--spinner-track)', ['Right', 'Bottom', 'Left']),
|
|
6601
|
+
borderTopColor: 'var(--spinner-ink)',
|
|
6602
|
+
},
|
|
6603
|
+
},
|
|
6604
|
+
},
|
|
6605
|
+
},
|
|
6606
|
+
},
|
|
5964
6607
|
};
|
|
5965
6608
|
// ── The content-tier sweep (#334) ─────────────────────────────────────────
|
|
5966
6609
|
/** daisy kbd: the raised base-200 cap with the doubled bottom edge. */
|
|
@@ -6006,6 +6649,35 @@ export const kbd = {
|
|
|
6006
6649
|
xl: { root: { base: { fontSize: 'var(--text-md)', padding: 'var(--space-xs) var(--space-lg)' } } },
|
|
6007
6650
|
},
|
|
6008
6651
|
},
|
|
6652
|
+
/**
|
|
6653
|
+
* Lynx (signalxjs/lynx#1234). `min-inline-size` and the logical bottom
|
|
6654
|
+
* edge do not resolve on lynx (lynx#1145, #1084), so the cap's floor is
|
|
6655
|
+
* a physical `min-width` and its doubled bottom edge a physical
|
|
6656
|
+
* longhand; the whole hairline is longhands so a var-bearing shorthand
|
|
6657
|
+
* cannot expand over the thicker edge (lynx#1161/#1162). A lynx view has
|
|
6658
|
+
* no inline formatting context: the cap is content-sized with an
|
|
6659
|
+
* explicit row (lynx#1165).
|
|
6660
|
+
*/
|
|
6661
|
+
targets: {
|
|
6662
|
+
lynx: {
|
|
6663
|
+
parts: {
|
|
6664
|
+
root: {
|
|
6665
|
+
base: {
|
|
6666
|
+
...lynxBtnFit,
|
|
6667
|
+
flexDirection: 'row',
|
|
6668
|
+
minWidth: '1.75em',
|
|
6669
|
+
border: '0 solid transparent',
|
|
6670
|
+
...lynxSides('border{}Width', 'var(--border)', ['Top', 'Right', 'Left']),
|
|
6671
|
+
borderBottomWidth: 'calc(var(--border) * 2)',
|
|
6672
|
+
...lynxSides('border{}Color', 'var(--color-base-300)'),
|
|
6673
|
+
},
|
|
6674
|
+
},
|
|
6675
|
+
},
|
|
6676
|
+
variants: {
|
|
6677
|
+
size: { xs: { root: { base: { minWidth: '1.5em' } } } },
|
|
6678
|
+
},
|
|
6679
|
+
},
|
|
6680
|
+
},
|
|
6009
6681
|
};
|
|
6010
6682
|
/**
|
|
6011
6683
|
* daisy status: the dot with the soft halo ring daisy draws around it —
|
|
@@ -6046,6 +6718,37 @@ export const status = {
|
|
|
6046
6718
|
xl: { root: { base: { '--status-size': 'calc(var(--size-selector) * 3.5)' } } },
|
|
6047
6719
|
},
|
|
6048
6720
|
},
|
|
6721
|
+
/**
|
|
6722
|
+
* Lynx (signalxjs/lynx#1234). The halo mixes the recipe-local
|
|
6723
|
+
* `--status-ink`, which cannot bake, so it is its own token mixed from
|
|
6724
|
+
* each role's `--color-*` (baked per theme). `inline-size`/`block-size`
|
|
6725
|
+
* do not resolve on lynx (lynx#1145): the dot is sized physically, and
|
|
6726
|
+
* it is a flex box rather than an inline-block the engine has no
|
|
6727
|
+
* formatting context for.
|
|
6728
|
+
*/
|
|
6729
|
+
targets: {
|
|
6730
|
+
lynx: {
|
|
6731
|
+
tokens: {
|
|
6732
|
+
'--status-halo': 'color-mix(in oklch, var(--color-base-content) 25%, transparent)',
|
|
6733
|
+
},
|
|
6734
|
+
parts: {
|
|
6735
|
+
root: {
|
|
6736
|
+
base: {
|
|
6737
|
+
display: 'flex',
|
|
6738
|
+
flexShrink: '0',
|
|
6739
|
+
width: 'var(--status-size)',
|
|
6740
|
+
height: 'var(--status-size)',
|
|
6741
|
+
boxShadow: '0 0 0 calc(var(--status-size) / 4) var(--status-halo)',
|
|
6742
|
+
},
|
|
6743
|
+
},
|
|
6744
|
+
},
|
|
6745
|
+
variants: {
|
|
6746
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
6747
|
+
'--status-halo': `color-mix(in oklch, var(--color-${c}) 25%, transparent)`,
|
|
6748
|
+
} } }])),
|
|
6749
|
+
},
|
|
6750
|
+
},
|
|
6751
|
+
},
|
|
6049
6752
|
};
|
|
6050
6753
|
/**
|
|
6051
6754
|
* Indicator — pure position; the item's content brings its own paint.
|
|
@@ -6228,6 +6931,90 @@ export const stats = {
|
|
|
6228
6931
|
xl: { value: { base: { fontSize: 'var(--text-3xl)' } } },
|
|
6229
6932
|
},
|
|
6230
6933
|
},
|
|
6934
|
+
// signalxjs/lynx#1236 — the lynx replacements for what the shared
|
|
6935
|
+
// sections cannot say there.
|
|
6936
|
+
targets: {
|
|
6937
|
+
lynx: {
|
|
6938
|
+
parts: {
|
|
6939
|
+
// A row unless vertical: a lynx flex container does not
|
|
6940
|
+
// default to one.
|
|
6941
|
+
root: { base: { flexDirection: 'row' } },
|
|
6942
|
+
// Lynx has no grid, so `display: grid` left the item in the
|
|
6943
|
+
// engine's default linear layout. The bands stack in a
|
|
6944
|
+
// centred column instead, and the figure is pinned to the
|
|
6945
|
+
// item's end edge (below). The padding as longhands, so the
|
|
6946
|
+
// `figure` rule below can widen one side by specificity (a
|
|
6947
|
+
// var-bearing `padding` shorthand would expand over it,
|
|
6948
|
+
// signalxjs/lynx#1161).
|
|
6949
|
+
item: {
|
|
6950
|
+
base: {
|
|
6951
|
+
display: 'flex',
|
|
6952
|
+
flexDirection: 'column',
|
|
6953
|
+
justifyContent: 'center',
|
|
6954
|
+
position: 'relative',
|
|
6955
|
+
padding: '0',
|
|
6956
|
+
paddingTop: 'var(--space-lg)',
|
|
6957
|
+
paddingBottom: 'var(--space-lg)',
|
|
6958
|
+
paddingLeft: 'var(--space-xl)',
|
|
6959
|
+
paddingRight: 'var(--space-xl)',
|
|
6960
|
+
},
|
|
6961
|
+
selectors: {
|
|
6962
|
+
// The seams: lynx has no sibling combinator, so the
|
|
6963
|
+
// web's `item + item` edge cannot be selected. Every
|
|
6964
|
+
// item draws its leading seam and the `first`
|
|
6965
|
+
// modifier takes it off the first (raw css below).
|
|
6966
|
+
'&[data-orientation="horizontal"]': {
|
|
6967
|
+
borderLeftWidth: 'var(--border)',
|
|
6968
|
+
borderLeftStyle: 'solid',
|
|
6969
|
+
borderLeftColor: 'var(--color-base-200)',
|
|
6970
|
+
},
|
|
6971
|
+
'&[data-orientation="vertical"]': {
|
|
6972
|
+
borderTopWidth: 'var(--border)',
|
|
6973
|
+
borderTopStyle: 'solid',
|
|
6974
|
+
borderTopColor: 'var(--color-base-200)',
|
|
6975
|
+
},
|
|
6976
|
+
},
|
|
6977
|
+
},
|
|
6978
|
+
// The web's second grid column, spanning the three bands and
|
|
6979
|
+
// centred on them: pinned to the item's end edge, inside its
|
|
6980
|
+
// padding, and centred top to bottom.
|
|
6981
|
+
figure: {
|
|
6982
|
+
base: {
|
|
6983
|
+
position: 'absolute',
|
|
6984
|
+
top: '0',
|
|
6985
|
+
bottom: '0',
|
|
6986
|
+
right: 'var(--space-xl)',
|
|
6987
|
+
// The web centres the grid cell with align-self; the
|
|
6988
|
+
// insets place it here.
|
|
6989
|
+
alignSelf: 'auto',
|
|
6990
|
+
display: 'flex',
|
|
6991
|
+
flexDirection: 'column',
|
|
6992
|
+
justifyContent: 'center',
|
|
6993
|
+
},
|
|
6994
|
+
},
|
|
6995
|
+
},
|
|
6996
|
+
// lynx-zero stamps the row's first item `zx-m-first` (mount
|
|
6997
|
+
// order; no `:first-child`) and an item holding a figure
|
|
6998
|
+
// `zx-m-figure`. Rendering details, not author modifiers, so raw
|
|
6999
|
+
// lynx css (the toggle-group join ends' reasoning). The first
|
|
7000
|
+
// item drops the leading seam, its zero width spelled through
|
|
7001
|
+
// var() to beat the var-bearing seam (signalxjs/lynx#1161). An
|
|
7002
|
+
// item with a figure keeps the figure's column clear: the
|
|
7003
|
+
// padding, the web's `--space-md` column gap, and a 2rem figure
|
|
7004
|
+
// (daisy's `stat-figure` holds a `size-8` icon or avatar).
|
|
7005
|
+
css: `
|
|
7006
|
+
.zx-stats__item.zx-m-first.zx-o-horizontal {
|
|
7007
|
+
border-left-width: calc(var(--border) * 0);
|
|
7008
|
+
}
|
|
7009
|
+
.zx-stats__item.zx-m-first.zx-o-vertical {
|
|
7010
|
+
border-top-width: calc(var(--border) * 0);
|
|
7011
|
+
}
|
|
7012
|
+
.zx-stats__item.zx-m-figure {
|
|
7013
|
+
padding-right: calc(var(--space-xl) + var(--space-md) + 2rem);
|
|
7014
|
+
}
|
|
7015
|
+
`,
|
|
7016
|
+
},
|
|
7017
|
+
},
|
|
6231
7018
|
};
|
|
6232
7019
|
/**
|
|
6233
7020
|
* A timeline none of whose content sits on the start side: the root carries
|