@sigx/zero-daisyui 0.11.0 → 0.13.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.
@@ -86,7 +86,6 @@
86
86
  --depth-shade: #0000001a;
87
87
  --depth-sheen: #ffffff1a;
88
88
  --noise: 0;
89
- --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");
90
89
  }
91
90
 
92
91
  .zx-root.zx-theme-light {
@@ -163,7 +162,6 @@
163
162
  --depth-shade: #0000001a;
164
163
  --depth-sheen: #ffffff1a;
165
164
  --noise: 0;
166
- --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");
167
165
  }
168
166
 
169
167
  .zx-root.zx-theme-dark {
@@ -240,7 +238,6 @@
240
238
  --depth-shade: #0000001a;
241
239
  --depth-sheen: #ffffff1a;
242
240
  --noise: 0;
243
- --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");
244
241
  }
245
242
 
246
243
  .zx-root.zx-theme-dim {
@@ -317,7 +314,6 @@
317
314
  --depth-shade: #00000000;
318
315
  --depth-sheen: #ffffff00;
319
316
  --noise: 0;
320
- --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");
321
317
  }
322
318
 
323
319
  .zx-root.zx-theme-nord {
@@ -394,7 +390,6 @@
394
390
  --depth-shade: #00000000;
395
391
  --depth-sheen: #ffffff00;
396
392
  --noise: 0;
397
- --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");
398
393
  }
399
394
 
