@sigx/zero-daisyui 0.8.0 → 0.10.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.
Files changed (40) hide show
  1. package/README.md +3 -1
  2. package/dist/audit.json +55 -0
  3. package/dist/components.d.ts +9 -0
  4. package/dist/components.js +1 -0
  5. package/dist/css/components/avatar-group.css +168 -0
  6. package/dist/css/components/drawer.css +42 -6
  7. package/dist/css/index.css +209 -6
  8. package/dist/lynx/components/avatar-group.css +97 -0
  9. package/dist/lynx/components/button.css +2 -2
  10. package/dist/lynx/components/carousel.css +10 -10
  11. package/dist/lynx/components/checkbox.css +7 -7
  12. package/dist/lynx/components/combobox.css +2 -2
  13. package/dist/lynx/components/dialog.css +2 -2
  14. package/dist/lynx/components/diff.css +10 -10
  15. package/dist/lynx/components/divider.css +19 -0
  16. package/dist/lynx/components/drawer.css +24 -2
  17. package/dist/lynx/components/grid.css +1 -1
  18. package/dist/lynx/components/hover-card.css +1 -1
  19. package/dist/lynx/components/menu.css +2 -2
  20. package/dist/lynx/components/navbar.css +5 -5
  21. package/dist/lynx/components/number-input.css +1 -1
  22. package/dist/lynx/components/popover.css +1 -1
  23. package/dist/lynx/components/radio-group.css +6 -6
  24. package/dist/lynx/components/select.css +39 -10
  25. package/dist/lynx/components/slider.css +2 -2
  26. package/dist/lynx/components/tabs.css +1 -1
  27. package/dist/lynx/components/toast.css +2 -2
  28. package/dist/lynx/components/tooltip.css +1 -1
  29. package/dist/lynx/index.css +350 -182
  30. package/dist/lynx/manifest.json +30 -3
  31. package/dist/lynx/tokens.css +114 -114
  32. package/dist/manifest.json +28 -1
  33. package/dist/recipes.d.ts +1 -0
  34. package/dist/recipes.d.ts.map +1 -1
  35. package/dist/recipes.js +208 -13
  36. package/dist/recipes.js.map +1 -1
  37. package/dist/register.d.ts +9 -0
  38. package/dist/report.json +383 -16
  39. package/package.json +4 -4
  40. package/src/recipes.ts +212 -13
