Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 74 additions & 29 deletions packages/shadcn/src/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ const validationCheckSchema = z

const validateOnSchema = z.enum(["change", "blur", "submit"]).nullable();

/**
* Optional className prop for custom styling.
* All shadcn components accept this prop to allow style overrides.
*/
const classNameProp = z.string().nullable();

// =============================================================================
// shadcn/ui Component Definitions
// =============================================================================
Expand All @@ -37,6 +43,7 @@ export const shadcnComponentDefinitions = {
description: z.string().nullable(),
maxWidth: z.enum(["sm", "md", "lg", "full"]).nullable(),
centered: z.boolean().nullable(),
className: classNameProp,
}),
slots: ["default"],
description:
Expand All @@ -52,6 +59,7 @@ export const shadcnComponentDefinitions = {
justify: z
.enum(["start", "center", "end", "between", "around"])
.nullable(),
className: classNameProp,
}),
slots: ["default"],
description: "Flex container for layouts",
Expand All @@ -62,6 +70,7 @@ export const shadcnComponentDefinitions = {
props: z.object({
columns: z.number().nullable(),
gap: z.enum(["sm", "md", "lg"]).nullable(),
className: classNameProp,
}),
slots: ["default"],
description: "Grid layout (1-6 columns)",
Expand All @@ -71,6 +80,7 @@ export const shadcnComponentDefinitions = {
Separator: {
props: z.object({
orientation: z.enum(["horizontal", "vertical"]).nullable(),
className: classNameProp,
}),
description: "Visual separator line",
},
Expand All @@ -81,10 +91,12 @@ export const shadcnComponentDefinitions = {
z.object({
label: z.string(),
value: z.string(),
}),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The className prop was incorrectly placed inside z.array(z.object({...})) item schemas instead of at the component-level props, corrupting the schema for 6 components (Tabs, Accordion, Carousel, DropdownMenu, ToggleGroup, ButtonGroup).

Fix on Vercel

className: classNameProp,}),
),
defaultValue: z.string().nullable(),
value: z.string().nullable(),
className: classNameProp,
}),
slots: ["default"],
events: ["change"],
Expand All @@ -98,9 +110,11 @@ export const shadcnComponentDefinitions = {
z.object({
title: z.string(),
content: z.string(),
}),

className: classNameProp,}),
),
type: z.enum(["single", "multiple"]).nullable(),
className: classNameProp,
}),
description:
"Collapsible sections. Items as [{title, content}]. Type 'single' (default) or 'multiple'.",
Expand All @@ -110,6 +124,7 @@ export const shadcnComponentDefinitions = {
props: z.object({
title: z.string(),
defaultOpen: z.boolean().nullable(),
className: classNameProp,
}),
slots: ["default"],
description: "Collapsible section with trigger. Children render inside.",
Expand All @@ -120,6 +135,7 @@ export const shadcnComponentDefinitions = {
title: z.string(),
description: z.string().nullable(),
openPath: z.string(),
className: classNameProp,
}),
slots: ["default"],
description:
Expand All @@ -131,6 +147,7 @@ export const shadcnComponentDefinitions = {
title: z.string(),
description: z.string().nullable(),
openPath: z.string(),
className: classNameProp,
}),
slots: ["default"],
description:
Expand All @@ -143,8 +160,10 @@ export const shadcnComponentDefinitions = {
z.object({
title: z.string().nullable(),
description: z.string().nullable(),
}),

className: classNameProp,}),
),
className: classNameProp,
}),
description: "Horizontally scrollable carousel of cards.",
},
Expand All @@ -158,7 +177,8 @@ export const shadcnComponentDefinitions = {
columns: z.array(z.string()),
rows: z.array(z.array(z.string())),
caption: z.string().nullable(),
}),

className: classNameProp,}),
description:
'Data table. columns: header labels. rows: 2D array of cell strings, e.g. [["Alice","admin"],["Bob","user"]].',
example: {
Expand All @@ -174,7 +194,8 @@ export const shadcnComponentDefinitions = {
props: z.object({
text: z.string(),
level: z.enum(["h1", "h2", "h3", "h4"]).nullable(),
}),

className: classNameProp,}),
description: "Heading text (h1-h4)",
example: { text: "Welcome", level: "h1" },
},
Expand All @@ -183,7 +204,8 @@ export const shadcnComponentDefinitions = {
props: z.object({
text: z.string(),
variant: z.enum(["body", "caption", "muted", "lead", "code"]).nullable(),
}),

className: classNameProp,}),
description: "Paragraph text",
example: { text: "Hello, world!" },
},
Expand All @@ -194,7 +216,8 @@ export const shadcnComponentDefinitions = {
alt: z.string(),
width: z.number().nullable(),
height: z.number().nullable(),
}),

className: classNameProp,}),
description:
"Image component. Renders an img tag when src is provided, otherwise a placeholder.",
},
Expand All @@ -204,7 +227,8 @@ export const shadcnComponentDefinitions = {
src: z.string().nullable(),
name: z.string(),
size: z.enum(["sm", "md", "lg"]).nullable(),
}),

className: classNameProp,}),
description: "User avatar with fallback initials",
example: { name: "Jane Doe", size: "md" },
},
Expand All @@ -215,7 +239,8 @@ export const shadcnComponentDefinitions = {
variant: z
.enum(["default", "secondary", "destructive", "outline"])
.nullable(),
}),

className: classNameProp,}),
description: "Status badge",
example: { text: "Active", variant: "default" },
},
Expand All @@ -225,7 +250,8 @@ export const shadcnComponentDefinitions = {
title: z.string(),
message: z.string().nullable(),
type: z.enum(["info", "success", "warning", "error"]).nullable(),
}),