400
395
  .zx-root.zx-theme-sunset {
@@ -471,5 +466,4 @@
471
466
  --depth-shade: #00000000;
472
467
  --depth-sheen: #ffffff00;
473
468
  --noise: 0;
474
- --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");
475
469
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://signalxjs.github.io/zero/schemas/ds-manifest.schema.json",
3
3
  "manifestVersion": 1,
4
- "zeroVersion": "0.11.0",
4
+ "zeroVersion": "0.13.0",
5
5
  "name": "daisyui",
6
6
  "themes": [
7
7
  {
@@ -1 +1 @@
1
- {"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAwB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmQxE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,IAAI,EAAE,WAqVlB,CAAC;AA4BF,eAAO,MAAM,WAAW,EAAE,WA+EzB,CAAC;AAGF,eAAO,MAAM,YAAY,EAAE,WA0R1B,CAAC;AAmJF,eAAO,MAAM,MAAM,EAAE,WA0GpB,CAAC;AA4CF,eAAO,MAAM,OAAO,EAAE,WAoDrB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WA0CrB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,WAyCvB,CAAC;AAGF,eAAO,MAAM,IAAI,EAAE,WAsKlB,CAAC;AAIF,eAAO,MAAM,OAAO,EAAE,WAmCrB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,WAoDnB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,EAAE,WAuEtB,CAAC;AAkEF,eAAO,MAAM,QAAQ,EAAE,WA6PtB,CAAC;AAMF,eAAO,MAAM,UAAU,EAAE,WAoJxB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,WAkC3B,CAAC;AAgCF,eAAO,MAAM,QAAQ,EAAE,WAiGtB,CAAC;AAMF,eAAO,MAAM,MAAM,EAAE,WAocpB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAkGvB,CAAC;AA+DF,eAAO,MAAM,MAAM,EAAE,WAmQpB,CAAC;AAqDF,eAAO,MAAM,MAAM,EAAE,WAoTpB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,WA0EpB,CAAC;AAmBF,eAAO,MAAM,WAAW,EAAE,WA2EzB,CAAC;AAoBF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,WAuRnB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,WAqPtB,CAAC;AAIF,eAAO,MAAM,MAAM,EAAE,WAyHpB,CAAC;AASF,eAAO,MAAM,WAAW,EAAE,WA0MzB,CAAC;AAIF,eAAO,MAAM,WAAW,EAAE,WA0MzB,CAAC;AA4DF,eAAO,MAAM,WAAW,EAAE,WAoHzB,CAAC;AA0DF,eAAO,MAAM,QAAQ,EAAE,WAmJtB,CAAC;AAsCF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,WAkKnB,CAAC;AAMF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,WAsHtB,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,WAqGlB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,WAmFnB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,WA0ExB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,WA8FnB,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,OAAO,EAAE,WAoFrB,CAAC;AAGF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,WA6CtB,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,OAAO,EAAE,WAmCrB,CAAC;AAGF,uEAAuE;AACvE,eAAO,MAAM,GAAG,EAAE,WA0CjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM,EAAE,WAiCpB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,WA6EvB,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,KAAK,EAAE,WAkGnB,CAAC;AAWF,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,EAAE,WAmQtB,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,IAAI,EAAE,WA0GlB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,WAiFrB,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,WA+G5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,WAmElB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,EAAE,WAsDpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,WAmGrB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,WAqGzB,CAAC;AAsCF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,WAoGxB,CAAC;AAgCF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,WAmNnB,CAAC;AA0CF;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,EAAE,WA2IpB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,WA2LnB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,WA+JxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,WA8JtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,WA8ElB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,WAqDvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,WAwHlB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAAW,EAahC,CAAC"}
package/dist/recipes.js CHANGED
@@ -1621,6 +1621,10 @@ export const field = {
1621
1621
  label: {
1622
1622
  base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)', color: 'var(--field-accent)' },
1623
1623
  states: { disabled: { opacity: 'var(--disabled-opacity)' } },
1624
+ // lynx has no pseudo-elements and the anatomy no part to carry
1625
+ // the asterisk, so it stays a web-only mark: on lynx a required
1626
+ // field still stamps `required` on its root and label, and the
1627
+ // control inside adopts it (signalxjs/lynx#1205).
1624
1628
  selectors: {
1625
1629
  '&[data-required]::after': { content: '" *"', color: roleInk('error') },
1626
1630
  },
@@ -1711,6 +1715,33 @@ export const fieldset = {
1711
1715
  xl: { legend: { base: { fontSize: 'var(--text-lg)' } } },
1712
1716
  },
1713
1717
  },
1718
+ // Lynx has no grid: the one-column track becomes a flex column (the gap
1719
+ // survives), and the block/inline paddings are restated physically —
1720
+ // logical spellings resolve on iOS but not on Android (signalxjs/lynx#1084).
1721
+ targets: {
1722
+ lynx: {
1723
+ parts: {
1724
+ root: {
1725
+ base: {
1726
+ display: 'flex',
1727
+ flexDirection: 'column',
1728
+ paddingTop: 'var(--space-xs)',
1729
+ paddingBottom: 'var(--space-xs)',
1730
+ paddingLeft: '0',
1731
+ paddingRight: '0',
1732
+ },
1733
+ },
1734
+ legend: {
1735
+ base: {
1736
+ paddingTop: 'var(--space-sm)',
1737
+ paddingBottom: 'var(--space-sm)',
1738
+ paddingLeft: '0',
1739
+ paddingRight: '0',
1740
+ },
1741
+ },
1742
+ },
1743
+ },
1744
+ },
1714
1745
  };
1715
1746
  /**
1716
1747
  * daisy's tick, drawn by a clip-path that GROWS.
@@ -1723,6 +1754,12 @@ export const fieldset = {
1723
1754
  * horizontal bar and lifts it to the middle. All three verbatim from daisyUI
1724
1755
  * 5.7.8's `.checkbox:before` / `:checked` / `:indeterminate`.
1725
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)';
1726
1763
  const TICK_COLLAPSED = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%)';
1727
1764
  const TICK_DRAWN = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%)';
1728
1765
  const DASH_DRAWN = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%)';
@@ -1939,33 +1976,83 @@ export const checkbox = {
1939
1976
  targets: {
1940
1977
  lynx: {
1941
1978
  parts: {
1979
+ // The box/label row, pinned: `inline-flex` is rewritten to
1980
+ // `flex`, and the row must not depend on the engine's
1981
+ // default direction (signalxjs/lynx#1203).
1982
+ root: { base: { display: 'flex', flexDirection: 'row' } },
1942
1983
  control: {
1943
1984
  // The web fallback (`color-mix(in oklab, … currentColor …)`)
1944
1985
  // exists for consumers that unset the accent; this package
1945
1986
  // always defines it, so the plain accent IS the ring.
1946
- base: { border: 'var(--border) solid var(--checkbox-accent)' },
1987
+ //
1988
+ // `inline-block` is not a lynx display value (no inline
1989
+ // formatting context); a flex box keeps daisy's geometry —
1990
+ // the padding insets the 100%-sized indicator either way.
1991
+ base: { display: 'flex', border: 'var(--border) solid var(--checkbox-accent)' },
1992
+ // The ring as spread shadows that follow the box's radius
1993
+ // (signalxjs/lynx#1163 — lynx's `outline` ignores it and
1994
+ // has no offset), keeping the box's inset depth line.
1995
+ states: {
1996
+ 'focus-visible': lynxFocusRing('var(--checkbox-accent)', { under: '0 1px var(--depth-shade) inset' }),
1997
+ },
1947
1998
  },
1948
1999
  indicator: {
1949
- // The tick's fill: the control's `color:` names
1950
- // `--checkbox-on-accent`, and `currentColor` was that
1951
- // ink by inheritance — spend it directly.
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.
1952
2016
  //
1953
- // The tick's motion, restated as `transform`: the
1954
- // standalone `rotate`/`translate` properties resolve on
1955
- // iOS but not on Android (measured, signalxjs/lynx#1084),
1956
- // so the emitter refuses them. `transform` functions are
1957
- // proven on both platforms — and the transition follows
1958
- // 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.
1959
2021
  base: {
1960
- backgroundColor: 'var(--checkbox-on-accent)',
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%',
1961
2035
  transform: 'rotate(45deg)',
1962
- transition: 'clip-path var(--duration-slow) var(--ease-standard) var(--duration-instant), '
1963
- + 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
2036
+ transition: 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
1964
2037
  + 'transform var(--duration-slow) var(--ease-standard) var(--duration-instant)',
1965
2038
  },
1966
- // The order matches the standalone properties' fixed
1967
- // apply order (translate, then rotate).
1968
- states: { indeterminate: { transform: 'translateY(-35%) rotate(0deg)' } },
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
+ },
1969
2056
  },
1970
2057
  },
1971
2058
  },
@@ -1989,6 +2076,11 @@ export const radioGroup = {
1989
2076
  // `invalid` paints on each `item-control`, which carries the flag
1990
2077
  // itself (#267) — the root only groups the items.
1991
2078
  states: { invalid: {}, required: {} },
2079
+ // `orientation="horizontal"` lays the items in a wrapping row —
2080
+ // checkbox-group's rule (signalxjs/zero#393).
2081
+ selectors: {
2082
+ '&[data-orientation="horizontal"]': { flexDirection: 'row', flexWrap: 'wrap', columnGap: 'var(--space-lg)' },
2083
+ },
1992
2084
  },
1993
2085
  label: {
1994
2086
  base: { fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-semibold)' },
@@ -2096,9 +2188,20 @@ export const radioGroup = {
2096
2188
  targets: {
2097
2189
  lynx: {
2098
2190
  parts: {
2191
+ // The control/label row, pinned — as checkbox's root
2192
+ // (signalxjs/lynx#1203).
2193
+ item: { base: { display: 'flex', flexDirection: 'row' } },
2099
2194
  'item-control': {
2100
- base: { border: 'var(--border) solid var(--radio-accent)' },
2101
- states: { checked: { borderColor: 'var(--radio-accent)' } },
2195
+ // `display: flex` in place of `inline-block`, which lynx
2196
+ // does not have — the padding still insets the dot.
2197
+ base: { display: 'flex', border: 'var(--border) solid var(--radio-accent)' },
2198
+ states: {
2199
+ checked: { borderColor: 'var(--radio-accent)' },
2200
+ // The round ring (signalxjs/lynx#1163): spread
2201
+ // shadows follow the 9999px radius where `outline`
2202
+ // drew a square.
2203
+ 'focus-visible': lynxFocusRing('var(--radio-accent)', { under: '0 1px var(--depth-shade) inset' }),
2204
+ },
2102
2205
  },
2103
2206
  'item-indicator': {
2104
2207
  states: { checked: { backgroundColor: 'var(--radio-accent)' } },
@@ -3984,6 +4087,11 @@ export const combobox = {
3984
4087
  flexWrap: 'wrap',
3985
4088
  minWidth: '13rem',
3986
4089
  ...fieldControl,
4090
+ // The one field control that wraps: under `multiple` its tags
4091
+ // flow onto new rows, so the field step is a floor, not a cap —
4092
+ // a fixed height spills wrapped tags over the next field (#407).
4093
+ height: 'auto',
4094
+ minHeight: fieldHeight('md'),
3987
4095
  transition: 'border-color var(--duration-fast) var(--ease-standard)',
3988
4096
  },
3989
4097
  states: {
@@ -4287,7 +4395,50 @@ export const toggle = {
4287
4395
  },
4288
4396
  },
4289
4397
  defaultVariants: { color: 'primary', size: 'md' },
4398
+ // signalxjs/lynx#1204 — the lynx replacements for what this recipe's
4399
+ // shared sections cannot say there.
4400
+ targets: {
4401
+ lynx: {
4402
+ parts: {
4403
+ root: {
4404
+ // daisy's content-sized, never-squeezed btn (lynx#1165):
4405
+ // a lynx view has no inline formatting context, so the
4406
+ // toggle stretched to its column and a squeezed label
4407
+ // wrapped per character. The glyph + label sit in a row.
4408
+ // The hairline as longhands: a var-bearing `border`
4409
+ // shorthand expands after the cascade and would beat
4410
+ // any edge a later rule restates (lynx#1161/#1162).
4411
+ base: {
4412
+ ...lynxBtnFit,
4413
+ flexDirection: 'row',
4414
+ border: '0 solid transparent',
4415
+ ...lynxSides('border{}Width', 'var(--border)'),
4416
+ ...lynxSides('border{}Color', 'var(--color-base-300)'),
4417
+ },
4418
+ states: {
4419
+ // No hover on a touch platform: the held toggle
4420
+ // takes the wash daisy's hover gives it. The shared
4421
+ // pressed rule adds the btn's 1px sink.
4422
+ pressed: { background: 'var(--color-base-300)' },
4423
+ on: lynxSides('border{}Color', 'var(--toggle-accent)'),
4424
+ // lynx's `outline` ignores the radius and has no
4425
+ // offset (lynx#1163): the ring as spread shadows.
4426
+ 'focus-visible': lynxFocusRing('var(--toggle-accent)'),
4427
+ },
4428
+ selectors: {
4429
+ // A held on toggle keeps its fill (the web's
4430
+ // `[data-state=on]:hover` stays on the accent too).
4431
+ '&[data-state="on"][data-pressed]': { background: 'var(--toggle-accent)' },
4432
+ },
4433
+ },
4434
+ },
4435
+ },
4436
+ },
4290
4437
  };
4438
+ /** A toggle-group end item's outer corner: the frame's radius inside its hairline. */
4439
+ const joinInnerRadius = 'calc(var(--radius-field) - var(--border))';
4440
+ /** A zero seam width that still holds var(), so it can beat the var-bearing seam on lynx. */
4441
+ const noSeam = 'calc(var(--border) * 0)';
4291
4442
  // daisy "join" of btns: one bordered capsule, hairline seams between items,
4292
4443
  // the on item filled with the accent.
4293
4444
  export const toggleGroup = {
@@ -4395,6 +4546,101 @@ export const toggleGroup = {
4395
4546
  ])),
4396
4547
  },
