@boyernick/standard-ui-react 0.1.1-canary.6 → 0.1.1-canary.7

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": "@boyernick/standard-ui-react",
3
- "version": "0.1.1-canary.6",
3
+ "version": "0.1.1-canary.7",
4
4
  "description": "React components for StandardUI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -5,6 +5,7 @@ import type { ComponentProps, ReactElement } from "react"
5
5
  import { IconChevronDownSmall, IconCrossSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
7
  import { motion } from "./lib/motion"
8
+ import { popupInset, popupItem, popupLabel, popupMessage, popupSurface } from "./lib/popup"
8
9
 
9
10
  export type AutocompleteProps = ComponentProps<typeof BaseAutocomplete.Root>
10
11
  export type AutocompleteValueProps = ComponentProps<typeof BaseAutocomplete.Value>
@@ -167,7 +168,8 @@ export const AutocompletePopup = ({
167
168
  }: AutocompletePopupProps) => (
168
169
  <BaseAutocomplete.Popup
169
170
  className={cn(
170
- "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden rounded-xl border border-border-primary-solid bg-surface shadow-lg outline-none",
171
+ "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden",
172
+ popupSurface,
171
173
  motion.popupAnchor,
172
174
  className,
173
175
  )}
@@ -198,7 +200,7 @@ export const AutocompleteList = ({
198
200
  ...props
199
201
  }: AutocompleteListProps) => (
200
202
  <BaseAutocomplete.List
201
- className={cn("max-h-[inherit] overflow-y-auto p-1.5 outline-none empty:p-0", className)}
203
+ className={cn("max-h-[inherit] overflow-y-auto outline-none", popupInset, className)}
202
204
  {...props}
203
205
  />
204
206
  )
@@ -209,7 +211,7 @@ export const AutocompleteItem = ({
209
211
  }: AutocompleteItemProps) => (
210
212
  <BaseAutocomplete.Item
211
213
  className={cn(
212
- "flex min-h-9 cursor-default items-center gap-2 rounded-sm px-3 py-2 text-sm text-fg-primary outline-none select-none",
214
+ popupItem,
213
215
  motion.colors,
214
216
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary data-highlighted:text-fg-primary",
215
217
  className,
@@ -223,7 +225,7 @@ export const AutocompleteEmpty = ({
223
225
  ...props
224
226
  }: AutocompleteEmptyProps) => (
225
227
  <BaseAutocomplete.Empty
226
- className={cn("px-3 py-2 text-sm text-fg-tertiary empty:hidden", className)}
228
+ className={cn(cn(popupMessage, "empty:hidden"), className)}
227
229
  {...props}
228
230
  />
229
231
  )
@@ -233,7 +235,7 @@ export const AutocompleteStatus = ({
233
235
  ...props
234
236
  }: AutocompleteStatusProps) => (
235
237
  <BaseAutocomplete.Status
236
- className={cn("px-3 py-2 text-sm text-fg-tertiary", className)}
238
+ className={cn(popupMessage, className)}
237
239
  {...props}
238
240
  />
239
241
  )
@@ -250,7 +252,7 @@ export const AutocompleteGroupLabel = ({
250
252
  ...props
251
253
  }: AutocompleteGroupLabelProps) => (
252
254
  <BaseAutocomplete.GroupLabel
253
- className={cn("px-3 py-1.5 text-xs text-fg-tertiary", className)}
255
+ className={cn(popupLabel, className)}
254
256
  {...props}
255
257
  />
256
258
  )
package/src/combobox.tsx CHANGED
@@ -5,6 +5,7 @@ import type { ComponentProps, ReactElement } from "react"
5
5
  import { IconChevronDownSmall, IconCrossSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
7
  import { motion } from "./lib/motion"
8
+ import { popupInset, popupItem, popupLabel, popupMessage, popupSurface } from "./lib/popup"
8
9
 
9
10
  export type ComboboxProps<
10
11
  Value,
@@ -138,7 +139,7 @@ export const ComboboxClear = ({
138
139
  {...props}
139
140
  >
140
141
  {children ?? (
141
- <IconCrossSmall size={14} className="size-3.5" aria-hidden />
142
+ <IconCrossSmall size={16} className="size-4" aria-hidden />
142
143
  )}
143
144
  </BaseCombobox.Clear>
144
145
  )
@@ -159,7 +160,7 @@ export const ComboboxBackdrop = ({
159
160
  )
160
161
 
161
162
  export const ComboboxPositioner = ({
162
- sideOffset = 4,
163
+ sideOffset = 6,
163
164
  className,
164
165
  ...props
165
166
  }: ComboboxPositionerProps) => (
@@ -173,7 +174,8 @@ export const ComboboxPositioner = ({
173
174
  export const ComboboxPopup = ({ className, ...props }: ComboboxPopupProps) => (
174
175
  <BaseCombobox.Popup
175
176
  className={cn(
176
- "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden rounded-md border border-border-primary bg-surface shadow-md outline-none",
177
+ "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden",
178
+ popupSurface,
177
179
  motion.popupAnchor,
178
180
  className,
179
181
  )}
@@ -198,7 +200,7 @@ export const ComboboxArrow = ({ className, ...props }: ComboboxArrowProps) => (
198
200
 
199
201
  export const ComboboxList = ({ className, ...props }: ComboboxListProps) => (
200
202
  <BaseCombobox.List
201
- className={cn("max-h-[inherit] overflow-y-auto p-1 outline-none", className)}
203
+ className={cn("max-h-[inherit] overflow-y-auto outline-none", popupInset, className)}
202
204
  {...props}
203
205
  />
204
206
  )
@@ -206,7 +208,7 @@ export const ComboboxList = ({ className, ...props }: ComboboxListProps) => (
206
208
  export const ComboboxItem = ({ className, ...props }: ComboboxItemProps) => (
207
209
  <BaseCombobox.Item
208
210
  className={cn(
209
- "flex min-h-8 cursor-default items-center gap-2 rounded-xs px-2.5 py-1.5 text-sm text-fg-primary outline-none select-none",
211
+ popupItem,
210
212
  motion.colors,
211
213
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary data-highlighted:text-fg-primary",
212
214
  className,
@@ -237,14 +239,14 @@ export const ComboboxItemIndicator = ({
237
239
 
238
240
  export const ComboboxEmpty = ({ className, ...props }: ComboboxEmptyProps) => (
239
241
  <BaseCombobox.Empty
240
- className={cn("px-2.5 py-2 text-sm text-fg-tertiary empty:hidden", className)}
242
+ className={cn(cn(popupMessage, "empty:hidden"), className)}
241
243
  {...props}
242
244
  />
243
245
  )
244
246
 
245
247
  export const ComboboxStatus = ({ className, ...props }: ComboboxStatusProps) => (
246
248
  <BaseCombobox.Status
247
- className={cn("px-2.5 py-2 text-sm text-fg-tertiary", className)}
249
+ className={cn(popupMessage, className)}
248
250
  {...props}
249
251
  />
250
252
  )
@@ -258,7 +260,7 @@ export const ComboboxGroupLabel = ({
258
260
  ...props
259
261
  }: ComboboxGroupLabelProps) => (
260
262
  <BaseCombobox.GroupLabel
261
- className={cn("px-2.5 py-1.5 text-xs text-fg-tertiary", className)}
263
+ className={cn(popupLabel, className)}
262
264
  {...props}
263
265
  />
264
266
  )
@@ -290,7 +292,7 @@ export const ComboboxChips = ({ className, ...props }: ComboboxChipsProps) => (
290
292
  export const ComboboxChip = ({ className, ...props }: ComboboxChipProps) => (
291
293
  <BaseCombobox.Chip
292
294
  className={cn(
293
- "inline-flex h-6 items-center gap-1 rounded-xs bg-background-tertiary px-1.5 text-xs text-fg-primary",
295
+ "inline-flex h-6 items-center gap-1 rounded-xs bg-background-tertiary px-1.5 text-xs-strong text-fg-primary",
294
296
  className,
295
297
  )}
296
298
  {...props}
@@ -304,13 +306,13 @@ export const ComboboxChipRemove = ({
304
306
  }: ComboboxChipRemoveProps) => (
305
307
  <BaseCombobox.ChipRemove
306
308
  className={cn(
307
- "inline-flex size-3.5 cursor-pointer items-center justify-center text-fg-tertiary outline-none hover:text-fg-primary focus-visible:outline-none",
309
+ "inline-flex size-3.5 cursor-pointer items-center justify-center text-fg-primary outline-none focus-visible:outline-none",
308
310
  className,
309
311
  )}
310
312
  {...props}
311
313
  >
312
314
  {children ?? (
313
- <IconCrossSmall size={12} className="size-3" aria-hidden />
315
+ <IconCrossSmall size={14} className="size-3.5" aria-hidden />
314
316
  )}
315
317
  </BaseCombobox.ChipRemove>
316
318
  )
@@ -5,6 +5,7 @@ import type { ComponentProps } from "react"
5
5
  import { IconCheckmark1, IconChevronRightSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
7
  import { motion } from "./lib/motion"
8
+ import { popupInset, popupItem, popupLabel, popupSurface } from "./lib/popup"
8
9
 
9
10
  export type ContextMenuProps = ComponentProps<typeof BaseContextMenu.Root>
10
11
  export type ContextMenuTriggerProps = ComponentProps<
@@ -53,7 +54,7 @@ export type ContextMenuSubmenuTriggerProps = ComponentProps<
53
54
  >
54
55
 
55
56
  const itemClassName = cn(
56
- "flex min-h-8 cursor-default items-center gap-2 rounded-xs px-2.5 py-1.5 text-sm text-fg-primary outline-none select-none",
57
+ popupItem,
57
58
  motion.colors,
58
59
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary",
59
60
  )
@@ -100,7 +101,9 @@ export const ContextMenuPopup = ({
100
101
  }: ContextMenuPopupProps) => (
101
102
  <BaseContextMenu.Popup
102
103
  className={cn(
103
- "z-50 min-w-40 overflow-hidden rounded-md border border-border-primary bg-surface p-1 shadow-md outline-none",
104
+ "z-50 min-w-40 overflow-hidden",
105
+ popupSurface,
106
+ popupInset,
104
107
  motion.popupAnchor,
105
108
  className,
106
109
  )}
@@ -165,7 +168,7 @@ export const ContextMenuGroupLabel = ({
165
168
  ...props
166
169
  }: ContextMenuGroupLabelProps) => (
167
170
  <BaseContextMenu.GroupLabel
168
- className={cn("px-2.5 py-1.5 text-xs text-fg-tertiary", className)}
171
+ className={cn(popupLabel, className)}
169
172
  {...props}
170
173
  />
171
174
  )
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shared surface classes for anchored popups — autocomplete, combobox, select,
3
+ * menu, context menu, and anything else that floats a panel next to a trigger.
4
+ *
5
+ * These live in one place because they have to agree. A popup that picks its
6
+ * own radius or hairline reads as a different component to the one beside it,
7
+ * and the drift is invisible until two of them sit on the same page.
8
+ *
9
+ * The radii are related, not independent. `item` is nested inside `inset`, so
10
+ * it follows the nested-radius rule:
11
+ *
12
+ * inner = outer − padding 6px = 12px − 6px
13
+ *
14
+ * Change `surface`'s radius or `inset`'s padding and `item` has to move with
15
+ * it, or the corners stop being concentric. See `.cursor/rules/nested-radius`.
16
+ */
17
+
18
+ /** Radius, hairline and elevation. Add `overflow-hidden` where items can reach
19
+ * the corners; leave it off panels that nest their own popups.
20
+ *
21
+ * No `border` here on purpose. Every shadow token composes
22
+ * `--elevation-hairline`, so `shadow-lg` already draws the edge — and that
23
+ * hairline flips colour with the theme (black on light, white on dark) and
24
+ * thins to 0.5px on hi-dpi displays. A border on top stacks a second edge
25
+ * just outside the first, which reads as one muddy line darker than either,
26
+ * and none of that tracking comes with it. Leaving it off also keeps the
27
+ * edge out of the box model, so `popupInset` is the whole inset. */
28
+ export const popupSurface = "rounded-xl bg-surface shadow-lg outline-none"
29
+
30
+ /** Padding around a list of items. Collapses when there is nothing to pad, so
31
+ * a sibling empty state is not pushed off-centre by a list of zero items.
32
+ *
33
+ * 6px is the whole inset — `popupSurface` carries no border to add to it —
34
+ * which is what keeps 12 − 6 = 6 landing exactly on `popupItem`'s radius. */
35
+ export const popupInset = "p-1.5 empty:p-0"
36
+
37
+ /** One selectable row. */
38
+ export const popupItem =
39
+ "flex min-h-9 cursor-default items-center gap-2 rounded-sm px-3 py-2 text-sm text-fg-primary outline-none select-none"
40
+
41
+ /** A group heading. Shares the item's left edge. */
42
+ export const popupLabel = "px-3 py-1.5 text-xs text-fg-tertiary"
43
+
44
+ /** Empty and status text. Shares the item's left edge. */
45
+ export const popupMessage = "px-3 py-2 text-sm text-fg-tertiary"
package/src/menu.tsx CHANGED
@@ -5,6 +5,7 @@ import type { ComponentProps } from "react"
5
5
  import { IconCheckmark1, IconChevronRightSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
7
  import { motion } from "./lib/motion"
8
+ import { popupInset, popupItem, popupLabel, popupSurface } from "./lib/popup"
8
9
 
9
10
  export type MenuProps = ComponentProps<typeof BaseMenu.Root>
10
11
  export type MenuTriggerProps = ComponentProps<typeof BaseMenu.Trigger>
@@ -34,7 +35,7 @@ export type MenuSubmenuTriggerProps = ComponentProps<
34
35
  export type MenuViewportProps = ComponentProps<typeof BaseMenu.Viewport>
35
36
 
36
37
  const menuItemClassName = cn(
37
- "flex h-8 cursor-default items-center gap-2 rounded-sm px-2.5 py-1.5 text-sm text-fg-primary outline-none select-none",
38
+ popupItem,
38
39
  motion.colors,
39
40
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary",
40
41
  )
@@ -73,7 +74,9 @@ export const MenuPositioner = ({
73
74
  export const MenuPopup = ({ className, ...props }: MenuPopupProps) => (
74
75
  <BaseMenu.Popup
75
76
  className={cn(
76
- "z-50 min-w-40 overflow-hidden rounded-xl border border-[var(--border-faint)] bg-surface p-1.5 shadow-lg outline-none",
77
+ "z-50 min-w-40 overflow-hidden",
78
+ popupSurface,
79
+ popupInset,
77
80
  motion.popupAnchor,
78
81
  className,
79
82
  )}
@@ -123,7 +126,7 @@ export const MenuGroupLabel = ({
123
126
  ...props
124
127
  }: MenuGroupLabelProps) => (
125
128
  <BaseMenu.GroupLabel
126
- className={cn("px-2.5 py-1.5 text-xs text-fg-tertiary", className)}
129
+ className={cn(popupLabel, className)}
127
130
  {...props}
128
131
  />
129
132
  )
@@ -5,6 +5,7 @@ import type { ComponentProps } from "react"
5
5
  import { IconChevronDownSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
7
  import { motion } from "./lib/motion"
8
+ import { popupSurface } from "./lib/popup"
8
9
 
9
10
  export type NavigationMenuProps = ComponentProps<typeof BaseNavigationMenu.Root>
10
11
  export type NavigationMenuListProps = ComponentProps<
@@ -177,7 +178,8 @@ export const NavigationMenuPopup = ({
177
178
  }: NavigationMenuPopupProps) => (
178
179
  <BaseNavigationMenu.Popup
179
180
  className={cn(
180
- "relative z-50 w-[var(--popup-width)] overflow-hidden rounded-xl border border-border-primary bg-surface shadow-md outline-none",
181
+ "relative z-50 w-[var(--popup-width)] overflow-hidden",
182
+ popupSurface,
181
183
  "h-[var(--popup-height)] transition-[width,height,opacity] duration-[var(--duration-md)] ease-enter motion-reduce:transition-none",
182
184
  "data-starting-style:opacity-0 data-ending-style:opacity-0",
183
185
  className,
package/src/popover.tsx CHANGED
@@ -4,6 +4,7 @@ import { Popover as BasePopover } from "@base-ui/react/popover"
4
4
  import type { ComponentProps } from "react"
5
5
  import { cn } from "./lib/cn"
6
6
  import { motion } from "./lib/motion"
7
+ import { popupSurface } from "./lib/popup"
7
8
 
8
9
  export type PopoverProps = ComponentProps<typeof BasePopover.Root>
9
10
  export type PopoverTriggerProps = ComponentProps<typeof BasePopover.Trigger>
@@ -47,7 +48,8 @@ export const PopoverPositioner = ({
47
48
  export const PopoverPopup = ({ className, ...props }: PopoverPopupProps) => (
48
49
  <BasePopover.Popup
49
50
  className={cn(
50
- "z-50 flex w-72 flex-col gap-1 rounded-xl border border-border-primary bg-surface p-4 shadow-md outline-none",
51
+ "z-50 flex w-72 flex-col gap-1 p-4",
52
+ popupSurface,
51
53
  motion.popupAnchor,
52
54
  className,
53
55
  )}
@@ -4,6 +4,7 @@ import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card"
4
4
  import type { ComponentProps } from "react"
5
5
  import { cn } from "./lib/cn"
6
6
  import { motion } from "./lib/motion"
7
+ import { popupSurface } from "./lib/popup"
7
8
 
8
9
  export type PreviewCardProps = ComponentProps<typeof BasePreviewCard.Root>
9
10
  export type PreviewCardTriggerProps = ComponentProps<
@@ -67,7 +68,8 @@ export const PreviewCardPopup = ({
67
68
  }: PreviewCardPopupProps) => (
68
69
  <BasePreviewCard.Popup
69
70
  className={cn(
70
- "z-50 w-72 overflow-hidden rounded-xl border border-border-primary bg-surface p-4 shadow-md outline-none",
71
+ "z-50 w-72 overflow-hidden p-4",
72
+ popupSurface,
71
73
  motion.popupAnchor,
72
74
  className,
73
75
  )}
package/src/select.tsx CHANGED
@@ -5,6 +5,7 @@ import type { ComponentProps } from "react"
5
5
  import { IconChevronDownSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
7
  import { motion } from "./lib/motion"
8
+ import { popupInset, popupItem, popupLabel, popupSurface } from "./lib/popup"
8
9
 
9
10
  export type SelectProps = ComponentProps<typeof BaseSelect.Root>
10
11
  export type SelectTriggerProps = ComponentProps<typeof BaseSelect.Trigger>
@@ -71,7 +72,7 @@ export const SelectPortal = (props: SelectPortalProps) => (
71
72
 
72
73
  export const SelectPositioner = ({
73
74
  alignItemWithTrigger = false,
74
- sideOffset = 4,
75
+ sideOffset = 6,
75
76
  className,
76
77
  ...props
77
78
  }: SelectPositionerProps) => (
@@ -86,7 +87,8 @@ export const SelectPositioner = ({
86
87
  export const SelectPopup = ({ className, ...props }: SelectPopupProps) => (
87
88
  <BaseSelect.Popup
88
89
  className={cn(
89
- "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden rounded-md border border-border-primary bg-surface shadow-md outline-none",
90
+ "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden",
91
+ popupSurface,
90
92
  motion.popupAnchor,
91
93
  className,
92
94
  )}
@@ -96,7 +98,7 @@ export const SelectPopup = ({ className, ...props }: SelectPopupProps) => (
96
98
 
97
99
  export const SelectList = ({ className, ...props }: SelectListProps) => (
98
100
  <BaseSelect.List
99
- className={cn("max-h-[inherit] overflow-y-auto p-1 outline-none", className)}
101
+ className={cn("max-h-[inherit] overflow-y-auto outline-none", popupInset, className)}
100
102
  {...props}
101
103
  />
102
104
  )
@@ -104,7 +106,7 @@ export const SelectList = ({ className, ...props }: SelectListProps) => (
104
106
  export const SelectItem = ({ className, ...props }: SelectItemProps) => (
105
107
  <BaseSelect.Item
106
108
  className={cn(
107
- "flex min-h-8 cursor-default items-center gap-2 rounded-xs px-2.5 py-1.5 text-sm text-fg-primary outline-none select-none",
109
+ popupItem,
108
110
  motion.colors,
109
111
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary data-selected:text-fg-primary",
110
112
  className,
@@ -145,7 +147,7 @@ export const SelectGroupLabel = ({
145
147
  ...props
146
148
  }: SelectGroupLabelProps) => (
147
149
  <BaseSelect.GroupLabel
148
- className={cn("px-2.5 py-1.5 text-xs text-fg-tertiary", className)}
150
+ className={cn(popupLabel, className)}
149
151
  {...props}
150
152
  />
151
153
  )