@octanejs/shadcn 0.0.7 → 0.0.8

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": "@octanejs/shadcn",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -104,6 +104,27 @@
104
104
  "./react-aria/Tabs": "./src/bases/react-aria/ui/tabs.tsrx",
105
105
  "./react-aria/Textarea": "./src/bases/react-aria/ui/textarea.tsrx",
106
106
  "./react-aria/Toggle": "./src/bases/react-aria/ui/toggle.tsrx",
107
+ "./base-ui/Accordion": "./src/bases/base-ui/ui/accordion.tsrx",
108
+ "./base-ui/Alert": "./src/bases/base-ui/ui/alert.tsrx",
109
+ "./base-ui/AlertDialog": "./src/bases/base-ui/ui/alert-dialog.tsrx",
110
+ "./base-ui/AspectRatio": "./src/bases/base-ui/ui/aspect-ratio.tsrx",
111
+ "./base-ui/Button": "./src/bases/base-ui/ui/button.tsrx",
112
+ "./base-ui/Card": "./src/bases/base-ui/ui/card.tsrx",
113
+ "./base-ui/Checkbox": "./src/bases/base-ui/ui/checkbox.tsrx",
114
+ "./base-ui/Dialog": "./src/bases/base-ui/ui/dialog.tsrx",
115
+ "./base-ui/Empty": "./src/bases/base-ui/ui/empty.tsrx",
116
+ "./base-ui/Input": "./src/bases/base-ui/ui/input.tsrx",
117
+ "./base-ui/Kbd": "./src/bases/base-ui/ui/kbd.tsrx",
118
+ "./base-ui/Label": "./src/bases/base-ui/ui/label.tsrx",
119
+ "./base-ui/NativeSelect": "./src/bases/base-ui/ui/native-select.tsrx",
120
+ "./base-ui/Popover": "./src/bases/base-ui/ui/popover.tsrx",
121
+ "./base-ui/RadioGroup": "./src/bases/base-ui/ui/radio-group.tsrx",
122
+ "./base-ui/Separator": "./src/bases/base-ui/ui/separator.tsrx",
123
+ "./base-ui/Skeleton": "./src/bases/base-ui/ui/skeleton.tsrx",
124
+ "./base-ui/Spinner": "./src/bases/base-ui/ui/spinner.tsrx",
125
+ "./base-ui/Switch": "./src/bases/base-ui/ui/switch.tsrx",
126
+ "./base-ui/Textarea": "./src/bases/base-ui/ui/textarea.tsrx",
127
+ "./base-ui/Tooltip": "./src/bases/base-ui/ui/tooltip.tsrx",
107
128
  "./cn": "./src/lib/utils.ts",
108
129
  "./types": "./src/lib/types.ts",
109
130
  "./hooks/use-mobile": "./src/hooks/use-mobile.ts",
@@ -113,13 +134,14 @@
113
134
  "class-variance-authority": "0.7.1",
114
135
  "clsx": "^2.1.1",
115
136
  "tailwind-merge": "3.6.0",
116
- "@octanejs/aria": "0.0.16",
117
- "@octanejs/lucide": "0.1.17",
118
- "@octanejs/radix": "0.1.21",
119
- "@octanejs/sonner": "0.1.17"
137
+ "@octanejs/aria": "0.0.17",
138
+ "@octanejs/base-ui": "0.1.21",
139
+ "@octanejs/lucide": "0.1.18",
140
+ "@octanejs/radix": "0.1.22",
141
+ "@octanejs/sonner": "0.1.18"
120
142
  },
121
143
  "peerDependencies": {
122
- "octane": "0.1.22"
144
+ "octane": "0.1.23"
123
145
  },
124
146
  "devDependencies": {
125
147
  "@tsrx/react": "^0.2.56",
@@ -129,7 +151,7 @@
129
151
  "react": "^19.2.7",
130
152
  "react-dom": "^19.2.7",
131
153
  "vitest": "^4.1.10",
132
- "octane": "0.1.22"
154
+ "octane": "0.1.23"
133
155
  },