4397
4548
  defaultVariants: { color: 'primary' },
4549
+ // signalxjs/lynx#1204 — the lynx replacements for what this recipe's
4550
+ // shared sections cannot say there.
4551
+ targets: {
4552
+ lynx: {
4553
+ parts: {
4554
+ root: {
4555
+ // Content-sized like the web's inline-flex capsule (a
4556
+ // lynx view stretches to its column otherwise), a row
4557
+ // unless vertical, and the frame as longhands so the
4558
+ // orientation rules below can drop one edge by
4559
+ // specificity (a var-bearing `border` shorthand would
4560
+ // expand over them, lynx#1161/#1162).
4561
+ base: {
4562
+ width: 'max-content',
4563
+ flexShrink: '0',
4564
+ flexDirection: 'row',
4565
+ border: '0 solid transparent',
4566
+ ...lynxSides('border{}Width', 'var(--border)'),
4567
+ ...lynxSides('border{}Color', 'var(--color-base-300)'),
4568
+ },
4569
+ // The frame keeps all four edges (signalxjs/lynx#1218).
4570
+ // It used to give up its leading edge to the first item's
4571
+ // seam and trust its own clip to round that item's
4572
+ // corners, but on iOS the frame's clip does not round the
4573
+ // leading corners: the first item painted square, and its
4574
+ // seam was lost with them. The `first`/`last` modifiers
4575
+ // below carry the join's ends instead.
4576
+ },
4577
+ item: {
4578
+ base: { flexShrink: '0', flexWrap: 'nowrap' },
4579
+ states: {
4580
+ // No hover on a touch platform: a held item takes the
4581
+ // wash daisy's hover gives it; an on item keeps its
4582
+ // fill (the selector below).
4583
+ pressed: { background: 'var(--color-base-300)' },
4584
+ // The frame clips its items, so the ring is drawn
4585
+ // inside the item (lynx's `outline` has no offset
4586
+ // and ignores the radius, lynx#1163).
4587
+ 'focus-visible': lynxFocusRing('var(--toggle-group-accent)', { inset: true }),
4588
+ },
4589
+ selectors: {
4590
+ '&[data-state="on"][data-pressed]': { background: 'var(--toggle-group-accent)' },
4591
+ // The join seams: lynx has no sibling combinator, so
4592
+ // the web's `item + item` edge cannot be selected.
4593
+ // Every item draws its leading seam, and the `first`
4594
+ // modifier takes it off the item at the frame's edge.
4595
+ '&[data-orientation="horizontal"]': {
4596
+ borderLeftWidth: 'var(--border)',
4597
+ borderLeftStyle: 'solid',
4598
+ borderLeftColor: 'var(--color-base-300)',
4599
+ },
4600
+ '&[data-orientation="vertical"]': {
4601
+ borderTopWidth: 'var(--border)',
4602
+ borderTopStyle: 'solid',
4603
+ borderTopColor: 'var(--color-base-300)',
4604
+ },
4605
+ },
4606
+ },
4607
+ },
4608
+ // The ends of the join (signalxjs/lynx#1218). Lynx has no
4609
+ // `:first-child` or `:last-child`, so lynx-zero stamps the end
4610
+ // items itself, as `data-mod-first` / `data-mod-last` (the
4611
+ // `zx-m-first` / `zx-m-last` classes). They are a lynx-zero
4612
+ // rendering detail, not modifiers an author sets, so they are
4613
+ // not declared in the vocabulary and are styled from raw lynx
4614
+ // css. The end items round their own outer corners to the
4615
+ // frame's inner radius, so an on or held end item follows the
4616
+ // frame's corners without relying on its clip; `first` also
4617
+ // drops the leading seam, which would double the frame's edge.
4618
+ // That zero width is spelled through var() on purpose: a static
4619
+ // longhand loses to the var-bearing seam width whatever its
4620
+ // specificity (lynx expands var() declarations after the
4621
+ // cascade, signalxjs/lynx#1161).
4622
+ css: `
4623
+ .zx-toggle-group__item.zx-m-first.zx-o-horizontal {
4624
+ border-left-width: ${noSeam};
4625
+ border-top-left-radius: ${joinInnerRadius};
4626
+ border-bottom-left-radius: ${joinInnerRadius};
4627
+ }
4628
+ .zx-toggle-group__item.zx-m-first.zx-o-vertical {
4629
+ border-top-width: ${noSeam};
4630
+ border-top-left-radius: ${joinInnerRadius};
4631
+ border-top-right-radius: ${joinInnerRadius};
4632
+ }
4633
+ .zx-toggle-group__item.zx-m-last.zx-o-horizontal {
4634
+ border-top-right-radius: ${joinInnerRadius};
4635
+ border-bottom-right-radius: ${joinInnerRadius};
4636
+ }
4637
+ .zx-toggle-group__item.zx-m-last.zx-o-vertical {
4638
+ border-bottom-left-radius: ${joinInnerRadius};
4639
+ border-bottom-right-radius: ${joinInnerRadius};
4640
+ }
4641
+ `,
4642
+ },
4643
+ },
4398
4644
  };
