@sigx/zero-daisyui 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/report.json CHANGED
@@ -10408,12 +10408,6 @@
10408
10408
  "detail": "rewritten to px at 16px/rem — lynx resolves rem against its 14px default page font size (measured, signalxjs/lynx#1183), which would draw every rem-based size 12.5% small",
10409
10409
  "scope": "menu"
10410
10410
  },
10411
- {
10412
- "where": "lynx recipe for \"checkbox\".\"root\"",
10413
- "what": "display: inline-flex",
10414
- "detail": "rewritten to display: flex — lynx has no inline formatting context, and an unsupported display value keeps the default linear layout (signalxjs/lynx#1075)",
10415
- "scope": "checkbox"
10416
- },
10417
10411
  {
10418
10412
  "where": "lynx recipe for \"checkbox\"",
10419
10413
  "what": "--checkbox-size",
@@ -10426,12 +10420,6 @@
10426
10420
  "detail": "rewritten to px at 16px/rem — lynx resolves rem against its 14px default page font size (measured, signalxjs/lynx#1183), which would draw every rem-based size 12.5% small",
10427
10421
  "scope": "checkbox"
10428
10422
  },
10429
- {
10430
- "where": "lynx recipe for \"radio-group\".\"item\"",
10431
- "what": "display: inline-flex",
10432
- "detail": "rewritten to display: flex — lynx has no inline formatting context, and an unsupported display value keeps the default linear layout (signalxjs/lynx#1075)",
10433
- "scope": "radio-group"
10434
- },
10435
10423
  {
10436
10424
  "where": "lynx recipe for \"radio-group\"",
10437
10425
  "what": "--radio-size",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/zero-daisyui",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "DaisyUI-flavored design system for SignalX Zero — daisy's token values and component look as a pure tokens+recipes skin over @sigx/zero primitives. No Tailwind, no daisyUI plugin required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -62,12 +62,12 @@
62
62
  "url": "https://github.com/signalxjs/zero/issues"
63
63
  },
64
64
  "peerDependencies": {
65
- "@sigx/zero": "^0.11.0"
65
+ "@sigx/zero": "^0.12.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "typescript": "^7.0.2",
69
- "@sigx/zero": "^0.11.0",
70
- "@sigx/zero-kit": "^0.11.0"
69
+ "@sigx/zero": "^0.12.0",
70
+ "@sigx/zero-kit": "^0.12.0"
71
71
  },
72
72
  "publishConfig": {
73
73
  "access": "public"
package/src/recipes.ts CHANGED
@@ -1690,6 +1690,10 @@ export const field: RecipeInput = {
1690
1690
  label: {
1691
1691
  base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)', color: 'var(--field-accent)' },
1692
1692
  states: { disabled: { opacity: 'var(--disabled-opacity)' } },
1693
+ // lynx has no pseudo-elements and the anatomy no part to carry
1694
+ // the asterisk, so it stays a web-only mark: on lynx a required
1695
+ // field still stamps `required` on its root and label, and the
1696
+ // control inside adopts it (signalxjs/lynx#1205).
1693
1697
  selectors: {
1694
1698
  '&[data-required]::after': { content: '" *"', color: roleInk('error') },
1695
1699
  },
@@ -1781,6 +1785,33 @@ export const fieldset: RecipeInput = {
1781
1785
  xl: { legend: { base: { fontSize: 'var(--text-lg)' } } },
1782
1786
  },
1783
1787
  },
1788
+ // Lynx has no grid: the one-column track becomes a flex column (the gap
1789
+ // survives), and the block/inline paddings are restated physically —
1790
+ // logical spellings resolve on iOS but not on Android (signalxjs/lynx#1084).
1791
+ targets: {
1792
+ lynx: {
1793
+ parts: {
1794
+ root: {
1795
+ base: {
1796
+ display: 'flex',
1797
+ flexDirection: 'column',
1798
+ paddingTop: 'var(--space-xs)',
1799
+ paddingBottom: 'var(--space-xs)',
1800
+ paddingLeft: '0',
1801
+ paddingRight: '0',
1802
+ },
1803
+ },
1804
+ legend: {
1805
+ base: {
1806
+ paddingTop: 'var(--space-sm)',
1807
+ paddingBottom: 'var(--space-sm)',
1808
+ paddingLeft: '0',
1809
+ paddingRight: '0',
1810
+ },
1811
+ },
1812
+ },
1813
+ },
1814
+ },
1784
1815
  };
