@passui/react 0.1.45 → 0.1.46

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 (140) hide show
  1. package/README.md +57 -46
  2. package/dist/types/src/components/accordion/accordion.d.ts +1 -1
  3. package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +1 -1
  4. package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +1 -1
  5. package/dist/types/src/components/popover/anchored-surface.d.ts +5 -1
  6. package/dist/types/src/components/popover/index.d.ts +41 -0
  7. package/dist/types/src/components/popover/popover-content.d.ts +32 -0
  8. package/dist/types/src/components/popover/popover-root.d.ts +22 -0
  9. package/dist/types/src/components/popover/popover-trigger.d.ts +23 -0
  10. package/dist/types/src/components/stepper/rolling-digit.d.ts +1 -1
  11. package/dist/types/src/components/table/table.d.ts +1 -1
  12. package/dist/types/src/components/view-controller/view-controller.d.ts +2 -2
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/src/machines/popover.machine.d.ts +30 -0
  15. package/package.json +307 -307
  16. package/src/appearance/index.ts +2 -2
  17. package/src/appearance/platform.tsx +76 -60
  18. package/src/appearance/theme.tsx +409 -377
  19. package/src/components/accordion/accordion.tsx +58 -49
  20. package/src/components/ambient/ambient.tsx +25 -25
  21. package/src/components/ambient/index.ts +1 -1
  22. package/src/components/aspect-ratio/aspect-ratio.tsx +21 -16
  23. package/src/components/breadcrumb/breadcrumb.tsx +63 -57
  24. package/src/components/button/button-styles.ts +39 -32
  25. package/src/components/button/button.tsx +91 -84
  26. package/src/components/button/icon-button.tsx +27 -25
  27. package/src/components/button/pending-spinner.tsx +8 -8
  28. package/src/components/button/use-pending-indicator.ts +11 -11
  29. package/src/components/chip/chip.tsx +35 -30
  30. package/src/components/code-block/code-block.tsx +40 -39
  31. package/src/components/combo-box/combo-box.tsx +5 -5
  32. package/src/components/command/command.tsx +124 -94
  33. package/src/components/compact-date-picker/calendar-month.ts +75 -53
  34. package/src/components/compact-date-picker/compact-date-picker.tsx +308 -289
  35. package/src/components/compact-time-picker/compact-time-picker.tsx +288 -281
  36. package/src/components/compact-time-picker/time-value.ts +32 -29
  37. package/src/components/content-unavailable-view/content-unavailable-view.tsx +64 -48
  38. package/src/components/content-view/content-view.tsx +168 -159
  39. package/src/components/content-view/index.ts +1 -1
  40. package/src/components/context-menu/conceal-menu-source.ts +25 -25
  41. package/src/components/context-menu/context-menu-host.tsx +439 -402
  42. package/src/components/context-menu/context-menu-store.ts +125 -123
  43. package/src/components/context-menu/menu-navigation.ts +25 -18
  44. package/src/components/context-menu/menu-position.ts +33 -29
  45. package/src/components/context-menu/menu-reveal.ts +32 -32
  46. package/src/components/context-menu/use-context-menu.ts +63 -56
  47. package/src/components/conversation/conversation.tsx +265 -263
  48. package/src/components/conversation/index.ts +1 -1
  49. package/src/components/date-picker/date-picker.tsx +95 -94
  50. package/src/components/date-picker/date-value.ts +20 -10
  51. package/src/components/dialog/dialog-host.tsx +105 -103
  52. package/src/components/dialog/dialog-store.ts +37 -36
  53. package/src/components/dialog/dialog.tsx +192 -179
  54. package/src/components/dialog/index.ts +1 -1
  55. package/src/components/disclosure-group/disclosure-group.tsx +93 -86
  56. package/src/components/divider/divider.tsx +24 -22
  57. package/src/components/inline-disclosure/inline-disclosure.tsx +40 -36
  58. package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +23 -19
  59. package/src/components/labeled-content/labeled-content.tsx +68 -68
  60. package/src/components/locale-switcher/index.ts +1 -1
  61. package/src/components/locale-switcher/locale-switcher.tsx +279 -251
  62. package/src/components/menu/menu.tsx +75 -58
  63. package/src/components/morph/index.ts +2 -2
  64. package/src/components/morph/morph.tsx +227 -201
  65. package/src/components/morph/use-morph-disclosure.ts +19 -19
  66. package/src/components/one-time-code/one-time-code.tsx +216 -200
  67. package/src/components/picker/picker-trigger.ts +16 -13
  68. package/src/components/picker/picker.tsx +371 -345
  69. package/src/components/popover/anchored-surface.tsx +253 -229
  70. package/src/components/popover/index.ts +50 -0
  71. package/src/components/popover/popover-content.tsx +334 -0
  72. package/src/components/popover/popover-fit.ts +58 -53
  73. package/src/components/popover/popover-root.tsx +97 -0
  74. package/src/components/popover/popover-trigger.tsx +71 -0
  75. package/src/components/profile-image/profile-image.tsx +80 -64
  76. package/src/components/progress-view/progress-view.tsx +61 -59
  77. package/src/components/progress-view/use-liquid-progress.ts +38 -38
  78. package/src/components/radio-group/radio-group.tsx +71 -68
  79. package/src/components/search-field/expanding-search-field.tsx +191 -177
  80. package/src/components/search-field/search-field.tsx +59 -50
  81. package/src/components/search-list/search-list.tsx +246 -236
  82. package/src/components/section/section.tsx +58 -58
  83. package/src/components/secure-field/secure-field.tsx +3 -3
  84. package/src/components/segmented-control/segmented-control.tsx +136 -136
  85. package/src/components/sheet/modal-environment.ts +71 -64
  86. package/src/components/sheet/sheet-portal.tsx +58 -53
  87. package/src/components/sheet/sheet.tsx +146 -141
  88. package/src/components/sheet/use-sheet-focus.ts +97 -82
  89. package/src/components/sidebar/sidebar.tsx +78 -78
  90. package/src/components/skeleton/skeleton.tsx +30 -21
  91. package/src/components/slider/slider.tsx +113 -104
  92. package/src/components/slosh-slider/liquid-step.ts +32 -31
  93. package/src/components/slosh-slider/slosh-slider.tsx +265 -228
  94. package/src/components/stat-tile/index.ts +1 -1
  95. package/src/components/stat-tile/stat-tile.tsx +79 -68
  96. package/src/components/status-indicator/status-indicator.tsx +40 -37
  97. package/src/components/stepper/rolling-digit.tsx +82 -68
  98. package/src/components/stepper/stepper.tsx +122 -113
  99. package/src/components/swatch-picker/swatch-picker.tsx +120 -120
  100. package/src/components/tab-bar/tab-bar.tsx +149 -148
  101. package/src/components/tab-view/tab-view.tsx +140 -137
  102. package/src/components/table/table.tsx +117 -98
  103. package/src/components/text-editor/text-editor.tsx +34 -29
  104. package/src/components/text-field/text-field.tsx +182 -180
  105. package/src/components/toast/toast-store.ts +127 -115
  106. package/src/components/toast/toaster.tsx +361 -347
  107. package/src/components/toggle/toggle.tsx +297 -282
  108. package/src/components/tooltip/tooltip-host.tsx +157 -150
  109. package/src/components/view-controller/index.ts +1 -1
  110. package/src/components/view-controller/view-controller.tsx +64 -53
  111. package/src/components/wake-slider/wake-slider.tsx +220 -205
  112. package/src/hooks/scroll/minimize-decision.ts +11 -7
  113. package/src/hooks/use-anchored-disclosure.ts +19 -19
  114. package/src/hooks/use-anchored-placement.ts +29 -25
  115. package/src/hooks/use-dismiss.ts +33 -29
  116. package/src/hooks/use-floating-dismiss.ts +24 -22
  117. package/src/hooks/use-media-query.ts +67 -60
  118. package/src/hooks/use-minimize-on-scroll.ts +69 -60
  119. package/src/hooks/use-visual-viewport.ts +63 -63
  120. package/src/hooks/viewport/types.ts +16 -16
  121. package/src/hooks/viewport/viewport-metrics.ts +16 -10
  122. package/src/icons/icons.tsx +440 -328
  123. package/src/index.ts +91 -91
  124. package/src/lib/anchored-placement.ts +70 -65
  125. package/src/lib/class-names.ts +14 -13
  126. package/src/lib/clipboard.ts +8 -7
  127. package/src/lib/date.ts +61 -60
  128. package/src/lib/floating-layers.ts +39 -24
  129. package/src/lib/haptics.ts +10 -10
  130. package/src/lib/language.ts +9 -9
  131. package/src/lib/range-value.ts +27 -21
  132. package/src/lib/screen-edges.ts +33 -30
  133. package/src/lib/shapes.ts +38 -34
  134. package/src/machines/popover.machine.ts +51 -0
  135. package/src/motion/transitions.ts +46 -43
  136. package/src/styles/code-block.css +69 -43
  137. package/src/styles/motion-controls.css +393 -191
  138. package/src/tokens.ts +1 -1
  139. package/dist/types/src/components/popover/popover.d.ts +0 -21
  140. package/src/components/popover/popover.tsx +0 -334
