@sigx/zero-daisyui 0.13.0 → 0.15.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 +5 -0
- package/dist/css/components/container.css +1 -0
- package/dist/css/components/input.css +9 -0
- package/dist/css/components/nav-list.css +7 -0
- package/dist/css/components/spinner.css +5 -5
- package/dist/css/index.css +27 -5
- package/dist/lynx/components/alert.css +49 -7
- package/dist/lynx/components/avatar-group.css +13 -1
- package/dist/lynx/components/avatar.css +56 -1
- package/dist/lynx/components/badge.css +209 -6
- package/dist/lynx/components/breadcrumbs.css +30 -21
- package/dist/lynx/components/button.css +2 -7
- package/dist/lynx/components/card.css +12 -2
- package/dist/lynx/components/carousel.css +4 -4
- package/dist/lynx/components/chat-log.css +5 -5
- package/dist/lynx/components/chat.css +1 -1
- package/dist/lynx/components/combobox.css +5 -0
- package/dist/lynx/components/container.css +3 -2
- package/dist/lynx/components/diff.css +1 -1
- package/dist/lynx/components/divider.css +53 -8
- package/dist/lynx/components/drawer.css +8 -8
- package/dist/lynx/components/empty-state.css +7 -1
- package/dist/lynx/components/fieldset.css +1 -1
- package/dist/lynx/components/grid.css +2 -2
- package/dist/lynx/components/kbd.css +16 -3
- package/dist/lynx/components/menu.css +20 -1
- package/dist/lynx/components/nav-list.css +15 -2
- package/dist/lynx/components/navbar.css +9 -5
- package/dist/lynx/components/pagination.css +97 -65
- package/dist/lynx/components/radial-progress.css +10 -10
- package/dist/lynx/components/select.css +1 -2
- package/dist/lynx/components/skeleton.css +1 -1
- package/dist/lynx/components/spinner.css +22 -12
- package/dist/lynx/components/stack.css +2 -2
- package/dist/lynx/components/stats.css +39 -3
- package/dist/lynx/components/status.css +197 -11
- package/dist/lynx/components/steps.css +114 -22
- package/dist/lynx/components/table.css +1 -1
- package/dist/lynx/components/tabs.css +1 -1
- package/dist/lynx/components/textarea.css +1 -1
- package/dist/lynx/components/timeline.css +12 -25
- package/dist/lynx/components/toast.css +331 -12
- package/dist/lynx/components/tree-view.css +31 -4
- package/dist/lynx/index.css +1382 -262
- package/dist/lynx/manifest.json +3 -3
- 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 +934 -25
- package/dist/recipes.js.map +1 -1
- package/dist/report.json +565 -89
- package/package.json +4 -4
- package/src/recipes.ts +939 -25
package/dist/recipes.js
CHANGED
|
@@ -1558,12 +1558,34 @@ export const menu = {
|
|
|
1558
1558
|
// Trigger-carried axes — same wiring as dialog, same reason. The dropdown
|
|
1559
1559
|
// and its items are top-layer siblings the donut cannot reach.
|
|
1560
1560
|
variants: { color: btnColors(), size: btnSizes },
|
|
1561
|
-
// The btn paddings, restated physically — see `lynxBtnPad` (#1084)
|
|
1561
|
+
// The btn paddings, restated physically — see `lynxBtnPad` (#1084) —
|
|
1562
|
+
// and the trigger's press rendering, as popover's (`lynxBtnPressed`).
|
|
1562
1563
|
targets: {
|
|
1563
1564
|
web: floatingArrow('menu', panelArrowPaint),
|
|
1564
1565
|
lynx: {
|
|
1565
1566
|
parts: {
|
|
1566
|
-
|
|
1567
|
+
// Content-sized like daisy's btn (signalxjs/lynx#1165), an
|
|
1568
|
+
// explicit row (lynx does not default the direction the way a
|
|
1569
|
+
// browser does), the held state in place of the dropped
|
|
1570
|
+
// `:hover`, and the box-shadow ring (signalxjs/lynx#1163).
|
|
1571
|
+
trigger: {
|
|
1572
|
+
base: { ...lynxBtnPad(4), ...lynxBtnFit, flexDirection: 'row' },
|
|
1573
|
+
states: { pressed: lynxBtnPressed, ...lynxFocus() },
|
|
1574
|
+
},
|
|
1575
|
+
// The popups' rows stack and span the panel: a flex column
|
|
1576
|
+
// (a lynx view's default is a shrink-wrapping linear box).
|
|
1577
|
+
popup: { base: { display: 'flex', flexDirection: 'column' } },
|
|
1578
|
+
'sub-popup': { base: { display: 'flex', flexDirection: 'column' } },
|
|
1579
|
+
// Every row kind is a mark / label / shortcut / chevron row.
|
|
1580
|
+
// A touch list has no hover: lynx-zero stamps `highlighted` on
|
|
1581
|
+
// the row under the finger, so the web's highlight wash is the
|
|
1582
|
+
// press rendering as it stands. The ✓ (item-indicator) and ›
|
|
1583
|
+
// (sub-trigger) the web draws with `::after` render as
|
|
1584
|
+
// `<text>` glyphs in lynx-zero — there is no pseudo-element.
|
|
1585
|
+
item: { base: { flexDirection: 'row' } },
|
|
1586
|
+
'checkbox-item': { base: { flexDirection: 'row' } },
|
|
1587
|
+
'radio-item': { base: { flexDirection: 'row' } },
|
|
1588
|
+
'sub-trigger': { base: { flexDirection: 'row' } },
|
|
1567
1589
|
// The shortcut's reading-end push, restated physically (#1084).
|
|
1568
1590
|
shortcut: { base: { marginLeft: 'auto', paddingLeft: 'var(--space-lg)' } },
|
|
1569
1591
|
},
|
|
@@ -3674,6 +3696,80 @@ export const avatar = {
|
|
|
3674
3696
|
xl: { root: { base: { '--avatar-size': 'calc(var(--size-selector) * 16)', '--avatar-text': 'var(--text-lg)' } } },
|
|
3675
3697
|
},
|
|
3676
3698
|
},
|
|
3699
|
+
// signalxjs/lynx#1237 — the lynx layout for what the web gets from grid.
|
|
3700
|
+
targets: {
|
|
3701
|
+
lynx: {
|
|
3702
|
+
parts: {
|
|
3703
|
+
// No grid on lynx (signalxjs/lynx#1075): the web stacks the
|
|
3704
|
+
// image and the fallback in one grid cell. Here the root is a
|
|
3705
|
+
// flex box that does not shrink in a row, and both faces are
|
|
3706
|
+
// laid over it absolutely — the fallback, later in the tree,
|
|
3707
|
+
// paints over a pending image exactly as the grid's second
|
|
3708
|
+
// item does. lynx-zero renders neither face while its state
|
|
3709
|
+
// hides it (`hiddenIn`), so the web's `&:not([hidden])`
|
|
3710
|
+
// display rule has nothing to replace.
|
|
3711
|
+
root: {
|
|
3712
|
+
base: {
|
|
3713
|
+
display: 'flex',
|
|
3714
|
+
flexDirection: 'row',
|
|
3715
|
+
alignItems: 'center',
|
|
3716
|
+
justifyContent: 'center',
|
|
3717
|
+
flexShrink: '0',
|
|
3718
|
+
},
|
|
3719
|
+
},
|
|
3720
|
+
// Each face also rounds itself to the root's shape (below),
|
|
3721
|
+
// rather than trusting the root's clip to round an image on
|
|
3722
|
+
// both engines.
|
|
3723
|
+
image: {
|
|
3724
|
+
base: { position: 'absolute', top: '0', left: '0', borderRadius: '9999px' },
|
|
3725
|
+
},
|
|
3726
|
+
fallback: {
|
|
3727
|
+
base: {
|
|
3728
|
+
position: 'absolute',
|
|
3729
|
+
top: '0',
|
|
3730
|
+
left: '0',
|
|
3731
|
+
borderRadius: '9999px',
|
|
3732
|
+
display: 'flex',
|
|
3733
|
+
alignItems: 'center',
|
|
3734
|
+
justifyContent: 'center',
|
|
3735
|
+
textAlign: 'center',
|
|
3736
|
+
},
|
|
3737
|
+
},
|
|
3738
|
+
},
|
|
3739
|
+
variants: {
|
|
3740
|
+
shape: Object.fromEntries([
|
|
3741
|
+
['circle', '9999px'], ['square', '0'], ['rounded', 'var(--radius-selector)'],
|
|
3742
|
+
].map(([shape, radius]) => [shape, {
|
|
3743
|
+
image: { base: { borderRadius: radius } },
|
|
3744
|
+
fallback: { base: { borderRadius: radius } },
|
|
3745
|
+
}])),
|
|
3746
|
+
},
|
|
3747
|
+
// Every avatar after the first in an AvatarGroup overlaps its
|
|
3748
|
+
// neighbour by a quarter of its own box — the web's
|
|
3749
|
+
// `avatar-group` compose, `&:not(:first-child)`. Lynx has neither
|
|
3750
|
+
// the nested-scope compose nor `:first-child`, so lynx-zero stamps
|
|
3751
|
+
// those avatars `stacked` itself (`zx-m-stacked`, in mount order).
|
|
3752
|
+
// A lynx-zero rendering detail, not a modifier an author sets, so
|
|
3753
|
+
// it is styled from raw lynx css, one rule per avatar size.
|
|
3754
|
+
css: `
|
|
3755
|
+
.zx-avatar__root.zx-m-stacked {
|
|
3756
|
+
margin-left: calc(var(--size-selector) * -2.5);
|
|
3757
|
+
}
|
|
3758
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-xs {
|
|
3759
|
+
margin-left: calc(var(--size-selector) * -1.5);
|
|
3760
|
+
}
|
|
3761
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-sm {
|
|
3762
|
+
margin-left: calc(var(--size-selector) * -2);
|
|
3763
|
+
}
|
|
3764
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-lg {
|
|
3765
|
+
margin-left: calc(var(--size-selector) * -3);
|
|
3766
|
+
}
|
|
3767
|
+
.zx-avatar__root.zx-m-stacked.zx-a-size-xl {
|
|
3768
|
+
margin-left: calc(var(--size-selector) * -4);
|
|
3769
|
+
}
|
|
3770
|
+
`,
|
|
3771
|
+
},
|
|
3772
|
+
},
|
|
3677
3773
|
};
|
|
3678
3774
|
/**
|
|
3679
3775
|
* Avatar group (#297) — daisy's `avatar-group`: the faces overlap by a
|
|
@@ -3766,6 +3862,33 @@ export const avatarGroup = {
|
|
|
3766
3862
|
md: {},
|
|
3767
3863
|
},
|
|
3768
3864
|
},
|
|
3865
|
+
// signalxjs/lynx#1237. The avatars inside take the group's size and
|
|
3866
|
+
// colour from lynx-zero (the `composes` above has no class form on this
|
|
3867
|
+
// target), and their overlap is the avatar recipe's `stacked` rule.
|
|
3868
|
+
targets: {
|
|
3869
|
+
lynx: {
|
|
3870
|
+
parts: {
|
|
3871
|
+
// A row that sizes to its faces (a lynx view stretches to its
|
|
3872
|
+
// column otherwise).
|
|
3873
|
+
root: { base: { display: 'flex', flexDirection: 'row', width: 'max-content' } },
|
|
3874
|
+
// `inline-grid` centring as flex, and the logical inline
|
|
3875
|
+
// padding and overlap as physical spellings — logical
|
|
3876
|
+
// margins and paddings resolve on iOS but not on Android
|
|
3877
|
+
// (signalxjs/lynx#1084); lynx has no RTL flow, so start IS left.
|
|
3878
|
+
overflow: {
|
|
3879
|
+
base: {
|
|
3880
|
+
display: 'flex',
|
|
3881
|
+
alignItems: 'center',
|
|
3882
|
+
justifyContent: 'center',
|
|
3883
|
+
flexShrink: '0',
|
|
3884
|
+
paddingLeft: 'var(--space-2xs)',
|
|
3885
|
+
paddingRight: 'var(--space-2xs)',
|
|
3886
|
+
marginLeft: 'calc(var(--avatar-group-size) * -0.25)',
|
|
3887
|
+
},
|
|
3888
|
+
},
|
|
3889
|
+
},
|
|
3890
|
+
},
|
|
3891
|
+
},
|
|
3769
3892
|
};
|
|
3770
3893
|
/**
|
|
3771
3894
|
* A toast whose indicator is rendered (#292): the grid grows a leading column
|
|
@@ -4059,7 +4182,99 @@ export const toast = {
|
|
|
4059
4182
|
base: { position: 'absolute', top: 'var(--space-xs)', right: 'var(--space-xs)' },
|
|
4060
4183
|
states: lynxFocus({ gap: 'var(--toast-bg)' }),
|
|
4061
4184
|
},
|
|
4185
|
+
// The promise toast's mark (signalxjs/lynx#1196). No grid and
|
|
4186
|
+
// no `:has()`: the mark is laid over the card's leading
|
|
4187
|
+
// padding, at the root's own padding offsets (per size,
|
|
4188
|
+
// below), and lynx-zero stamps `marked` on every part of a
|
|
4189
|
+
// toast that shows it, so the text steps aside (the raw css
|
|
4190
|
+
// below) — the web's leading grid column.
|
|
4191
|
+
indicator: {
|
|
4192
|
+
base: {
|
|
4193
|
+
position: 'absolute',
|
|
4194
|
+
left: 'var(--space-lg)',
|
|
4195
|
+
top: 'var(--space-md)',
|
|
4196
|
+
width: 'var(--toast-mark)',
|
|
4197
|
+
height: 'var(--toast-mark)',
|
|
4198
|
+
},
|
|
4199
|
+
states: {
|
|
4200
|
+
// daisy's ring in the toast's ink. The track is the
|
|
4201
|
+
// ink at 25%, which cannot bake over the recipe-local
|
|
4202
|
+
// `--toast-ink`: the neutral toast's track mixes the
|
|
4203
|
+
// theme's base-content, and each colour's twin (the
|
|
4204
|
+
// variants below) its role. Longhands, and the head as
|
|
4205
|
+
// `border-top-color`: logical spellings are not lynx's
|
|
4206
|
+
// (signalxjs/lynx#1084), and a var-bearing shorthand
|
|
4207
|
+
// would repaint the head after the cascade (#1161).
|
|
4208
|
+
loading: {
|
|
4209
|
+
// The shared shorthand is dropped on lynx (its
|
|
4210
|
+
// track cannot bake); a static one here keeps it
|
|
4211
|
+
// that way should the shared value ever bake.
|
|
4212
|
+
border: '0 solid transparent',
|
|
4213
|
+
borderStyle: 'solid',
|
|
4214
|
+
...lynxSides('border{}Width', 'calc(var(--border) * 2)'),
|
|
4215
|
+
...lynxSides('border{}Color', 'color-mix(in oklab, var(--color-base-content) 25%, transparent)', ['Right', 'Bottom', 'Left']),
|
|
4216
|
+
borderTopColor: 'var(--toast-ink)',
|
|
4217
|
+
},
|
|
4218
|
+
// The tick without clip-path, which lynx does not apply
|
|
4219
|
+
// (signalxjs/lynx#1216): the checkbox's two-border
|
|
4220
|
+
// check — a box 3/8 of the mark wide and 3/4 tall with
|
|
4221
|
+
// its right and bottom borders drawn, turned 45° —
|
|
4222
|
+
// pulled to the middle of the mark's slot by its
|
|
4223
|
+
// margins. The fill the web cuts the tick from is
|
|
4224
|
+
// cleared.
|
|
4225
|
+
complete: {
|
|
4226
|
+
background: 'transparent',
|
|
4227
|
+
boxSizing: 'border-box',
|
|
4228
|
+
width: 'calc(var(--toast-mark) * 0.375)',
|
|
4229
|
+
height: 'calc(var(--toast-mark) * 0.75)',
|
|
4230
|
+
marginLeft: 'calc(var(--toast-mark) * 0.3125)',
|
|
4231
|
+
borderStyle: 'solid',
|
|
4232
|
+
borderColor: TOAST_SUCCESS_INK,
|
|
4233
|
+
borderTopWidth: '0',
|
|
4234
|
+
borderLeftWidth: '0',
|
|
4235
|
+
borderRightWidth: 'calc(var(--border) * 2)',
|
|
4236
|
+
borderBottomWidth: 'calc(var(--border) * 2)',
|
|
4237
|
+
transform: 'rotate(45deg)',
|
|
4238
|
+
},
|
|
4239
|
+
// The cross without clip-path: its two diagonal bars
|
|
4240
|
+
// as hard-stop gradients over a clear fill.
|
|
4241
|
+
error: {
|
|
4242
|
+
background: `linear-gradient(45deg, transparent 41%, ${TOAST_ERROR_INK} 41%, ${TOAST_ERROR_INK} 59%, transparent 59%), `
|
|
4243
|
+
+ `linear-gradient(-45deg, transparent 41%, ${TOAST_ERROR_INK} 41%, ${TOAST_ERROR_INK} 59%, transparent 59%)`,
|
|
4244
|
+
},
|
|
4245
|
+
},
|
|
4246
|
+
},
|
|
4247
|
+
},
|
|
4248
|
+
variants: {
|
|
4249
|
+
// Each colour's ring track: its role at 25% (the web's mix
|
|
4250
|
+
// over `--toast-ink`, which is that role).
|
|
4251
|
+
color: Object.fromEntries(ROLES.map((role) => [role, {
|
|
4252
|
+
indicator: {
|
|
4253
|
+
states: {
|
|
4254
|
+
loading: lynxSides('border{}Color', `color-mix(in oklab, var(--color-${role}) 25%, transparent)`, ['Right', 'Bottom', 'Left']),
|
|
4255
|
+
},
|
|
4256
|
+
},
|
|
4257
|
+
}])),
|
|
4258
|
+
// The mark sits at the root's padding offsets for its size.
|
|
4259
|
+
size: {
|
|
4260
|
+
xs: { indicator: { base: { left: 'var(--space-md)', top: 'var(--space-xs)' } } },
|
|
4261
|
+
sm: { indicator: { base: { left: 'var(--space-md)', top: 'var(--space-sm)' } } },
|
|
4262
|
+
lg: { indicator: { base: { left: 'var(--space-xl)', top: 'var(--space-lg)' } } },
|
|
4263
|
+
xl: { indicator: { base: { left: 'var(--space-2xl)', top: 'var(--space-xl)' } } },
|
|
4264
|
+
},
|
|
4062
4265
|
},
|
|
4266
|
+
// The text of a marked toast steps past the mark and the web's
|
|
4267
|
+
// column gap. `marked` is a lynx-zero rendering detail (the
|
|
4268
|
+
// spelling of `:has(> indicator)`), not a modifier an author sets,
|
|
4269
|
+
// so it is styled from raw lynx css. The reduced-motion and
|
|
4270
|
+
// forced-colors stops cannot follow: lynx emits no `@media`.
|
|
4271
|
+
css: `
|
|
4272
|
+
.zx-toast__title.zx-m-marked,
|
|
4273
|
+
.zx-toast__description.zx-m-marked,
|
|
4274
|
+
.zx-toast__action.zx-m-marked {
|
|
4275
|
+
margin-left: calc(var(--toast-mark) + var(--space-md));
|
|
4276
|
+
}
|
|
4277
|
+
`,
|
|
4063
4278
|
},
|
|
4064
4279
|
},
|
|
4065
4280
|
// The viewport's `open` (the stack expanded) is a fact this skin has no
|
|
@@ -4154,7 +4369,8 @@ export const combobox = {
|
|
|
4154
4369
|
fontWeight: 'var(--weight-medium)',
|
|
4155
4370
|
lineHeight: 'var(--leading-none)',
|
|
4156
4371
|
},
|
|
4157
|
-
|
|
4372
|
+
// Arrowed onto from the input (#411).
|
|
4373
|
+
states: { disabled: {}, ...focusRing },
|
|
4158
4374
|
},
|
|
4159
4375
|
'tag-label': { base: { whiteSpace: 'nowrap' } },
|
|
4160
4376
|
'tag-remove': {
|
|
@@ -5059,6 +5275,28 @@ const treeRowSelectors = {
|
|
|
5059
5275
|
background: 'var(--color-base-300)',
|
|
5060
5276
|
},
|
|
5061
5277
|
};
|
|
5278
|
+
/**
|
|
5279
|
+
* The tree rows' lynx states (signalxjs/lynx#1256). The web's press wash is
|
|
5280
|
+
* `[data-pressed]:not([data-disabled]):not([data-selected])`; lynx has no
|
|
5281
|
+
* absence classes, so the wash is the plain `pressed` state and a selected
|
|
5282
|
+
* row restores its accent fill below. The web's descendant rule that swaps
|
|
5283
|
+
* the check box's pair on a selected row (`[data-selected] &` on the box)
|
|
5284
|
+
* becomes the ROW's own custom properties: they inherit to the box inside
|
|
5285
|
+
* it. The focus ring is inset (rows sit flush) and drawn as a box-shadow,
|
|
5286
|
+
* which follows the row's radius where lynx's `outline` does not.
|
|
5287
|
+
*/
|
|
5288
|
+
const lynxTreeRowStates = {
|
|
5289
|
+
pressed: { background: 'var(--color-base-300)' },
|
|
5290
|
+
selected: {
|
|
5291
|
+
'--tree-check': 'var(--tree-on-accent)',
|
|
5292
|
+
'--tree-on-check': 'var(--tree-accent)',
|
|
5293
|
+
},
|
|
5294
|
+
...lynxFocus({ inset: true }),
|
|
5295
|
+
};
|
|
5296
|
+
/** A held selected row keeps its accent fill (the web's `:not([data-selected])`). */
|
|
5297
|
+
const lynxTreeRowSelectors = {
|
|
5298
|
+
'&[data-selected][data-pressed]': { background: 'var(--tree-accent)' },
|
|
5299
|
+
};
|
|
5062
5300
|
/**
|
|
5063
5301
|
* `tickGlyphFallback` for the node check box, whose tick is its `::after`:
|
|
5064
5302
|
* the geometry drops and the same pseudo carries the glyph, in the medium's
|
|
@@ -5208,12 +5446,37 @@ export const treeView = {
|
|
|
5208
5446
|
xl: { root: { base: { '--tree-text': 'var(--text-lg)' } } },
|
|
5209
5447
|
},
|
|
5210
5448
|
},
|
|
5211
|
-
//
|
|
5212
|
-
// not
|
|
5213
|
-
//
|
|
5449
|
+
// lynx (signalxjs/lynx#1256, zero#478). No hover on a touch platform, no
|
|
5450
|
+
// `:not()`, no pseudo-elements, no descendant selectors: each web rule
|
|
5451
|
+
// those carry is restated on the part the runtime stamps. The skin's
|
|
5452
|
+
// `:dir(rtl)` mirror and the `@media` stops have no lynx form (no RTL
|
|
5453
|
+
// flow, no conditions) and stay dropped.
|
|
5214
5454
|
targets: {
|
|
5215
5455
|
lynx: {
|
|
5216
5456
|
parts: {
|
|
5457
|
+
item: { states: lynxTreeRowStates, selectors: lynxTreeRowSelectors },
|
|
5458
|
+
'branch-trigger': { states: lynxTreeRowStates, selectors: lynxTreeRowSelectors },
|
|
5459
|
+
// `inline-block` is not a lynx display value. The glyph is a
|
|
5460
|
+
// `<text>` part: a 1em box centred on it, so the quarter turn
|
|
5461
|
+
// pivots on the chevron instead of swinging it sideways.
|
|
5462
|
+
'branch-indicator': { base: { display: 'flex', width: '1em', textAlign: 'center' } },
|
|
5463
|
+
// The mark is a `<text>` child lynx-zero renders (`✓` checked,
|
|
5464
|
+
// `−` indeterminate) in place of the web's clip-path `::after`
|
|
5465
|
+
// tick — clip-path and pseudo-elements are both dropped on
|
|
5466
|
+
// lynx. The glyph is the forced-colors fallback's shape
|
|
5467
|
+
// (`nodeTickFallback`), inked with the box's `-on` pair and
|
|
5468
|
+
// sized by inheritance from the row.
|
|
5469
|
+
'node-checkbox': {
|
|
5470
|
+
base: {
|
|
5471
|
+
display: 'flex',
|
|
5472
|
+
alignItems: 'center',
|
|
5473
|
+
justifyContent: 'center',
|
|
5474
|
+
color: 'var(--tree-on-check)',
|
|
5475
|
+
},
|
|
5476
|
+
},
|
|
5477
|
+
// The indent, restated physically: logical spellings resolve
|
|
5478
|
+
// on iOS but not on Android (measured, signalxjs/lynx#1084),
|
|
5479
|
+
// so the emitter refuses them.
|
|
5217
5480
|
'branch-content': { base: { paddingLeft: 'var(--space-lg)' } },
|
|
5218
5481
|
},
|
|
5219
5482
|
},
|
|
@@ -5314,6 +5577,10 @@ export const input = {
|
|
|
5314
5577
|
selectors: {
|
|
5315
5578
|
// Same muting, same reason as `number-input/input` (#264).
|
|
5316
5579
|
'&::placeholder': { color: 'color-mix(in oklab, var(--color-base-content) 60%, transparent)' },
|
|
5580
|
+
// zero draws its own ClearTrigger and clears on Escape, and Firefox
|
|
5581
|
+
// draws no native clear: hide the engine's cancel button everywhere (#446).
|
|
5582
|
+
'&::-webkit-search-cancel-button': { appearance: 'none', display: 'none' },
|
|
5583
|
+
'&::-webkit-search-decoration': { appearance: 'none' },
|
|
5317
5584
|
},
|
|
5318
5585
|
},
|
|
5319
5586
|
// daisy's `label` inside `.input`: a quiet prefix/suffix at one edge,
|
|
@@ -5510,11 +5777,18 @@ export const textarea = {
|
|
|
5510
5777
|
// lands on top of the 2px gap and paints one solid band. A fixed-height
|
|
5511
5778
|
// box never re-lays out; this auto-height one does, as its native field
|
|
5512
5779
|
// measures and grows. So the ring is spelled with one outset shadow and
|
|
5513
|
-
//
|
|
5780
|
+
// inset shadows, which paint areas the outset never reaches: the ring
|
|
5514
5781
|
// just outside the box, the box's own border repainted as the base-100
|
|
5515
|
-
// gap, and the hairline redrawn inside it by the inset spread, in the
|
|
5782
|
+
// gap, and the hairline redrawn inside it by the edge inset's spread, in the
|
|
5516
5783
|
// edge colour (`--textarea-edge`, the error colour while invalid). The
|
|
5517
5784
|
// gap is the border's width instead of the web's 2px.
|
|
5785
|
+
//
|
|
5786
|
+
// Android paints inset shadows OVER the border, where iOS paints them
|
|
5787
|
+
// under it (signalxjs/lynx#1230): the base-100 border vanished and the
|
|
5788
|
+
// whole 2px inset showed as a grey band against the ring. So the gap is
|
|
5789
|
+
// also drawn as a 1px base-100 inset stacked above the edge inset. On
|
|
5790
|
+
// Android that inset is the gap. On iOS the border already covers it,
|
|
5791
|
+
// whichever order the layers end up in, so iOS paints as before.
|
|
5518
5792
|
targets: {
|
|
5519
5793
|
lynx: {
|
|
5520
5794
|
parts: {
|
|
@@ -5531,6 +5805,9 @@ export const textarea = {
|
|
|
5531
5805
|
outline: 'none',
|
|
5532
5806
|
borderColor: 'var(--color-base-100)',
|
|
5533
5807
|
boxShadow: '0 0 0 2px var(--textarea-accent), '
|
|
5808
|
+
// The gap: the border's width in base-100,
|
|
5809
|
+
// for Android, which paints this over the border.
|
|
5810
|
+
+ 'inset 0 0 0 1px var(--color-base-100), '
|
|
5534
5811
|
// Twice the border (1px in every shipped
|
|
5535
5812
|
// theme): the inner half shows as the hairline.
|
|
5536
5813
|
+ 'inset 0 0 0 2px var(--textarea-edge)',
|
|
@@ -5656,6 +5933,42 @@ export const card = {
|
|
|
5656
5933
|
xl: { root: { base: { '--card-pad': 'var(--space-2xl)' } } },
|
|
5657
5934
|
},
|
|
5658
5935
|
},
|
|
5936
|
+
targets: {
|
|
5937
|
+
lynx: {
|
|
5938
|
+
parts: {
|
|
5939
|
+
// The root does not clip on lynx: a clipping layer takes its
|
|
5940
|
+
// own shadow with it on iOS, and the card's elevation is the
|
|
5941
|
+
// daisy look. The media band rounds its own corners instead
|
|
5942
|
+
// (below), which is all the clip was for.
|
|
5943
|
+
root: { base: { overflow: 'visible' } },
|
|
5944
|
+
// `inline-size` has no lynx spelling; the image selector and
|
|
5945
|
+
// the `:first-child` / `:last-child` corners are dropped (no
|
|
5946
|
+
// child or structural pseudo-classes). The band clips its
|
|
5947
|
+
// own content, so an `<image>` inside it takes the corners.
|
|
5948
|
+
media: { base: { width: '100%', overflow: 'hidden' } },
|
|
5949
|
+
// A lynx view is a column unless told otherwise.
|
|
5950
|
+
footer: { base: { flexDirection: 'row' } },
|
|
5951
|
+
},
|
|
5952
|
+
// The corners the media band shares with the card
|
|
5953
|
+
// (signalxjs/lynx#1235). Lynx has no `:first-child` or
|
|
5954
|
+
// `:last-child`, so lynx-zero stamps the band itself from its
|
|
5955
|
+
// mount order among the card's bands, as `data-mod-first` /
|
|
5956
|
+
// `data-mod-last` (the `zx-m-first` / `zx-m-last` classes). They
|
|
5957
|
+
// are a lynx-zero rendering detail, not modifiers an author sets,
|
|
5958
|
+
// so they are not declared in the vocabulary and are styled from
|
|
5959
|
+
// raw lynx css (toggle-group's join ends are the precedent).
|
|
5960
|
+
css: `
|
|
5961
|
+
.zx-card__media.zx-m-first {
|
|
5962
|
+
border-top-left-radius: var(--radius-box);
|
|
5963
|
+
border-top-right-radius: var(--radius-box);
|
|
5964
|
+
}
|
|
5965
|
+
.zx-card__media.zx-m-last {
|
|
5966
|
+
border-bottom-left-radius: var(--radius-box);
|
|
5967
|
+
border-bottom-right-radius: var(--radius-box);
|
|
5968
|
+
}
|
|
5969
|
+
`,
|
|
5970
|
+
},
|
|
5971
|
+
},
|
|
5659
5972
|
};
|
|
5660
5973
|
/**
|
|
5661
5974
|
* daisy "alert" flavor: the role's soft tint edge to edge, its ink for the
|
|
@@ -5746,6 +6059,100 @@ export const alert = {
|
|
|
5746
6059
|
xl: { root: { base: { padding: 'var(--space-xl) var(--space-2xl)' } } },
|
|
5747
6060
|
},
|
|
5748
6061
|
},
|
|
6062
|
+
targets: {
|
|
6063
|
+
// No grid on lynx (signalxjs/lynx#1075, #1235): the web's three
|
|
6064
|
+
// columns (the icon over both rows | the title over the description
|
|
6065
|
+
// | the close button) are rebuilt as a flex column with the icon and
|
|
6066
|
+
// the close button placed absolutely — the toast's close pin is the
|
|
6067
|
+
// precedent. The padding moves onto two properties so the icon and
|
|
6068
|
+
// the close button can follow it per size.
|
|
6069
|
+
lynx: {
|
|
6070
|
+
tokens: {
|
|
6071
|
+
'--alert-pad-y': 'var(--space-md)',
|
|
6072
|
+
'--alert-pad-x': 'var(--space-lg)',
|
|
6073
|
+
},
|
|
6074
|
+
parts: {
|
|
6075
|
+
root: {
|
|
6076
|
+
base: {
|
|
6077
|
+
display: 'flex',
|
|
6078
|
+
flexDirection: 'column',
|
|
6079
|
+
alignItems: 'stretch',
|
|
6080
|
+
gap: 'var(--space-sm)',
|
|
6081
|
+
position: 'relative',
|
|
6082
|
+
boxSizing: 'border-box',
|
|
6083
|
+
padding: 'var(--alert-pad-y) var(--alert-pad-x)',
|
|
6084
|
+
},
|
|
6085
|
+
},
|
|
6086
|
+
// The icon column: a --text-xl slot at the padding edge,
|
|
6087
|
+
// centred over the alert's height (the web's two-row span).
|
|
6088
|
+
icon: {
|
|
6089
|
+
base: {
|
|
6090
|
+
position: 'absolute',
|
|
6091
|
+
left: 'var(--alert-pad-x)',
|
|
6092
|
+
top: '0',
|
|
6093
|
+
bottom: '0',
|
|
6094
|
+
width: 'var(--text-xl)',
|
|
6095
|
+
display: 'flex',
|
|
6096
|
+
justifyContent: 'center',
|
|
6097
|
+
},
|
|
6098
|
+
},
|
|
6099
|
+
// Pinned top-right, its glyph on the padding edge. `inherit`
|
|
6100
|
+
// is spelled as the root's own ink. A square chip, the
|
|
6101
|
+
// glyph centred in it: the web's `line-height: 1` + 2xs
|
|
6102
|
+
// padding make an almost-square box, but lynx does not carry
|
|
6103
|
+
// the line-height into the glyph's `<text>`, so the padded
|
|
6104
|
+
// box was a narrow × in a tall line — a 14 × 20pt pill whose
|
|
6105
|
+
// press wash and focus ring drew as vertical ovals
|
|
6106
|
+
// (signalxjs/lynx#1253). The box is sized outright instead.
|
|
6107
|
+
close: {
|
|
6108
|
+
base: {
|
|
6109
|
+
position: 'absolute',
|
|
6110
|
+
top: 'calc(var(--alert-pad-y) - var(--space-2xs))',
|
|
6111
|
+
right: 'calc(var(--alert-pad-x) - var(--space-2xs))',
|
|
6112
|
+
color: 'var(--color-base-content)',
|
|
6113
|
+
fontSize: 'var(--text-md)',
|
|
6114
|
+
display: 'flex',
|
|
6115
|
+
alignItems: 'center',
|
|
6116
|
+
justifyContent: 'center',
|
|
6117
|
+
boxSizing: 'border-box',
|
|
6118
|
+
padding: '0',
|
|
6119
|
+
width: 'calc(var(--text-md) + var(--space-2xs) * 2)',
|
|
6120
|
+
height: 'calc(var(--text-md) + var(--space-2xs) * 2)',
|
|
6121
|
+
},
|
|
6122
|
+
states: lynxFocus({ gap: 'var(--alert-tint)' }),
|
|
6123
|
+
},
|
|
6124
|
+
},
|
|
6125
|
+
variants: {
|
|
6126
|
+
size: {
|
|
6127
|
+
xs: { root: { base: { '--alert-pad-y': 'var(--space-2xs)', '--alert-pad-x': 'var(--space-sm)' } } },
|
|
6128
|
+
sm: { root: { base: { '--alert-pad-y': 'var(--space-xs)', '--alert-pad-x': 'var(--space-md)' } } },
|
|
6129
|
+
lg: { root: { base: { '--alert-pad-y': 'var(--space-lg)', '--alert-pad-x': 'var(--space-xl)' } } },
|
|
6130
|
+
xl: { root: { base: { '--alert-pad-y': 'var(--space-xl)', '--alert-pad-x': 'var(--space-2xl)' } } },
|
|
6131
|
+
},
|
|
6132
|
+
},
|
|
6133
|
+
// The title and description keep clear of the icon column and
|
|
6134
|
+
// the close button's corner. Lynx has no `:has()`, so lynx-zero
|
|
6135
|
+
// stamps what the alert renders onto both text parts, as
|
|
6136
|
+
// `data-mod-with-icon` / `data-mod-with-close` (the
|
|
6137
|
+
// `zx-m-with-icon` / `zx-m-with-close` classes) — a lynx-zero
|
|
6138
|
+
// rendering detail like toggle-group's join ends, styled from raw
|
|
6139
|
+
// lynx css.
|
|
6140
|
+
css: `
|
|
6141
|
+
.zx-alert__title.zx-m-with-icon {
|
|
6142
|
+
margin-left: calc(var(--text-xl) + var(--space-md));
|
|
6143
|
+
}
|
|
6144
|
+
.zx-alert__description.zx-m-with-icon {
|
|
6145
|
+
margin-left: calc(var(--text-xl) + var(--space-md));
|
|
6146
|
+
}
|
|
6147
|
+
.zx-alert__title.zx-m-with-close {
|
|
6148
|
+
margin-right: calc(var(--text-md) + var(--space-md));
|
|
6149
|
+
}
|
|
6150
|
+
.zx-alert__description.zx-m-with-close {
|
|
6151
|
+
margin-right: calc(var(--text-md) + var(--space-md));
|
|
6152
|
+
}
|
|
6153
|
+
`,
|
|
6154
|
+
},
|
|
6155
|
+
},
|
|
5749
6156
|
};
|
|
5750
6157
|
/**
|
|
5751
6158
|
* EmptyState (zero#131) — daisy has no empty-state component; this is the
|
|
@@ -5827,6 +6234,25 @@ export const emptyState = {
|
|
|
5827
6234
|
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)' } } },
|
|
5828
6235
|
},
|
|
5829
6236
|
},
|
|
6237
|
+
// signalxjs/lynx#1236 — the lynx replacements for what the shared
|
|
6238
|
+
// sections cannot say there.
|
|
6239
|
+
targets: {
|
|
6240
|
+
lynx: {
|
|
6241
|
+
parts: {
|
|
6242
|
+
// Physical margins: Android resolves no logical margin
|
|
6243
|
+
// (signalxjs/lynx#1084).
|
|
6244
|
+
icon: { base: { marginBottom: 'var(--space-xs)' } },
|
|
6245
|
+
// Title and description are lynx `<text>`: a view's
|
|
6246
|
+
// `text-align` and `color` do not reach one unless the app
|
|
6247
|
+
// turned CSS inheritance on, so each states its own ink and
|
|
6248
|
+
// centring. The web's 36ch measure becomes the root's width:
|
|
6249
|
+
// `ch` is unmeasured on lynx.
|
|
6250
|
+
title: { base: { color: 'var(--color-base-content)', textAlign: 'center' } },
|
|
6251
|
+
description: { base: { textAlign: 'center', maxInlineSize: '100%' } },
|
|
6252
|
+
actions: { base: { flexDirection: 'row', marginTop: 'var(--space-sm)' } },
|
|
6253
|
+
},
|
|
6254
|
+
},
|
|
6255
|
+
},
|
|
5830
6256
|
};
|
|
5831
6257
|
/**
|
|
5832
6258
|
* daisy "badge" flavor: a fully-rounded pill with a hairline, filled by the
|
|
@@ -5928,6 +6354,62 @@ export const badge = {
|
|
|
5928
6354
|
}, keyframes: {
|
|
5929
6355
|
'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; }',
|
|
5930
6356
|
},
|
|
6357
|
+
/**
|
|
6358
|
+
* Lynx (signalxjs/lynx#1234). The dot's `currentColor` never resolves
|
|
6359
|
+
* there (lynx#1079), so the uncoloured dot and its ring take the pill's
|
|
6360
|
+
* ink token, the value `currentColor` names on the web. The running halo
|
|
6361
|
+
* and pulse mix the recipe-local `--badge-dot`, which cannot bake, so the
|
|
6362
|
+
* halo is its own token mixed from each role's `--color-*` (baked per
|
|
6363
|
+
* theme) and the pulse breathes opacity instead of the halo's spread.
|
|
6364
|
+
* `inline-size`/`block-size` do not resolve on lynx (lynx#1145): the dot
|
|
6365
|
+
* is sized physically. A lynx view has no inline formatting context, so
|
|
6366
|
+
* the pill is content-sized with an explicit row (lynx#1165), and its
|
|
6367
|
+
* hairline and the dot's ring are longhands (lynx#1161/#1162).
|
|
6368
|
+
*/
|
|
6369
|
+
targets: {
|
|
6370
|
+
lynx: {
|
|
6371
|
+
tokens: {
|
|
6372
|
+
'--badge-dot': 'var(--badge-ink)',
|
|
6373
|
+
'--badge-dot-ring': 'var(--badge-ink)',
|
|
6374
|
+
'--badge-dot-halo': 'color-mix(in oklch, var(--color-base-content) 35%, transparent)',
|
|
6375
|
+
},
|
|
6376
|
+
parts: {
|
|
6377
|
+
root: {
|
|
6378
|
+
base: {
|
|
6379
|
+
...lynxBtnFit,
|
|
6380
|
+
flexDirection: 'row',
|
|
6381
|
+
border: '0 solid transparent',
|
|
6382
|
+
...lynxSides('border{}Width', 'var(--border)'),
|
|
6383
|
+
},
|
|
6384
|
+
},
|
|
6385
|
+
dot: {
|
|
6386
|
+
base: {
|
|
6387
|
+
display: 'flex',
|
|
6388
|
+
width: '0.5em',
|
|
6389
|
+
height: '0.5em',
|
|
6390
|
+
flexShrink: '0',
|
|
6391
|
+
border: '0 solid transparent',
|
|
6392
|
+
...lynxSides('border{}Width', '0.1em'),
|
|
6393
|
+
...lynxSides('border{}Color', 'var(--badge-dot-ring)'),
|
|
6394
|
+
},
|
|
6395
|
+
states: {
|
|
6396
|
+
running: {
|
|
6397
|
+
boxShadow: '0 0 0 0.2em var(--badge-dot-halo)',
|
|
6398
|
+
animation: 'zero-daisy-badge-pulse 1.6s ease-in-out infinite',
|
|
6399
|
+
},
|
|
6400
|
+
},
|
|
6401
|
+
},
|
|
6402
|
+
},
|
|
6403
|
+
variants: {
|
|
6404
|
+
color: Object.fromEntries(ROLES.map((c) => [c, {
|
|
6405
|
+
dot: { base: { '--badge-dot-halo': `color-mix(in oklch, var(--color-${c}) 35%, transparent)` } },
|
|
6406
|
+
}])),
|
|
6407
|
+
},
|
|
6408
|
+
keyframes: {
|
|
6409
|
+
'zero-daisy-badge-pulse': 'from, to { opacity: 1; } 50% { opacity: 0.45; }',
|
|
6410
|
+
},
|
|
6411
|
+
},
|
|
6412
|
+
},
|
|
5931
6413
|
};
|
|
5932
6414
|
/** daisy "divider": the base-300 rule, thickening with the ramp. */
|
|
5933
6415
|
export const divider = {
|
|
@@ -5997,6 +6479,11 @@ export const divider = {
|
|
|
5997
6479
|
},
|
|
5998
6480
|
targets: {
|
|
5999
6481
|
lynx: {
|
|
6482
|
+
// `auto` behind a custom property, so the labelled root's size
|
|
6483
|
+
// is var-bearing too and can beat the var-bearing line thickness
|
|
6484
|
+
// below (lynx expands var() declarations after the cascade,
|
|
6485
|
+
// signalxjs/lynx#1161).
|
|
6486
|
+
tokens: { '--divider-fit': 'auto' },
|
|
6000
6487
|
parts: {
|
|
6001
6488
|
// Lynx drops the ::before/::after segments the rule is drawn
|
|
6002
6489
|
// with above, which left the lynx divider with no line and,
|
|
@@ -6011,7 +6498,64 @@ export const divider = {
|
|
|
6011
6498
|
'&[data-orientation="vertical"]': { inlineSize: 'var(--divider-thickness)' },
|
|
6012
6499
|
},
|
|
6013
6500
|
},
|
|
6501
|
+
// The label's inset, physical (Android resolves no logical
|
|
6502
|
+
// padding, signalxjs/lynx#1084), and flush on the side its
|
|
6503
|
+
// placement gave up. Lynx has no descendant combinator from
|
|
6504
|
+
// the root, so lynx-zero stamps the root's orientation on the
|
|
6505
|
+
// label itself; vertical, the inset turns to the block axis.
|
|
6506
|
+
// Every zero is spelled through var() so it beats the
|
|
6507
|
+
// var-bearing inset (signalxjs/lynx#1161).
|
|
6508
|
+
label: {
|
|
6509
|
+
base: {
|
|
6510
|
+
paddingLeft: 'var(--space-md)',
|
|
6511
|
+
paddingRight: 'var(--space-md)',
|
|
6512
|
+
},
|
|
6513
|
+
selectors: {
|
|
6514
|
+
'&[data-placement="start"]': { paddingLeft: 'calc(var(--space-md) * 0)' },
|
|
6515
|
+
'&[data-placement="end"]': { paddingRight: 'calc(var(--space-md) * 0)' },
|
|
6516
|
+
'&[data-orientation="vertical"]': {
|
|
6517
|
+
paddingLeft: 'calc(var(--space-md) * 0)',
|
|
6518
|
+
paddingRight: 'calc(var(--space-md) * 0)',
|
|
6519
|
+
paddingTop: 'var(--space-sm)',
|
|
6520
|
+
paddingBottom: 'var(--space-sm)',
|
|
6521
|
+
},
|
|
6522
|
+
'&[data-orientation="vertical"][data-placement="start"]': { paddingTop: 'calc(var(--space-sm) * 0)' },
|
|
6523
|
+
'&[data-orientation="vertical"][data-placement="end"]': { paddingBottom: 'calc(var(--space-sm) * 0)' },
|
|
6524
|
+
},
|
|
6525
|
+
},
|
|
6014
6526
|
},
|
|
6527
|
+
// A labelled divider (signalxjs/lynx#1236). With no pseudo-
|
|
6528
|
+
// elements, lynx-zero draws the rule's two segments as views
|
|
6529
|
+
// carrying the root's own line classes plus `zx-m-segment` —
|
|
6530
|
+
// so each paints this recipe's line, ink and thickness ramp — and
|
|
6531
|
+
// leaves out the one on the label's placed side (the web's
|
|
6532
|
+
// `:has(> [data-placement])::before { display: none }`). The root
|
|
6533
|
+
// holding them is stamped `zx-m-labelled` and stops being a line:
|
|
6534
|
+
// no fill, its cross size following the label, a row when
|
|
6535
|
+
// horizontal. Both are lynx-zero rendering details, not
|
|
6536
|
+
// modifiers an author sets, so they are styled from raw lynx css
|
|
6537
|
+
// (the toggle-group join ends' reasoning).
|
|
6538
|
+
css: `
|
|
6539
|
+
.zx-divider__root.zx-m-labelled {
|
|
6540
|
+
--divider-ink: transparent;
|
|
6541
|
+
}
|
|
6542
|
+
.zx-divider__root.zx-m-labelled.zx-o-horizontal {
|
|
6543
|
+
flex-direction: row;
|
|
6544
|
+
block-size: var(--divider-fit);
|
|
6545
|
+
}
|
|
6546
|
+
.zx-divider__root.zx-m-labelled.zx-o-vertical {
|
|
6547
|
+
inline-size: var(--divider-fit);
|
|
6548
|
+
}
|
|
6549
|
+
.zx-divider__root.zx-m-segment {
|
|
6550
|
+
flex: 1 1 0;
|
|
6551
|
+
}
|
|
6552
|
+
.zx-divider__root.zx-m-segment.zx-o-horizontal {
|
|
6553
|
+
min-width: 0;
|
|
6554
|
+
}
|
|
6555
|
+
.zx-divider__root.zx-m-segment.zx-o-vertical {
|
|
6556
|
+
min-height: 0;
|
|
6557
|
+
}
|
|
6558
|
+
`,
|
|
6015
6559
|
},
|
|
6016
6560
|
},
|
|
6017
6561
|
};
|
|
@@ -6067,12 +6611,28 @@ export const skeleton = {
|
|
|
6067
6611
|
keyframes: {
|
|
6068
6612
|
'zero-daisyui-skeleton': 'from, to { opacity: 1; } 50% { opacity: 0.55; }',
|
|
6069
6613
|
},
|
|
6614
|
+
// signalxjs/lynx#1237. The pulse is an opacity loop on the part itself —
|
|
6615
|
+
// no pseudo-element, so it carries as is; only its keyframes are
|
|
6616
|
+
// restated as three plain stops, not the `from, to` selector list. The
|
|
6617
|
+
// reduced-motion stop cannot follow: lynx emits no `@media`.
|
|
6618
|
+
targets: {
|
|
6619
|
+
lynx: {
|
|
6620
|
+
keyframes: {
|
|
6621
|
+
'zero-daisyui-skeleton': '0% { opacity: 1; } 50% { opacity: 0.55; } 100% { opacity: 1; }',
|
|
6622
|
+
},
|
|
6623
|
+
},
|
|
6624
|
+
},
|
|
6070
6625
|
};
|
|
6071
|
-
/**
|
|
6626
|
+
/**
|
|
6627
|
+
* daisy "loading-spinner": the ring, with one quadrant in the role's ink.
|
|
6628
|
+
* Sized on daisy's `loading-*` ramp — `--size-selector * 4…8` (16–32px), md
|
|
6629
|
+
* the `loading` default of `* 6` (signalxjs/lynx#1237: the ramp was
|
|
6630
|
+
* `--size-field * 0.35…0.9`, a 2px box that the 2px border swallowed).
|
|
6631
|
+
*/
|
|
6072
6632
|
export const spinner = {
|
|
6073
6633
|
component: 'spinner',
|
|
6074
6634
|
tokens: {
|
|
6075
|
-
'--spinner-size': 'calc(var(--size-
|
|
6635
|
+
'--spinner-size': 'calc(var(--size-selector) * 6)',
|
|
6076
6636
|
'--spinner-ink': 'var(--color-primary)',
|
|
6077
6637
|
'--spinner-track': 'var(--color-base-300)',
|
|
6078
6638
|
},
|
|
@@ -6096,14 +6656,40 @@ export const spinner = {
|
|
|
6096
6656
|
'--spinner-ink': `var(--color-${c})`,
|
|
6097
6657
|
} } }])),
|
|
6098
6658
|
size: {
|
|
6099
|
-
xs: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
6100
|
-
sm: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
6659
|
+
xs: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 4)' } } },
|
|
6660
|
+
sm: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 5)' } } },
|
|
6101
6661
|
md: {},
|
|
6102
|
-
lg: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
6103
|
-
xl: { root: { base: { '--spinner-size': 'calc(var(--size-
|
|
6662
|
+
lg: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 7)' } } },
|
|
6663
|
+
xl: { root: { base: { '--spinner-size': 'calc(var(--size-selector) * 8)' } } },
|
|
6104
6664
|
},
|
|
6105
6665
|
},
|
|
6106
6666
|
keyframes: { 'zero-daisyui-spin': 'to { transform: rotate(360deg); }' },
|
|
6667
|
+
// signalxjs/lynx#1237 — the ring in physical spellings. The logical box
|
|
6668
|
+
// size and `border-block-start-color` are not lynx's; the ring is
|
|
6669
|
+
// restated as longhands, because a var-bearing `border` shorthand
|
|
6670
|
+
// expands after the cascade and would repaint the head in the track
|
|
6671
|
+
// colour (signalxjs/lynx#1161). A flex box, since `inline-block` is not a
|
|
6672
|
+
// lynx display value, that keeps its size in a row. The reduced-motion
|
|
6673
|
+
// stop cannot follow: lynx emits no `@media`.
|
|
6674
|
+
targets: {
|
|
6675
|
+
lynx: {
|
|
6676
|
+
parts: {
|
|
6677
|
+
root: {
|
|
6678
|
+
base: {
|
|
6679
|
+
display: 'flex',
|
|
6680
|
+
flexShrink: '0',
|
|
6681
|
+
width: 'var(--spinner-size)',
|
|
6682
|
+
height: 'var(--spinner-size)',
|
|
6683
|
+
border: '0 solid transparent',
|
|
6684
|
+
borderStyle: 'solid',
|
|
6685
|
+
...lynxSides('border{}Width', 'calc(var(--border) * 2)'),
|
|
6686
|
+
...lynxSides('border{}Color', 'var(--spinner-track)', ['Right', 'Bottom', 'Left']),
|
|
6687
|
+
borderTopColor: 'var(--spinner-ink)',
|
|
6688
|
+
},
|
|
6689
|
+
},
|
|
6690
|
+
},
|
|
6691
|
+
},
|
|
6692
|
+
},
|
|
6107
6693
|
};
|
|
6108
6694
|
// ── The content-tier sweep (#334) ─────────────────────────────────────────
|
|
6109
6695
|
/** daisy kbd: the raised base-200 cap with the doubled bottom edge. */
|
|
@@ -6149,6 +6735,44 @@ export const kbd = {
|
|
|
6149
6735
|
xl: { root: { base: { fontSize: 'var(--text-md)', padding: 'var(--space-xs) var(--space-lg)' } } },
|
|
6150
6736
|
},
|
|
6151
6737
|
},
|
|
6738
|
+
/**
|
|
6739
|
+
* Lynx (signalxjs/lynx#1234). `min-inline-size` and the logical bottom
|
|
6740
|
+
* edge do not resolve on lynx (lynx#1145, #1084), so the cap's floor is
|
|
6741
|
+
* a physical `min-width` and its doubled bottom edge a physical
|
|
6742
|
+
* longhand; the whole hairline is longhands so a var-bearing shorthand
|
|
6743
|
+
* cannot expand over the thicker edge (lynx#1161/#1162). A lynx view has
|
|
6744
|
+
* no inline formatting context: the cap is content-sized with an
|
|
6745
|
+
* explicit row (lynx#1165).
|
|
6746
|
+
*/
|
|
6747
|
+
targets: {
|
|
6748
|
+
lynx: {
|
|
6749
|
+
parts: {
|
|
6750
|
+
root: {
|
|
6751
|
+
base: {
|
|
6752
|
+
...lynxBtnFit,
|
|
6753
|
+
flexDirection: 'row',
|
|
6754
|
+
minWidth: '1.75em',
|
|
6755
|
+
border: '0 solid transparent',
|
|
6756
|
+
...lynxSides('border{}Width', 'var(--border)', ['Top', 'Right', 'Left']),
|
|
6757
|
+
borderBottomWidth: 'calc(var(--border) * 2)',
|
|
6758
|
+
...lynxSides('border{}Color', 'var(--color-base-300)'),
|
|
6759
|
+
// The web's monospace cap comes from the browser's
|
|
6760
|
+
// UA style for `<kbd>`, not the recipe; lynx has no
|
|
6761
|
+
// UA sheet, so the face is spelled here
|
|
6762
|
+
// (signalxjs/lynx#1254). Concrete names, not a
|
|
6763
|
+
// token: iOS Lynx walks the list with
|
|
6764
|
+
// `UIFont fontWithName:` (Menlo ships on iOS, the
|
|
6765
|
+
// CSS generics do not resolve there), and
|
|
6766
|
+
// `monospace` serves the lynx web target.
|
|
6767
|
+
fontFamily: 'Menlo, monospace',
|
|
6768
|
+
},
|
|
6769
|
+
},
|
|
6770
|
+
},
|
|
6771
|
+
variants: {
|
|
6772
|
+
size: { xs: { root: { base: { minWidth: '1.5em' } } } },
|
|
6773
|
+
},
|
|
6774
|
+
},
|
|
6775
|
+
},
|
|
6152
6776
|
};
|
|
6153
6777
|
/**
|
|
6154
6778
|
* daisy status: the dot with the soft halo ring daisy draws around it —
|
|
@@ -6189,6 +6813,37 @@ export const status = {
|
|
|
6189
6813
|
xl: { root: { base: { '--status-size': 'calc(var(--size-selector) * 3.5)' } } },
|
|
6190
6814
|
},
|
|
6191
6815
|
},
|
|
6816
|
+
/**
|
|
6817
|
+
* Lynx (signalxjs/lynx#1234). The halo mixes the recipe-local
|
|
6818
|
+
* `--status-ink`, which cannot bake, so it is its own token mixed from
|
|
6819
|
+
* each role's `--color-*` (baked per theme). `inline-size`/`block-size`
|
|
6820
|
+
* do not resolve on lynx (lynx#1145): the dot is sized physically, and
|
|
6821
|
+
* it is a flex box rather than an inline-block the engine has no
|
|
6822
|
+
* formatting context for.
|
|
6823
|
+
*/
|
|
6824
|
+
targets: {
|
|
6825
|
+
lynx: {
|
|
6826
|
+
tokens: {
|
|
6827
|
+
'--status-halo': 'color-mix(in oklch, var(--color-base-content) 25%, transparent)',
|
|
6828
|
+
},
|
|
6829
|
+
parts: {
|
|
6830
|
+
root: {
|
|
6831
|
+
base: {
|
|
6832
|
+
display: 'flex',
|
|
6833
|
+
flexShrink: '0',
|
|
6834
|
+
width: 'var(--status-size)',
|
|
6835
|
+
height: 'var(--status-size)',
|
|
6836
|
+
boxShadow: '0 0 0 calc(var(--status-size) / 4) var(--status-halo)',
|
|
6837
|
+
},
|
|
6838
|
+
},
|
|
6839
|
+
},
|
|
6840
|
+
variants: {
|
|
6841
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
|
|
6842
|
+
'--status-halo': `color-mix(in oklch, var(--color-${c}) 25%, transparent)`,
|
|
6843
|
+
} } }])),
|
|
6844
|
+
},
|
|
6845
|
+
},
|
|
6846
|
+
},
|
|
6192
6847
|
};
|
|
6193
6848
|
/**
|
|
6194
6849
|
* Indicator — pure position; the item's content brings its own paint.
|
|
@@ -6371,6 +7026,90 @@ export const stats = {
|
|
|
6371
7026
|
xl: { value: { base: { fontSize: 'var(--text-3xl)' } } },
|
|
6372
7027
|
},
|
|
6373
7028
|
},
|
|
7029
|
+
// signalxjs/lynx#1236 — the lynx replacements for what the shared
|
|
7030
|
+
// sections cannot say there.
|
|
7031
|
+
targets: {
|
|
7032
|
+
lynx: {
|
|
7033
|
+
parts: {
|
|
7034
|
+
// A row unless vertical: a lynx flex container does not
|
|
7035
|
+
// default to one.
|
|
7036
|
+
root: { base: { flexDirection: 'row' } },
|
|
7037
|
+
// Lynx has no grid, so `display: grid` left the item in the
|
|
7038
|
+
// engine's default linear layout. The bands stack in a
|
|
7039
|
+
// centred column instead, and the figure is pinned to the
|
|
7040
|
+
// item's end edge (below). The padding as longhands, so the
|
|
7041
|
+
// `figure` rule below can widen one side by specificity (a
|
|
7042
|
+
// var-bearing `padding` shorthand would expand over it,
|
|
7043
|
+
// signalxjs/lynx#1161).
|
|
7044
|
+
item: {
|
|
7045
|
+
base: {
|
|
7046
|
+
display: 'flex',
|
|
7047
|
+
flexDirection: 'column',
|
|
7048
|
+
justifyContent: 'center',
|
|
7049
|
+
position: 'relative',
|
|
7050
|
+
padding: '0',
|
|
7051
|
+
paddingTop: 'var(--space-lg)',
|
|
7052
|
+
paddingBottom: 'var(--space-lg)',
|
|
7053
|
+
paddingLeft: 'var(--space-xl)',
|
|
7054
|
+
paddingRight: 'var(--space-xl)',
|
|
7055
|
+
},
|
|
7056
|
+
selectors: {
|
|
7057
|
+
// The seams: lynx has no sibling combinator, so the
|
|
7058
|
+
// web's `item + item` edge cannot be selected. Every
|
|
7059
|
+
// item draws its leading seam and the `first`
|
|
7060
|
+
// modifier takes it off the first (raw css below).
|
|
7061
|
+
'&[data-orientation="horizontal"]': {
|
|
7062
|
+
borderLeftWidth: 'var(--border)',
|
|
7063
|
+
borderLeftStyle: 'solid',
|
|
7064
|
+
borderLeftColor: 'var(--color-base-200)',
|
|
7065
|
+
},
|
|
7066
|
+
'&[data-orientation="vertical"]': {
|
|
7067
|
+
borderTopWidth: 'var(--border)',
|
|
7068
|
+
borderTopStyle: 'solid',
|
|
7069
|
+
borderTopColor: 'var(--color-base-200)',
|
|
7070
|
+
},
|
|
7071
|
+
},
|
|
7072
|
+
},
|
|
7073
|
+
// The web's second grid column, spanning the three bands and
|
|
7074
|
+
// centred on them: pinned to the item's end edge, inside its
|
|
7075
|
+
// padding, and centred top to bottom.
|
|
7076
|
+
figure: {
|
|
7077
|
+
base: {
|
|
7078
|
+
position: 'absolute',
|
|
7079
|
+
top: '0',
|
|
7080
|
+
bottom: '0',
|
|
7081
|
+
right: 'var(--space-xl)',
|
|
7082
|
+
// The web centres the grid cell with align-self; the
|
|
7083
|
+
// insets place it here.
|
|
7084
|
+
alignSelf: 'auto',
|
|
7085
|
+
display: 'flex',
|
|
7086
|
+
flexDirection: 'column',
|
|
7087
|
+
justifyContent: 'center',
|
|
7088
|
+
},
|
|
7089
|
+
},
|
|
7090
|
+
},
|
|
7091
|
+
// lynx-zero stamps the row's first item `zx-m-first` (mount
|
|
7092
|
+
// order; no `:first-child`) and an item holding a figure
|
|
7093
|
+
// `zx-m-figure`. Rendering details, not author modifiers, so raw
|
|
7094
|
+
// lynx css (the toggle-group join ends' reasoning). The first
|
|
7095
|
+
// item drops the leading seam, its zero width spelled through
|
|
7096
|
+
// var() to beat the var-bearing seam (signalxjs/lynx#1161). An
|
|
7097
|
+
// item with a figure keeps the figure's column clear: the
|
|
7098
|
+
// padding, the web's `--space-md` column gap, and a 2rem figure
|
|
7099
|
+
// (daisy's `stat-figure` holds a `size-8` icon or avatar).
|
|
7100
|
+
css: `
|
|
7101
|
+
.zx-stats__item.zx-m-first.zx-o-horizontal {
|
|
7102
|
+
border-left-width: calc(var(--border) * 0);
|
|
7103
|
+
}
|
|
7104
|
+
.zx-stats__item.zx-m-first.zx-o-vertical {
|
|
7105
|
+
border-top-width: calc(var(--border) * 0);
|
|
7106
|
+
}
|
|
7107
|
+
.zx-stats__item.zx-m-figure {
|
|
7108
|
+
padding-right: calc(var(--space-xl) + var(--space-md) + 2rem);
|
|
7109
|
+
}
|
|
7110
|
+
`,
|
|
7111
|
+
},
|
|
7112
|
+
},
|
|
6374
7113
|
};
|
|
6375
7114
|
/**
|
|
6376
7115
|
* A timeline none of whose content sits on the start side: the root carries
|
|
@@ -7085,6 +7824,28 @@ export const navbar = {
|
|
|
7085
7824
|
xl: { root: { base: { minBlockSize: '6rem', fontSize: 'var(--text-lg)' } } },
|
|
7086
7825
|
},
|
|
7087
7826
|
},
|
|
7827
|
+
// Lynx (signalxjs/lynx#1257): `min-block-size` is not a lynx property,
|
|
7828
|
+
// so the bar's height ramp is restated as `min-height`; and a lynx flex
|
|
7829
|
+
// container is only a row when it says so, so the bar and its three
|
|
7830
|
+
// sections spell `flex-direction: row`.
|
|
7831
|
+
targets: {
|
|
7832
|
+
lynx: {
|
|
7833
|
+
parts: {
|
|
7834
|
+
root: { base: { flexDirection: 'row', minHeight: '4rem' } },
|
|
7835
|
+
start: { base: { flexDirection: 'row' } },
|
|
7836
|
+
center: { base: { flexDirection: 'row' } },
|
|
7837
|
+
end: { base: { flexDirection: 'row' } },
|
|
7838
|
+
},
|
|
7839
|
+
variants: {
|
|
7840
|
+
size: {
|
|
7841
|
+
xs: { root: { base: { minHeight: '2.5rem' } } },
|
|
7842
|
+
sm: { root: { base: { minHeight: '3rem' } } },
|
|
7843
|
+
lg: { root: { base: { minHeight: '5rem' } } },
|
|
7844
|
+
xl: { root: { base: { minHeight: '6rem' } } },
|
|
7845
|
+
},
|
|
7846
|
+
},
|
|
7847
|
+
},
|
|
7848
|
+
},
|
|
7088
7849
|
};
|
|
7089
7850
|
/**
|
|
7090
7851
|
* NavList (zero#132) — daisy's `menu` idiom for a sidebar: rounded rows, a
|
|
@@ -7143,6 +7904,14 @@ export const navList = {
|
|
|
7143
7904
|
alignItems: 'center',
|
|
7144
7905
|
gap: 'var(--space-sm)',
|
|
7145
7906
|
textDecoration: 'none',
|
|
7907
|
+
// Without an href the link is a <button> (#451): drop its chrome.
|
|
7908
|
+
appearance: 'none',
|
|
7909
|
+
background: 'none',
|
|
7910
|
+
border: '0',
|
|
7911
|
+
font: 'inherit',
|
|
7912
|
+
textAlign: 'start',
|
|
7913
|
+
inlineSize: '100%',
|
|
7914
|
+
cursor: 'pointer',
|
|
7146
7915
|
color: 'var(--nav-ink)',
|
|
7147
7916
|
padding: 'var(--space-sm) var(--space-lg)',
|
|
7148
7917
|
borderRadius: 'var(--radius-field)',
|
|
@@ -7190,6 +7959,27 @@ export const navList = {
|
|
|
7190
7959
|
xl: { root: { base: { fontSize: 'var(--text-lg)' } }, link: { base: { padding: 'var(--space-lg) var(--space-xl)' } } },
|
|
7191
7960
|
},
|
|
7192
7961
|
},
|
|
7962
|
+
// The web's block and inline boxes, restated for lynx (signalxjs/lynx#1259).
|
|
7963
|
+
// No `:hover` on a touch platform, and the link's anatomy declares no
|
|
7964
|
+
// `pressed` flag: a held link has lynx-zero's main-thread press scale.
|
|
7965
|
+
targets: {
|
|
7966
|
+
lynx: {
|
|
7967
|
+
parts: {
|
|
7968
|
+
// `display: block` has no lynx projection: a column, so the
|
|
7969
|
+
// link spans the list as a block `<li>` makes it.
|
|
7970
|
+
item: { base: { display: 'flex', flexDirection: 'column' } },
|
|
7971
|
+
// The icon / label / meta row, explicit (lynx does not
|
|
7972
|
+
// default the direction the way a browser does).
|
|
7973
|
+
link: { base: { flexDirection: 'row' } },
|
|
7974
|
+
// `inline-size` restated physically; the glyph centred in
|
|
7975
|
+
// its column on both axes.
|
|
7976
|
+
icon: { base: { width: '1.25em', flexDirection: 'row', alignItems: 'center' } },
|
|
7977
|
+
// The count's push to the far edge, restated physically —
|
|
7978
|
+
// `margin-inline-start` does not resolve on Android (#1084).
|
|
7979
|
+
meta: { base: { marginLeft: 'auto', flexDirection: 'row' } },
|
|
7980
|
+
},
|
|
7981
|
+
},
|
|
7982
|
+
},
|
|
7193
7983
|
};
|
|
7194
7984
|
/**
|
|
7195
7985
|
* Breadcrumbs — daisy's breadcrumbs: a compact text-sm trail, links
|
|
@@ -7298,6 +8088,37 @@ export const breadcrumbs = {
|
|
|
7298
8088
|
xl: { root: { base: { fontSize: 'var(--text-lg)' } } },
|
|
7299
8089
|
},
|
|
7300
8090
|
},
|
|
8091
|
+
// Lynx (signalxjs/lynx#1257):
|
|
8092
|
+
// - The list, the items and the ellipsis are explicit rows (a lynx flex
|
|
8093
|
+
// container is only a row when it says so).
|
|
8094
|
+
// - The trigger's `hover` ink is dropped on a touch platform; the
|
|
8095
|
+
// `pressed` flag carries it instead. Its resting ink moves onto a
|
|
8096
|
+
// lynx-only token so the base rule stays a plain var() read: the
|
|
8097
|
+
// emitter bakes a `color-mix` declaration per theme under the host
|
|
8098
|
+
// class, and that baked rule would outrank the pressed one.
|
|
8099
|
+
// - The focus ring is the lynx box-shadow ring (outline has no offset
|
|
8100
|
+
// and ignores the radius there).
|
|
8101
|
+
// - The link's `hover` stays dropped: the link anatomy declares no
|
|
8102
|
+
// pressed flag, so there is nothing on lynx to carry it.
|
|
8103
|
+
// - The separator is a real part on both targets (lynx-zero renders it
|
|
8104
|
+
// as a text glyph), so daisy's `::before` chevron needs no lynx form.
|
|
8105
|
+
targets: {
|
|
8106
|
+
lynx: {
|
|
8107
|
+
tokens: { '--bc-trigger-ink': 'color-mix(in oklch, var(--color-base-content) 75%, transparent)' },
|
|
8108
|
+
parts: {
|
|
8109
|
+
list: { base: { flexDirection: 'row' } },
|
|
8110
|
+
item: { base: { flexDirection: 'row' } },
|
|
8111
|
+
ellipsis: { base: { flexDirection: 'row' } },
|
|
8112
|
+
'ellipsis-trigger': {
|
|
8113
|
+
base: { color: 'var(--bc-trigger-ink)' },
|
|
8114
|
+
states: {
|
|
8115
|
+
pressed: { color: 'var(--color-base-content)' },
|
|
8116
|
+
'focus-visible': lynxFocusRing('var(--color-primary)'),
|
|
8117
|
+
},
|
|
8118
|
+
},
|
|
8119
|
+
},
|
|
8120
|
+
},
|
|
8121
|
+
},
|
|
7301
8122
|
};
|
|
7302
8123
|
/**
|
|
7303
8124
|
* The four pagination triggers — prev/next and, with `withEdges`, the
|
|
@@ -7334,6 +8155,17 @@ const pageTrigger = {
|
|
|
7334
8155
|
[`&${rtl}`]: { scale: '-1 1' },
|
|
7335
8156
|
},
|
|
7336
8157
|
};
|
|
8158
|
+
/** A pagination cell's box on lynx — see the recipe's lynx section. */
|
|
8159
|
+
const pageCellLynx = {
|
|
8160
|
+
minWidth: 'var(--pg-size)',
|
|
8161
|
+
height: 'var(--pg-size)',
|
|
8162
|
+
flexShrink: '0',
|
|
8163
|
+
};
|
|
8164
|
+
/** The four triggers on lynx: the cell box, the held wash + sink, the ring. */
|
|
8165
|
+
const pageTriggerLynx = {
|
|
8166
|
+
base: pageCellLynx,
|
|
8167
|
+
states: { pressed: { background: 'var(--color-base-300)' }, ...lynxFocus() },
|
|
8168
|
+
};
|
|
7337
8169
|
/**
|
|
7338
8170
|
* Pagination — daisy's join-of-buttons made of standalone btn cells: the
|
|
7339
8171
|
* quiet base-200 fill, base-300 hover, the current page inverted into the
|
|
@@ -7430,13 +8262,35 @@ export const pagination = {
|
|
|
7430
8262
|
xl: { root: { base: { '--pg-size': 'calc(var(--size-field) * 14)', '--pg-font': 'var(--text-lg)' } } },
|
|
7431
8263
|
},
|
|
7432
8264
|
},
|
|
7433
|
-
//
|
|
7434
|
-
//
|
|
7435
|
-
//
|
|
8265
|
+
// Lynx (signalxjs/lynx#1258):
|
|
8266
|
+
// - the cells' size, restated physically: `min-inline-size` and
|
|
8267
|
+
// `block-size` do not resolve on lynx (lynx#1145), so every cell
|
|
8268
|
+
// collapsed to its glyph. Read through `--pg-size`, so the compile
|
|
8269
|
+
// inlines the size steps per part, as it does the web's;
|
|
8270
|
+
// - the item's inline padding, physical: logical spellings resolve on
|
|
8271
|
+
// iOS but not on Android (measured, lynx#1084);
|
|
8272
|
+
// - the press: the web answers a pointer with `:hover`, which a touch
|
|
8273
|
+
// platform drops, so the held cell paints daisy's hover wash with the
|
|
8274
|
+
// 1px sink (`lynxBtnPressed`'s pair) — the current page keeps its
|
|
8275
|
+
// accent fill and only sinks;
|
|
8276
|
+
// - the focus ring as `lynxFocus`: lynx's outline has no offset and
|
|
8277
|
+
// ignores the radius (lynx#1163);
|
|
8278
|
+
// - no row shrink: a cell never squeezes below its size in a narrow row.
|
|
8279
|
+
// The glyphs' rtl flip has no replacement: lynx has no RTL flow.
|
|
7436
8280
|
targets: {
|
|
7437
8281
|
lynx: {
|
|
7438
8282
|
parts: {
|
|
7439
|
-
|
|
8283
|
+
root: { base: { flexDirection: 'row' } },
|
|
8284
|
+
item: {
|
|
8285
|
+
base: { ...pageCellLynx, paddingLeft: 'var(--space-2xs)', paddingRight: 'var(--space-2xs)' },
|
|
8286
|
+
states: lynxFocus(),
|
|
8287
|
+
selectors: { '&[data-pressed][data-state="inactive"]': { background: 'var(--color-base-300)' } },
|
|
8288
|
+
},
|
|
8289
|
+
ellipsis: { base: pageCellLynx },
|
|
8290
|
+
'first-trigger': pageTriggerLynx,
|
|
8291
|
+
'prev-trigger': pageTriggerLynx,
|
|
8292
|
+
'next-trigger': pageTriggerLynx,
|
|
8293
|
+
'last-trigger': pageTriggerLynx,
|
|
7440
8294
|
},
|
|
7441
8295
|
},
|
|
7442
8296
|
},
|
|
@@ -7453,6 +8307,11 @@ export const pagination = {
|
|
|
7453
8307
|
*/
|
|
7454
8308
|
const stepInk = (accent, role) => `color-mix(in oklab, ${accent} `
|
|
7455
8309
|
+ `${Math.min(ROLE_INK_KEEP[role] ?? 55, 70)}%, var(--color-base-content))`;
|
|
8310
|
+
/**
|
|
8311
|
+
* The complete disc's fill: 20% of the accent over base-100, in OKLAB (see
|
|
8312
|
+
* the indicator's `complete` state for why not OKLCH).
|
|
8313
|
+
*/
|
|
8314
|
+
const stepTint = (accent) => `color-mix(in oklab, ${accent} 20%, var(--color-base-100))`;
|
|
7456
8315
|
/** A Steps root holding wizard parts (#296) — the panel or the triggers. */
|
|
7457
8316
|
const STEPS_WIZARD = '&:has(> [data-scope="steps"]:is([data-part="content"], [data-part="prev-trigger"], [data-part="next-trigger"]))';
|
|
7458
8317
|
/** Steps' Back/Next (#296): the neutral daisy btn, with its 1px sink. */
|
|
@@ -7568,7 +8427,7 @@ export const steps = {
|
|
|
7568
8427
|
// (warning's yellow came out pink) — which the browser parity
|
|
7569
8428
|
// check caught once per-step colour put all eight roles on
|
|
7570
8429
|
// the disc (#112).
|
|
7571
|
-
complete: { background: '
|
|
8430
|
+
complete: { background: stepTint('var(--steps-accent)'), color: 'var(--steps-complete-ink)' },
|
|
7572
8431
|
inactive: { background: 'var(--color-base-200)', color: 'var(--color-base-content)' },
|
|
7573
8432
|
invalid: { background: 'var(--color-error)', color: 'var(--color-error-content)' },
|
|
7574
8433
|
},
|
|
@@ -7656,32 +8515,82 @@ export const steps = {
|
|
|
7656
8515
|
xl: { root: { base: { '--steps-ind': 'calc(var(--size-selector) * 9)', '--steps-font': 'var(--text-md)' } } },
|
|
7657
8516
|
},
|
|
7658
8517
|
},
|
|
7659
|
-
//
|
|
7660
|
-
//
|
|
7661
|
-
//
|
|
7662
|
-
//
|
|
8518
|
+
// Lynx. Physical spellings throughout: logical insets, margins and
|
|
8519
|
+
// paddings resolve on iOS but not on Android (measured,
|
|
8520
|
+
// signalxjs/lynx#1084), and `inline-size` / `block-size` do not resolve
|
|
8521
|
+
// at all (lynx#1145) — without the restatement the discs and lines did
|
|
8522
|
+
// not paint. Also (signalxjs/lynx#1258):
|
|
8523
|
+
// - the complete disc's tint and ink read `--steps-accent` through a
|
|
8524
|
+
// colour function, which cannot bake; the base pair is restated over
|
|
8525
|
+
// primary, and each colour already restates the ink on its item, so
|
|
8526
|
+
// the tint joins it there;
|
|
8527
|
+
// - the bridge runs from this disc's edge to the next disc's edge —
|
|
8528
|
+
// `50% + ind/2` for `100% - ind` — instead of centre to centre under
|
|
8529
|
+
// the disc, so it needs no z-index across sibling items; the item
|
|
8530
|
+
// stops clipping (a lynx view's `overflow` defaults to hidden) so the
|
|
8531
|
+
// bridge can reach past its box;
|
|
8532
|
+
// - the `:has()` wizard wrap becomes the `wizard` stamp lynx-zero puts on
|
|
8533
|
+
// the root while a panel or trigger is mounted (raw css below);
|
|
8534
|
+
// - the focus ring as `lynxFocus` (lynx#1163), and Back/Next's press as
|
|
8535
|
+
// daisy's hover wash + sink (`lynxBtnPressed`), `:hover` being dropped.
|
|
7663
8536
|
targets: {
|
|
7664
8537
|
lynx: {
|
|
8538
|
+
tokens: {
|
|
8539
|
+
'--steps-complete-ink': stepInk('var(--color-primary)', 'primary'),
|
|
8540
|
+
'--steps-complete-tint': stepTint('var(--color-primary)'),
|
|
8541
|
+
},
|
|
7665
8542
|
parts: {
|
|
8543
|
+
root: { base: { flexDirection: 'row', overflow: 'visible' } },
|
|
7666
8544
|
item: {
|
|
8545
|
+
base: { overflow: 'visible' },
|
|
8546
|
+
states: lynxFocus(),
|
|
7667
8547
|
selectors: {
|
|
7668
8548
|
'&[data-orientation="vertical"]': { paddingBottom: 'var(--space-lg)' },
|
|
7669
8549
|
},
|
|
7670
8550
|
},
|
|
8551
|
+
indicator: {
|
|
8552
|
+
base: { width: 'var(--steps-ind)', height: 'var(--steps-ind)' },
|
|
8553
|
+
states: { complete: { background: 'var(--steps-complete-tint)' } },
|
|
8554
|
+
},
|
|
7671
8555
|
separator: {
|
|
7672
8556
|
selectors: {
|
|
7673
8557
|
'&[data-orientation="horizontal"]': {
|
|
7674
|
-
top: 'calc(var(--space-xs) + var(--steps-ind) / 2)',
|
|
7675
|
-
left: '50%',
|
|
8558
|
+
top: 'calc(var(--space-xs) + (var(--steps-ind) - var(--border)) / 2)',
|
|
8559
|
+
left: 'calc(50% + var(--steps-ind) / 2)',
|
|
8560
|
+
width: 'calc(100% - var(--steps-ind))',
|
|
8561
|
+
height: 'var(--border)',
|
|
7676
8562
|
},
|
|
7677
8563
|
'&[data-orientation="vertical"]': {
|
|
7678
8564
|
left: 'calc(var(--space-xs) + (var(--steps-ind) - var(--border)) / 2)',
|
|
7679
8565
|
top: 'calc(var(--space-xs) + var(--steps-ind))',
|
|
7680
8566
|
bottom: 'calc(var(--space-xs) * -1)',
|
|
8567
|
+
width: 'var(--border)',
|
|
7681
8568
|
},
|
|
7682
8569
|
},
|
|
7683
8570
|
},
|
|
8571
|
+
'prev-trigger': {
|
|
8572
|
+
base: { ...lynxBtnPad(4), ...lynxBtnFit },
|
|
8573
|
+
states: { pressed: lynxBtnPressed, ...lynxFocus() },
|
|
8574
|
+
},
|
|
8575
|
+
'next-trigger': {
|
|
8576
|
+
base: { ...lynxBtnPad(4), ...lynxBtnFit, marginLeft: 'auto' },
|
|
8577
|
+
states: { pressed: lynxBtnPressed, ...lynxFocus() },
|
|
8578
|
+
},
|
|
8579
|
+
},
|
|
8580
|
+
variants: {
|
|
8581
|
+
color: Object.fromEntries(ROLES.map((c) => [c, { item: { base: {
|
|
8582
|
+
'--steps-complete-tint': stepTint(`var(--color-${c})`),
|
|
8583
|
+
} } }])),
|
|
7684
8584
|
},
|
|
8585
|
+
// lynx-zero stamps `wizard` on the root while a Content,
|
|
8586
|
+
// PrevTrigger or NextTrigger is mounted — the web's `:has()`.
|
|
8587
|
+
// A lynx-zero rendering detail, not a modifier an author sets.
|
|
8588
|
+
css: `
|
|
8589
|
+
.zx-steps__root.zx-m-wizard {
|
|
8590
|
+
flex-wrap: wrap;
|
|
8591
|
+
row-gap: var(--space-md);
|
|
8592
|
+
}
|
|
8593
|
+
`,
|
|
7685
8594
|
},
|
|
7686
8595
|
},
|
|
7687
8596
|
};
|