svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:-mx-0.5 [&>svg]:shrink-0",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-item"
data-variant={variant}
{...props}
@@ -120,7 +120,7 @@ export function MenuLinkItem({
className,
)}
closeOnClick={closeOnClick}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-link-item"
data-variant={variant}
{...props}
@@ -239,7 +239,7 @@ export function MenuGroupLabel({
"px-2 py-1.5 font-medium text-muted-foreground text-xs data-inset:ps-9 sm:data-inset:ps-8",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-label"
{...props}
/>
@@ -295,7 +295,7 @@ export function MenuSubTrigger({
"flex min-h-8 items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-popup-open:bg-accent data-inset:ps-8 data-highlighted:text-accent-foreground data-popup-open:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not(:last-child)]:-mx-0.5 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-sub-trigger"
{...props}
>
diff --git a/apps/ui/registry/default/ui/meter.tsx b/apps/ui/registry/default/ui/meter.tsx
index 14a48e34b..51e85581b 100644
--- a/apps/ui/registry/default/ui/meter.tsx
+++ b/apps/ui/registry/default/ui/meter.tsx
@@ -12,6 +12,7 @@ export function Meter({
return (
{children ? (
diff --git a/apps/ui/registry/default/ui/otp-field.tsx b/apps/ui/registry/default/ui/otp-field.tsx
index d32010799..78983bf1a 100644
--- a/apps/ui/registry/default/ui/otp-field.tsx
+++ b/apps/ui/registry/default/ui/otp-field.tsx
@@ -15,7 +15,7 @@ export function OTPField({
return (
{children}
@@ -53,6 +56,9 @@ export function PreviewCardPopup({
);
}
+export const PreviewCardCreateHandle: typeof PreviewCardPrimitive.createHandle =
+ PreviewCardPrimitive.createHandle;
+
export {
PreviewCardPrimitive,
PreviewCard as HoverCard,
diff --git a/apps/ui/registry/default/ui/radio-group.tsx b/apps/ui/registry/default/ui/radio-group.tsx
index 0f0343ff2..cb81cb844 100644
--- a/apps/ui/registry/default/ui/radio-group.tsx
+++ b/apps/ui/registry/default/ui/radio-group.tsx
@@ -11,7 +11,7 @@ export function RadioGroup({
}: RadioGroupPrimitive.Props): React.ReactElement {
return (
diff --git a/apps/ui/registry/default/ui/scroll-area.tsx b/apps/ui/registry/default/ui/scroll-area.tsx
index 276a7e5b7..730da4783 100644
--- a/apps/ui/registry/default/ui/scroll-area.tsx
+++ b/apps/ui/registry/default/ui/scroll-area.tsx
@@ -23,6 +23,7 @@ export function ScrollArea({
return (
boolean)) => {
+ (value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === "function" ? value(open) : value;
if (setOpenProp) {
setOpenProp(openState);
@@ -106,12 +106,7 @@ export function SidebarProvider({
}
// This sets the cookie to keep the sidebar state.
- await cookieStore.set({
- expires: Date.now() + SIDEBAR_COOKIE_MAX_AGE * 1000,
- name: SIDEBAR_COOKIE_NAME,
- path: "/",
- value: String(openState),
- });
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
[setOpenProp, open],
);
diff --git a/apps/ui/registry/default/ui/toggle-group.tsx b/apps/ui/registry/default/ui/toggle-group.tsx
index c2f76f1e7..1472f0847 100644
--- a/apps/ui/registry/default/ui/toggle-group.tsx
+++ b/apps/ui/registry/default/ui/toggle-group.tsx
@@ -20,13 +20,14 @@ export const ToggleGroupContext: React.Context<
export function ToggleGroup({
className,
- variant = "default",
- size = "default",
+ variant,
+ size,
orientation = "horizontal",
children,
...props
}: ToggleGroupPrimitive.Props &
VariantProps): React.ReactElement {
+ const resolvedGroupVariant = variant ?? "default";
return (
@@ -64,8 +65,8 @@ export function ToggleGroupItem({
VariantProps): React.ReactElement {
const context = React.useContext(ToggleGroupContext);
- const resolvedVariant = context.variant || variant;
- const resolvedSize = context.size || size;
+ const resolvedVariant = variant ?? context.variant ?? "default";
+ const resolvedSize = size ?? context.size ?? "default";
return (
- {children}
+ {children}
);
}
diff --git a/packages/ui/src/components/alert-dialog.tsx b/packages/ui/src/components/alert-dialog.tsx
index 4a8040aff..00db110db 100644
--- a/packages/ui/src/components/alert-dialog.tsx
+++ b/packages/ui/src/components/alert-dialog.tsx
@@ -1,6 +1,8 @@
"use client";
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
+import { mergeProps } from "@base-ui/react/merge-props";
+import { useRender } from "@base-ui/react/use-render";
import { cn } from "@coss/ui/lib/utils";
import type React from "react";
@@ -73,7 +75,7 @@ export function AlertDialogPopup({
>
): React.ReactElement {
- return (
-
- );
+}: useRender.ComponentProps<"div">): React.ReactElement {
+ const defaultProps = {
+ className: cn(
+ "flex flex-col gap-2 p-6 text-center max-sm:pb-4 sm:text-left",
+ className,
+ ),
+ "data-slot": "alert-dialog-header",
+ };
+
+ return useRender({
+ defaultTagName: "div",
+ props: mergeProps<"div">(defaultProps, props),
+ render,
+ });
}
export function AlertDialogFooter({
className,
variant = "default",
+ render,
...props
}: React.ComponentProps<"div"> & {
variant?: "default" | "bare";
-}): React.ReactElement {
- return (
-
- );
+} & useRender.ComponentProps<"div">): React.ReactElement {
+ const defaultProps = {
+ className: cn(
+ "flex flex-col-reverse gap-2 px-6 sm:flex-row sm:justify-end sm:rounded-b-[calc(var(--radius-2xl)-1px)]",
+ variant === "default" && "border-t bg-muted/72 py-4",
+ variant === "bare" && "pb-6",
+ className,
+ ),
+ "data-slot": "alert-dialog-footer",
+ };
+
+ return useRender({
+ defaultTagName: "div",
+ props: mergeProps<"div">(defaultProps, props),
+ render,
+ });
}
export function AlertDialogTitle({
diff --git a/packages/ui/src/components/autocomplete.tsx b/packages/ui/src/components/autocomplete.tsx
index ca7a95469..0ebe043e4 100644
--- a/packages/ui/src/components/autocomplete.tsx
+++ b/packages/ui/src/components/autocomplete.tsx
@@ -114,20 +114,16 @@ export function AutocompletePopup({
side={side}
sideOffset={sideOffset}
>
-
-
- {children}
-
-
+ {children}
+
);
diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx
index b44298d53..8ca383d8c 100644
--- a/packages/ui/src/components/button.tsx
+++ b/packages/ui/src/components/button.tsx
@@ -6,9 +6,10 @@ import { Spinner } from "@coss/ui/components/spinner";
import { cn } from "@coss/ui/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
import type * as React from "react";
+import { isValidElement } from "react";
export const buttonVariants = cva(
- "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-loading:select-none data-loading:text-transparent sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0",
+ "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 aria-disabled:pointer-events-none aria-disabled:opacity-64 data-loading:pointer-events-none data-loading:select-none data-loading:text-transparent sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0",
{
defaultVariants: {
size: "default",
@@ -65,8 +66,10 @@ export function Button({
...props
}: ButtonProps): React.ReactElement {
const isDisabled: boolean = Boolean(loading || disabledProp);
+ const rendersNativeButton: boolean =
+ !render || (isValidElement(render) && render.type === "button");
const typeValue: React.ButtonHTMLAttributes["type"] =
- render ? undefined : "button";
+ rendersNativeButton ? "button" : undefined;
const defaultProps = {
children: (
@@ -81,10 +84,10 @@ export function Button({
>
),
className: cn(buttonVariants({ className, size, variant })),
- "aria-disabled": loading || undefined,
+ "aria-disabled": isDisabled && !rendersNativeButton ? true : undefined,
"data-loading": loading ? "" : undefined,
"data-slot": "button",
- disabled: isDisabled,
+ disabled: rendersNativeButton && isDisabled ? true : undefined,
type: typeValue,
};
diff --git a/packages/ui/src/components/checkbox-group.tsx b/packages/ui/src/components/checkbox-group.tsx
index fe027427d..ebcdeadd8 100644
--- a/packages/ui/src/components/checkbox-group.tsx
+++ b/packages/ui/src/components/checkbox-group.tsx
@@ -11,6 +11,7 @@ export function CheckboxGroup({
return (
);
diff --git a/packages/ui/src/components/combobox.tsx b/packages/ui/src/components/combobox.tsx
index e374f6686..3f36148c1 100644
--- a/packages/ui/src/components/combobox.tsx
+++ b/packages/ui/src/components/combobox.tsx
@@ -109,11 +109,12 @@ export function ComboboxInput({
/>
{showTrigger && (
@@ -122,11 +123,12 @@ export function ComboboxInput({
)}
{showClear && (
diff --git a/packages/ui/src/components/command.tsx b/packages/ui/src/components/command.tsx
index 564c90d72..0cd455bc7 100644
--- a/packages/ui/src/components/command.tsx
+++ b/packages/ui/src/components/command.tsx
@@ -82,7 +82,7 @@ export function CommandDialogPopup({
svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:-mx-0.5 [&>svg]:shrink-0",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="context-menu-item"
data-variant={variant}
{...props}
@@ -121,7 +121,7 @@ export function ContextMenuLinkItem({
className,
)}
closeOnClick={closeOnClick}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="context-menu-link-item"
data-variant={variant}
{...props}
@@ -245,7 +245,7 @@ export function ContextMenuGroupLabel({
"px-2 py-1.5 font-medium text-muted-foreground text-xs data-inset:ps-9 sm:data-inset:ps-8",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="context-menu-label"
{...props}
/>
@@ -303,7 +303,7 @@ export function ContextMenuSubTrigger({
"flex min-h-8 items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-popup-open:bg-accent data-inset:ps-8 data-highlighted:text-accent-foreground data-popup-open:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not(:last-child)]:-mx-0.5 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="context-menu-sub-trigger"
{...props}
>
diff --git a/packages/ui/src/components/drawer.tsx b/packages/ui/src/components/drawer.tsx
index ba453ae3e..966ea845a 100644
--- a/packages/ui/src/components/drawer.tsx
+++ b/packages/ui/src/components/drawer.tsx
@@ -170,7 +170,7 @@ export function DrawerPopup({
position === "left" &&
"data-starting-style:transform-[translateX(calc(-100%-var(--inset)))] data-ending-style:transform-[translateX(calc(-100%-var(--inset)))] transform-[translateX(var(--drawer-swipe-movement-x))] w-[calc(100%-(--spacing(12)))] max-w-md border-e after:inset-y-0 after:end-full after:w-(--bleed) has-data-[slot=drawer-bar]:pe-2",
position === "right" &&
- "transform-[translateX(var(--drawer-swipe-movement-x))] data-ending-style:transform-[translateX(calc(100%+var(--inset)))] data-starting-style:transform-[translateX(calc(100%+var(--inset)))] col-start-2 w-[calc(100%-(--spacing(12)))] max-w-md border-s after:inset-y-0 after:start-full after:w-(--bleed) has-data-[slot=drawer-bar]:ps-2",
+ "transform-[translateX(var(--drawer-swipe-movement-x))] data-ending-style:transform-[translateX(calc(100%+var(--inset)))] data-starting-style:transform-[translateX(calc(100%+var(--inset)))] w-[calc(100%-(--spacing(12)))] max-w-md border-s after:inset-y-0 after:start-full after:w-(--bleed) has-data-[slot=drawer-bar]:ps-2",
variant !== "straight" &&
cn(
position === "bottom" && "rounded-t-2xl",
diff --git a/packages/ui/src/components/empty.tsx b/packages/ui/src/components/empty.tsx
index 56d894832..48f03953d 100644
--- a/packages/ui/src/components/empty.tsx
+++ b/packages/ui/src/components/empty.tsx
@@ -80,10 +80,7 @@ export function EmptyMedia({
/>
>
)}
-
+
);
}
diff --git a/packages/ui/src/components/field.tsx b/packages/ui/src/components/field.tsx
index c3bcba36b..f4fe1944c 100644
--- a/packages/ui/src/components/field.tsx
+++ b/packages/ui/src/components/field.tsx
@@ -72,9 +72,16 @@ export function FieldError({
);
}
-export const FieldControl: typeof FieldPrimitive.Control =
- FieldPrimitive.Control;
-export const FieldValidity: typeof FieldPrimitive.Validity =
- FieldPrimitive.Validity;
+export function FieldControl(
+ props: FieldPrimitive.Control.Props,
+): React.ReactElement {
+ return svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:-mx-0.5 [&>svg]:shrink-0",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-item"
data-variant={variant}
{...props}
@@ -120,7 +120,7 @@ export function MenuLinkItem({
className,
)}
closeOnClick={closeOnClick}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-link-item"
data-variant={variant}
{...props}
@@ -239,7 +239,7 @@ export function MenuGroupLabel({
"px-2 py-1.5 font-medium text-muted-foreground text-xs data-inset:ps-9 sm:data-inset:ps-8",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-label"
{...props}
/>
@@ -295,7 +295,7 @@ export function MenuSubTrigger({
"flex min-h-8 items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-popup-open:bg-accent data-inset:ps-8 data-highlighted:text-accent-foreground data-popup-open:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not(:last-child)]:-mx-0.5 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
className,
)}
- data-inset={inset}
+ data-inset={inset || undefined}
data-slot="menu-sub-trigger"
{...props}
>
diff --git a/packages/ui/src/components/meter.tsx b/packages/ui/src/components/meter.tsx
index e3454c9cb..a4de3b00e 100644
--- a/packages/ui/src/components/meter.tsx
+++ b/packages/ui/src/components/meter.tsx
@@ -12,6 +12,7 @@ export function Meter({
return (
{children ? (
diff --git a/packages/ui/src/components/otp-field.tsx b/packages/ui/src/components/otp-field.tsx
index d87bcae9e..75b01adda 100644
--- a/packages/ui/src/components/otp-field.tsx
+++ b/packages/ui/src/components/otp-field.tsx
@@ -15,7 +15,7 @@ export function OTPField({
return (
{children}
@@ -53,6 +56,9 @@ export function PreviewCardPopup({
);
}
+export const PreviewCardCreateHandle: typeof PreviewCardPrimitive.createHandle =
+ PreviewCardPrimitive.createHandle;
+
export {
PreviewCardPrimitive,
PreviewCard as HoverCard,
diff --git a/packages/ui/src/components/radio-group.tsx b/packages/ui/src/components/radio-group.tsx
index 53ddf6637..c6b121e7b 100644
--- a/packages/ui/src/components/radio-group.tsx
+++ b/packages/ui/src/components/radio-group.tsx
@@ -11,7 +11,7 @@ export function RadioGroup({
}: RadioGroupPrimitive.Props): React.ReactElement {
return (
diff --git a/packages/ui/src/components/scroll-area.tsx b/packages/ui/src/components/scroll-area.tsx
index 914d036b8..95c1ce769 100644
--- a/packages/ui/src/components/scroll-area.tsx
+++ b/packages/ui/src/components/scroll-area.tsx
@@ -23,6 +23,7 @@ export function ScrollArea({
return (
boolean)) => {
+ (value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === "function" ? value(open) : value;
if (setOpenProp) {
setOpenProp(openState);
@@ -106,12 +106,7 @@ export function SidebarProvider({
}
// This sets the cookie to keep the sidebar state.
- await cookieStore.set({
- expires: Date.now() + SIDEBAR_COOKIE_MAX_AGE * 1000,
- name: SIDEBAR_COOKIE_NAME,
- path: "/",
- value: String(openState),
- });
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
[setOpenProp, open],
);
diff --git a/packages/ui/src/components/toggle-group.tsx b/packages/ui/src/components/toggle-group.tsx
index 9a0dea497..8b336b382 100644
--- a/packages/ui/src/components/toggle-group.tsx
+++ b/packages/ui/src/components/toggle-group.tsx
@@ -20,13 +20,14 @@ export const ToggleGroupContext: React.Context<
export function ToggleGroup({
className,
- variant = "default",
- size = "default",
+ variant,
+ size,
orientation = "horizontal",
children,
...props
}: ToggleGroupPrimitive.Props &
VariantProps): React.ReactElement {
+ const resolvedGroupVariant = variant ?? "default";
return (
@@ -64,8 +65,8 @@ export function ToggleGroupItem({
VariantProps): React.ReactElement {
const context = React.useContext(ToggleGroupContext);
- const resolvedVariant = context.variant || variant;
- const resolvedSize = context.size || size;
+ const resolvedVariant = variant ?? context.variant ?? "default";
+ const resolvedSize = size ?? context.size ?? "default";
return (