className: classNameProp,}),
description: "Alert banner",
example: {
title: "Note",
Expand All @@ -239,7 +265,8 @@ export const shadcnComponentDefinitions = {
value: z.number(),
max: z.number().nullable(),
label: z.string().nullable(),
}),

className: classNameProp,}),
description: "Progress bar (value 0-100)",
example: { value: 65, max: 100, label: "Upload progress" },
},
Expand All @@ -249,31 +276,35 @@ export const shadcnComponentDefinitions = {
width: z.string().nullable(),
height: z.string().nullable(),
rounded: z.boolean().nullable(),
}),

className: classNameProp,}),
description: "Loading placeholder skeleton",
},

Spinner: {
props: z.object({
size: z.enum(["sm", "md", "lg"]).nullable(),
label: z.string().nullable(),
}),

className: classNameProp,}),
description: "Loading spinner indicator",
},

Tooltip: {
props: z.object({
content: z.string(),
text: z.string(),
}),

className: classNameProp,}),
description: "Hover tooltip. Shows content on hover over text.",
},

Popover: {
props: z.object({
trigger: z.string(),
content: z.string(),
}),

className: classNameProp,}),
description: "Popover that appears on click of trigger.",
},

Expand All @@ -290,7 +321,8 @@ export const shadcnComponentDefinitions = {
value: z.string().nullable(),
checks: validationCheckSchema,
validateOn: validateOnSchema,
}),

className: classNameProp,}),
events: ["submit", "focus", "blur"],
description:
"Text input field. Use { $bindState } on value for two-way binding. Use checks for validation (e.g. required, email, minLength). validateOn controls timing (default: blur).",
Expand All @@ -311,7 +343,8 @@ export const shadcnComponentDefinitions = {
value: z.string().nullable(),
checks: validationCheckSchema,
validateOn: validateOnSchema,
}),

className: classNameProp,}),
description:
"Multi-line text input. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: blur).",
},
Expand All @@ -325,7 +358,8 @@ export const shadcnComponentDefinitions = {
value: z.string().nullable(),
checks: validationCheckSchema,
validateOn: validateOnSchema,
}),

className: classNameProp,}),
events: ["change"],
description:
"Dropdown select input. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: change).",
Expand All @@ -338,7 +372,8 @@ export const shadcnComponentDefinitions = {
checked: z.boolean().nullable(),
checks: validationCheckSchema,
validateOn: validateOnSchema,
}),

className: classNameProp,}),
events: ["change"],
description:
"Checkbox input. Use { $bindState } on checked for binding. Use checks for validation. validateOn controls timing (default: change).",
Expand All @@ -352,7 +387,8 @@ export const shadcnComponentDefinitions = {
value: z.string().nullable(),
checks: validationCheckSchema,
validateOn: validateOnSchema,
}),

className: classNameProp,}),
events: ["change"],
description:
"Radio button group. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: change).",
Expand All @@ -365,7 +401,8 @@ export const shadcnComponentDefinitions = {
checked: z.boolean().nullable(),
checks: validationCheckSchema,
validateOn: validateOnSchema,
}),

className: classNameProp,}),
events: ["change"],
description:
"Toggle switch. Use { $bindState } on checked for binding. Use checks for validation. validateOn controls timing (default: change).",
Expand All @@ -378,7 +415,8 @@ export const shadcnComponentDefinitions = {
max: z.number().nullable(),
step: z.number().nullable(),
value: z.number().nullable(),
}),

className: classNameProp,}),
events: ["change"],
description: "Range slider input. Use { $bindState } on value for binding.",
},
Expand All @@ -392,7 +430,8 @@ export const shadcnComponentDefinitions = {
label: z.string(),
variant: z.enum(["primary", "secondary", "danger"]).nullable(),
disabled: z.boolean().nullable(),
}),

className: classNameProp,}),
events: ["press"],
description: "Clickable button. Bind on.press for handler.",
example: { label: "Submit", variant: "primary" },
Expand All @@ -402,7 +441,8 @@ export const shadcnComponentDefinitions = {
props: z.object({
label: z.string(),
href: z.string(),
}),

className: classNameProp,}),
events: ["press"],
description: "Anchor link. Bind on.press for click handler.",
},
Expand All @@ -414,7 +454,8 @@ export const shadcnComponentDefinitions = {
z.object({
label: z.string(),
value: z.string(),
}),

className: classNameProp,}),
),
value: z.string().nullable(),
}),
Expand All @@ -428,7 +469,8 @@ export const shadcnComponentDefinitions = {
label: z.string(),
pressed: z.boolean().nullable(),
variant: z.enum(["default", "outline"]).nullable(),
}),

className: classNameProp,}),
events: ["change"],
description:
"Toggle button. Use { $bindState } on pressed for state binding.",
Expand All @@ -440,7 +482,8 @@ export const shadcnComponentDefinitions = {
z.object({
label: z.string(),
value: z.string(),
}),

className: classNameProp,}),
),
type: z.enum(["single", "multiple"]).nullable(),
value: z.string().nullable(),
Expand All @@ -456,7 +499,8 @@ export const shadcnComponentDefinitions = {
z.object({
label: z.string(),
value: z.string(),
}),

className: classNameProp,}),
),
selected: z.string().nullable(),
}),
Expand All @@ -469,7 +513,8 @@ export const shadcnComponentDefinitions = {
props: z.object({
totalPages: z.number(),
page: z.number().nullable(),
}),

className: classNameProp,}),
events: ["change"],
description:
"Page navigation. Use { $bindState } on page for current page number.",
Expand Down
Loading