134
156
  "scripts": {
135
157
  "test": "vitest run",
@@ -0,0 +1,71 @@
1
+ // Base UI base accordion — transcribed from upstream's `bases/base-ui/ui/accordion.tsx`
2
+ // (maintainer-supplied), class strings and data-slot names verbatim. Runs on
3
+ // @octanejs/base-ui/accordion.
4
+ //
5
+ // Note the part mapping: upstream shadcn names the content part `AccordionContent` while the
6
+ // underlying Base UI part is `Accordion.Panel`, and `AccordionTrigger` renders the
7
+ // `Accordion.Header` wrapper itself rather than exposing it. Both are upstream's shape, kept
8
+ // as-is so consumers following the shadcn docs get the components they expect.
9
+ //
10
+ // Octane adaptations: no `"use client"` (octane has no Server Components); upstream's
11
+ // IconPlaceholder resolves at port time to lucide via @octanejs/lucide.
12
+ import { Accordion as AccordionPrimitive } from '@octanejs/base-ui/accordion';
13
+ import { ChevronDownIcon, ChevronUpIcon } from '@octanejs/lucide';
14
+
15
+ import { cn } from '../../../lib/utils';
16
+
17
+ export function Accordion({ className, ...props }: Record<string, any>) @{
18
+ <AccordionPrimitive.Root
19
+ data-slot="accordion"
20
+ className={cn('flex w-full flex-col', className)}
21
+ {...props}
22
+ />
23
+ }
24
+
25
+ export function AccordionItem({ className, ...props }: Record<string, any>) @{
26
+ <AccordionPrimitive.Item
27
+ data-slot="accordion-item"
28
+ className={cn('not-last:border-b', className)}
29
+ {...props}
30
+ />
31
+ }
32
+
33
+ export function AccordionTrigger({ className, children, ...props }: Record<string, any>) @{
34
+ <AccordionPrimitive.Header className="flex">
35
+ <AccordionPrimitive.Trigger
36
+ data-slot="accordion-trigger"
37
+ className={cn(
38
+ 'group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground',
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <ChevronDownIcon
45
+ data-slot="accordion-trigger-icon"
46
+ className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
47
+ />
48
+ <ChevronUpIcon
49
+ data-slot="accordion-trigger-icon"
50
+ className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
51
+ />
52
+ </AccordionPrimitive.Trigger>
53
+ </AccordionPrimitive.Header>
54
+ }
55
+
56
+ // `className` styles the INNER div, not the panel — upstream keeps the panel's own class string
57
+ // fixed so the open/close animation hooks cannot be overridden away by a consumer class.
58
+ export function AccordionContent({ className, children, ...props }: Record<string, any>) @{
59
+ <AccordionPrimitive.Panel
60
+ data-slot="accordion-content"
61
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
62
+ {...props}
63
+ >
64
+ <div
65
+ className={cn(
66
+ 'h-(--accordion-panel-height) pt-0 pb-2.5 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
67
+ className,
68
+ )}
69
+ >{children}</div>
70
+ </AccordionPrimitive.Panel>
71
+ }
@@ -0,0 +1,149 @@
1
+ // Base UI base alert-dialog — transcribed from upstream's `bases/base-ui/ui/alert-dialog.tsx`
2
+ // (maintainer-supplied), running on @octanejs/base-ui's AlertDialog. Upstream part mapping:
3
+ // Overlay -> Backdrop, Content -> Popup, Cancel -> Close. This port also composes Action via Close.
4
+ //
5
+ // TWO DELIBERATE DEPARTURES FROM THE SOURCE:
6
+ // - Upstream's AlertDialogTitle carries `cn-font-heading`, a semantic hook from the pinned cn-*
7
+ // system. This package ships the default-Tailwind utilities-inlined flavor instead, the
8
+ // react-aria base already drops this exact class for that reason, and nothing in the playground
9
+ // defines it (0 occurrences in the built CSS), so keeping it would ship a class that resolves
10
+ // to nothing. Every other class string is verbatim.
11
+ // - Upstream renders AlertDialogAction as a plain Button, so it does not dismiss the dialog.
12
+ // Composing it through Close preserves the public Button-like API and supplies the expected
13
+ // confirm-and-dismiss behavior shown by Base UI's own alert-dialog examples.
14
+ //
15
+ // Octane adaptations: no `"use client"`; refs are props. AlertDialogContent composes its
16
+ // Portal/Overlay/Popup tree internally, so consumer children still flow through the ordinary
17
+ // props.children channel.
18
+ import { AlertDialog as AlertDialogPrimitive } from '@octanejs/base-ui/alert-dialog';
19
+
20
+ import { cn } from '../../../lib/utils';
21
+ import type { DivProps } from '../../../lib/types';
22
+ import { Button } from './button.tsrx';
23
+
24
+ export function AlertDialog({ ...props }: Record<string, any>) @{
25
+ <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
26
+ }
27
+
28
+ export function AlertDialogTrigger({ ...props }: Record<string, any>) @{
29
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
30
+ }
31
+
32
+ export function AlertDialogPortal({ ...props }: Record<string, any>) @{
33
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
34
+ }
35
+
36
+ export function AlertDialogOverlay({ className, ...props }: Record<string, any>) @{
37
+ <AlertDialogPrimitive.Backdrop
38
+ data-slot="alert-dialog-overlay"
39
+ className={cn(
40
+ 'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ }
46
+
47
+ export function AlertDialogContent({ className, size = 'default', ...props }: Record<
48
+ string,
49
+ any
50
+ >) @{
51
+ <AlertDialogPortal>
52
+ <AlertDialogOverlay key="overlay" />
53
+ <AlertDialogPrimitive.Popup
54
+ key="content"
55
+ data-slot="alert-dialog-content"
56
+ data-size={size}
57
+ className={cn(
58
+ 'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
59
+ className,
60
+ )}
61
+ {...props}
62
+ />
63
+ </AlertDialogPortal>
64
+ }
65
+
66
+ export function AlertDialogHeader({ className, ...props }: DivProps) @{
67
+ <div
68
+ data-slot="alert-dialog-header"
69
+ className={cn(
70
+ 'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',
71
+ className,
72
+ )}
73
+ {...props}
74
+ />
75
+ }
76
+
77
+ export function AlertDialogFooter({ className, ...props }: DivProps) @{
78
+ <div
79
+ data-slot="alert-dialog-footer"
80
+ className={cn(
81
+ '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
82
+ className,
83
+ )}
84
+ {...props}
85
+ />
86
+ }
87
+
88
+ export function AlertDialogMedia({ className, ...props }: DivProps) @{
89
+ <div
90
+ data-slot="alert-dialog-media"
91
+ className={cn(
92
+ 'mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*=\'size-\'])]:size-6',
93
+ className,
94
+ )}
95
+ {...props}
96
+ />
97
+ }
98
+
99
+ export function AlertDialogTitle({ className, ...props }: Record<string, any>) @{
100
+ <AlertDialogPrimitive.Title
101
+ data-slot="alert-dialog-title"
102
+ className={cn(
103
+ 'text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2',
104
+ className,
105
+ )}
106
+ {...props}
107
+ />
108
+ }
109
+
110
+ export function AlertDialogDescription({ className, ...props }: Record<string, any>) @{
111
+ <AlertDialogPrimitive.Description
112
+ data-slot="alert-dialog-description"
113
+ className={cn(
114
+ 'text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
115
+ className,
116
+ )}
117
+ {...props}
118
+ />
119
+ }
120
+
121
+ export function AlertDialogAction({
122
+ className,
123
+ variant = 'default',
124
+ size = 'default',
125
+ ...props
126
+ }: Record<string, any>) @{
127
+ <AlertDialogPrimitive.Close
128
+ data-slot="alert-dialog-action"
129
+ className={cn(className)}
130
+ render={<Button variant={variant} size={size} />}
131
+ {...props}
132
+ />
133
+ }
134
+
135
+ // `render` takes an ELEMENT here, not a function — that is Base UI's render-prop contract, and
136
+ // it is what lets each action keep Close's dismiss behavior while looking like a Button.
137
+ export function AlertDialogCancel({
138
+ className,
139
+ variant = 'outline',
140
+ size = 'default',
141
+ ...props
142
+ }: Record<string, any>) @{
143
+ <AlertDialogPrimitive.Close
144
+ data-slot="alert-dialog-cancel"
145
+ className={cn(className)}
146
+ render={<Button variant={variant} size={size} />}
147
+ {...props}
148
+ />
149
+ }
@@ -0,0 +1,68 @@
1
+ // Base UI base alert — structure and cva strings from upstream `bases/base-ui/ui/alert.tsx`.
2
+ //
3
+ // Alert uses no headless primitive in any base, and this file's nine string literals were
4
+ // verified byte-identical to the React Aria base's `alert.tsrx`. That is a finding, not an
5
+ // assumption: the two bases DO diverge elsewhere (the Radix base's Skeleton is `bg-accent`
6
+ // where aria's is `bg-muted`), so identity is checked per component rather than inherited.
7
+ //
8
+ // Octane adaptations: `React.ComponentProps<'div'>` → the package's `DivProps`; no
9
+ // `"use client"` (octane has no Server Components). Pure template JSX, no consumer-children
10
+ // interleave, so every part uses the `@{ … }` shorthand.
11
+ import { cva, type VariantProps } from 'class-variance-authority';
12
+
13
+ import { cn } from '../../../lib/utils';
14
+ import type { DivProps } from '../../../lib/types';
15
+
16
+ export const alertVariants = cva(
17
+ 'group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*=\'size-\'])]:size-4',
18
+ {
19
+ variants: {
20
+ variant: {
21
+ default: 'bg-card text-card-foreground',
22
+ destructive: 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: 'default',
27
+ },
28
+ },
29
+ );
30
+
31
+ export interface AlertProps extends DivProps {
32
+ variant?: VariantProps<typeof alertVariants>['variant'];
33
+ }
34
+
35
+ export function Alert({ className, variant, ...props }: AlertProps) @{
36
+ <div
37
+ data-slot="alert"
38
+ role="alert"
39
+ className={cn(alertVariants({ variant }), className)}
40
+ {...props}
41
+ />
42
+ }
43
+
44
+ export function AlertTitle({ className, ...props }: DivProps) @{
45
+ <div
46
+ data-slot="alert-title"
47
+ className={cn(
48
+ 'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground',
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ }
54
+
55
+ export function AlertDescription({ className, ...props }: DivProps) @{
56
+ <div
57
+ data-slot="alert-description"
58
+ className={cn(
59
+ 'text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ }
65
+
66
+ export function AlertAction({ className, ...props }: DivProps) @{
67
+ <div data-slot="alert-action" className={cn('absolute top-2 right-2', className)} {...props} />
68
+ }
@@ -0,0 +1,24 @@
1
+ // Base UI base aspect-ratio — presentational, no headless primitive in any base.
2
+ //
3
+ // UNVERIFIED PROVENANCE: derived from this package's React Aria base rather than transcribed
4
+ // from upstream's `bases/base-ui/ui/aspect-ratio.tsx`, which was not available at port time.
5
+ // `alert` was confirmed byte-identical between the two bases, which is why this is plausible —
6
+ // but it is not proof, and the bases DO diverge elsewhere. Diff the class strings against the
7
+ // upstream source before treating this as ported.
8
+ import { cn } from '../../../lib/utils';
9
+ import type { DivProps } from '../../../lib/types';
10
+
11
+ export interface AspectRatioProps extends DivProps {
12
+ ratio: number;
13
+ }
14
+
15
+ export function AspectRatio({ ratio, className, ...props }: AspectRatioProps) @{
16
+ <div
17
+ data-slot="aspect-ratio"
18
+ style={{
19
+ '--ratio': ratio,
20
+ } as Record<string, string | number>}
21
+ className={cn('relative aspect-(--ratio)', className)}
22
+ {...props}
23
+ />
24
+ }
@@ -0,0 +1,69 @@
1
+ // Base UI base button — runs on the @octanejs/base-ui `Button` primitive.
2
+ //
3
+ // TWO THINGS HERE ARE RECORDED FACTS, not guesses, both from the React Aria base's own
4
+ // provenance note:
5
+ // 1. Upstream's bases SHARE button's cva strings exactly, so the class strings below are the
6
+ // package's shipped utilities-inlined flavor and match the other bases.
7
+ // 2. The Base UI base OMITS the `data-variant`/`data-size` attributes the aria base sets. They
8
+ // are deliberately absent rather than forgotten — no selector in these strings keys off
9
+ // them, so nothing styles differently for their absence.
10
+ //
11
+ // NO LinkButton. The aria base has one because RAC ships a `Link` primitive slotted as a button;
12
+ // Base UI has no such primitive, and upstream's Base UI base composes links through the `render`
13
+ // prop instead. Inventing one here would be an API this base does not have — `pagination`, which
14
+ // consumes it in the aria base, therefore stays unported until the upstream source says how.
15
+ import { cva, type VariantProps } from 'class-variance-authority';
16
+ import { Button as ButtonPrimitive } from '@octanejs/base-ui/button';
17
+
18
+ import { cn } from '../../../lib/utils';
19
+
20
+ export const buttonVariants = cva(
21
+ 'group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
22
+ {
23
+ variants: {
24
+ variant: {
25
+ default: 'bg-primary text-primary-foreground hover:bg-primary/80',
26
+ outline: 'border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
27
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
28
+ ghost: 'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50',
29
+ destructive: 'bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40',
30
+ link: 'text-primary underline-offset-4 hover:underline',
31
+ },
32
+ size: {
33
+ default: 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
34
+ xs: 'h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*=\'size-\'])]:size-3',
35
+ sm: 'h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*=\'size-\'])]:size-3.5',
36
+ lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
37
+ icon: 'size-8',
38
+ 'icon-xs': 'size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*=\'size-\'])]:size-3',
39
+ 'icon-sm': 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
40
+ 'icon-lg': 'size-9',
41
+ },
42
+ },
43
+ defaultVariants: {
44
+ variant: 'default',
45
+ size: 'default',
46
+ },
47
+ },
48
+ );
49
+
50
+ type ButtonVariants = VariantProps<typeof buttonVariants>;
51
+
52
+ export interface ButtonProps extends Record<string, any> {
53
+ variant?: ButtonVariants['variant'];
54
+ size?: ButtonVariants['size'];
55
+ className?: string;
56
+ }
57
+
58
+ export function Button({
59
+ className,
60
+ variant = 'default',
61
+ size = 'default',
62
+ ...props
63
+ }: ButtonProps) @{
64
+ <ButtonPrimitive
65
+ data-slot="button"
66
+ className={cn(buttonVariants({ variant, size, className }))}
67
+ {...props}
68
+ />
69
+ }
@@ -0,0 +1,76 @@
1
+ // Base UI base card — presentational, no headless primitive in any base.
2
+ //
3
+ // UNVERIFIED PROVENANCE: derived from this package's React Aria base rather than transcribed
4
+ // from upstream's `bases/base-ui/ui/card.tsx`, which was not available at port time.
5
+ // Diff the class strings against the upstream source before treating this as ported.
6
+ import { cn } from '../../../lib/utils';
7
+ import type { DivProps } from '../../../lib/types';
8
+
9
+ export interface CardProps extends DivProps {
10
+ size?: 'default' | 'sm';
11
+ }
12
+
13
+ export function Card({ className, size = 'default', ...props }: CardProps) @{
14
+ <div
15
+ data-slot="card"
16
+ data-size={size}
17
+ className={cn(
18
+ 'group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl',
19
+ className,
20
+ )}
21
+ {...props}
22
+ />
23
+ }
24
+
25
+ export function CardHeader({ className, ...props }: DivProps) @{
26
+ <div
27
+ data-slot="card-header"
28
+ className={cn(
29
+ 'group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)',
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ }
35
+
36
+ export function CardTitle({ className, ...props }: DivProps) @{
37
+ <div
38
+ data-slot="card-title"
39
+ className={cn(
40
+ 'text-base leading-snug font-medium group-data-[size=sm]/card:text-sm',
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ }
46
+
47
+ export function CardDescription({ className, ...props }: DivProps) @{
48
+ <div
49
+ data-slot="card-description"
50
+ className={cn('text-sm text-muted-foreground', className)}
51
+ {...props}
52
+ />
53
+ }
54
+
55
+ export function CardAction({ className, ...props }: DivProps) @{
56
+ <div
57
+ data-slot="card-action"
58
+ className={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
59
+ {...props}
60
+ />
61
+ }
62
+
63
+ export function CardContent({ className, ...props }: DivProps) @{
64
+ <div data-slot="card-content" className={cn('px-(--card-spacing)', className)} {...props} />
65
+ }
66
+
67
+ export function CardFooter({ className, ...props }: DivProps) @{
68
+ <div
69
+ data-slot="card-footer"
70
+ className={cn(
71
+ 'flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)',
72
+ className,
73
+ )}
74
+ {...props}
75
+ />
76
+ }
@@ -0,0 +1,43 @@
1
+ // Base UI base checkbox — runs on the @octanejs/base-ui `Checkbox` primitive.
2
+ //
3
+ // THE CONDITIONAL UTILITIES ARE ADAPTED, NOT COPIED, and the reason is structural rather than
4
+ // stylistic. Base UI's Checkbox.Root renders a `<span role="checkbox">` — verified by rendering
5
+ // it, not assumed — with a visually-hidden native `<input>` alongside it for form submission.
6
+ // A `<span>` is never `:disabled`, so Tailwind's `disabled:` variant (which compiles to the
7
+ // `:disabled` pseudo-class) can NEVER match. The primitive publishes `data-disabled` instead:
8
+ //
9
+ // radix base: `disabled:opacity-50` -> matches, Root is a real button
10
+ // base-ui base: `data-disabled:opacity-50` <- what this file uses
11
+ //
12
+ // `data-checked:` needs no change — the radix base already uses that exact form, and Base UI
13
+ // emits `data-checked`/`data-unchecked` too. `focus-visible:` also stays: the span carries
14
+ // tabindex, so the native pseudo-class applies.
15
+ //
16
+ // UNVERIFIED PROVENANCE: the non-conditional utilities are taken from this package's radix base
17
+ // rather than transcribed from upstream's Base UI source.
18
+ import { Checkbox as CheckboxPrimitive } from '@octanejs/base-ui/checkbox';
19
+ import { CheckIcon } from '@octanejs/lucide';
20
+
21
+ import { cn } from '../../../lib/utils';
22
+
23
+ export interface CheckboxProps extends Record<string, unknown> {
24
+ className?: string;
25
+ }
26
+
27
+ export function Checkbox({ className, ...props }: CheckboxProps) @{
28
+ <CheckboxPrimitive.Root
29
+ data-slot="checkbox"
30
+ className={cn(
31
+ 'peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary',
32
+ className,
33
+ )}
34
+ {...props}
35
+ >
36
+ <CheckboxPrimitive.Indicator
37
+ data-slot="checkbox-indicator"
38
+ className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
39
+ >
40
+ <CheckIcon />
41
+ </CheckboxPrimitive.Indicator>
42
+ </CheckboxPrimitive.Root>
43
+ }