@@ -1,127 +1,136 @@
1
- import { useId } from "react"
2
- import { Button, type ButtonSize } from "../button/button"
3
- import { cn } from "../../lib/class-names"
4
- import { clampRange, stepRange } from "../../lib/range-value"
5
- import { RollingDigit } from "./rolling-digit"
1
+ import { useId } from "react";
2
+ import { Button, type ButtonSize } from "../button/button";
3
+ import { cn } from "../../lib/class-names";
4
+ import { clampRange, stepRange } from "../../lib/range-value";
5
+ import { RollingDigit } from "./rolling-digit";
6
6
 
7
- export type StepperSize = "sm" | "md" | "lg"
7
+ export type StepperSize = "sm" | "md" | "lg";
8
8
 
9
9
  const labelText: Record<StepperSize, string> = {
10
- sm: "min-w-0 text-footnote text-label",
11
- md: "min-w-0 text-subheadline text-label",
12
- lg: "min-w-0 text-body text-label",
13
- }
10
+ sm: "min-w-0 text-footnote text-label",
11
+ md: "min-w-0 text-subheadline text-label",
12
+ lg: "min-w-0 text-body text-label",
13
+ };
14
14
  const outputText: Record<StepperSize, string> = {
15
- sm: "text-footnote tabular-nums text-label",
16
- md: "text-subheadline tabular-nums text-label",
17
- lg: "text-body tabular-nums text-label",
18
- }
15
+ sm: "text-footnote tabular-nums text-label",
16
+ md: "text-subheadline tabular-nums text-label",
17
+ lg: "text-body tabular-nums text-label",
18
+ };
19
19
  const buttonSizes: Record<StepperSize, { size: ButtonSize; cls: string }> = {
20
- sm: {
21
- size: "icon-sm",
22
- cls: "size-7 rounded-none text-subheadline text-accent-text [@media(pointer:coarse)]:size-10",
23
- },
24
- md: {
25
- size: "icon",
26
- cls: "size-8 rounded-none text-title3 text-accent-text [@media(pointer:coarse)]:size-11",
27
- },
28
- lg: {
29
- size: "icon-lg",
30
- cls: "size-9 rounded-none text-title2 text-accent-text [@media(pointer:coarse)]:size-12",
31
- },
32
- }
20
+ sm: {
21
+ size: "icon-sm",
22
+ cls: "size-7 rounded-none text-subheadline text-accent-text [@media(pointer:coarse)]:size-10",
23
+ },
24
+ md: {
25
+ size: "icon",
26
+ cls: "size-8 rounded-none text-title3 text-accent-text [@media(pointer:coarse)]:size-11",
27
+ },
28
+ lg: {
29
+ size: "icon-lg",
30
+ cls: "size-9 rounded-none text-title2 text-accent-text [@media(pointer:coarse)]:size-12",
31
+ },
32
+ };
33
33
 
