@sigx/zero-daisyui 0.12.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.
package/dist/recipes.js CHANGED
@@ -1754,6 +1754,12 @@ export const fieldset = {
1754
1754
  * horizontal bar and lifts it to the middle. All three verbatim from daisyUI
1755
1755
  * 5.7.8's `.checkbox:before` / `:checked` / `:indeterminate`.
1756
1756
  */
1757
+ /**
1758
+ * The lynx tick's stroke: 20% of the checkbox's content box — the width of
1759
+ * daisy's polygon bars — as a length, since borders take no percentage.
1760
+ * The content box is the control less its padding and border on both sides.
1761
+ */
1762
+ const CHECKBOX_LYNX_STROKE = 'calc((var(--checkbox-size) - var(--checkbox-pad) * 2 - var(--border) * 2) * 0.2)';
1757
1763
  const TICK_COLLAPSED = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%)';
1758
1764
  const TICK_DRAWN = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%)';
1759
1765
  const DASH_DRAWN = 'polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%)';
@@ -1991,26 +1997,62 @@ export const checkbox = {
1991
1997
  },
1992
1998
  },
1993
1999
  indicator: {
1994
- // The tick's fill: the control's `color:` names
1995
- // `--checkbox-on-accent`, and `currentColor` was that
1996
- // 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.
1997
2016
  //
1998
- // The tick's motion, restated as `transform`: the
1999
- // standalone `rotate`/`translate` properties resolve on
2000
- // iOS but not on Android (measured, signalxjs/lynx#1084),
2001
- // so the emitter refuses them. `transform` functions are
2002
- // proven on both platforms — and the transition follows
2003
- // the property that now moves.
2017
+ // The motion is `transform`: the standalone `rotate` /
2018
+ // `translate` properties resolve on iOS but not on
2019
+ // Android (signalxjs/lynx#1084). No sheen shadow — on a
2020
+ // hollow box it would band the inside of the L.
2004
2021
  base: {
2005
- 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%',
2006
2035
  transform: 'rotate(45deg)',
2007
- transition: 'clip-path var(--duration-slow) var(--ease-standard) var(--duration-instant), '
2008
- + 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
2036
+ transition: 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
2009
2037
  + 'transform var(--duration-slow) var(--ease-standard) var(--duration-instant)',
2010
2038
  },
2011
- // The order matches the standalone properties' fixed
2012
- // apply order (translate, then rotate).
2013
- 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
+ },
2014
2056
  },
2015
2057
  },
2016
2058
  },
@@ -4045,6 +4087,11 @@ export const combobox = {
4045
4087
  flexWrap: 'wrap',
4046
4088
  minWidth: '13rem',
4047
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'),
4048
4095
  transition: 'border-color var(--duration-fast) var(--ease-standard)',
4049
4096
  },
4050
4097
  states: {
@@ -4388,6 +4435,10 @@ export const toggle = {
4388
4435
  },
4389
4436
  },
4390
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)';
4391
4442
  // daisy "join" of btns: one bordered capsule, hairline seams between items,
4392
4443
  // the on item filled with the accent.
4393
4444
  export const toggleGroup = {
@@ -4515,15 +4566,13 @@ export const toggleGroup = {
4515
4566
  ...lynxSides('border{}Width', 'var(--border)'),
4516
4567
  ...lynxSides('border{}Color', 'var(--color-base-300)'),
4517
4568
  },
4518
- selectors: {
4519
- // The join seams: lynx has no sibling combinator, so
4520
- // the web's `item + item` edge cannot be selected.
4521
- // Every item draws its leading seam instead (below),
4522
- // and the frame gives up that edge — the first item's
4523
- // seam stands in for it, inside the frame's clip.
4524
- '&[data-orientation="horizontal"]': { borderLeftWidth: '0' },
4525
- '&[data-orientation="vertical"]': { borderTopWidth: '0' },
4526
- },
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.
4527
4576
  },
4528
4577
  item: {
4529
4578
  base: { flexShrink: '0', flexWrap: 'nowrap' },
@@ -4539,6 +4588,10 @@ export const toggleGroup = {
4539
4588
  },
4540
4589
  selectors: {
4541
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.
4542
4595
  '&[data-orientation="horizontal"]': {
4543
4596
  borderLeftWidth: 'var(--border)',
4544
4597
  borderLeftStyle: 'solid',
@@ -4552,6 +4605,40 @@ export const toggleGroup = {
4552
4605
  },
4553
4606
  },
4554
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
+ `,
4555
4642
  },
4556
4643
  },
4557
4644
  };
@@ -4719,7 +4806,19 @@ export const numberInput = {
4719
4806
  base: { overflow: 'visible' },
4720
4807
  states: { 'focus-visible': lynxFocusRing('var(--number-input-accent)') },
4721
4808
  },
4722
- input: { base: { color: 'var(--color-base-content)', flexGrow: '1' } },
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
+ },
4723
4822
  'increment-trigger': {
4724
4823
  base: {
4725
4824
  display: 'flex',
@@ -5323,6 +5422,8 @@ export const input = {
5323
5422
  },
5324
5423
  },
5325
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})`;
5326
5427
  /**
5327
5428
  * daisy "textarea" flavor. The `fieldControl` box minus its `height`: the
5328
5429
  * whole point of a textarea is that its height is the content's (and the
@@ -5395,12 +5496,54 @@ export const textarea = {
5395
5496
  // are explicit here — `color: inherit` and `::placeholder` never reach a
5396
5497
  // native widget — and the web's `:hover` border and outline ring become
5397
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.
5398
5518
  targets: {
5399
5519
  lynx: {
5400
5520
  parts: {
5401
5521
  textarea: {
5402
- base: { color: 'var(--color-base-content)', '-x-placeholder-color': textFieldPlaceholder },
5403
- states: { 'focus-visible': lynxFocusRing('var(--textarea-accent)') },
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') } } },
5404
5547
  },
5405
5548
  },
5406
5549
  },