4399
4645
  // daisy "join" of an input and two btns: one bordered capsule (the control),
4400
4646
  // hairline seams on the triggers' inner edges, neutral btn fills for the steppers.
@@ -4538,14 +4784,65 @@ export const numberInput = {
4538
4784
  xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } } },
4539
4785
  },
4540
4786
  },
4541
- // The steppers' inline padding, restated physically: logical spellings
4542
- // resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
4543
- // the emitter refuses them.
4787
+ // Lynx restatements:
4788
+ // - the steppers' inline padding and their seams, physically: logical
4789
+ // spellings resolve on iOS but not on Android (measured,
4790
+ // signalxjs/lynx#1084);
4791
+ // - the steppers are views around a text glyph, with none of a
4792
+ // button's content centering;
4793
+ // - the native input takes no `inherit`: its ink is spelled out, and it
4794
+ // grows into any room a stretched control leaves between the steppers;
4795
+ // - the web root is inline-flex and hugs its content; lynx's flex column
4796
+ // stretches, so the children align to the start instead;
4797
+ // - the focus ring is `lynxFocusRing` (lynx's outline ignores the radius
4798
+ // and has no offset, signalxjs/lynx#1163). A shadow must not be
4799
+ // clipped by the box's own `overflow: hidden`, so the control stops
4800
+ // clipping and the steppers round their own outer corners instead.
4544
4801
  targets: {
4545
4802
  lynx: {
4546
4803
  parts: {
4547
- 'increment-trigger': { base: { paddingLeft: 'var(--space-lg)', paddingRight: 'var(--space-lg)' } },
4548
- 'decrement-trigger': { base: { paddingLeft: 'var(--space-lg)', paddingRight: 'var(--space-lg)' } },
4804
+ root: { base: { alignItems: 'flex-start' } },
4805
+ control: {
4806
+ base: { overflow: 'visible' },
4807
+ states: { 'focus-visible': lynxFocusRing('var(--number-input-accent)') },
4808
+ },
4809
+ // `::placeholder` does not exist on lynx, so the web's 60%
4810
+ // mix rides lynx's own `-x-placeholder-color` (baked per
4811
+ // theme) — without it the platform default reads dark on
4812
+ // dark (signalxjs/lynx#1222). Spelled out, not the
4813
+ // `textFieldPlaceholder` const: that one is declared further
4814
+ // down the module, after this literal is evaluated.
4815
+ input: {
4816
+ base: {
4817
+ color: 'var(--color-base-content)',
4818
+ '-x-placeholder-color': 'color-mix(in oklab, var(--color-base-content) 60%, transparent)',
4819
+ flexGrow: '1',
4820
+ },
4821
+ },
4822
+ 'increment-trigger': {
4823
+ base: {
4824
+ display: 'flex',
4825
+ alignItems: 'center',
4826
+ justifyContent: 'center',
4827
+ paddingLeft: 'var(--space-lg)',
4828
+ paddingRight: 'var(--space-lg)',
4829
+ borderLeft: 'var(--border) solid var(--color-base-300)',
4830
+ borderTopRightRadius: 'var(--radius-field)',
4831
+ borderBottomRightRadius: 'var(--radius-field)',
4832
+ },
4833
+ },
4834
+ 'decrement-trigger': {
4835
+ base: {
4836
+ display: 'flex',
4837
+ alignItems: 'center',
4838
+ justifyContent: 'center',
4839
+ paddingLeft: 'var(--space-lg)',
4840
+ paddingRight: 'var(--space-lg)',
4841
+ borderRight: 'var(--border) solid var(--color-base-300)',
4842
+ borderTopLeftRadius: 'var(--radius-field)',
4843
+ borderBottomLeftRadius: 'var(--radius-field)',
4844
+ },
4845
+ },
4549
4846
  },
4550
4847
  },
4551
4848
  },