package/src/recipes.ts CHANGED
@@ -2939,6 +2939,38 @@ const fieldControl: NonNullable<PartStyles['base']> = {
2939
2939
  */
2940
2940
  const SELECT_CLEARABLE = '[data-scope="select"][data-part="root"]:has(> [data-scope="select"][data-part="clear-trigger"]) &';
2941
2941
 
2942
+ /**
2943
+ * The select trigger's lynx steps — `[inline padding in --size-field units,
2944
+ * font-size step]` — as the size variants set them (`lynxBtnPad` and the
2945
+ * web ramp's `fontSize`). One table, so the clear-trigger's offset below
2946
+ * cannot drift from the trigger it sits in.
2947
+ */
2948
+ const SELECT_LYNX_STEPS = {
2949
+ xs: [2, 'xs'],
2950
+ sm: [3, 'sm'],
2951
+ md: [4, 'sm'],
2952
+ lg: [5, 'md'],
2953
+ xl: [6, 'lg'],
2954
+ } as const satisfies Record<keyof typeof FIELD_STEPS, readonly [number, string]>;
2955
+
2956
+ /**
2957
+ * Where the lynx clear-trigger's 1.5rem chip sits at one size step
2958
+ * (signalxjs/lynx#1184): centred in the field — the root is exactly the
2959
+ * trigger's height, so half the field less half the chip, from the top —
2960
+ * and one `--space-xs` short of the chevron's 1em box, which sits inside the
2961
+ * trigger's border and end padding at that step's font size. Physical
2962
+ * `top`/`right` and direct `calc(var())` only: lynx resolves no logical
2963
+ * insets on Android and no `min()`, and drops a `var()` that holds a
2964
+ * `calc()`.
2965
+ */
2966
+ const lynxSelectClear = (step: keyof typeof SELECT_LYNX_STEPS): NonNullable<PartStyles['base']> => {
2967
+ const [pad, text] = SELECT_LYNX_STEPS[step];
2968
+ return {
2969
+ top: `calc(var(--size-field) * ${FIELD_STEPS[step] / 2} - 0.75rem)`,
2970
+ right: `calc(var(--border) + var(--size-field) * ${pad} + var(--text-${text}) + var(--space-xs))`,
2971
+ };
2972
+ };
2973
+
2942
2974
  export const select: RecipeInput = {
2943
2975
  component: 'select',
2944
2976
  // Accent as text/border ink only — daisy's highlighted item is a neutral
@@ -3144,9 +3176,23 @@ export const select: RecipeInput = {
3144
3176
  // A touch list has no highlight: the pressed item takes the
3145
3177
  // highlighted item's wash.
3146
3178
  item: { states: { pressed: { background: 'var(--color-base-200)' } } },
3179
+ // The × and the ▾ get a spacing contract (signalxjs/lynx#1184).
3180
+ // The web offsets the clear-trigger by "the md padding + 1em",
3181
+ // and the 1em is the clear-trigger's own text-sm: at lg/xl the
3182
+ // trigger's padding and chevron both grow, so the × slid onto
3183
+ // the ▾. Here the chevron is a 1em box (right-aligned, so an
3184
+ // unclearable select looks as before) and the clear-trigger a
3185
+ // 1.5rem square (WCAG 2.5.8's target) placed per size beside
3186
+ // it — see `lynxSelectClear`. The web's `min()` height and
3187
+ // `margin-block: auto` centring are dropped on lynx, which
3188
+ // left the box stretched to the field's full height: its
3189
+ // focus ring was a tall pill. The ring is inset, so it hugs
3190
+ // the chip and can never reach the chevron, and no gap shadow
3191
+ // paints past the chip's rounded corners.
3192
+ indicator: { base: { width: '1em', textAlign: 'right' } },
3147
3193
  'clear-trigger': {
3148
- base: { top: 'var(--space-xs)', bottom: 'var(--space-xs)', right: 'calc(var(--size-field) * 4 + 1em + var(--space-xs))', minWidth: '1.5rem' },
3149
- states: lynxFocus(),
3194
+ base: { ...lynxSelectClear('md'), width: '1.5rem', height: '1.5rem', minWidth: '1.5rem', boxSizing: 'border-box' },
3195
+ states: lynxFocus({ inset: true }),
3150
3196
  },
3151
3197
  },
3152
3198
  variants: {
@@ -3155,7 +3201,10 @@ export const select: RecipeInput = {
3155
3201
  color: Object.fromEntries(ROLES.map((c) => [c, { popup: { base: {
3156
3202
  '--select-accent': `var(--color-${c})`,
3157
3203
  } } }])),
3158
- size: { ...lynxBtnSizes('trigger'), md: { trigger: { base: lynxBtnPad(4) } } },
3204
+ size: Object.fromEntries((Object.keys(SELECT_LYNX_STEPS) as (keyof typeof SELECT_LYNX_STEPS)[]).map((step) => [step, {
3205
+ trigger: { base: lynxBtnPad(SELECT_LYNX_STEPS[step][0]) },
3206
+ 'clear-trigger': { base: lynxSelectClear(step) },
3207
+ }])),
3159
3208
  },
3160
3209
  },
3161
3210
  },
@@ -3599,6 +3648,100 @@ export const avatar: RecipeInput = {
3599
3648
  },
3600
3649
  };
3601
3650
 