34
34
  export interface StepperProps {
35
- value: number
36
- onChange: (value: number) => void
37
- label: string
38
- /** Localized accessible names for each action. */
39
- labels: { decrement: string; increment: string }
40
- min?: number
41
- max?: number
42
- step?: number
43
- disabled?: boolean
44
- className?: string
45
- /** Formats the displayed value. */
46
- formatValue?: (value: number) => string
47
- /** Label, output and +/- button scale. @default "md" */
48
- size?: StepperSize
35
+ value: number;
36
+ onChange: (value: number) => void;
37
+ label: string;
38
+ /** Localized accessible names for each action. */
39
+ labels: { decrement: string; increment: string };
40
+ min?: number;
41
+ max?: number;
42
+ step?: number;
43
+ disabled?: boolean;
44
+ className?: string;
45
+ /** Formats the displayed value. */
46
+ formatValue?: (value: number) => string;
47
+ /** Label, output and +/- button scale. @default "md" */
48
+ size?: StepperSize;
49
49
  }
50
50
 
51
51
  /** Discrete increments with disabled boundary actions and a spoken value. */
52
52
  export function Stepper({
53
- value,
54
- onChange,
55
- label,
56
- labels,
57
- min = 0,
58
- max = 100,
59
- step = 1,
60
- disabled = false,
61
- className,
62
- formatValue,
63
- size = "md",
53
+ value,
54
+ onChange,
55
+ label,
56
+ labels,
57
+ min = 0,
58
+ max = 100,
59
+ step = 1,
60
+ disabled = false,
61
+ className,
62
+ formatValue,
63
+ size = "md",
64
64
  }: StepperProps) {
65
- const id = useId()
66
- const upper = Math.max(min, max)
67
- const current = clampRange(value, min, upper)
68
- const decrement = stepRange(current, -1, min, upper, step > 0 ? step : 1)
69
- const increment = stepRange(current, 1, min, upper, step > 0 ? step : 1)
70
- const displayed = formatValue?.(current) ?? String(current)
71
- const digits = displayed.replace(/[^0-9]/g, "")
72
- let digitIndex = 0
65
+ const id = useId();
66
+ const upper = Math.max(min, max);
67
+ const current = clampRange(value, min, upper);
68
+ const decrement = stepRange(current, -1, min, upper, step > 0 ? step : 1);
69
+ const increment = stepRange(current, 1, min, upper, step > 0 ? step : 1);
70
+ const displayed = formatValue?.(current) ?? String(current);
71
+ const digits = displayed.replace(/[^0-9]/g, "");
72
+ let digitIndex = 0;
73
73
 
74
- return (
75
- <div className={cn("flex min-w-0 items-center justify-between gap-3", className)}>
76
- <span id={id} className={labelText[size]}>
77
- {label}
78
- </span>
79
- <div className="flex shrink-0 items-center gap-3">
80
- <output aria-live="polite" aria-labelledby={id} className={outputText[size]}>
81
- <span aria-hidden="true" className="inline-flex items-center">
82
- {Array.from(displayed).map((character, index) => {
83
- if (!/[0-9]/.test(character))
84
- return <span key={`separator-${index}`}>{character}</span>
85
- const prefix = digits.slice(0, ++digitIndex)
86
- const place = digits.length - digitIndex
87
- return (
88
- <RollingDigit
89
- key={`place-${place}`}
90
- count={Number(prefix)}
91
- place={place}
92
- />
93
- )
94
- })}
95
- </span>
96
- <span className="sr-only">{displayed}</span>
97
- </output>
98
- <div className="flex items-center divide-x divide-separator overflow-hidden rounded-lg bg-fill">
99
- <Button
100
- type="button"
101
- variant="ghost"
102
- size={buttonSizes[size].size}
103
- aria-label={labels.decrement}
104
- aria-describedby={id}
105
- disabled={disabled || decrement === current}
106
- onClick={() => onChange(decrement)}
107
- className={buttonSizes[size].cls}
108
- >
109
- −
110
- </Button>
111
- <Button
112
- type="button"
113
- variant="ghost"
114
- size={buttonSizes[size].size}
115
- aria-label={labels.increment}
116
- aria-describedby={id}
117
- disabled={disabled || increment === current}
118
- onClick={() => onChange(increment)}
119
- className={buttonSizes[size].cls}
120
- >
121
- +
122
- </Button>
123
- </div>
124
- </div>
125
- </div>
126
- )
74
+ return (
75
+ <div
76
+ className={cn(
77
+ "flex min-w-0 items-center justify-between gap-3",
78
+ className,
79
+ )}
80
+ >
81
+ <span id={id} className={labelText[size]}>
82
+ {label}
83
+ </span>
84
+ <div className="flex shrink-0 items-center gap-3">
85
+ <output
86
+ aria-live="polite"
87
+ aria-labelledby={id}
88
+ className={outputText[size]}
89
+ >
90
+ <span aria-hidden="true" className="inline-flex items-center">
91
+ {Array.from(displayed).map((character) => {
92
+ if (!/[0-9]/.test(character))
93
+ return <span key={`sep-${character}`}>{character}</span>;
94
+ const prefix = digits.slice(0, ++digitIndex);
95
+ const place = digits.length - digitIndex;
96
+ return (
97
+ <RollingDigit
98
+ key={`place-${place}`}
99
+ count={Number(prefix)}
100
+ place={place}
101
+ />
102
+ );
103
+ })}
104
+ </span>
105
+ <span className="sr-only">{displayed}</span>
106
+ </output>
107
+ <div className="divide-separator bg-fill flex items-center divide-x overflow-hidden rounded-lg">
108
+ <Button
109
+ type="button"
110
+ variant="ghost"
111
+ size={buttonSizes[size].size}
112
+ aria-label={labels.decrement}
113
+ aria-describedby={id}
114
+ disabled={disabled || decrement === current}
115
+ onClick={() => onChange(decrement)}
116
+ className={buttonSizes[size].cls}
117
+ >
118
+ −
119
+ </Button>
120
+ <Button
121
+ type="button"
122
+ variant="ghost"
123
+ size={buttonSizes[size].size}
124
+ aria-label={labels.increment}
125
+ aria-describedby={id}
126
+ disabled={disabled || increment === current}
127
+ onClick={() => onChange(increment)}
128
+ className={buttonSizes[size].cls}
129
+ >
130
+ +
131
+ </Button>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ );
127
136
  }
