@boyernick/standard-ui-react 0.1.0 → 0.1.1-canary.10
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 +9 -3
- package/src/accordion.tsx +1 -1
- package/src/alert-dialog.tsx +1 -1
- package/src/autocomplete.tsx +12 -11
- package/src/badge.tsx +73 -9
- package/src/breadcrumb.tsx +39 -21
- package/src/button.tsx +2 -2
- package/src/calendar.tsx +22 -11
- package/src/card.tsx +57 -14
- package/src/carousel.tsx +191 -25
- package/src/checkbox.tsx +35 -13
- package/src/collapsible.tsx +4 -2
- package/src/combobox.tsx +13 -12
- package/src/command.tsx +24 -12
- package/src/context-menu.tsx +6 -3
- package/src/date-picker.tsx +174 -0
- package/src/dialog.tsx +1 -1
- package/src/drawer.tsx +1 -1
- package/src/empty.tsx +4 -4
- package/src/field.tsx +1 -1
- package/src/icons.tsx +27 -0
- package/src/illustrations.tsx +219 -141
- package/src/index.ts +29 -0
- package/src/input.tsx +1 -1
- package/src/kbd.tsx +43 -0
- package/src/lib/cn.ts +43 -1
- package/src/lib/motion.ts +128 -18
- package/src/lib/popup.ts +45 -0
- package/src/markdown-editor.tsx +1 -1
- package/src/menu.tsx +7 -4
- package/src/meter.tsx +1 -1
- package/src/navigation-menu.tsx +5 -3
- package/src/popover.tsx +3 -1
- package/src/preview-card.tsx +3 -1
- package/src/progress.tsx +1 -1
- package/src/scroll-area.tsx +1 -1
- package/src/select.tsx +8 -6
- package/src/slider.tsx +1 -1
- package/src/switch.tsx +2 -2
- package/src/text-animate.tsx +2 -4
- package/src/textarea.tsx +1 -1
- package/src/toast.tsx +2 -2
package/src/lib/motion.ts
CHANGED
|
@@ -1,23 +1,133 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared motion classes for StandardUI.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Every value resolves to a token in `@boyernick/standard-ui-tokens` — the
|
|
5
|
+
* `--ease-*` ladder and the `--duration-*` scale. Nothing here hardcodes a
|
|
6
|
+
* curve or a millisecond count.
|
|
7
|
+
*
|
|
8
|
+
* The easing ladder is ordered by *onset* — how quickly a transition commits:
|
|
9
|
+
*
|
|
10
|
+
* passive ambient, system-initiated (slowest onset)
|
|
11
|
+
* enter surfaces arriving
|
|
12
|
+
* move things travelling across a surface
|
|
13
|
+
* snap direct response to a pointer (sharpest onset)
|
|
14
|
+
*
|
|
15
|
+
* Two kinds of token live here, and they behave differently:
|
|
16
|
+
*
|
|
17
|
+
* Mount tokens — backdrop, popupCenter, popupAnchor, accordionPanel,
|
|
18
|
+
* checkIndicator.
|
|
19
|
+
* These carry separate enter and exit behaviour. Base UI flags the exit
|
|
20
|
+
* with `data-ending-style`, and the `data-ending-style:` variants below
|
|
21
|
+
* re-declare duration and easing for that direction. Exits always run at
|
|
22
|
+
* `--duration-exit`, so dismissing anything costs the same no matter how
|
|
23
|
+
* long the entrance took.
|
|
24
|
+
*
|
|
25
|
+
* State tokens — tabsIndicator, colors, transform, all.
|
|
26
|
+
* These animate between states on a mounted element, so there is no enter
|
|
27
|
+
* or exit to distinguish; they take a single curve.
|
|
28
|
+
*
|
|
29
|
+
* Fades are linear on purpose. Perceived brightness is already non-linear, so
|
|
30
|
+
* easing an opacity change makes it look like it sticks at the end.
|
|
5
31
|
*/
|
|
32
|
+
|
|
33
|
+
const DURATION = {
|
|
34
|
+
xs: "duration-[var(--duration-xs)]",
|
|
35
|
+
sm: "duration-[var(--duration-sm)]",
|
|
36
|
+
md: "duration-[var(--duration-md)]",
|
|
37
|
+
lg: "duration-[var(--duration-lg)]",
|
|
38
|
+
} as const
|
|
39
|
+
|
|
40
|
+
/** Every exit runs at the same length — see `--duration-exit`. */
|
|
41
|
+
const EXIT_DURATION = "data-ending-style:duration-[var(--duration-exit)]"
|
|
42
|
+
|
|
43
|
+
const REDUCED = "motion-reduce:transition-none"
|
|
44
|
+
|
|
6
45
|
export const motion = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"transition-
|
|
22
|
-
|
|
46
|
+
/** Overlay scrims. Linear both ways — see the note on fades above. */
|
|
47
|
+
backdrop: [
|
|
48
|
+
"transition-opacity",
|
|
49
|
+
DURATION.sm,
|
|
50
|
+
"ease-linear",
|
|
51
|
+
REDUCED,
|
|
52
|
+
"data-starting-style:opacity-0",
|
|
53
|
+
"data-ending-style:opacity-0",
|
|
54
|
+
EXIT_DURATION,
|
|
55
|
+
"data-ending-style:ease-linear",
|
|
56
|
+
].join(" "),
|
|
57
|
+
|
|
58
|
+
/** Centred dialog / alert — animates `scale`, so translate centring holds. */
|
|
59
|
+
popupCenter: [
|
|
60
|
+
"transition-[scale,opacity]",
|
|
61
|
+
DURATION.md,
|
|
62
|
+
"ease-enter",
|
|
63
|
+
REDUCED,
|
|
64
|
+
"data-starting-style:scale-[0.96] data-starting-style:opacity-0",
|
|
65
|
+
"data-ending-style:scale-[0.96] data-ending-style:opacity-0",
|
|
66
|
+
EXIT_DURATION,
|
|
67
|
+
"data-ending-style:ease-move",
|
|
68
|
+
].join(" "),
|
|
69
|
+
|
|
70
|
+
/** Select, tooltip, menus — transform-origin comes from Base UI. */
|
|
71
|
+
popupAnchor: [
|
|
72
|
+
"origin-[var(--transform-origin)]",
|
|
73
|
+
"transition-[transform,scale,opacity]",
|
|
74
|
+
DURATION.sm,
|
|
75
|
+
"ease-enter",
|
|
76
|
+
REDUCED,
|
|
77
|
+
"data-starting-style:scale-[0.96] data-starting-style:opacity-0",
|
|
78
|
+
"data-ending-style:scale-[0.96] data-ending-style:opacity-0",
|
|
79
|
+
EXIT_DURATION,
|
|
80
|
+
"data-ending-style:ease-move",
|
|
81
|
+
].join(" "),
|
|
82
|
+
|
|
83
|
+
/** Height expand and collapse. */
|
|
84
|
+
accordionPanel: [
|
|
85
|
+
"transition-[height]",
|
|
86
|
+
DURATION.md,
|
|
87
|
+
"ease-move",
|
|
88
|
+
REDUCED,
|
|
89
|
+
"data-starting-style:h-0",
|
|
90
|
+
"data-ending-style:h-0",
|
|
91
|
+
EXIT_DURATION,
|
|
92
|
+
"data-ending-style:ease-move",
|
|
93
|
+
].join(" "),
|
|
94
|
+
|
|
95
|
+
/** Check and minus glyphs mounting inside a small control. Snaps, because
|
|
96
|
+
* it answers a click directly, and scales from the centre so the mark
|
|
97
|
+
* reads as landing in the box rather than fading onto it. */
|
|
98
|
+
checkIndicator: [
|
|
99
|
+
"transition-[scale,opacity]",
|
|
100
|
+
DURATION.sm,
|
|
101
|
+
"ease-snap",
|
|
102
|
+
REDUCED,
|
|
103
|
+
"data-starting-style:scale-50 data-starting-style:opacity-0",
|
|
104
|
+
"data-ending-style:scale-50 data-ending-style:opacity-0",
|
|
105
|
+
EXIT_DURATION,
|
|
106
|
+
"data-ending-style:ease-move",
|
|
107
|
+
].join(" "),
|
|
108
|
+
|
|
109
|
+
/** Sliding tab underline or pill — follows a pointer, so it snaps. */
|
|
110
|
+
tabsIndicator: [
|
|
111
|
+
"transition-[translate,width]",
|
|
112
|
+
DURATION.md,
|
|
113
|
+
"ease-snap",
|
|
114
|
+
REDUCED,
|
|
115
|
+
].join(" "),
|
|
116
|
+
|
|
117
|
+
/** Hover and pressed colour changes. */
|
|
118
|
+
colors: ["transition-colors", DURATION.sm, "ease-enter", REDUCED].join(" "),
|
|
119
|
+
|
|
120
|
+
/** Icon rotates and simple transforms. */
|
|
121
|
+
transform: [
|
|
122
|
+
"transition-transform",
|
|
123
|
+
DURATION.sm,
|
|
124
|
+
"ease-move",
|
|
125
|
+
REDUCED,
|
|
126
|
+
].join(" "),
|
|
127
|
+
|
|
128
|
+
/** Every property. Reach for a narrower token first. */
|
|
129
|
+
all: ["transition-all", DURATION.sm, "ease-enter", REDUCED].join(" "),
|
|
130
|
+
|
|
131
|
+
/** Long, ambient movement — progress fills, meters, background shifts. */
|
|
132
|
+
passive: ["transition-all", DURATION.lg, "ease-passive", REDUCED].join(" "),
|
|
23
133
|
} as const
|
package/src/lib/popup.ts
ADDED
|
@@ -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/markdown-editor.tsx
CHANGED
|
@@ -108,7 +108,7 @@ type Format = {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
const formats: Format[] = [
|
|
111
|
-
{ label: "Bold", marker: "**", className: "font-
|
|
111
|
+
{ label: "Bold", marker: "**", className: "font-medium" },
|
|
112
112
|
{ label: "Italic", marker: "*", className: "italic" },
|
|
113
113
|
{ label: "Code", marker: "`", className: "font-mono" },
|
|
114
114
|
]
|
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
|
-
|
|
38
|
+
popupItem,
|
|
38
39
|
motion.colors,
|
|
39
40
|
"data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary",
|
|
40
41
|
)
|
|
@@ -59,7 +60,7 @@ export const MenuBackdrop = ({ className, ...props }: MenuBackdropProps) => (
|
|
|
59
60
|
)
|
|
60
61
|
|
|
61
62
|
export const MenuPositioner = ({
|
|
62
|
-
sideOffset =
|
|
63
|
+
sideOffset = 8,
|
|
63
64
|
className,
|
|
64
65
|
...props
|
|
65
66
|
}: MenuPositionerProps) => (
|
|
@@ -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
|
|
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(
|
|
129
|
+
className={cn(popupLabel, className)}
|
|
127
130
|
{...props}
|
|
128
131
|
/>
|
|
129
132
|
)
|
package/src/meter.tsx
CHANGED
|
@@ -53,7 +53,7 @@ export const MeterIndicator = ({
|
|
|
53
53
|
}: MeterIndicatorProps) => (
|
|
54
54
|
<BaseMeter.Indicator
|
|
55
55
|
className={cn(
|
|
56
|
-
"h-full rounded-full bg-brand-primary transition-[width] duration-
|
|
56
|
+
"h-full rounded-full bg-brand-primary transition-[width] duration-[var(--duration-lg)] ease-enter motion-reduce:transition-none",
|
|
57
57
|
className,
|
|
58
58
|
)}
|
|
59
59
|
{...props}
|
package/src/navigation-menu.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 { popupSurface } from "./lib/popup"
|
|
8
9
|
|
|
9
10
|
export type NavigationMenuProps = ComponentProps<typeof BaseNavigationMenu.Root>
|
|
10
11
|
export type NavigationMenuListProps = ComponentProps<
|
|
@@ -101,7 +102,7 @@ export const NavigationMenuIcon = ({
|
|
|
101
102
|
}: NavigationMenuIconProps) => (
|
|
102
103
|
<BaseNavigationMenu.Icon
|
|
103
104
|
className={cn(
|
|
104
|
-
"inline-flex text-fg-tertiary transition-transform duration-
|
|
105
|
+
"inline-flex text-fg-tertiary transition-transform duration-[var(--duration-sm)] ease-enter motion-reduce:transition-none data-popup-open:rotate-180",
|
|
105
106
|
className,
|
|
106
107
|
)}
|
|
107
108
|
{...props}
|
|
@@ -177,8 +178,9 @@ export const NavigationMenuPopup = ({
|
|
|
177
178
|
}: NavigationMenuPopupProps) => (
|
|
178
179
|
<BaseNavigationMenu.Popup
|
|
179
180
|
className={cn(
|
|
180
|
-
"relative z-50 w-[var(--popup-width)] overflow-hidden
|
|
181
|
-
|
|
181
|
+
"relative z-50 w-[var(--popup-width)] overflow-hidden",
|
|
182
|
+
popupSurface,
|
|
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,
|
|
184
186
|
)}
|
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
|
|
51
|
+
"z-50 flex w-72 flex-col gap-1 p-4",
|
|
52
|
+
popupSurface,
|
|
51
53
|
motion.popupAnchor,
|
|
52
54
|
className,
|
|
53
55
|
)}
|
package/src/preview-card.tsx
CHANGED
|
@@ -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
|
|
71
|
+
"z-50 w-72 overflow-hidden p-4",
|
|
72
|
+
popupSurface,
|
|
71
73
|
motion.popupAnchor,
|
|
72
74
|
className,
|
|
73
75
|
)}
|
package/src/progress.tsx
CHANGED
|
@@ -52,7 +52,7 @@ export const ProgressIndicator = ({
|
|
|
52
52
|
}: ProgressIndicatorProps) => (
|
|
53
53
|
<BaseProgress.Indicator
|
|
54
54
|
className={cn(
|
|
55
|
-
"h-full rounded-full bg-brand-primary transition-[width] duration-
|
|
55
|
+
"h-full rounded-full bg-brand-primary transition-[width] duration-[var(--duration-lg)] ease-enter motion-reduce:transition-none",
|
|
56
56
|
className,
|
|
57
57
|
)}
|
|
58
58
|
{...props}
|
package/src/scroll-area.tsx
CHANGED
|
@@ -49,7 +49,7 @@ export const ScrollAreaScrollbar = ({
|
|
|
49
49
|
}: ScrollAreaScrollbarProps) => (
|
|
50
50
|
<BaseScrollArea.Scrollbar
|
|
51
51
|
className={cn(
|
|
52
|
-
"flex touch-none p-0.5 opacity-100 transition-opacity duration-
|
|
52
|
+
"flex touch-none p-0.5 opacity-100 transition-opacity duration-[var(--duration-sm)] ease-enter motion-reduce:transition-none data-[orientation=horizontal]:h-2 data-[orientation=vertical]:w-2",
|
|
53
53
|
className,
|
|
54
54
|
)}
|
|
55
55
|
{...props}
|
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>
|
|
@@ -33,7 +34,7 @@ export const SelectTrigger = ({
|
|
|
33
34
|
className={cn(
|
|
34
35
|
"flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-border-secondary bg-surface px-3 text-sm text-fg-primary inset-shadow-outline-top outline-none",
|
|
35
36
|
motion.all,
|
|
36
|
-
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 aria-invalid:border-destructive aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-destructive/20 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-placeholder:text-fg-quaternary data-popup-open:border-border-secondary
|
|
37
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 aria-invalid:border-destructive aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-destructive/20 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-placeholder:text-fg-quaternary data-popup-open:border-border-secondary",
|
|
37
38
|
className,
|
|
38
39
|
)}
|
|
39
40
|
{...props}
|
|
@@ -71,7 +72,7 @@ export const SelectPortal = (props: SelectPortalProps) => (
|
|
|
71
72
|
|
|
72
73
|
export const SelectPositioner = ({
|
|
73
74
|
alignItemWithTrigger = false,
|
|
74
|
-
sideOffset =
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
150
|
+
className={cn(popupLabel, className)}
|
|
149
151
|
{...props}
|
|
150
152
|
/>
|
|
151
153
|
)
|
package/src/slider.tsx
CHANGED
|
@@ -38,7 +38,7 @@ export const SliderIndicator = ({
|
|
|
38
38
|
}: SliderIndicatorProps) => (
|
|
39
39
|
<BaseSlider.Indicator
|
|
40
40
|
className={cn(
|
|
41
|
-
"absolute h-full rounded-full bg-brand-primary transition-[width] duration-
|
|
41
|
+
"absolute h-full rounded-full bg-brand-primary transition-[width] duration-[var(--duration-sm)] ease-enter motion-reduce:transition-none",
|
|
42
42
|
className,
|
|
43
43
|
)}
|
|
44
44
|
{...props}
|
package/src/switch.tsx
CHANGED
|
@@ -9,11 +9,11 @@ export type SwitchProps = ComponentProps<typeof BaseSwitch.Root>
|
|
|
9
9
|
export const Switch = ({ className, ...props }: SwitchProps) => (
|
|
10
10
|
<BaseSwitch.Root
|
|
11
11
|
className={cn(
|
|
12
|
-
"relative inline-flex h-6 w-10 shrink-0 items-center rounded-full bg-background-quaternary transition-colors duration-
|
|
12
|
+
"relative inline-flex h-6 w-10 shrink-0 items-center rounded-full bg-background-quaternary transition-colors duration-[var(--duration-sm)] ease-enter motion-reduce:transition-none outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 data-checked:bg-brand-primary data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
13
13
|
className,
|
|
14
14
|
)}
|
|
15
15
|
{...props}
|
|
16
16
|
>
|
|
17
|
-
<BaseSwitch.Thumb className="pointer-events-none block size-5 translate-x-0.5 rounded-full bg-surface shadow-sm transition-transform duration-
|
|
17
|
+
<BaseSwitch.Thumb className="pointer-events-none block size-5 translate-x-0.5 rounded-full bg-surface shadow-sm transition-transform duration-[var(--duration-sm)] ease-enter motion-reduce:transition-none data-checked:translate-x-[1.125rem]" />
|
|
18
18
|
</BaseSwitch.Root>
|
|
19
19
|
)
|
package/src/text-animate.tsx
CHANGED
|
@@ -45,7 +45,6 @@ export const TextAnimate = ({
|
|
|
45
45
|
|
|
46
46
|
useEffect(() => {
|
|
47
47
|
let cancelled = false
|
|
48
|
-
let frame = 0
|
|
49
48
|
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
50
49
|
|
|
51
50
|
const start = () => {
|
|
@@ -114,7 +113,6 @@ export const TextAnimate = ({
|
|
|
114
113
|
return () => {
|
|
115
114
|
cancelled = true
|
|
116
115
|
if (timeoutId) clearTimeout(timeoutId)
|
|
117
|
-
cancelAnimationFrame(frame)
|
|
118
116
|
}
|
|
119
117
|
}, [chars, delay, effect, replay, speed, text])
|
|
120
118
|
|
|
@@ -124,9 +122,9 @@ export const TextAnimate = ({
|
|
|
124
122
|
className={cn(
|
|
125
123
|
"inline-block text-fg-primary",
|
|
126
124
|
effect === "fade" &&
|
|
127
|
-
"animate-[text-fade-in_0.
|
|
125
|
+
"animate-[text-fade-in_0.6s_var(--ease-enter)_both] motion-reduce:animate-none",
|
|
128
126
|
effect === "blur" &&
|
|
129
|
-
"animate-[text-blur-in_0.
|
|
127
|
+
"animate-[text-blur-in_0.7s_var(--ease-enter)_both] motion-reduce:animate-none",
|
|
130
128
|
className,
|
|
131
129
|
)}
|
|
132
130
|
style={{ animationDelay: `${delay}ms` }}
|
package/src/textarea.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import type { TextareaHTMLAttributes } from "react"
|
|
|
3
3
|
import { cn } from "./lib/cn"
|
|
4
4
|
|
|
5
5
|
const textareaVariants = cva(
|
|
6
|
-
"flex min-h-20 w-full cursor-text resize-y rounded-md px-3 py-2 text-sm text-fg-primary transition-[color,box-shadow] duration-
|
|
6
|
+
"flex min-h-20 w-full cursor-text resize-y rounded-md px-3 py-2 text-sm text-fg-primary transition-[color,box-shadow] duration-[var(--duration-sm)] ease-enter placeholder:text-fg-quaternary outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 aria-invalid:border-destructive aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-destructive/20 disabled:cursor-not-allowed disabled:opacity-50",
|
|
7
7
|
{
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
package/src/toast.tsx
CHANGED
|
@@ -41,7 +41,7 @@ export const ToastRoot = ({ className, ...props }: ToastRootProps) => (
|
|
|
41
41
|
className={cn(
|
|
42
42
|
"relative z-[calc(1000-var(--toast-index))] mb-2 box-border w-full rounded-xl border border-border-primary bg-surface p-4 shadow-lg outline-none",
|
|
43
43
|
"h-[var(--toast-frontmost-height,var(--toast-height))]",
|
|
44
|
-
"transition-[transform,opacity] duration-
|
|
44
|
+
"transition-[transform,opacity] duration-[var(--duration-md)] ease-enter motion-reduce:transition-none",
|
|
45
45
|
"data-starting-style:opacity-0 data-starting-style:translate-y-2",
|
|
46
46
|
"data-ending-style:opacity-0 data-ending-style:translate-y-2",
|
|
47
47
|
"data-expanded:h-[var(--toast-height)] data-expanded:translate-y-[var(--toast-offset-y)]",
|
|
@@ -56,7 +56,7 @@ export const ToastRoot = ({ className, ...props }: ToastRootProps) => (
|
|
|
56
56
|
export const ToastContent = ({ className, ...props }: ToastContentProps) => (
|
|
57
57
|
<BaseToast.Content
|
|
58
58
|
className={cn(
|
|
59
|
-
"flex flex-col gap-1 overflow-hidden transition-opacity duration-
|
|
59
|
+
"flex flex-col gap-1 overflow-hidden transition-opacity duration-[var(--duration-sm)]",
|
|
60
60
|
"data-behind:pointer-events-none data-behind:opacity-0 data-expanded:data-behind:opacity-100",
|
|
61
61
|
className,
|
|
62
62
|
)}
|