3651
+ /**
3652
+ * Avatar group (#297) — daisy's `avatar-group`: the faces overlap by a
3653
+ * quarter, each already cut out by the avatar's own base-100 gap ring, and the
3654
+ * "+N" chip is daisy's `avatar-placeholder` (neutral fill, neutral-content
3655
+ * ink, a circle) wearing the same ring. The group's `size` borrows the
3656
+ * avatar's own size step (`composes`), so a group sizes its faces without a
3657
+ * prop on each, and an avatar's own `size` still wins. Colour is daisy's
3658
+ * ring colour: it rings the chip, and it is borrowed by the avatars too, so
3659
+ * one `color` on the group rings every face alike.
3660
+ */
3661
+ const avatarGroupSteps: Record<string, [string, string]> = {
3662
+ xs: ['calc(var(--size-selector) * 6)', 'var(--text-xs)'],
3663
+ sm: ['calc(var(--size-selector) * 8)', 'var(--text-xs)'],
3664
+ lg: ['calc(var(--size-selector) * 12)', 'var(--text-md)'],
3665
+ xl: ['calc(var(--size-selector) * 16)', 'var(--text-lg)'],
3666
+ };
3667
+
3668
+ export const avatarGroup: RecipeInput = {
3669
+ component: 'avatar-group',
3670
+ tokens: {
3671
+ '--avatar-group-size': 'calc(var(--size-selector) * 10)',
3672
+ '--avatar-group-text': 'var(--text-sm)',
3673
+ '--avatar-group-ring': 'var(--color-primary)',
3674
+ },
3675
+ parts: {
3676
+ root: {
3677
+ base: {
3678
+ display: 'inline-flex',
3679
+ alignItems: 'center',
3680
+ verticalAlign: 'middle',
3681
+ isolation: 'isolate',
3682
+ },
3683
+ },
3684
+ overflow: {
3685
+ base: {
3686
+ // Positioned like the avatars, or it would paint under the
3687
+ // face it overlaps.
3688
+ position: 'relative',
3689
+ display: 'inline-grid',
3690
+ placeItems: 'center',
3691
+ boxSizing: 'border-box',
3692
+ minWidth: 'var(--avatar-group-size)',
3693
+ height: 'var(--avatar-group-size)',
3694
+ paddingInline: 'var(--space-2xs)',
3695
+ marginInlineStart: 'calc(var(--avatar-group-size) * -0.25)',
3696
+ borderRadius: '9999px',
3697
+ boxShadow: '0 0 0 2px var(--color-base-100), 0 0 0 4px var(--avatar-group-ring)',
3698
+ background: 'var(--color-neutral)',
3699
+ color: 'var(--color-neutral-content)',
3700
+ fontSize: 'var(--avatar-group-text)',
3701
+ fontWeight: 'var(--weight-semibold)',
3702
+ fontVariantNumeric: 'tabular-nums',
3703
+ whiteSpace: 'nowrap',
3704
+ userSelect: 'none',
3705
+ },
3706
+ },
3707
+ },
3708
+ composes: {
3709
+ avatar: {
3710
+ parts: {
3711
+ root: {
3712
+ selectors: {
3713
+ '&:not(:first-child)': { marginInlineStart: 'calc(var(--avatar-group-size) * -0.25)' },
3714
+ },
3715
+ },
3716
+ },
3717
+ },
3718
+ },
3719
+ compoundVariants: [
3720
+ ...Object.keys(avatarGroupSteps).map((size) => ({
3721
+ match: { size },
3722
+ parts: {},
3723
+ composes: { avatar: { axes: { size } } },
3724
+ })),
3725
+ ...ROLES.map((color) => ({
3726
+ match: { color },
3727
+ parts: {},
3728
+ composes: { avatar: { axes: { color } } },
3729
+ })),
3730
+ ],
3731
+ variants: {
3732
+ color: Object.fromEntries(ROLES.map((c) => [c, { root: { base: {
3733
+ '--avatar-group-ring': `var(--color-${c})`,
3734
+ } } }])),
3735
+ size: {
3736
+ ...Object.fromEntries(Object.entries(avatarGroupSteps).map(([size, [box, text]]) => [size, { root: { base: {
3737
+ '--avatar-group-size': box, '--avatar-group-text': text,
3738
+ } } }])),
3739
+ // `md` is the un-attributed render — the defaults in `tokens:`.
3740
+ md: {},
3741
+ },
3742
+ },
3743
+ };
3744
+
3602
3745
  /**
3603
3746
  * A toast whose indicator is rendered (#292): the grid grows a leading column
3604
3747
  * for the mark, and the text, action and close step one column along.
@@ -5527,6 +5670,25 @@ export const divider: RecipeInput = {
5527
5670
  xl: { root: { base: { '--divider-thickness': 'calc(var(--border) * 3)' } } },
5528
5671
  },
5529
5672
  },
5673
+ targets: {
5674
+ lynx: {
5675
+ parts: {
5676
+ // Lynx drops the ::before/::after segments the rule is drawn
5677
+ // with above, which left the lynx divider with no line and,
5678
+ // since only those rules read `--divider-thickness`, no size
5679
+ // ramp either (#375). There the root itself is the line, as
5680
+ // it was before the segments (#356): filled with the ink and
5681
+ // one thickness across.
5682
+ root: {
5683
+ base: { background: 'var(--divider-ink)' },
5684
+ selectors: {
5685
+ '&[data-orientation="horizontal"]': { blockSize: 'var(--divider-thickness)' },
5686
+ '&[data-orientation="vertical"]': { inlineSize: 'var(--divider-thickness)' },
5687
+ },
5688
+ },
5689
+ },
5690
+ },
5691
+ },
5530
5692
  };
5531
5693
 
5532
5694
  // ── Loading (#314) ────────────────────────────────────────────────────────
@@ -7224,9 +7386,11 @@ export const steps: RecipeInput = {
7224
7386
  /**
7225
7387
  * The modal sheet's slide (#83): in from its edge, back out to it.
7226
7388
  *
7227
- * `translate` is physical, so the travel is a custom property that flips with
7228
- * the placement AND the direction — off the reading start is leftward in LTR
7229
- * and rightward in RTL (the `rtl` hedge, as everywhere here). Keyed on
7389
+ * `translate` is physical, so the travel is a custom property — the whole
7390
+ * `x y` pair — that flips with the placement AND the direction: off the
7391
+ * reading start is leftward in LTR and rightward in RTL (the `rtl` hedge, as
7392
+ * everywhere here), while a `top` / `bottom` sheet travels on Y and mirrors
7393
+ * in neither (#291). Keyed on
7230
7394
  * `data-l-dock="sheet"`, the regime, which holds through the exit where
7231
7395
  * `:modal` does not, so the slide-out leaves from the sheet's own box.
7232
7396
  * Opacity stays at 1: the travel is the transition. Each direction rides the
@@ -7240,16 +7404,19 @@ const sheetSlide = (enter: string, exit: string): PartStyles => {
7240
7404
  return `translate ${tempo}, display ${duration} allow-discrete, overlay ${duration} allow-discrete`;
7241
7405
  };
7242
7406
  return {
7243
- base: { '--drawer-travel': '-100%' },
7407
+ base: { '--drawer-travel': '-100% 0' },
7244
7408
  selectors: {
7245
- '&[data-placement="end"]': { '--drawer-travel': '100%' },
7246
- [`&[data-placement="start"]${rtl}`]: { '--drawer-travel': '100%' },
7247
- [`&[data-placement="end"]${rtl}`]: { '--drawer-travel': '-100%' },
7248
- [sheet]: { opacity: '1', translate: 'var(--drawer-travel) 0', transition: travel(exit) },
7409
+ '&[data-placement="end"]': { '--drawer-travel': '100% 0' },
7410
+ [`&[data-placement="start"]${rtl}`]: { '--drawer-travel': '100% 0' },
7411
+ [`&[data-placement="end"]${rtl}`]: { '--drawer-travel': '-100% 0' },
7412
+ // The block edges travel on Y, and no direction mirrors them (#291).
7413
+ '&[data-placement="top"]': { '--drawer-travel': '0 -100%' },
7414
+ '&[data-placement="bottom"]': { '--drawer-travel': '0 100%' },
7415
+ [sheet]: { opacity: '1', translate: 'var(--drawer-travel)', transition: travel(exit) },
7249
7416
  [open]: { translate: 'none', transition: travel(enter) },
7250
7417
  },
7251
7418
  at: {
7252
- 'starting-style': { selectors: { [open]: { opacity: '1', translate: 'var(--drawer-travel) 0' } } },
7419
+ 'starting-style': { selectors: { [open]: { opacity: '1', translate: 'var(--drawer-travel)' } } },
7253
7420
  'reduced-motion': { selectors: { [sheet]: { transition: 'none' }, [open]: { transition: 'none' } } },
7254
7421
  },
7255
7422
  };
@@ -7319,6 +7486,38 @@ export const drawer: RecipeInput = {
7319
7486
  },
7320
7487
  '&[data-placement="start"][data-l-dock="sheet"]': { insetInlineStart: '0', insetInlineEnd: 'auto' },
7321
7488
  '&[data-placement="end"][data-l-dock="sheet"]': { insetInlineStart: 'auto', insetInlineEnd: '0' },
7489
+ /**
7490
+ * The block edges (#291): a sheet across the viewport's width,
7491
+ * pinned with `inset-block-*` — nothing here mirrors in RTL. Its
7492
+ * height is this recipe's: content-sized up to most of the
7493
+ * viewport, so the scrim stays in reach. Full width by default —
7494
+ * `measure` still caps the INLINE size (centred by the auto
7495
+ * margins), which the `:where()` keeps below the layout step
7496
+ * table's specificity whatever order the files load in.
7497
+ */
7498
+ '&:where([data-placement="top"], [data-placement="bottom"])': { '--l-measure': 'none' },
7499
+ '&[data-placement="top"][data-l-dock="sheet"]': {
7500
+ insetBlockStart: '0',
7501
+ insetBlockEnd: 'auto',
7502
+ insetInlineStart: '0',
7503
+ insetInlineEnd: '0',
7504
+ marginInline: 'auto',
7505
+ blockSize: 'auto',
7506
+ maxBlockSize: '85dvh',
7507
+ borderEndStartRadius: 'var(--radius-box)',
7508
+ borderEndEndRadius: 'var(--radius-box)',
7509
+ },
7510
+ '&[data-placement="bottom"][data-l-dock="sheet"]': {
7511
+ insetBlockStart: 'auto',
7512
+ insetBlockEnd: '0',
7513
+ insetInlineStart: '0',
7514
+ insetInlineEnd: '0',
7515
+ marginInline: 'auto',
7516
+ blockSize: 'auto',
7517
+ maxBlockSize: '85dvh',
7518
+ borderStartStartRadius: 'var(--radius-box)',
7519
+ borderStartEndRadius: 'var(--radius-box)',
7520
+ },
7322
7521
  },
7323
7522
  }),
7324
7523
  backdrop: {
@@ -8151,7 +8350,7 @@ export const diff: RecipeInput = {
8151
8350
 
8152
8351
  export const recipes: RecipeInput[] = [
8153
8352
  tabs, collapsible, switchRecipe, dialog, popover, tooltip, hoverCard, menu, menubar,
8154
- field, fieldset, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, toast, combobox,
8353
+ field, fieldset, checkbox, checkboxGroup, radioGroup, progress, slider, accordion, select, button, avatar, avatarGroup, toast, combobox,
8155
8354
  toggle, toggleGroup, numberInput, ratingGroup, treeView, input, textarea,
8156
8355
  card, alert, emptyState, badge, divider, skeleton, spinner,
8157
8356
  kbd, status, indicator, stats, timeline, chat, chatLog, radialProgress, join,