@sigx/zero-daisyui 0.2.0-beta.4 → 0.2.0-beta.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/zero-daisyui",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.6",
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",
@@ -44,12 +44,12 @@
44
44
  "url": "https://github.com/andtii/zero-wip/issues"
45
45
  },
46
46
  "peerDependencies": {
47
- "@sigx/zero": "^0.2.0-beta.4"
47
+ "@sigx/zero": "^0.2.0-beta.6"
48
48
  },
49
49
  "devDependencies": {
50
50
  "typescript": "^6.0.3",
51
- "@sigx/zero": "^0.2.0-beta.4",
52
- "@sigx/zero-kit": "^0.2.0-beta.4"
51
+ "@sigx/zero": "^0.2.0-beta.6",
52
+ "@sigx/zero-kit": "^0.2.0-beta.6"
53
53
  },
54
54
  "publishConfig": {
55
55
  "access": "public"
package/src/recipes.ts CHANGED
@@ -463,18 +463,35 @@ export const tabs: RecipeInput = {
463
463
  defaultVariants: { size: 'md', variant: 'border' },
464
464
  targets: {
465
465
  lynx: {
466
+ parts: {
467
+ // The default active ink — base-content, exactly what the
468
+ // shared `states.active.color` paints. One class, so any
469
+ // color-axis definition below (host + two classes, baked per
470
+ // theme) wins on specificity.
471
+ tab: { base: { '--tab-active-ink': 'var(--color-base-content)' } },
472
+ },
466
473
  variants: {
474
+ // `currentColor` never resolves on lynx (measured on device
475
+ // on both platforms, signalxjs/lynx#1079 — the underline was
476
+ // transparent everywhere), so the color axis restates its
477
+ // active ink as a plain custom property. `roleInk` reads
478
+ // theme colors through color-mix(), which the emitter bakes
479
+ // once per theme — the same per-color/per-theme literal the
480
+ // active `color:` rules already land as.
481
+ color: Object.fromEntries(
482
+ ROLES.map((c) => [c, { tab: { base: { '--tab-active-ink': roleInk(c) } } }]),
483
+ ),
467
484
  variant: {
468
485
  // The underline is a ::before on the web, which lynx
469
486
  // drops — redrawn as a real bottom border on the tab
470
487
  // itself (transparent at rest so activation never moves
471
- // the row). `currentColor` keeps it following the color
472
- // axis ink, the same mechanism switch's shared border
473
- // already relies on for lynx.
488
+ // the row). The active ink rides `--tab-active-ink`
489
+ // above — a plain var() chain, device-proven — instead
490
+ // of `currentColor`, which lynx never resolves.
474
491
  border: {
475
492
  tab: {
476
493
  base: { borderBottom: '3px solid transparent' },
477
- states: { active: { borderBottomColor: 'currentColor' } },
494
+ states: { active: { borderBottomColor: 'var(--tab-active-ink)' } },
478
495
  },
479
496
  },
480
497
  // `min()` does not exist on lynx (signalxjs/lynx#1066) and
@@ -902,6 +919,26 @@ const btnSizes: Record<string, Record<string, PartStyles>> = {
902
919
  xl: { trigger: { base: { height: fieldHeight('xl'), paddingInline: 'calc(var(--size-field) * 6)', fontSize: 'var(--text-lg)' } } },
903
920
  };
904
921
 
922
+ /**
923
+ * `btn`'s inline padding restated physically, for the `targets.lynx`
924
+ * sections of every btn-wearing recipe: logical spellings resolve on iOS but
925
+ * not on Android (measured, signalxjs/lynx#1084), so the lynx emitter
926
+ * refuses `padding-inline`. Physical is that target's norm — lynx has no
927
+ * RTL flow for the logical spelling to flip with.
928
+ */
929
+ const lynxBtnPad = (mult: number): NonNullable<PartStyles['base']> => ({
930
+ paddingLeft: `calc(var(--size-field) * ${mult})`,
931
+ paddingRight: `calc(var(--size-field) * ${mult})`,
932
+ });
933
+
934
+ /** The btn size ramp's physical inline padding, for `targets.lynx.variants`. */
935
+ const lynxBtnSizes = (part: string): Record<string, Record<string, PartStyles>> => ({
936
+ xs: { [part]: { base: lynxBtnPad(2) } },
937
+ sm: { [part]: { base: lynxBtnPad(3) } },
938
+ lg: { [part]: { base: lynxBtnPad(5) } },
939
+ xl: { [part]: { base: lynxBtnPad(6) } },
940
+ });
941
+
905
942
  export const dialog: RecipeInput = {
906
943
  component: 'dialog',
907
944
  keyframes: {
@@ -982,6 +1019,19 @@ export const dialog: RecipeInput = {
982
1019
  // Trigger-carried axes — see `btnColors` for why the popup is out of
983
1020
  // reach and the trigger is the whole story here.
984
1021
  variants: { color: btnColors(), size: btnSizes },
1022
+ // The btn paddings and the footer's push-down, restated physically —
1023
+ // see `lynxBtnPad` for the #1084 verdict this answers.
1024
+ targets: {
1025
+ lynx: {
1026
+ parts: {
1027
+ trigger: { base: lynxBtnPad(4) },
1028
+ close: { base: lynxBtnPad(4) },
1029
+ cancel: { base: lynxBtnPad(4) },
1030
+ footer: { base: { marginTop: '1.5rem' } },
1031
+ },
1032
+ variants: { size: lynxBtnSizes('trigger') },
1033
+ },
1034
+ },
985
1035
  };
986
1036
 
987
1037
  // daisy "dropdown-content"/card look for floating panels.
@@ -1024,6 +1074,16 @@ export const popover: RecipeInput = {
1024
1074
  },
1025
1075
  // Trigger-carried axes — same wiring as dialog, same reason.
1026
1076
  variants: { color: btnColors(), size: btnSizes },
1077
+ // The btn paddings, restated physically — see `lynxBtnPad` (#1084).
1078
+ targets: {
1079
+ lynx: {
1080
+ parts: {
1081
+ trigger: { base: lynxBtnPad(4) },
1082
+ close: { base: lynxBtnPad(3) },
1083
+ },
1084
+ variants: { size: lynxBtnSizes('trigger') },
1085
+ },
1086
+ },
1027
1087
  };
1028
1088
 
1029
1089
  export const tooltip: RecipeInput = {
@@ -1060,6 +1120,13 @@ export const tooltip: RecipeInput = {
1060
1120
  // Trigger-carried axes — same wiring as dialog, same reason. The bubble
1061
1121
  // stays daisy's neutral tooltip whatever the trigger's colour.
1062
1122
  variants: { color: btnColors(), size: btnSizes },
1123
+ // The btn paddings, restated physically — see `lynxBtnPad` (#1084).
1124
+ targets: {
1125
+ lynx: {
1126
+ parts: { trigger: { base: lynxBtnPad(4) } },
1127
+ variants: { size: lynxBtnSizes('trigger') },
1128
+ },
1129
+ },
1063
1130
  };
1064
1131
 
1065
1132
  // daisy "menu in a dropdown" look.
@@ -1206,6 +1273,13 @@ export const menu: RecipeInput = {
1206
1273
  // Trigger-carried axes — same wiring as dialog, same reason. The dropdown
1207
1274
  // and its items are top-layer siblings the donut cannot reach.
1208
1275
  variants: { color: btnColors(), size: btnSizes },
1276
+ // The btn paddings, restated physically — see `lynxBtnPad` (#1084).
1277
+ targets: {
1278
+ lynx: {
1279
+ parts: { trigger: { base: lynxBtnPad(4) } },
1280
+ variants: { size: lynxBtnSizes('trigger') },
1281
+ },
1282
+ },
1209
1283
  };
1210
1284
 
1211
1285
  export const field: RecipeInput = {
@@ -1475,6 +1549,45 @@ export const checkbox: RecipeInput = {
1475
1549
  // control and its text. Declared rather than left implicit so the
1476
1550
  // delegation reads as a decision.
1477
1551
  skipStates: { root: ['focus-visible'] },
1552
+ // `currentColor` never resolves on lynx (measured on device on both
1553
+ // platforms, signalxjs/lynx#1079), so the tick shipped invisible and the
1554
+ // ring's color-mix-over-currentColor fallback dropped the whole border.
1555
+ // Both restated with the named accents the web spellings resolve to —
1556
+ // plain var() chains, the switch's device-proven #380 mechanism.
1557
+ targets: {
1558
+ lynx: {
1559
+ parts: {
1560
+ control: {
1561
+ // The web fallback (`color-mix(in oklab, … currentColor …)`)
1562
+ // exists for consumers that unset the accent; this package
1563
+ // always defines it, so the plain accent IS the ring.
1564
+ base: { border: 'var(--border) solid var(--checkbox-accent)' },
1565
+ },
1566
+ indicator: {
1567
+ // The tick's fill: the control's `color:` names
1568
+ // `--checkbox-on-accent`, and `currentColor` was that
1569
+ // ink by inheritance — spend it directly.
1570
+ //
1571
+ // The tick's motion, restated as `transform`: the
1572
+ // standalone `rotate`/`translate` properties resolve on
1573
+ // iOS but not on Android (measured, signalxjs/lynx#1084),
1574
+ // so the emitter refuses them. `transform` functions are
1575
+ // proven on both platforms — and the transition follows
1576
+ // the property that now moves.
1577
+ base: {
1578
+ backgroundColor: 'var(--checkbox-on-accent)',
1579
+ transform: 'rotate(45deg)',
1580
+ transition: 'clip-path var(--duration-slow) var(--ease-standard) var(--duration-instant), '
1581
+ + 'opacity var(--duration-instant) var(--ease-standard) var(--duration-instant), '
1582
+ + 'transform var(--duration-slow) var(--ease-standard) var(--duration-instant)',
1583
+ },
1584
+ // The order matches the standalone properties' fixed
1585
+ // apply order (translate, then rotate).
1586
+ states: { indeterminate: { transform: 'translateY(-35%) rotate(0deg)' } },
1587
+ },
1588
+ },
1589
+ },
1590
+ },
1478
1591
  };
1479
1592
 
1480
1593
  // --------------------------------------------------------------------------
@@ -1592,6 +1705,25 @@ export const radioGroup: RecipeInput = {
1592
1705
  // wraps it. Declared rather than left implicit so the delegation reads
1593
1706
  // as a decision.
1594
1707
  skipStates: { item: ['focus-visible'] },
1708
+ // `currentColor` never resolves on lynx (measured on device on both
1709
+ // platforms, signalxjs/lynx#1079), so the checked ring and dot shipped
1710
+ // invisible, and the unchecked ring's color-mix-over-currentColor
1711
+ // fallback dropped the whole border. All three restated with the accent
1712
+ // the control's own `color:` names — plain var() chains, the switch's
1713
+ // device-proven #380 mechanism.
1714
+ targets: {
1715
+ lynx: {
1716
+ parts: {
1717
+ 'item-control': {
1718
+ base: { border: 'var(--border) solid var(--radio-accent)' },
1719
+ states: { checked: { borderColor: 'var(--radio-accent)' } },
1720
+ },
1721
+ 'item-indicator': {
1722
+ states: { checked: { backgroundColor: 'var(--radio-accent)' } },
1723
+ },
1724
+ },
1725
+ },
1726
+ },
1595
1727
  };
1596
1728
 
1597
1729
  // --------------------------------------------------------------------------
@@ -1703,6 +1835,18 @@ export const progress: RecipeInput = {
1703
1835
  // other way.
1704
1836
  'zero-daisy-indeterminate': 'from { margin-inline-start: -40%; } to { margin-inline-start: 100%; }',
1705
1837
  },
1838
+ // The same sweep with a physical margin: logical spellings resolve on
1839
+ // iOS but not on Android (measured, signalxjs/lynx#1084), so the emitter
1840
+ // refuses the shared keyframes. Physical is the lynx target's norm — no
1841
+ // RTL flow there for the sweep to mirror with. Keyframes merge per name,
1842
+ // so this REPLACES the shared animation on lynx.
1843
+ targets: {
1844
+ lynx: {
1845
+ keyframes: {
1846
+ 'zero-daisy-indeterminate': 'from { margin-left: -40%; } to { margin-left: 100%; }',
1847
+ },
1848
+ },
1849
+ },
1706
1850
  };
1707
1851
 
1708
1852
  export const slider: RecipeInput = {
@@ -1828,6 +1972,22 @@ export const slider: RecipeInput = {
1828
1972
  targets: {
1829
1973
  lynx: {
1830
1974
  parts: {
1975
+ track: {
1976
+ // The web track's `margin-block` breathing room,
1977
+ // restated physically: logical spellings resolve on iOS
1978
+ // but not on Android (measured, signalxjs/lynx#1084), so
1979
+ // the emitter refuses them. Physical is the lynx
1980
+ // target's norm — no RTL flow there.
1981
+ base: {
1982
+ marginTop: 'calc(var(--size-selector) * 1.5)',
1983
+ marginBottom: 'calc(var(--size-selector) * 1.5)',
1984
+ },
1985
+ },
1986
+ mark: {
1987
+ // Same #1084 verdict: the tick labels hang below the
1988
+ // track off a physical padding.
1989
+ base: { paddingTop: 'calc(var(--size-selector) * 2 + var(--space-2xs))' },
1990
+ },
1831
1991
  range: {
1832
1992
  // The web fill deepens the accent 90/10 toward
1833
1993
  // base-content (`progressFill`'s recipe); the plain
@@ -1851,9 +2011,20 @@ export const slider: RecipeInput = {
1851
2011
  // thumb — a native-input trick with no lynx equivalent;
1852
2012
  // the composed `.zx-slider__range` box above IS that
1853
2013
  // fill here, so the knob only needs its own two paints.
2014
+ //
2015
+ // The centering geometry is restated physically beside
2016
+ // the paint: `inset-block-start`/`translate`/
2017
+ // `margin-inline-start` resolve on iOS but not on
2018
+ // Android (measured, signalxjs/lynx#1084 — the knob sat
2019
+ // off-center there), so the emitter refuses them.
2020
+ // `top` + `transform: translateY()` + `margin-left` are
2021
+ // proven on both platforms.
1854
2022
  base: {
1855
2023
  background: 'var(--color-base-100)',
1856
2024
  border: '0.25rem solid var(--slider-accent)',
2025
+ top: '50%',
2026
+ transform: 'translateY(-50%)',
2027
+ marginLeft: 'calc(var(--size-selector) * -2.5)',
1857
2028
  },
1858
2029
  },
1859
2030
  },
@@ -2074,6 +2245,17 @@ export const select: RecipeInput = {
2074
2245
  // Base equals the default (`tokens:` accent, `fieldControl`'s md height),
2075
2246
  // so the twins restate it; declared for the manifest (signalxjs/lynx#1070).
2076
2247
  defaultVariants: { color: 'primary', size: 'md' },
2248
+ // The btn paddings, restated physically — see `lynxBtnPad` (#1084).
2249
+ // The trigger ramp restates `md` too (unlike `btnSizes`), so its lynx
2250
+ // counterpart does as well.
2251
+ targets: {
2252
+ lynx: {
2253
+ parts: { trigger: { base: lynxBtnPad(4) } },
2254
+ variants: {
2255
+ size: { ...lynxBtnSizes('trigger'), md: { trigger: { base: lynxBtnPad(4) } } },
2256
+ },
2257
+ },
2258
+ },
2077
2259
  };
2078
2260
 
2079
2261
 
@@ -2303,6 +2485,17 @@ export const button: RecipeInput = {
2303
2485
  },
2304
2486
  defaultVariants: { color: 'primary', variant: 'solid', size: 'md' },
2305
2487
  keyframes: { 'zero-daisyui-btn-spin': 'to { transform: rotate(360deg) }' },
2488
+ // The icon chips' zeroed inline padding, restated physically: logical
2489
+ // spellings resolve on iOS but not on Android (measured,
2490
+ // signalxjs/lynx#1084), so the emitter refuses them.
2491
+ targets: {
2492
+ lynx: {
2493
+ modifiers: {
2494
+ square: { root: { base: { paddingLeft: '0', paddingRight: '0' } } },
2495
+ circle: { root: { base: { paddingLeft: '0', paddingRight: '0' } } },
2496
+ },
2497
+ },
2498
+ },
2306
2499
  };
2307
2500
 
2308
2501
  export const avatar: RecipeInput = {
@@ -2530,6 +2723,24 @@ export const toast: RecipeInput = {
2530
2723
  // signalxjs/lynx#1070. No `color` default: the un-attributed toast is a
2531
2724
  // neutral base-200 surface, outside the color vocabulary.
2532
2725
  defaultVariants: { size: 'md' },
2726
+ // The corner placements' physical anchors: logical inset spellings
2727
+ // resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
2728
+ // the emitter refuses them. Physical is the lynx target's norm — no RTL
2729
+ // flow there, so `start` IS left and `end` IS right.
2730
+ targets: {
2731
+ lynx: {
2732
+ parts: {
2733
+ viewport: {
2734
+ selectors: {
2735
+ '&[data-placement="top-start"]': { left: '0' },
2736
+ '&[data-placement="top-end"]': { right: '0' },
2737
+ '&[data-placement="bottom-start"]': { left: '0' },
2738
+ '&[data-placement="bottom-end"]': { right: '0' },
2739
+ },
2740
+ },
2741
+ },
2742
+ },
2743
+ },
2533
2744
  };
2534
2745
 
2535
2746
  export const combobox: RecipeInput = {
@@ -3012,6 +3223,17 @@ export const numberInput: RecipeInput = {
3012
3223
  xl: { control: { base: { height: fieldHeight('xl') } }, input: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)' } } },
3013
3224
  },
3014
3225
  },
3226
+ // The steppers' inline padding, restated physically: logical spellings
3227
+ // resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
3228
+ // the emitter refuses them.
3229
+ targets: {
3230
+ lynx: {
3231
+ parts: {
3232
+ 'increment-trigger': { base: { paddingLeft: 'var(--space-lg)', paddingRight: 'var(--space-lg)' } },
3233
+ 'decrement-trigger': { base: { paddingLeft: 'var(--space-lg)', paddingRight: 'var(--space-lg)' } },
3234
+ },
3235
+ },
3236
+ },
3015
3237
  };
3016
3238
 
3017
3239
  /**
@@ -3309,6 +3531,16 @@ export const treeView: RecipeInput = {
3309
3531
  xl: { root: { base: { '--tree-text': 'var(--text-lg)' } } },
3310
3532
  },
3311
3533
  },
3534
+ // The indent, restated physically: logical spellings resolve on iOS but
3535
+ // not on Android (measured, signalxjs/lynx#1084), so the emitter refuses
3536
+ // them. Physical is the lynx target's norm — no RTL flow there.
3537
+ targets: {
3538
+ lynx: {
3539
+ parts: {
3540
+ 'branch-content': { base: { paddingLeft: 'var(--space-lg)' } },
3541
+ },
3542
+ },
3543
+ },
3312
3544
  };
3313
3545
 
3314
3546
  /**
@@ -3531,6 +3763,26 @@ export const nativeSelect: RecipeInput = {
3531
3763
  xl: { control: { base: { height: fieldHeight('xl'), fontSize: 'var(--text-lg)', padding: 'var(--space-lg) var(--space-xl)', paddingInlineEnd: 'calc(var(--space-xl) + 1.25em)' } } },
3532
3764
  },
3533
3765
  },
3766
+ // The chevron gutter and the indicator anchor, restated physically:
3767
+ // logical spellings resolve on iOS but not on Android (measured,
3768
+ // signalxjs/lynx#1084), so the emitter refuses them. Physical is the
3769
+ // lynx target's norm — no RTL flow there, so `end` IS right.
3770
+ targets: {
3771
+ lynx: {
3772
+ parts: {
3773
+ control: { base: { paddingRight: 'calc(var(--space-md) + 1.25em)' } },
3774
+ indicator: { base: { right: 'var(--space-md)' } },
3775
+ },
3776
+ variants: {
3777
+ size: {
3778
+ xs: { control: { base: { paddingRight: 'calc(var(--space-xs) + 1.25em)' } } },
3779
+ sm: { control: { base: { paddingRight: 'calc(var(--space-sm) + 1.25em)' } } },
3780
+ lg: { control: { base: { paddingRight: 'calc(var(--space-lg) + 1.25em)' } } },
3781
+ xl: { control: { base: { paddingRight: 'calc(var(--space-xl) + 1.25em)' } } },
3782
+ },
3783
+ },
3784
+ },
3785
+ },
3534
3786
  };
3535
3787
 
3536
3788
  // ── Content tier (#311) ───────────────────────────────────────────────────
@@ -4004,6 +4256,30 @@ export const indicator: RecipeInput = {
4004
4256
  xl: { item: { base: { fontSize: 'var(--text-lg)' } } },
4005
4257
  },
4006
4258
  },
4259
+ // The corner slots, restated physically: logical inset spellings and the
4260
+ // standalone `translate` property resolve on iOS but not on Android
4261
+ // (measured, signalxjs/lynx#1084), so the emitter refuses them. Physical
4262
+ // anchors plus `transform: translate()` are proven on both platforms,
4263
+ // and physical is the lynx target's norm — no RTL flow there, so the
4264
+ // hand-flipped RTL rules above have nothing to engage with either.
4265
+ targets: {
4266
+ lynx: {
4267
+ parts: {
4268
+ item: {
4269
+ selectors: {
4270
+ '&[data-placement="top-start"]': { top: '0', left: '0', transform: 'translate(-50%, -50%)' },
4271
+ '&[data-placement="top"]': { top: '0', left: '50%', transform: 'translate(-50%, -50%)' },
4272
+ '&[data-placement="top-end"]': { top: '0', right: '0', transform: 'translate(50%, -50%)' },
4273
+ '&[data-placement="start"]': { top: '50%', left: '0', transform: 'translate(-50%, -50%)' },
4274
+ '&[data-placement="end"]': { top: '50%', right: '0', transform: 'translate(50%, -50%)' },
4275
+ '&[data-placement="bottom-start"]': { bottom: '0', left: '0', transform: 'translate(-50%, 50%)' },
4276
+ '&[data-placement="bottom"]': { bottom: '0', left: '50%', transform: 'translate(-50%, 50%)' },
4277
+ '&[data-placement="bottom-end"]': { bottom: '0', right: '0', transform: 'translate(50%, 50%)' },
4278
+ },
4279
+ },
4280
+ },
4281
+ },
4282
+ },
4007
4283
  };
4008
4284
 
4009
4285
  /** daisy stats: the rounded base-100 panel with rule-thin item joins. */
@@ -4324,6 +4600,16 @@ export const chat: RecipeInput = {
4324
4600
  xl: { bubble: { base: { fontSize: 'var(--text-lg)', padding: 'var(--space-md) var(--space-xl)' } } },
4325
4601
  },
4326
4602
  },
4603
+ // The row's vertical padding, restated physically: logical spellings
4604
+ // resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
4605
+ // the emitter refuses them.
4606
+ targets: {
4607
+ lynx: {
4608
+ parts: {
4609
+ root: { base: { paddingTop: 'var(--space-2xs)', paddingBottom: 'var(--space-2xs)' } },
4610
+ },
4611
+ },
4612
+ },
4327
4613
  };
4328
4614
 
4329
4615
  /** daisy radial-progress: --value became --progress-percent; same ring. */
@@ -4780,6 +5066,16 @@ export const pagination: RecipeInput = {
4780
5066
  xl: { root: { base: { '--pg-size': 'calc(var(--size-field) * 14)', '--pg-font': 'var(--text-lg)' } } },
4781
5067
  },
4782
5068
  },
5069
+ // The cell's inline padding, restated physically: logical spellings
5070
+ // resolve on iOS but not on Android (measured, signalxjs/lynx#1084), so
5071
+ // the emitter refuses them.
5072
+ targets: {
5073
+ lynx: {
5074
+ parts: {
5075
+ item: { base: { paddingLeft: 'var(--space-2xs)', paddingRight: 'var(--space-2xs)' } },
5076
+ },
5077
+ },
5078
+ },
4783
5079
  };
4784
5080
 
4785
5081
  /**
@@ -4927,6 +5223,34 @@ export const steps: RecipeInput = {
4927
5223
  xl: { root: { base: { '--steps-ind': 'calc(var(--size-selector) * 9)', '--steps-font': 'var(--text-md)' } } },
4928
5224
  },
4929
5225
  },
5226
+ // The bridge's anchors and the vertical item's tail padding, restated
5227
+ // physically: logical spellings resolve on iOS but not on Android
5228
+ // (measured, signalxjs/lynx#1084), so the emitter refuses them. Physical
5229
+ // is the lynx target's norm — no RTL flow there to mirror with.
5230
+ targets: {
5231
+ lynx: {
5232
+ parts: {
5233
+ item: {
5234
+ selectors: {
5235
+ '&[data-orientation="vertical"]': { paddingBottom: 'var(--space-lg)' },
5236
+ },
5237
+ },
5238
+ separator: {
5239
+ selectors: {
5240
+ '&[data-orientation="horizontal"]': {
5241
+ top: 'calc(var(--space-xs) + var(--steps-ind) / 2)',
5242
+ left: '50%',
5243
+ },
5244
+ '&[data-orientation="vertical"]': {
5245
+ left: 'calc(var(--space-xs) + (var(--steps-ind) - var(--border)) / 2)',
5246
+ top: 'calc(var(--space-xs) + var(--steps-ind))',
5247
+ bottom: 'calc(var(--space-xs) * -1)',
5248
+ },
5249
+ },
5250
+ },
5251
+ },
5252
+ },
5253
+ },
4930
5254
  };
4931
5255
 
4932
5256
  /**
@@ -5000,6 +5324,16 @@ export const drawer: RecipeInput = {
5000
5324
  // Trigger-carried axes — see `btnColors` for why the panel is out of
5001
5325
  // reach and the trigger is the whole story here.
5002
5326
  variants: { color: btnColors(), size: btnSizes },
5327
+ // The btn paddings, restated physically — see `lynxBtnPad` (#1084).
5328
+ targets: {
5329
+ lynx: {
5330
+ parts: {
5331
+ trigger: { base: lynxBtnPad(4) },
5332
+ close: { base: lynxBtnPad(4) },
5333
+ },
5334
+ variants: { size: lynxBtnSizes('trigger') },
5335
+ },
5336
+ },
5003
5337
  };
5004
5338
 
5005
5339
  /**
@@ -5390,6 +5724,22 @@ export const carousel: RecipeInput = {
5390
5724
  xl: { root: { base: { '--carousel-dot': '0.875rem', '--carousel-nav': '3rem' } } },
5391
5725
  },
5392
5726
  },
5727
+ // The nav buttons' anchors, restated physically: logical inset
5728
+ // spellings resolve on iOS but not on Android (measured,
5729
+ // signalxjs/lynx#1084), so the emitter refuses them. Physical is the
5730
+ // lynx target's norm — no RTL flow there, so `start` IS left.
5731
+ targets: {
5732
+ lynx: {
5733
+ parts: {
5734
+ 'prev-trigger': {
5735
+ base: { top: 'calc(50% - var(--carousel-nav) / 2)', left: 'var(--space-sm)' },
5736
+ },
5737
+ 'next-trigger': {
5738
+ base: { top: 'calc(50% - var(--carousel-nav) / 2)', right: 'var(--space-sm)' },
5739
+ },
5740
+ },
5741
+ },
5742
+ },
5393
5743
  };
5394
5744
 
5395
5745
  /**
@@ -5634,6 +5984,21 @@ export const diff: RecipeInput = {
5634
5984
  },
5635
5985
  },
5636
5986
  },
5987
+ // The reveal pane's pin and the handle's centering margin, restated
5988
+ // physically: logical spellings resolve on iOS but not on Android
5989
+ // (measured, signalxjs/lynx#1084), so the emitter refuses them.
5990
+ // Physical is the lynx target's norm — no RTL flow there to mirror
5991
+ // with.
5992
+ lynx: {
5993
+ parts: {
5994
+ after: {
5995
+ base: { top: '0', bottom: '0', left: '0' },
5996
+ },
5997
+ handle: {
5998
+ base: { top: '0', bottom: '0', marginLeft: 'calc(var(--diff-hit) / -2)' },
5999
+ },
6000
+ },
6001
+ },
5637
6002
  },
5638
6003
  };
5639
6004