1785
1816
 
1786
1817
  /**
@@ -2012,11 +2043,25 @@ export const checkbox: RecipeInput = {
2012
2043
  targets: {
2013
2044
  lynx: {
2014
2045
  parts: {
2046
+ // The box/label row, pinned: `inline-flex` is rewritten to
2047
+ // `flex`, and the row must not depend on the engine's
2048
+ // default direction (signalxjs/lynx#1203).
2049
+ root: { base: { display: 'flex', flexDirection: 'row' } },
2015
2050
  control: {
2016
2051
  // The web fallback (`color-mix(in oklab, … currentColor …)`)
2017
2052
  // exists for consumers that unset the accent; this package
2018
2053
  // always defines it, so the plain accent IS the ring.
2019
- base: { border: 'var(--border) solid var(--checkbox-accent)' },
2054
+ //
2055
+ // `inline-block` is not a lynx display value (no inline
2056
+ // formatting context); a flex box keeps daisy's geometry —
2057
+ // the padding insets the 100%-sized indicator either way.
2058
+ base: { display: 'flex', border: 'var(--border) solid var(--checkbox-accent)' },
2059
+ // The ring as spread shadows that follow the box's radius
2060
+ // (signalxjs/lynx#1163 — lynx's `outline` ignores it and
2061
+ // has no offset), keeping the box's inset depth line.
2062
+ states: {
2063
+ 'focus-visible': lynxFocusRing('var(--checkbox-accent)', { under: '0 1px var(--depth-shade) inset' }),
2064
+ },
2020
2065
  },
2021
2066
  indicator: {
2022
2067
  // The tick's fill: the control's `color:` names
@@ -2064,6 +2109,11 @@ export const radioGroup: RecipeInput = {
2064
2109
  // `invalid` paints on each `item-control`, which carries the flag
2065
2110
  // itself (#267) — the root only groups the items.
2066
2111
  states: { invalid: {}, required: {} },
2112
+ // `orientation="horizontal"` lays the items in a wrapping row —
2113
+ // checkbox-group's rule (signalxjs/zero#393).
2114
+ selectors: {
2115
+ '&[data-orientation="horizontal"]': { flexDirection: 'row', flexWrap: 'wrap', columnGap: 'var(--space-lg)' },
2116
+ },
2067
2117
  },
2068
2118
  label: {
2069
2119
  base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)' },
@@ -2171,9 +2221,20 @@ export const radioGroup: RecipeInput = {
2171
2221
  targets: {
2172
2222
  lynx: {
2173
2223
  parts: {
2224
+ // The control/label row, pinned — as checkbox's root
2225
+ // (signalxjs/lynx#1203).
2226
+ item: { base: { display: 'flex', flexDirection: 'row' } },
2174
2227
  'item-control': {
2175
- base: { border: 'var(--border) solid var(--radio-accent)' },
2176
- states: { checked: { borderColor: 'var(--radio-accent)' } },
2228
+ // `display: flex` in place of `inline-block`, which lynx
2229
+ // does not have — the padding still insets the dot.
2230
+ base: { display: 'flex', border: 'var(--border) solid var(--radio-accent)' },
2231
+ states: {
2232
+ checked: { borderColor: 'var(--radio-accent)' },
2233
+ // The round ring (signalxjs/lynx#1163): spread
2234
+ // shadows follow the 9999px radius where `outline`
2235
+ // drew a square.
2236
+ 'focus-visible': lynxFocusRing('var(--radio-accent)', { under: '0 1px var(--depth-shade) inset' }),
2237
+ },
2177
2238
  },
2178
2239
  'item-indicator': {
2179
2240
  states: { checked: { backgroundColor: 'var(--radio-accent)' } },
@@ -4394,6 +4455,45 @@ export const toggle: RecipeInput = {
4394
4455
  },
4395
4456
  },
4396
4457
  defaultVariants: { color: 'primary', size: 'md' },
4458
+ // signalxjs/lynx#1204 — the lynx replacements for what this recipe's
4459
+ // shared sections cannot say there.
4460
+ targets: {
4461
+ lynx: {
4462
+ parts: {
4463
+ root: {
4464
+ // daisy's content-sized, never-squeezed btn (lynx#1165):
4465
+ // a lynx view has no inline formatting context, so the
4466
+ // toggle stretched to its column and a squeezed label
4467
+ // wrapped per character. The glyph + label sit in a row.
4468
+ // The hairline as longhands: a var-bearing `border`
4469
+ // shorthand expands after the cascade and would beat
4470
+ // any edge a later rule restates (lynx#1161/#1162).
4471
+ base: {
4472
+ ...lynxBtnFit,
4473
+ flexDirection: 'row',
4474
+ border: '0 solid transparent',
4475
+ ...lynxSides('border{}Width', 'var(--border)'),
4476
+ ...lynxSides('border{}Color', 'var(--color-base-300)'),
4477
+ },
4478
+ states: {
4479
+ // No hover on a touch platform: the held toggle
4480
+ // takes the wash daisy's hover gives it. The shared
4481
+ // pressed rule adds the btn's 1px sink.
4482
+ pressed: { background: 'var(--color-base-300)' },
4483
+ on: lynxSides('border{}Color', 'var(--toggle-accent)'),
4484
+ // lynx's `outline` ignores the radius and has no
4485
+ // offset (lynx#1163): the ring as spread shadows.
4486
+ 'focus-visible': lynxFocusRing('var(--toggle-accent)'),
4487
+ },
4488
+ selectors: {
4489
+ // A held on toggle keeps its fill (the web's
4490
+ // `[data-state=on]:hover` stays on the accent too).
4491
+ '&[data-state="on"][data-pressed]': { background: 'var(--toggle-accent)' },
4492
+ },
4493
+ },
4494
+ },
4495
+ },
4496
+ },
4397
4497
  };
4398
4498
 
4399
4499
  // daisy "join" of btns: one bordered capsule, hairline seams between items,
@@ -4505,6 +4605,65 @@ export const toggleGroup: RecipeInput = {
4505
4605
  ),
4506
4606
  },
4507
4607
  defaultVariants: { color: 'primary' },
4608
+ // signalxjs/lynx#1204 — the lynx replacements for what this recipe's
4609
+ // shared sections cannot say there.
4610
+ targets: {
4611
+ lynx: {
4612
+ parts: {
4613
+ root: {
4614
+ // Content-sized like the web's inline-flex capsule (a
4615
+ // lynx view stretches to its column otherwise), a row
4616
+ // unless vertical, and the frame as longhands so the
4617
+ // orientation rules below can drop one edge by
4618
+ // specificity (a var-bearing `border` shorthand would
4619
+ // expand over them, lynx#1161/#1162).
4620
+ base: {
4621
+ width: 'max-content',
4622
+ flexShrink: '0',
4623
+ flexDirection: 'row',
4624
+ border: '0 solid transparent',
4625
+ ...lynxSides('border{}Width', 'var(--border)'),
4626
+ ...lynxSides('border{}Color', 'var(--color-base-300)'),
4627
+ },
4628
+ selectors: {
4629
+ // The join seams: lynx has no sibling combinator, so
4630
+ // the web's `item + item` edge cannot be selected.
4631
+ // Every item draws its leading seam instead (below),
4632
+ // and the frame gives up that edge — the first item's
4633
+ // seam stands in for it, inside the frame's clip.
4634
+ '&[data-orientation="horizontal"]': { borderLeftWidth: '0' },
4635
+ '&[data-orientation="vertical"]': { borderTopWidth: '0' },
4636
+ },
4637
+ },
4638
+ item: {
4639
+ base: { flexShrink: '0', flexWrap: 'nowrap' },
4640
+ states: {
4641
+ // No hover on a touch platform: a held item takes the
4642
+ // wash daisy's hover gives it; an on item keeps its
4643
+ // fill (the selector below).
4644
+ pressed: { background: 'var(--color-base-300)' },
4645
+ // The frame clips its items, so the ring is drawn
4646
+ // inside the item (lynx's `outline` has no offset
4647
+ // and ignores the radius, lynx#1163).
4648
+ 'focus-visible': lynxFocusRing('var(--toggle-group-accent)', { inset: true }),
4649
+ },
4650
+ selectors: {
4651
+ '&[data-state="on"][data-pressed]': { background: 'var(--toggle-group-accent)' },
4652
+ '&[data-orientation="horizontal"]': {
4653
+ borderLeftWidth: 'var(--border)',
4654
+ borderLeftStyle: 'solid',
4655
+ borderLeftColor: 'var(--color-base-300)',
4656
+ },
4657
+ '&[data-orientation="vertical"]': {
4658
+ borderTopWidth: 'var(--border)',
4659
+ borderTopStyle: 'solid',
4660
+ borderTopColor: 'var(--color-base-300)',
4661
+ },
4662
+ },
4663
+ },
4664
+ },
4665
+ },
4666
+ },
4508
4667
  };
4509
4668
 
4510
4669
  // daisy "join" of an input and two btns: one bordered capsule (the control),
@@ -4649,14 +4808,53 @@ export const numberInput: RecipeInput = {
4649
4808
  xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } } },
4650
4809
  },
4651
4810
  },
4652
- // The steppers' inline padding, restated physically: logical spellings
4653
- // resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
4654
- // the emitter refuses them.
4811
+ // Lynx restatements:
4812
+ // - the steppers' inline padding and their seams, physically: logical
4813
+ // spellings resolve on iOS but not on Android (measured,
4814
+ // signalxjs/lynx#1084);
4815
+ // - the steppers are views around a text glyph, with none of a
4816
+ // button's content centering;
4817
+ // - the native input takes no `inherit`: its ink is spelled out, and it
4818
+ // grows into any room a stretched control leaves between the steppers;
4819
+ // - the web root is inline-flex and hugs its content; lynx's flex column
4820
+ // stretches, so the children align to the start instead;
4821
+ // - the focus ring is `lynxFocusRing` (lynx's outline ignores the radius
4822
+ // and has no offset, signalxjs/lynx#1163). A shadow must not be
4823
+ // clipped by the box's own `overflow: hidden`, so the control stops
4824
+ // clipping and the steppers round their own outer corners instead.
4655
4825
  targets: {
4656
4826
  lynx: {
4657
4827
  parts: {
4658
- 'increment-trigger': { base: { paddingLeft: 'var(--space-lg)', paddingRight: 'var(--space-lg)' } },
4659
- 'decrement-trigger': { base: { paddingLeft: 'var(--space-lg)', paddingRight: 'var(--space-lg)' } },
4828
+ root: { base: { alignItems: 'flex-start' } },
4829
+ control: {
4830
+ base: { overflow: 'visible' },
4831
+ states: { 'focus-visible': lynxFocusRing('var(--number-input-accent)') },
4832
+ },
4833
+ input: { base: { color: 'var(--color-base-content)', flexGrow: '1' } },
4834
+ 'increment-trigger': {
4835
+ base: {
4836
+ display: 'flex',
4837
+ alignItems: 'center',
4838
+ justifyContent: 'center',
4839
+ paddingLeft: 'var(--space-lg)',
4840
+ paddingRight: 'var(--space-lg)',
4841
+ borderLeft: 'var(--border) solid var(--color-base-300)',
4842
+ borderTopRightRadius: 'var(--radius-field)',
4843
+ borderBottomRightRadius: 'var(--radius-field)',
4844
+ },
4845
+ },
4846
+ 'decrement-trigger': {
4847
+ base: {
4848
+ display: 'flex',
4849
+ alignItems: 'center',
4850
+ justifyContent: 'center',
4851
+ paddingLeft: 'var(--space-lg)',
4852
+ paddingRight: 'var(--space-lg)',
4853
+ borderRight: 'var(--border) solid var(--color-base-300)',
4854
+ borderTopLeftRadius: 'var(--radius-field)',
4855
+ borderBottomLeftRadius: 'var(--radius-field)',
4856
+ },
4857
+ },
4660
4858
  },
4661
4859
  },
4662
4860
  },
@@ -5070,6 +5268,15 @@ const affixSize = (fontSize: string) => ({
5070
5268
  'visibility-trigger': { base: { fontSize } },
5071
5269
  });
5072
5270
 
5271
+ /**
5272
+ * A text field's placeholder ink on lynx (signalxjs/lynx#1205): the web's
5273
+ * `::placeholder` mix, for lynx's `-x-placeholder-color` property — the
5274
+ * emitter bakes the `color-mix()` to one literal per theme.
5275
+ */
5276
+ const textFieldPlaceholder = 'color-mix(in oklab, var(--color-base-content) 60%, transparent)';
5277
+ /** The ghost field button's held look on lynx — the web's hover wash, on a press. */
5278
+ const fieldButtonHeld: CssProps = { color: 'var(--color-base-content)', background: 'var(--color-base-200)' };
5279
+
5073
5280
  /**
5074
5281
  * daisy "input" flavor: the shared field box with nothing in it but the text.
5075
5282
  * Same `fieldControl` metrics as select's trigger, combobox's control and the
@@ -5191,6 +5398,53 @@ export const input: RecipeInput = {
5191
5398
  xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } }, ...affixSize('var(--text-lg)') },
5192
5399
  },
5193
5400
  },
5401
+ // lynx (signalxjs/lynx#1205). The field is a native text widget inside
5402
+ // a view `control`, so what the web gets from the cascade is restated:
5403
+ targets: {
5404
+ lynx: {
5405
+ parts: {
5406
+ // The web control is an `inline-flex` row; the emitter's
5407
+ // `flex` rewrite keeps no direction, so the row is explicit.
5408
+ // No `:hover` border on a touch platform — the ring below
5409
+ // (box-shadow spread, signalxjs/lynx#1163) is the focus cue,
5410
+ // in the root's accent like the web outline.
5411
+ control: {
5412
+ base: { flexDirection: 'row' },
5413
+ states: { 'focus-visible': lynxFocusRing('var(--input-accent)') },
5414
+ },
5415
+ // `color: inherit` never reaches a native text field, and
5416
+ // `::placeholder` does not exist: the ink is explicit and the
5417
+ // placeholder takes lynx's own `-x-placeholder-color`, the
5418
+ // web's 60% mix baked per theme.
5419
+ input: {
5420
+ base: {
5421
+ color: 'var(--color-base-content)',
5422
+ '-x-placeholder-color': textFieldPlaceholder,
5423
+ flexGrow: '1',
5424
+ flexShrink: '1',
5425
+ },
5426
+ },
5427
+ // The adornment's edge padding, physical: Android never
5428
+ // resolves the logical spellings (signalxjs/lynx#1084).
5429
+ adornment: {
5430
+ selectors: {
5431
+ '&[data-placement="start"]': { paddingLeft: 'var(--space-md)' },
5432
+ '&[data-placement="end"]': { paddingRight: 'var(--space-md)' },
5433
+ },
5434
+ },
5435
+ // Same for the ghost buttons' end margin; the web's `:hover`
5436
+ // wash answers a press instead.
5437
+ 'clear-trigger': {
5438
+ base: { marginRight: 'var(--space-xs)' },
5439
+ states: { pressed: fieldButtonHeld },
5440
+ },
5441
+ 'visibility-trigger': {
5442
+ base: { marginRight: 'var(--space-xs)' },
5443
+ states: { pressed: fieldButtonHeld },
5444
+ },
5445
+ },
5446
+ },
5447
+ },
5194
5448
  };
5195
5449
 
5196
5450
  /**
@@ -5259,6 +5513,22 @@ export const textarea: RecipeInput = {
5259
5513
  xl: { textarea: { base: { fontSize: 'var(--text-lg)', minHeight: `calc(${fieldHeight('xl')} * 2)`, padding: 'var(--space-lg) var(--space-xl)' } } },
5260
5514
  },
5261
5515
  },
5516
+ // lynx (signalxjs/lynx#1205): lynx-zero renders the part as a view (the
5517
+ // box, whose state classes repaint) around the native field, which wears
5518
+ // the part's base class as its text face. So the ink and the placeholder
5519
+ // are explicit here — `color: inherit` and `::placeholder` never reach a
5520
+ // native widget — and the web's `:hover` border and outline ring become
5521
+ // the lynx focus ring in the root's accent (signalxjs/lynx#1163).
5522
+ targets: {
5523
+ lynx: {
5524
+ parts: {
5525
+ textarea: {
5526
+ base: { color: 'var(--color-base-content)', '-x-placeholder-color': textFieldPlaceholder },
5527
+ states: { 'focus-visible': lynxFocusRing('var(--textarea-accent)') },
5528
+ },
5529
+ },
5530
+ },
5531
+ },
5262
5532
  };
5263
5533
 
5264
5534
  // ── Content tier (#311) ───────────────────────────────────────────────────