@@ -1,138 +1,138 @@
1
- import { motion } from "motion/react"
2
- import { useId } from "react"
3
- import { CheckIcon } from "../../icons/icons"
4
- import { cn } from "../../lib/class-names"
5
- import { haptic } from "../../lib/haptics"
6
- import { spring } from "../../motion/transitions"
1
+ import { motion } from "motion/react";
2
+ import { useId } from "react";
3
+ import { CheckIcon } from "../../icons/icons";
4
+ import { cn } from "../../lib/class-names";
5
+ import { haptic } from "../../lib/haptics";
6
+ import { spring } from "../../motion/transitions";
7
7
 
8
8
  export interface SwatchPickerOption<T extends string> {
9
- value: T
10
- label: string
11
- /** Swatch background color. */
12
- color: string
13
- /** Color of the selected checkmark. */
14
- ink: string
9
+ value: T;
10
+ label: string;
11
+ /** Swatch background color. */
12
+ color: string;
13
+ /** Color of the selected checkmark. */
14
+ ink: string;
15
15
  }
16
16
 
17
- export type SwatchPickerSize = "sm" | "md" | "lg"
17
+ export type SwatchPickerSize = "sm" | "md" | "lg";
18
18
 
19
19
  const swatchSizes: Record<SwatchPickerSize, string> = {
20
- sm: "size-6",
21
- md: "size-8",
22
- lg: "size-10",
23
- }
20
+ sm: "size-6",
21
+ md: "size-8",
22
+ lg: "size-10",
23
+ };
24
24
  const checkSizes: Record<SwatchPickerSize, number> = {
25
- sm: 13,
26
- md: 16,
27
- lg: 19,
28
- }
25
+ sm: 13,
26
+ md: 16,
27
+ lg: 19,
28
+ };
29
29
  const captionText: Record<SwatchPickerSize, string> = {
30
- sm: "text-caption2",
31
- md: "text-caption2",
32
- lg: "text-caption",
33
- }
30
+ sm: "text-caption2",
31
+ md: "text-caption2",
32
+ lg: "text-caption",
33
+ };
34
34
  const fieldsetGaps: Record<SwatchPickerSize, string> = {
35
- sm: "gap-2",
36
- md: "gap-3",
37
- lg: "gap-4",
38
- }
35
+ sm: "gap-2",
36
+ md: "gap-3",
37
+ lg: "gap-4",
38
+ };
39
39
  const labelGaps: Record<SwatchPickerSize, string> = {
40
- sm: "gap-1",
41
- md: "gap-1.5",
42
- lg: "gap-2",
43
- }
40
+ sm: "gap-1",
41
+ md: "gap-1.5",
42
+ lg: "gap-2",
43
+ };
44
44
 