@@ -4948,6 +5245,14 @@ const affixSize = (fontSize) => ({
4948
5245
  'clear-trigger': { base: { fontSize } },
4949
5246
  'visibility-trigger': { base: { fontSize } },
4950
5247
  });
5248
+ /**
5249
+ * A text field's placeholder ink on lynx (signalxjs/lynx#1205): the web's
5250
+ * `::placeholder` mix, for lynx's `-x-placeholder-color` property — the
5251
+ * emitter bakes the `color-mix()` to one literal per theme.
5252
+ */
5253
+ const textFieldPlaceholder = 'color-mix(in oklab, var(--color-base-content) 60%, transparent)';
5254
+ /** The ghost field button's held look on lynx — the web's hover wash, on a press. */
5255
+ const fieldButtonHeld = { color: 'var(--color-base-content)', background: 'var(--color-base-200)' };
4951
5256
  /**
4952
5257
  * daisy "input" flavor: the shared field box with nothing in it but the text.
4953
5258
  * Same `fieldControl` metrics as select's trigger, combobox's control and the
@@ -5069,7 +5374,56 @@ export const input = {
5069
5374
  xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } }, ...affixSize('var(--text-lg)') },
5070
5375
  },
5071
5376
  },
5377
+ // lynx (signalxjs/lynx#1205). The field is a native text widget inside
5378
+ // a view `control`, so what the web gets from the cascade is restated:
5379
+ targets: {
5380
+ lynx: {
5381
+ parts: {
5382
+ // The web control is an `inline-flex` row; the emitter's
5383
+ // `flex` rewrite keeps no direction, so the row is explicit.
5384
+ // No `:hover` border on a touch platform — the ring below
5385
+ // (box-shadow spread, signalxjs/lynx#1163) is the focus cue,
5386
+ // in the root's accent like the web outline.
5387
+ control: {
5388
+ base: { flexDirection: 'row' },
5389
+ states: { 'focus-visible': lynxFocusRing('var(--input-accent)') },
5390
+ },
5391
+ // `color: inherit` never reaches a native text field, and
5392
+ // `::placeholder` does not exist: the ink is explicit and the
5393
+ // placeholder takes lynx's own `-x-placeholder-color`, the
5394
+ // web's 60% mix baked per theme.
5395
+ input: {
5396
+ base: {
5397
+ color: 'var(--color-base-content)',
5398
+ '-x-placeholder-color': textFieldPlaceholder,
5399
+ flexGrow: '1',
5400
+ flexShrink: '1',
5401
+ },
5402
+ },
5403
+ // The adornment's edge padding, physical: Android never
5404
+ // resolves the logical spellings (signalxjs/lynx#1084).
5405
+ adornment: {
5406
+ selectors: {
5407
+ '&[data-placement="start"]': { paddingLeft: 'var(--space-md)' },
5408
+ '&[data-placement="end"]': { paddingRight: 'var(--space-md)' },
5409
+ },
5410
+ },
5411
+ // Same for the ghost buttons' end margin; the web's `:hover`
5412
+ // wash answers a press instead.
5413
+ 'clear-trigger': {
5414
+ base: { marginRight: 'var(--space-xs)' },
5415
+ states: { pressed: fieldButtonHeld },
5416
+ },
5417
+ 'visibility-trigger': {
5418
+ base: { marginRight: 'var(--space-xs)' },
5419
+ states: { pressed: fieldButtonHeld },
5420
+ },
5421
+ },
5422
+ },
5423
+ },
5072
5424
  };
5425
+ /** The lynx textarea floor: two field heights, as ONE calc() (signalxjs/lynx#1219). */
5426
+ const textareaFloor = (step) => `calc(var(--size-field) * ${FIELD_STEPS[step] * 2})`;
5073
5427
  /**
5074
5428
  * daisy "textarea" flavor. The `fieldControl` box minus its `height`: the
5075
5429
  * whole point of a textarea is that its height is the content's (and the
@@ -5136,6 +5490,64 @@ export const textarea = {
5136
5490
  xl: { textarea: { base: { fontSize: 'var(--text-lg)', minHeight: `calc(${fieldHeight('xl')} * 2)`, padding: 'var(--space-lg) var(--space-xl)' } } },
5137
5491
  },
5138
5492
  },
5493
+ // lynx (signalxjs/lynx#1205): lynx-zero renders the part as a view (the
5494
+ // box, whose state classes repaint) around the native field, which wears
5495
+ // the part's base class as its text face. So the ink and the placeholder
5496
+ // are explicit here — `color: inherit` and `::placeholder` never reach a
5497
+ // native widget — and the web's `:hover` border and outline ring become
5498
+ // the lynx focus ring in the root's accent (signalxjs/lynx#1163).
5499
+ //
5500
+ // The floor is one flat `calc(var(--size-field) * n)` per size
5501
+ // (signalxjs/lynx#1219): the shared `calc(fieldHeight * 2)` nests a
5502
+ // calc() in a calc(), and on device the box never took it — an empty
5503
+ // textarea sat one line tall.
5504
+ //
5505
+ // The focus ring is NOT `lynxFocusRing`'s two stacked outset shadows
5506
+ // (signalxjs/lynx#1220). Lynx iOS re-inserts a view's outset shadow
5507
+ // layers at a stale index whenever it re-lays the view out
5508
+ // (`LynxBackgroundManager.updateShadow` reads the sublayer count before
5509
+ // removing the old layers), which reverses their stacking: the 4px ring
5510
+ // lands on top of the 2px gap and paints one solid band. A fixed-height
5511
+ // box never re-lays out; this auto-height one does, as its native field
5512
+ // measures and grows. So the ring is spelled with one outset shadow and
5513
+ // one inset shadow, which paint disjoint areas and cannot swap: the ring
5514
+ // 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
5516
+ // edge colour (`--textarea-edge`, the error colour while invalid). The
5517
+ // gap is the border's width instead of the web's 2px.
5518
+ targets: {
5519
+ lynx: {
5520
+ parts: {
5521
+ textarea: {
5522
+ base: {
5523
+ color: 'var(--color-base-content)',
5524
+ '-x-placeholder-color': textFieldPlaceholder,
5525
+ '--textarea-edge': 'var(--color-base-300)',
5526
+ minHeight: textareaFloor('md'),
5527
+ },
5528
+ states: {
5529
+ invalid: { '--textarea-edge': 'var(--color-error)' },
5530
+ 'focus-visible': {
5531
+ outline: 'none',
5532
+ borderColor: 'var(--color-base-100)',
5533
+ boxShadow: '0 0 0 2px var(--textarea-accent), '
5534
+ // Twice the border (1px in every shipped
5535
+ // theme): the inner half shows as the hairline.
5536
+ + 'inset 0 0 0 2px var(--textarea-edge)',
5537
+ },
5538
+ },
5539
+ },
5540
+ },
5541
+ variants: {
5542
+ size: {
5543
+ xs: { textarea: { base: { minHeight: textareaFloor('xs') } } },
5544
+ sm: { textarea: { base: { minHeight: textareaFloor('sm') } } },
5545
+ lg: { textarea: { base: { minHeight: textareaFloor('lg') } } },
5546
+ xl: { textarea: { base: { minHeight: textareaFloor('xl') } } },
5547
+ },
5548
+ },
5549
+ },
5550
+ },
5139
5551
  };
5140
5552
  // ── Content tier (#311) ───────────────────────────────────────────────────
5141
5553
  /**