45
45
  export interface SwatchPickerProps<T extends string> {
46
- value: T
47
- options: ReadonlyArray<SwatchPickerOption<T>>
48
- onChange: (value: T) => void
49
- /** Accessible name for the group. */
50
- label: string
51
- /** Custom classes for the swatch container. */
52
- className?: string
53
- /** Swatch diameter, check icon and caption scale. @default "md" */
54
- size?: SwatchPickerSize
46
+ value: T;
47
+ options: ReadonlyArray<SwatchPickerOption<T>>;
48
+ onChange: (value: T) => void;
49
+ /** Accessible name for the group. */
50
+ label: string;
51
+ /** Custom classes for the swatch container. */
52
+ className?: string;
53
+ /** Swatch diameter, check icon and caption scale. @default "md" */
54
+ size?: SwatchPickerSize;
55
55
  }
56
56
 
57
57
  /** Colour swatches with a check on the chosen one. */
58
58
  export function SwatchPicker<T extends string>({
59
- value,
60
- options,
61
- onChange,
62
- label,
63
- className,
64
- size = "md",
59
+ value,
60
+ options,
61
+ onChange,
62
+ label,
63
+ className,
64
+ size = "md",
65
65
  }: SwatchPickerProps<T>) {
66
- const name = useId()
67
- return (
68
- <fieldset className={cn("flex flex-wrap", fieldsetGaps[size], className)}>
69
- <legend className="sr-only">{label}</legend>
70
- {options.map((option) => {
71
- const selected = option.value === value
72
- return (
73
- <label
74
- key={option.value}
75
- className={cn(
76
- "group flex cursor-pointer flex-col items-center",
77
- labelGaps[size]
78
- )}
79
- >
80
- <input
81
- type="radio"
82
- name={name}
83
- value={option.value}
84
- checked={selected}
85
- onChange={() => {
86
- haptic("selection")
87
- onChange(option.value)
88
- }}
89
- className="peer sr-only"
90
- />
91
- <span
92
- className={cn(
93
- "flex items-center justify-center rounded-full ring-offset-1 ring-offset-elevated transition-[box-shadow,transform] duration-fast group-active:scale-95 peer-focus-visible:ring-2 peer-focus-visible:ring-accent",
94
- swatchSizes[size],
95
- selected
96
- ? "ring-[1.5px] ring-label/70"
97
- : "ring-0 group-hover:ring-[1.5px] group-hover:ring-separator"
98
- )}
99
- style={{
100
- background: option.color,
101
- }}
102
- >
103
- {selected && (
104
- <motion.span
105
- initial={{
106
- scale: 0.4,
107
- opacity: 0,
108
- }}
109
- animate={{
110
- scale: 1,
111
- opacity: 1,
112
- }}
113
- transition={spring.bouncy}
114
- >
115
- <CheckIcon
116
- size={checkSizes[size]}
117
- strokeWidth={2.6}
118
- style={{
119
- color: option.ink,
120
- }}
121
- />
122
- </motion.span>
123
- )}
124
- </span>
125
- <span
126
- className={cn(
127
- captionText[size],
128
- selected ? "font-semibold text-label" : "text-label-tertiary"
129
- )}
130
- >
131
- {option.label}
132
- </span>
133
- </label>
134
- )
135
- })}
136
- </fieldset>
137
- )
66
+ const name = useId();
67
+ return (
68
+ <fieldset className={cn("flex flex-wrap", fieldsetGaps[size], className)}>
69
+ <legend className="sr-only">{label}</legend>
70
+ {options.map((option) => {
71
+ const selected = option.value === value;
72
+ return (
73
+ <label
74
+ key={option.value}
75
+ className={cn(
76
+ "group flex cursor-pointer flex-col items-center",
77
+ labelGaps[size],
78
+ )}
79
+ >
80
+ <input
81
+ type="radio"
82
+ name={name}
83
+ value={option.value}
84
+ checked={selected}
85
+ onChange={() => {
86
+ haptic("selection");
87
+ onChange(option.value);
88
+ }}
89
+ className="peer sr-only"
90
+ />
91
+ <span
92
+ className={cn(
93
+ "ring-offset-elevated duration-fast peer-focus-visible:ring-accent flex items-center justify-center rounded-full ring-offset-1 transition-[box-shadow,transform] group-active:scale-95 peer-focus-visible:ring-2",
94
+ swatchSizes[size],
95
+ selected
96
+ ? "ring-label/70 ring-[1.5px]"
97
+ : "group-hover:ring-separator ring-0 group-hover:ring-[1.5px]",
98
+ )}
99
+ style={{
100
+ background: option.color,
101
+ }}
102
+ >
103
+ {selected && (
104
+ <motion.span
105
+ initial={{
106
+ scale: 0.4,
107
+ opacity: 0,
108
+ }}
109
+ animate={{
110
+ scale: 1,
111
+ opacity: 1,
112
+ }}
113
+ transition={spring.bouncy}
114
+ >
115
+ <CheckIcon
116
+ size={checkSizes[size]}
117
+ strokeWidth={2.6}
118
+ style={{
119
+ color: option.ink,
120
+ }}
121
+ />
122
+ </motion.span>
123
+ )}
124
+ </span>
125
+ <span
126
+ className={cn(
127
+ captionText[size],
128
+ selected ? "text-label font-semibold" : "text-label-tertiary",
129
+ )}
130
+ >
131
+ {option.label}
132
+ </span>
133
+ </label>
134
+ );
135
+ })}
136
+ </fieldset>
137
+ );
138
138
  }