Compare commits

...

91 Commits

Author SHA1 Message Date
shadcn
f5a0945caf chore: add changelog 2026-01-20 19:09:05 +04:00
shadcn
eecffac153 fix 2026-01-20 14:01:55 +04:00
shadcn
93b1b012f1 fix 2026-01-20 13:55:54 +04:00
shadcn
113be481d5 fix: create page 2026-01-20 13:32:33 +04:00
shadcn
90952b1b0c docs: update 2026-01-20 13:08:50 +04:00
shadcn
c470caca45 Merge branch 'main' into shadcn/base-docs 2026-01-20 12:58:40 +04:00
shadcn
25e88fe4e9 Revert "Refactor Tooltip component to remove TooltipProvider (#9329)" (#9388)
This reverts commit d3590ceff9.
2026-01-20 12:58:22 +04:00
shadcn
63ad095a34 Merge branch 'main' into shadcn/base-docs 2026-01-20 12:53:09 +04:00
shadcn
d3b3ecde53 fi 2026-01-20 12:52:55 +04:00
shadcn
f337ccef13 fix 2026-01-20 12:42:04 +04:00
shadcn
818c060efc fix 2026-01-20 11:46:12 +04:00
Francois Botha
d3590ceff9 Refactor Tooltip component to remove TooltipProvider (#9329) 2026-01-20 11:38:32 +04:00
shadcn
528b27a63f feat: add docs for sidebar 2026-01-20 11:35:07 +04:00
shadcn
a7bd5d2d22 fix: tabs 2026-01-20 10:22:08 +04:00
shadcn
62b1b89bcc fix: tabs 2026-01-20 09:09:41 +04:00
shadcn
97371b578e feat: add more components 2026-01-19 22:31:25 +04:00
shadcn
d6878556ba feat: add spinner and switch 2026-01-19 21:34:56 +04:00
shadcn
e7d802fc07 feat: add skeleton, slider and sonner 2026-01-19 21:18:56 +04:00
shadcn
572e5d4532 feat: more components 2026-01-19 18:04:17 +04:00
shadcn
90f8057a24 feat: more components 2026-01-19 17:23:10 +04:00
shadcn
151ae29653 feat: add more components 2026-01-19 17:05:41 +04:00
shadcn
40e5bf5eff feat: add native-select 2026-01-19 16:41:52 +04:00
shadcn
1fd52c41f9 feat: add menubar 2026-01-19 16:23:09 +04:00
shadcn
63c28a1496 feat: add kbd and label 2026-01-19 16:12:19 +04:00
shadcn
6a9d68cc2c feat: add item 2026-01-19 15:56:32 +04:00
shadcn
587c76f46f feat: add input-otp 2026-01-19 15:34:34 +04:00
shadcn
52a4b1d466 feat: add input-group 2026-01-19 13:56:42 +04:00
shadcn
7ecba59894 feat: add input 2026-01-19 12:49:14 +04:00
shadcn
4073811f64 Merge branch 'main' into shadcn/base-docs 2026-01-19 11:41:24 +04:00
shadcn
5f966a282a Merge branch 'main' into shadcn/base-docs
# Conflicts:
#	apps/v4/lib/llm.ts
#	apps/v4/lib/registry.ts
#	apps/v4/public/r/styles/base-lyra/accordion-example.json
#	apps/v4/public/r/styles/base-lyra/accordion.json
#	apps/v4/public/r/styles/base-lyra/alert-dialog-example.json
#	apps/v4/public/r/styles/base-lyra/alert-example.json
#	apps/v4/public/r/styles/base-lyra/avatar-example.json
#	apps/v4/public/r/styles/base-lyra/badge-example.json
#	apps/v4/public/r/styles/base-lyra/breadcrumb.json
#	apps/v4/public/r/styles/base-lyra/button-example.json
#	apps/v4/public/r/styles/base-lyra/button-group-example.json
#	apps/v4/public/r/styles/base-lyra/calendar-example.json
#	apps/v4/public/r/styles/base-lyra/calendar.json
#	apps/v4/public/r/styles/base-lyra/card-example.json
#	apps/v4/public/r/styles/base-lyra/carousel.json
#	apps/v4/public/r/styles/base-lyra/chart-example.json
#	apps/v4/public/r/styles/base-lyra/chatgpt.json
#	apps/v4/public/r/styles/base-lyra/checkbox.json
#	apps/v4/public/r/styles/base-lyra/collapsible-example.json
#	apps/v4/public/r/styles/base-lyra/combobox-example.json
#	apps/v4/public/r/styles/base-lyra/combobox.json
#	apps/v4/public/r/styles/base-lyra/command-example.json
#	apps/v4/public/r/styles/base-lyra/command.json
#	apps/v4/public/r/styles/base-lyra/component-example.json
#	apps/v4/public/r/styles/base-lyra/context-menu-example.json
#	apps/v4/public/r/styles/base-lyra/context-menu.json
#	apps/v4/public/r/styles/base-lyra/dialog-example.json
#	apps/v4/public/r/styles/base-lyra/dialog.json
#	apps/v4/public/r/styles/base-lyra/dropdown-menu-example.json
#	apps/v4/public/r/styles/base-lyra/dropdown-menu.json
#	apps/v4/public/r/styles/base-lyra/empty-example.json
#	apps/v4/public/r/styles/base-lyra/github.json
#	apps/v4/public/r/styles/base-lyra/input-group-example.json
#	apps/v4/public/r/styles/base-lyra/input-otp-example.json
#	apps/v4/public/r/styles/base-lyra/input-otp.json
#	apps/v4/public/r/styles/base-lyra/item-example.json
#	apps/v4/public/r/styles/base-lyra/kbd-example.json
#	apps/v4/public/r/styles/base-lyra/menubar-example.json
#	apps/v4/public/r/styles/base-lyra/menubar.json
#	apps/v4/public/r/styles/base-lyra/native-select.json
#	apps/v4/public/r/styles/base-lyra/navigation-menu-example.json
#	apps/v4/public/r/styles/base-lyra/navigation-menu.json
#	apps/v4/public/r/styles/base-lyra/pagination.json
#	apps/v4/public/r/styles/base-lyra/preview.json
#	apps/v4/public/r/styles/base-lyra/progress-example.json
#	apps/v4/public/r/styles/base-lyra/radio-group.json
#	apps/v4/public/r/styles/base-lyra/select-example.json
#	apps/v4/public/r/styles/base-lyra/select.json
#	apps/v4/public/r/styles/base-lyra/sheet.json
#	apps/v4/public/r/styles/base-lyra/sidebar-example.json
#	apps/v4/public/r/styles/base-lyra/sidebar-floating-example.json
#	apps/v4/public/r/styles/base-lyra/sidebar-icon-example.json
#	apps/v4/public/r/styles/base-lyra/sidebar-inset-example.json
#	apps/v4/public/r/styles/base-lyra/sidebar.json
#	apps/v4/public/r/styles/base-lyra/spinner-example.json
#	apps/v4/public/r/styles/base-lyra/spinner.json
#	apps/v4/public/r/styles/base-lyra/table-example.json
#	apps/v4/public/r/styles/base-lyra/tabs-example.json
#	apps/v4/public/r/styles/base-lyra/toggle-example.json
#	apps/v4/public/r/styles/base-lyra/toggle-group-example.json
#	apps/v4/public/r/styles/base-lyra/tooltip-example.json
#	apps/v4/public/r/styles/base-lyra/vercel.json
#	apps/v4/public/r/styles/base-maia/accordion-example.json
#	apps/v4/public/r/styles/base-maia/accordion.json
#	apps/v4/public/r/styles/base-maia/alert-dialog-example.json
#	apps/v4/public/r/styles/base-maia/alert-example.json
#	apps/v4/public/r/styles/base-maia/avatar-example.json
#	apps/v4/public/r/styles/base-maia/badge-example.json
#	apps/v4/public/r/styles/base-maia/breadcrumb.json
#	apps/v4/public/r/styles/base-maia/button-example.json
#	apps/v4/public/r/styles/base-maia/button-group-example.json
#	apps/v4/public/r/styles/base-maia/calendar-example.json
#	apps/v4/public/r/styles/base-maia/calendar.json
#	apps/v4/public/r/styles/base-maia/card-example.json
#	apps/v4/public/r/styles/base-maia/carousel.json
#	apps/v4/public/r/styles/base-maia/chart-example.json
#	apps/v4/public/r/styles/base-maia/chatgpt.json
#	apps/v4/public/r/styles/base-maia/checkbox.json
#	apps/v4/public/r/styles/base-maia/collapsible-example.json
#	apps/v4/public/r/styles/base-maia/combobox-example.json
#	apps/v4/public/r/styles/base-maia/combobox.json
#	apps/v4/public/r/styles/base-maia/command-example.json
#	apps/v4/public/r/styles/base-maia/command.json
#	apps/v4/public/r/styles/base-maia/component-example.json
#	apps/v4/public/r/styles/base-maia/context-menu-example.json
#	apps/v4/public/r/styles/base-maia/context-menu.json
#	apps/v4/public/r/styles/base-maia/dialog-example.json
#	apps/v4/public/r/styles/base-maia/dialog.json
#	apps/v4/public/r/styles/base-maia/dropdown-menu-example.json
#	apps/v4/public/r/styles/base-maia/dropdown-menu.json
#	apps/v4/public/r/styles/base-maia/empty-example.json
#	apps/v4/public/r/styles/base-maia/github.json
#	apps/v4/public/r/styles/base-maia/input-group-example.json
#	apps/v4/public/r/styles/base-maia/input-otp-example.json
#	apps/v4/public/r/styles/base-maia/input-otp.json
#	apps/v4/public/r/styles/base-maia/item-example.json
#	apps/v4/public/r/styles/base-maia/kbd-example.json
#	apps/v4/public/r/styles/base-maia/menubar-example.json
#	apps/v4/public/r/styles/base-maia/menubar.json
#	apps/v4/public/r/styles/base-maia/native-select.json
#	apps/v4/public/r/styles/base-maia/navigation-menu-example.json
#	apps/v4/public/r/styles/base-maia/navigation-menu.json
#	apps/v4/public/r/styles/base-maia/pagination.json
#	apps/v4/public/r/styles/base-maia/preview.json
#	apps/v4/public/r/styles/base-maia/progress-example.json
#	apps/v4/public/r/styles/base-maia/radio-group.json
#	apps/v4/public/r/styles/base-maia/select-example.json
#	apps/v4/public/r/styles/base-maia/select.json
#	apps/v4/public/r/styles/base-maia/sheet.json
#	apps/v4/public/r/styles/base-maia/sidebar-example.json
#	apps/v4/public/r/styles/base-maia/sidebar-floating-example.json
#	apps/v4/public/r/styles/base-maia/sidebar-icon-example.json
#	apps/v4/public/r/styles/base-maia/sidebar-inset-example.json
#	apps/v4/public/r/styles/base-maia/sidebar.json
#	apps/v4/public/r/styles/base-maia/spinner-example.json
#	apps/v4/public/r/styles/base-maia/spinner.json
#	apps/v4/public/r/styles/base-maia/table-example.json
#	apps/v4/public/r/styles/base-maia/tabs-example.json
#	apps/v4/public/r/styles/base-maia/toggle-example.json
#	apps/v4/public/r/styles/base-maia/toggle-group-example.json
#	apps/v4/public/r/styles/base-maia/tooltip-example.json
#	apps/v4/public/r/styles/base-maia/vercel.json
#	apps/v4/public/r/styles/base-mira/accordion-example.json
#	apps/v4/public/r/styles/base-mira/accordion.json
#	apps/v4/public/r/styles/base-mira/alert-dialog-example.json
#	apps/v4/public/r/styles/base-mira/alert-example.json
#	apps/v4/public/r/styles/base-mira/avatar-example.json
#	apps/v4/public/r/styles/base-mira/badge-example.json
#	apps/v4/public/r/styles/base-mira/breadcrumb.json
#	apps/v4/public/r/styles/base-mira/button-example.json
#	apps/v4/public/r/styles/base-mira/button-group-example.json
#	apps/v4/public/r/styles/base-mira/calendar-example.json
#	apps/v4/public/r/styles/base-mira/calendar.json
#	apps/v4/public/r/styles/base-mira/card-example.json
#	apps/v4/public/r/styles/base-mira/carousel.json
#	apps/v4/public/r/styles/base-mira/chart-example.json
#	apps/v4/public/r/styles/base-mira/chatgpt.json
#	apps/v4/public/r/styles/base-mira/checkbox.json
#	apps/v4/public/r/styles/base-mira/collapsible-example.json
#	apps/v4/public/r/styles/base-mira/combobox-example.json
#	apps/v4/public/r/styles/base-mira/combobox.json
#	apps/v4/public/r/styles/base-mira/command-example.json
#	apps/v4/public/r/styles/base-mira/command.json
#	apps/v4/public/r/styles/base-mira/component-example.json
#	apps/v4/public/r/styles/base-mira/context-menu-example.json
#	apps/v4/public/r/styles/base-mira/context-menu.json
#	apps/v4/public/r/styles/base-mira/dialog-example.json
#	apps/v4/public/r/styles/base-mira/dialog.json
#	apps/v4/public/r/styles/base-mira/dropdown-menu-example.json
#	apps/v4/public/r/styles/base-mira/dropdown-menu.json
#	apps/v4/public/r/styles/base-mira/empty-example.json
#	apps/v4/public/r/styles/base-mira/github.json
#	apps/v4/public/r/styles/base-mira/input-group-example.json
#	apps/v4/public/r/styles/base-mira/input-otp-example.json
#	apps/v4/public/r/styles/base-mira/input-otp.json
#	apps/v4/public/r/styles/base-mira/item-example.json
#	apps/v4/public/r/styles/base-mira/kbd-example.json
#	apps/v4/public/r/styles/base-mira/menubar-example.json
#	apps/v4/public/r/styles/base-mira/menubar.json
#	apps/v4/public/r/styles/base-mira/native-select.json
#	apps/v4/public/r/styles/base-mira/navigation-menu-example.json
#	apps/v4/public/r/styles/base-mira/navigation-menu.json
#	apps/v4/public/r/styles/base-mira/pagination.json
#	apps/v4/public/r/styles/base-mira/preview.json
#	apps/v4/public/r/styles/base-mira/progress-example.json
#	apps/v4/public/r/styles/base-mira/radio-group.json
#	apps/v4/public/r/styles/base-mira/select-example.json
#	apps/v4/public/r/styles/base-mira/select.json
#	apps/v4/public/r/styles/base-mira/sheet.json
#	apps/v4/public/r/styles/base-mira/sidebar-example.json
#	apps/v4/public/r/styles/base-mira/sidebar-floating-example.json
#	apps/v4/public/r/styles/base-mira/sidebar-icon-example.json
#	apps/v4/public/r/styles/base-mira/sidebar-inset-example.json
#	apps/v4/public/r/styles/base-mira/sidebar.json
#	apps/v4/public/r/styles/base-mira/spinner-example.json
#	apps/v4/public/r/styles/base-mira/spinner.json
#	apps/v4/public/r/styles/base-mira/table-example.json
#	apps/v4/public/r/styles/base-mira/tabs-example.json
#	apps/v4/public/r/styles/base-mira/toggle-example.json
#	apps/v4/public/r/styles/base-mira/toggle-group-example.json
#	apps/v4/public/r/styles/base-mira/tooltip-example.json
#	apps/v4/public/r/styles/base-mira/vercel.json
#	apps/v4/public/r/styles/base-nova/accordion-example.json
#	apps/v4/public/r/styles/base-nova/accordion.json
#	apps/v4/public/r/styles/base-nova/alert-dialog-example.json
#	apps/v4/public/r/styles/base-nova/alert-example.json
#	apps/v4/public/r/styles/base-nova/avatar-example.json
#	apps/v4/public/r/styles/base-nova/badge-example.json
#	apps/v4/public/r/styles/base-nova/breadcrumb.json
#	apps/v4/public/r/styles/base-nova/button-example.json
#	apps/v4/public/r/styles/base-nova/button-group-example.json
#	apps/v4/public/r/styles/base-nova/calendar-example.json
#	apps/v4/public/r/styles/base-nova/calendar.json
#	apps/v4/public/r/styles/base-nova/card-example.json
#	apps/v4/public/r/styles/base-nova/carousel.json
#	apps/v4/public/r/styles/base-nova/chart-example.json
#	apps/v4/public/r/styles/base-nova/chatgpt.json
#	apps/v4/public/r/styles/base-nova/checkbox.json
#	apps/v4/public/r/styles/base-nova/collapsible-example.json
#	apps/v4/public/r/styles/base-nova/combobox-example.json
#	apps/v4/public/r/styles/base-nova/combobox.json
#	apps/v4/public/r/styles/base-nova/command-example.json
#	apps/v4/public/r/styles/base-nova/command.json
#	apps/v4/public/r/styles/base-nova/component-example.json
#	apps/v4/public/r/styles/base-nova/context-menu-example.json
#	apps/v4/public/r/styles/base-nova/context-menu.json
#	apps/v4/public/r/styles/base-nova/dialog-example.json
#	apps/v4/public/r/styles/base-nova/dialog.json
#	apps/v4/public/r/styles/base-nova/dropdown-menu-example.json
#	apps/v4/public/r/styles/base-nova/dropdown-menu.json
#	apps/v4/public/r/styles/base-nova/empty-example.json
#	apps/v4/public/r/styles/base-nova/github.json
#	apps/v4/public/r/styles/base-nova/input-group-example.json
#	apps/v4/public/r/styles/base-nova/input-otp-example.json
#	apps/v4/public/r/styles/base-nova/input-otp.json
#	apps/v4/public/r/styles/base-nova/item-example.json
#	apps/v4/public/r/styles/base-nova/kbd-example.json
#	apps/v4/public/r/styles/base-nova/menubar-example.json
#	apps/v4/public/r/styles/base-nova/menubar.json
#	apps/v4/public/r/styles/base-nova/native-select.json
#	apps/v4/public/r/styles/base-nova/navigation-menu-example.json
#	apps/v4/public/r/styles/base-nova/navigation-menu.json
#	apps/v4/public/r/styles/base-nova/pagination.json
#	apps/v4/public/r/styles/base-nova/preview.json
#	apps/v4/public/r/styles/base-nova/progress-example.json
#	apps/v4/public/r/styles/base-nova/radio-group.json
#	apps/v4/public/r/styles/base-nova/select-example.json
#	apps/v4/public/r/styles/base-nova/select.json
#	apps/v4/public/r/styles/base-nova/sheet.json
#	apps/v4/public/r/styles/base-nova/sidebar-example.json
#	apps/v4/public/r/styles/base-nova/sidebar-floating-example.json
#	apps/v4/public/r/styles/base-nova/sidebar-icon-example.json
#	apps/v4/public/r/styles/base-nova/sidebar-inset-example.json
#	apps/v4/public/r/styles/base-nova/sidebar.json
#	apps/v4/public/r/styles/base-nova/spinner-example.json
#	apps/v4/public/r/styles/base-nova/spinner.json
#	apps/v4/public/r/styles/base-nova/table-example.json
#	apps/v4/public/r/styles/base-nova/tabs-example.json
#	apps/v4/public/r/styles/base-nova/toggle-example.json
#	apps/v4/public/r/styles/base-nova/toggle-group-example.json
#	apps/v4/public/r/styles/base-nova/tooltip-example.json
#	apps/v4/public/r/styles/base-nova/vercel.json
#	apps/v4/public/r/styles/base-vega/accordion-example.json
#	apps/v4/public/r/styles/base-vega/accordion.json
#	apps/v4/public/r/styles/base-vega/alert-dialog-example.json
#	apps/v4/public/r/styles/base-vega/alert-example.json
#	apps/v4/public/r/styles/base-vega/avatar-example.json
#	apps/v4/public/r/styles/base-vega/badge-example.json
#	apps/v4/public/r/styles/base-vega/breadcrumb.json
#	apps/v4/public/r/styles/base-vega/button-example.json
#	apps/v4/public/r/styles/base-vega/button-group-example.json
#	apps/v4/public/r/styles/base-vega/calendar-example.json
#	apps/v4/public/r/styles/base-vega/calendar.json
#	apps/v4/public/r/styles/base-vega/card-example.json
#	apps/v4/public/r/styles/base-vega/carousel.json
#	apps/v4/public/r/styles/base-vega/chart-example.json
#	apps/v4/public/r/styles/base-vega/chatgpt.json
#	apps/v4/public/r/styles/base-vega/checkbox.json
#	apps/v4/public/r/styles/base-vega/collapsible-example.json
#	apps/v4/public/r/styles/base-vega/combobox-example.json
#	apps/v4/public/r/styles/base-vega/combobox.json
#	apps/v4/public/r/styles/base-vega/command-example.json
#	apps/v4/public/r/styles/base-vega/command.json
#	apps/v4/public/r/styles/base-vega/component-example.json
#	apps/v4/public/r/styles/base-vega/context-menu-example.json
#	apps/v4/public/r/styles/base-vega/context-menu.json
#	apps/v4/public/r/styles/base-vega/dialog-example.json
#	apps/v4/public/r/styles/base-vega/dialog.json
#	apps/v4/public/r/styles/base-vega/dropdown-menu-example.json
#	apps/v4/public/r/styles/base-vega/dropdown-menu.json
#	apps/v4/public/r/styles/base-vega/empty-example.json
#	apps/v4/public/r/styles/base-vega/github.json
#	apps/v4/public/r/styles/base-vega/input-group-example.json
#	apps/v4/public/r/styles/base-vega/input-otp-example.json
#	apps/v4/public/r/styles/base-vega/input-otp.json
#	apps/v4/public/r/styles/base-vega/item-example.json
#	apps/v4/public/r/styles/base-vega/kbd-example.json
#	apps/v4/public/r/styles/base-vega/menubar-example.json
#	apps/v4/public/r/styles/base-vega/menubar.json
#	apps/v4/public/r/styles/base-vega/native-select.json
#	apps/v4/public/r/styles/base-vega/navigation-menu-example.json
#	apps/v4/public/r/styles/base-vega/navigation-menu.json
#	apps/v4/public/r/styles/base-vega/pagination.json
#	apps/v4/public/r/styles/base-vega/preview.json
#	apps/v4/public/r/styles/base-vega/progress-example.json
#	apps/v4/public/r/styles/base-vega/radio-group.json
#	apps/v4/public/r/styles/base-vega/select-example.json
#	apps/v4/public/r/styles/base-vega/select.json
#	apps/v4/public/r/styles/base-vega/sheet.json
#	apps/v4/public/r/styles/base-vega/sidebar-example.json
#	apps/v4/public/r/styles/base-vega/sidebar-floating-example.json
#	apps/v4/public/r/styles/base-vega/sidebar-icon-example.json
#	apps/v4/public/r/styles/base-vega/sidebar-inset-example.json
#	apps/v4/public/r/styles/base-vega/sidebar.json
#	apps/v4/public/r/styles/base-vega/spinner-example.json
#	apps/v4/public/r/styles/base-vega/spinner.json
#	apps/v4/public/r/styles/base-vega/table-example.json
#	apps/v4/public/r/styles/base-vega/tabs-example.json
#	apps/v4/public/r/styles/base-vega/toggle-example.json
#	apps/v4/public/r/styles/base-vega/toggle-group-example.json
#	apps/v4/public/r/styles/base-vega/tooltip-example.json
#	apps/v4/public/r/styles/base-vega/vercel.json
#	apps/v4/public/r/styles/radix-lyra/accordion-example.json
#	apps/v4/public/r/styles/radix-lyra/accordion.json
#	apps/v4/public/r/styles/radix-lyra/alert-dialog-example.json
#	apps/v4/public/r/styles/radix-lyra/alert-example.json
#	apps/v4/public/r/styles/radix-lyra/avatar-example.json
#	apps/v4/public/r/styles/radix-lyra/badge-example.json
#	apps/v4/public/r/styles/radix-lyra/breadcrumb.json
#	apps/v4/public/r/styles/radix-lyra/button-example.json
#	apps/v4/public/r/styles/radix-lyra/button-group-example.json
#	apps/v4/public/r/styles/radix-lyra/calendar-example.json
#	apps/v4/public/r/styles/radix-lyra/calendar.json
#	apps/v4/public/r/styles/radix-lyra/card-example.json
#	apps/v4/public/r/styles/radix-lyra/carousel.json
#	apps/v4/public/r/styles/radix-lyra/chart-example.json
#	apps/v4/public/r/styles/radix-lyra/chatgpt.json
#	apps/v4/public/r/styles/radix-lyra/checkbox.json
#	apps/v4/public/r/styles/radix-lyra/collapsible-example.json
#	apps/v4/public/r/styles/radix-lyra/combobox-example.json
#	apps/v4/public/r/styles/radix-lyra/combobox.json
#	apps/v4/public/r/styles/radix-lyra/command-example.json
#	apps/v4/public/r/styles/radix-lyra/command.json
#	apps/v4/public/r/styles/radix-lyra/component-example.json
#	apps/v4/public/r/styles/radix-lyra/context-menu-example.json
#	apps/v4/public/r/styles/radix-lyra/context-menu.json
#	apps/v4/public/r/styles/radix-lyra/dialog-example.json
#	apps/v4/public/r/styles/radix-lyra/dialog.json
#	apps/v4/public/r/styles/radix-lyra/dropdown-menu-example.json
#	apps/v4/public/r/styles/radix-lyra/dropdown-menu.json
#	apps/v4/public/r/styles/radix-lyra/empty-example.json
#	apps/v4/public/r/styles/radix-lyra/github.json
#	apps/v4/public/r/styles/radix-lyra/input-group-example.json
#	apps/v4/public/r/styles/radix-lyra/input-otp-example.json
#	apps/v4/public/r/styles/radix-lyra/input-otp.json
#	apps/v4/public/r/styles/radix-lyra/item-example.json
#	apps/v4/public/r/styles/radix-lyra/kbd-example.json
#	apps/v4/public/r/styles/radix-lyra/menubar-example.json
#	apps/v4/public/r/styles/radix-lyra/menubar.json
#	apps/v4/public/r/styles/radix-lyra/native-select.json
#	apps/v4/public/r/styles/radix-lyra/navigation-menu-example.json
#	apps/v4/public/r/styles/radix-lyra/navigation-menu.json
#	apps/v4/public/r/styles/radix-lyra/pagination.json
#	apps/v4/public/r/styles/radix-lyra/preview.json
#	apps/v4/public/r/styles/radix-lyra/progress-example.json
#	apps/v4/public/r/styles/radix-lyra/radio-group.json
#	apps/v4/public/r/styles/radix-lyra/select-example.json
#	apps/v4/public/r/styles/radix-lyra/select.json
#	apps/v4/public/r/styles/radix-lyra/sheet.json
#	apps/v4/public/r/styles/radix-lyra/sidebar-example.json
#	apps/v4/public/r/styles/radix-lyra/sidebar-floating-example.json
#	apps/v4/public/r/styles/radix-lyra/sidebar-icon-example.json
#	apps/v4/public/r/styles/radix-lyra/sidebar-inset-example.json
#	apps/v4/public/r/styles/radix-lyra/sidebar.json
#	apps/v4/public/r/styles/radix-lyra/spinner-example.json
#	apps/v4/public/r/styles/radix-lyra/spinner.json
#	apps/v4/public/r/styles/radix-lyra/table-example.json
#	apps/v4/public/r/styles/radix-lyra/tabs-example.json
#	apps/v4/public/r/styles/radix-lyra/toggle-example.json
#	apps/v4/public/r/styles/radix-lyra/toggle-group-example.json
#	apps/v4/public/r/styles/radix-lyra/tooltip-example.json
#	apps/v4/public/r/styles/radix-lyra/vercel.json
#	apps/v4/public/r/styles/radix-maia/accordion-example.json
#	apps/v4/public/r/styles/radix-maia/accordion.json
#	apps/v4/public/r/styles/radix-maia/alert-dialog-example.json
#	apps/v4/public/r/styles/radix-maia/alert-example.json
#	apps/v4/public/r/styles/radix-maia/avatar-example.json
#	apps/v4/public/r/styles/radix-maia/badge-example.json
#	apps/v4/public/r/styles/radix-maia/breadcrumb.json
#	apps/v4/public/r/styles/radix-maia/button-example.json
#	apps/v4/public/r/styles/radix-maia/button-group-example.json
#	apps/v4/public/r/styles/radix-maia/calendar-example.json
#	apps/v4/public/r/styles/radix-maia/calendar.json
#	apps/v4/public/r/styles/radix-maia/card-example.json
#	apps/v4/public/r/styles/radix-maia/carousel.json
#	apps/v4/public/r/styles/radix-maia/chart-example.json
#	apps/v4/public/r/styles/radix-maia/chatgpt.json
#	apps/v4/public/r/styles/radix-maia/checkbox.json
#	apps/v4/public/r/styles/radix-maia/collapsible-example.json
#	apps/v4/public/r/styles/radix-maia/combobox-example.json
#	apps/v4/public/r/styles/radix-maia/combobox.json
#	apps/v4/public/r/styles/radix-maia/command-example.json
#	apps/v4/public/r/styles/radix-maia/command.json
#	apps/v4/public/r/styles/radix-maia/component-example.json
#	apps/v4/public/r/styles/radix-maia/context-menu-example.json
#	apps/v4/public/r/styles/radix-maia/context-menu.json
#	apps/v4/public/r/styles/radix-maia/dialog-example.json
#	apps/v4/public/r/styles/radix-maia/dialog.json
#	apps/v4/public/r/styles/radix-maia/dropdown-menu-example.json
#	apps/v4/public/r/styles/radix-maia/dropdown-menu.json
#	apps/v4/public/r/styles/radix-maia/empty-example.json
#	apps/v4/public/r/styles/radix-maia/github.json
#	apps/v4/public/r/styles/radix-maia/input-group-example.json
#	apps/v4/public/r/styles/radix-maia/input-otp-example.json
#	apps/v4/public/r/styles/radix-maia/input-otp.json
#	apps/v4/public/r/styles/radix-maia/item-example.json
#	apps/v4/public/r/styles/radix-maia/kbd-example.json
#	apps/v4/public/r/styles/radix-maia/menubar-example.json
#	apps/v4/public/r/styles/radix-maia/menubar.json
#	apps/v4/public/r/styles/radix-maia/native-select.json
#	apps/v4/public/r/styles/radix-maia/navigation-menu-example.json
#	apps/v4/public/r/styles/radix-maia/navigation-menu.json
#	apps/v4/public/r/styles/radix-maia/pagination.json
#	apps/v4/public/r/styles/radix-maia/preview.json
#	apps/v4/public/r/styles/radix-maia/progress-example.json
#	apps/v4/public/r/styles/radix-maia/radio-group.json
#	apps/v4/public/r/styles/radix-maia/select-example.json
#	apps/v4/public/r/styles/radix-maia/select.json
#	apps/v4/public/r/styles/radix-maia/sheet.json
#	apps/v4/public/r/styles/radix-maia/sidebar-example.json
#	apps/v4/public/r/styles/radix-maia/sidebar-floating-example.json
#	apps/v4/public/r/styles/radix-maia/sidebar-icon-example.json
#	apps/v4/public/r/styles/radix-maia/sidebar-inset-example.json
#	apps/v4/public/r/styles/radix-maia/sidebar.json
#	apps/v4/public/r/styles/radix-maia/spinner-example.json
#	apps/v4/public/r/styles/radix-maia/spinner.json
#	apps/v4/public/r/styles/radix-maia/table-example.json
#	apps/v4/public/r/styles/radix-maia/tabs-example.json
#	apps/v4/public/r/styles/radix-maia/toggle-example.json
#	apps/v4/public/r/styles/radix-maia/toggle-group-example.json
#	apps/v4/public/r/styles/radix-maia/tooltip-example.json
#	apps/v4/public/r/styles/radix-maia/vercel.json
#	apps/v4/public/r/styles/radix-mira/accordion-example.json
#	apps/v4/public/r/styles/radix-mira/accordion.json
#	apps/v4/public/r/styles/radix-mira/alert-dialog-example.json
#	apps/v4/public/r/styles/radix-mira/alert-example.json
#	apps/v4/public/r/styles/radix-mira/avatar-example.json
#	apps/v4/public/r/styles/radix-mira/badge-example.json
#	apps/v4/public/r/styles/radix-mira/breadcrumb.json
#	apps/v4/public/r/styles/radix-mira/button-example.json
#	apps/v4/public/r/styles/radix-mira/button-group-example.json
#	apps/v4/public/r/styles/radix-mira/calendar-example.json
#	apps/v4/public/r/styles/radix-mira/calendar.json
#	apps/v4/public/r/styles/radix-mira/card-example.json
#	apps/v4/public/r/styles/radix-mira/carousel.json
#	apps/v4/public/r/styles/radix-mira/chart-example.json
#	apps/v4/public/r/styles/radix-mira/chatgpt.json
#	apps/v4/public/r/styles/radix-mira/checkbox.json
#	apps/v4/public/r/styles/radix-mira/collapsible-example.json
#	apps/v4/public/r/styles/radix-mira/combobox-example.json
#	apps/v4/public/r/styles/radix-mira/combobox.json
#	apps/v4/public/r/styles/radix-mira/command-example.json
#	apps/v4/public/r/styles/radix-mira/command.json
#	apps/v4/public/r/styles/radix-mira/component-example.json
#	apps/v4/public/r/styles/radix-mira/context-menu-example.json
#	apps/v4/public/r/styles/radix-mira/context-menu.json
#	apps/v4/public/r/styles/radix-mira/dialog-example.json
#	apps/v4/public/r/styles/radix-mira/dialog.json
#	apps/v4/public/r/styles/radix-mira/dropdown-menu-example.json
#	apps/v4/public/r/styles/radix-mira/dropdown-menu.json
#	apps/v4/public/r/styles/radix-mira/empty-example.json
#	apps/v4/public/r/styles/radix-mira/github.json
#	apps/v4/public/r/styles/radix-mira/input-group-example.json
#	apps/v4/public/r/styles/radix-mira/input-otp-example.json
#	apps/v4/public/r/styles/radix-mira/input-otp.json
#	apps/v4/public/r/styles/radix-mira/item-example.json
#	apps/v4/public/r/styles/radix-mira/kbd-example.json
#	apps/v4/public/r/styles/radix-mira/menubar-example.json
#	apps/v4/public/r/styles/radix-mira/menubar.json
#	apps/v4/public/r/styles/radix-mira/native-select.json
#	apps/v4/public/r/styles/radix-mira/navigation-menu-example.json
#	apps/v4/public/r/styles/radix-mira/navigation-menu.json
#	apps/v4/public/r/styles/radix-mira/pagination.json
#	apps/v4/public/r/styles/radix-mira/preview.json
#	apps/v4/public/r/styles/radix-mira/progress-example.json
#	apps/v4/public/r/styles/radix-mira/radio-group.json
#	apps/v4/public/r/styles/radix-mira/select-example.json
#	apps/v4/public/r/styles/radix-mira/select.json
#	apps/v4/public/r/styles/radix-mira/sheet.json
#	apps/v4/public/r/styles/radix-mira/sidebar-example.json
#	apps/v4/public/r/styles/radix-mira/sidebar-floating-example.json
#	apps/v4/public/r/styles/radix-mira/sidebar-icon-example.json
#	apps/v4/public/r/styles/radix-mira/sidebar-inset-example.json
#	apps/v4/public/r/styles/radix-mira/sidebar.json
#	apps/v4/public/r/styles/radix-mira/spinner-example.json
#	apps/v4/public/r/styles/radix-mira/spinner.json
#	apps/v4/public/r/styles/radix-mira/table-example.json
#	apps/v4/public/r/styles/radix-mira/tabs-example.json
#	apps/v4/public/r/styles/radix-mira/toggle-example.json
#	apps/v4/public/r/styles/radix-mira/toggle-group-example.json
#	apps/v4/public/r/styles/radix-mira/tooltip-example.json
#	apps/v4/public/r/styles/radix-mira/vercel.json
#	apps/v4/public/r/styles/radix-nova/accordion-example.json
#	apps/v4/public/r/styles/radix-nova/accordion.json
#	apps/v4/public/r/styles/radix-nova/alert-dialog-example.json
#	apps/v4/public/r/styles/radix-nova/alert-example.json
#	apps/v4/public/r/styles/radix-nova/avatar-example.json
#	apps/v4/public/r/styles/radix-nova/badge-example.json
#	apps/v4/public/r/styles/radix-nova/breadcrumb.json
#	apps/v4/public/r/styles/radix-nova/button-example.json
#	apps/v4/public/r/styles/radix-nova/button-group-example.json
#	apps/v4/public/r/styles/radix-nova/calendar-example.json
#	apps/v4/public/r/styles/radix-nova/calendar.json
#	apps/v4/public/r/styles/radix-nova/card-example.json
#	apps/v4/public/r/styles/radix-nova/carousel.json
#	apps/v4/public/r/styles/radix-nova/chart-example.json
#	apps/v4/public/r/styles/radix-nova/chatgpt.json
#	apps/v4/public/r/styles/radix-nova/checkbox.json
#	apps/v4/public/r/styles/radix-nova/collapsible-example.json
#	apps/v4/public/r/styles/radix-nova/combobox-example.json
#	apps/v4/public/r/styles/radix-nova/combobox.json
#	apps/v4/public/r/styles/radix-nova/command-example.json
#	apps/v4/public/r/styles/radix-nova/command.json
#	apps/v4/public/r/styles/radix-nova/component-example.json
#	apps/v4/public/r/styles/radix-nova/context-menu-example.json
#	apps/v4/public/r/styles/radix-nova/context-menu.json
#	apps/v4/public/r/styles/radix-nova/dialog-example.json
#	apps/v4/public/r/styles/radix-nova/dialog.json
#	apps/v4/public/r/styles/radix-nova/dropdown-menu-example.json
#	apps/v4/public/r/styles/radix-nova/dropdown-menu.json
#	apps/v4/public/r/styles/radix-nova/empty-example.json
#	apps/v4/public/r/styles/radix-nova/github.json
#	apps/v4/public/r/styles/radix-nova/input-group-example.json
#	apps/v4/public/r/styles/radix-nova/input-otp-example.json
#	apps/v4/public/r/styles/radix-nova/input-otp.json
#	apps/v4/public/r/styles/radix-nova/item-example.json
#	apps/v4/public/r/styles/radix-nova/kbd-example.json
#	apps/v4/public/r/styles/radix-nova/menubar-example.json
#	apps/v4/public/r/styles/radix-nova/menubar.json
#	apps/v4/public/r/styles/radix-nova/native-select.json
#	apps/v4/public/r/styles/radix-nova/navigation-menu-example.json
#	apps/v4/public/r/styles/radix-nova/navigation-menu.json
#	apps/v4/public/r/styles/radix-nova/pagination.json
#	apps/v4/public/r/styles/radix-nova/preview.json
#	apps/v4/public/r/styles/radix-nova/progress-example.json
#	apps/v4/public/r/styles/radix-nova/radio-group.json
#	apps/v4/public/r/styles/radix-nova/select-example.json
#	apps/v4/public/r/styles/radix-nova/select.json
#	apps/v4/public/r/styles/radix-nova/sheet.json
#	apps/v4/public/r/styles/radix-nova/sidebar-example.json
#	apps/v4/public/r/styles/radix-nova/sidebar-floating-example.json
#	apps/v4/public/r/styles/radix-nova/sidebar-icon-example.json
#	apps/v4/public/r/styles/radix-nova/sidebar-inset-example.json
#	apps/v4/public/r/styles/radix-nova/sidebar.json
#	apps/v4/public/r/styles/radix-nova/spinner-example.json
#	apps/v4/public/r/styles/radix-nova/spinner.json
#	apps/v4/public/r/styles/radix-nova/table-example.json
#	apps/v4/public/r/styles/radix-nova/tabs-example.json
#	apps/v4/public/r/styles/radix-nova/toggle-example.json
#	apps/v4/public/r/styles/radix-nova/toggle-group-example.json
#	apps/v4/public/r/styles/radix-nova/tooltip-example.json
#	apps/v4/public/r/styles/radix-nova/vercel.json
#	apps/v4/public/r/styles/radix-vega/accordion-example.json
#	apps/v4/public/r/styles/radix-vega/accordion.json
#	apps/v4/public/r/styles/radix-vega/alert-dialog-example.json
#	apps/v4/public/r/styles/radix-vega/alert-example.json
#	apps/v4/public/r/styles/radix-vega/avatar-example.json
#	apps/v4/public/r/styles/radix-vega/badge-example.json
#	apps/v4/public/r/styles/radix-vega/breadcrumb.json
#	apps/v4/public/r/styles/radix-vega/button-example.json
#	apps/v4/public/r/styles/radix-vega/button-group-example.json
#	apps/v4/public/r/styles/radix-vega/calendar-example.json
#	apps/v4/public/r/styles/radix-vega/calendar.json
#	apps/v4/public/r/styles/radix-vega/card-example.json
#	apps/v4/public/r/styles/radix-vega/carousel.json
#	apps/v4/public/r/styles/radix-vega/chart-example.json
#	apps/v4/public/r/styles/radix-vega/chatgpt.json
#	apps/v4/public/r/styles/radix-vega/checkbox.json
#	apps/v4/public/r/styles/radix-vega/collapsible-example.json
#	apps/v4/public/r/styles/radix-vega/combobox-example.json
#	apps/v4/public/r/styles/radix-vega/combobox.json
#	apps/v4/public/r/styles/radix-vega/command-example.json
#	apps/v4/public/r/styles/radix-vega/command.json
#	apps/v4/public/r/styles/radix-vega/component-example.json
#	apps/v4/public/r/styles/radix-vega/context-menu-example.json
#	apps/v4/public/r/styles/radix-vega/context-menu.json
#	apps/v4/public/r/styles/radix-vega/dialog-example.json
#	apps/v4/public/r/styles/radix-vega/dialog.json
#	apps/v4/public/r/styles/radix-vega/dropdown-menu-example.json
#	apps/v4/public/r/styles/radix-vega/dropdown-menu.json
#	apps/v4/public/r/styles/radix-vega/empty-example.json
#	apps/v4/public/r/styles/radix-vega/github.json
#	apps/v4/public/r/styles/radix-vega/input-group-example.json
#	apps/v4/public/r/styles/radix-vega/input-otp-example.json
#	apps/v4/public/r/styles/radix-vega/input-otp.json
#	apps/v4/public/r/styles/radix-vega/item-example.json
#	apps/v4/public/r/styles/radix-vega/kbd-example.json
#	apps/v4/public/r/styles/radix-vega/menubar-example.json
#	apps/v4/public/r/styles/radix-vega/menubar.json
#	apps/v4/public/r/styles/radix-vega/native-select.json
#	apps/v4/public/r/styles/radix-vega/navigation-menu-example.json
#	apps/v4/public/r/styles/radix-vega/navigation-menu.json
#	apps/v4/public/r/styles/radix-vega/pagination.json
#	apps/v4/public/r/styles/radix-vega/preview.json
#	apps/v4/public/r/styles/radix-vega/progress-example.json
#	apps/v4/public/r/styles/radix-vega/radio-group.json
#	apps/v4/public/r/styles/radix-vega/select-example.json
#	apps/v4/public/r/styles/radix-vega/select.json
#	apps/v4/public/r/styles/radix-vega/sheet.json
#	apps/v4/public/r/styles/radix-vega/sidebar-example.json
#	apps/v4/public/r/styles/radix-vega/sidebar-floating-example.json
#	apps/v4/public/r/styles/radix-vega/sidebar-icon-example.json
#	apps/v4/public/r/styles/radix-vega/sidebar-inset-example.json
#	apps/v4/public/r/styles/radix-vega/sidebar.json
#	apps/v4/public/r/styles/radix-vega/spinner-example.json
#	apps/v4/public/r/styles/radix-vega/spinner.json
#	apps/v4/public/r/styles/radix-vega/table-example.json
#	apps/v4/public/r/styles/radix-vega/tabs-example.json
#	apps/v4/public/r/styles/radix-vega/toggle-example.json
#	apps/v4/public/r/styles/radix-vega/toggle-group-example.json
#	apps/v4/public/r/styles/radix-vega/tooltip-example.json
#	apps/v4/public/r/styles/radix-vega/vercel.json
#	apps/v4/scripts/build-registry.mts
2026-01-19 11:39:57 +04:00
phjjj
d04bc84a51 fix(registry): add missing {name} placeholder to motion-primitives url (#9381)
Co-authored-by: 박해준 <aaagowns@viewlingo.com>
2026-01-19 11:34:29 +04:00
Sunny Patel
f68465e815 docs(theming): add missing destructive-foreground CSS variable (#9379)
Fixes #9337

The `destructive-foreground` variable is used in components but was
missing from the theming documentation. Added the variable to all
color schemes (Neutral, Stone, Zinc, Gray, Slate) in both light and
dark modes.
2026-01-19 11:32:01 +04:00
shadcn
094edfcfe6 fix: charts 2026-01-18 12:11:20 +04:00
shadcn
5a42652c41 fix: theme for charts 2026-01-18 12:02:49 +04:00
shadcn
3409681949 fix: iframe display in dark mode 2026-01-18 11:53:59 +04:00
shadcn
1c989f9155 feat: inline component list on components page (#9368)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 18:40:11 +04:00
shadcn
0aea23013c fix: debug charts (#9364)
* fix: ts-morph for charts

* fix

* perf: parallelize chart loading and add LRU caching

- Prefetch all chart data in parallel using Promise.all()
- Add LRU cache for syntax highlighting (cross-request caching)
- Add LRU cache for registry items (cross-request caching)
- Parallelize file reads within registry items

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix

* fix

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 18:28:51 +04:00
shadcn
bfce3031a3 Merge branch 'main' of github.com:shadcn-ui/ui 2026-01-17 13:49:37 +04:00
shadcn
cfb81c61de docs: add shadcn/create callout 2026-01-17 13:49:30 +04:00
Luis Llanes
7860ab83d1 chore(registry): update @shadcraft registry url (#9348)
* chore(registry): update @shadcraft registry url

* fix

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-01-17 13:30:24 +04:00
Паламар Роман
2acaf954d7 Fix: Preserve 'use client' directive in universal registry items (#8798)
* fix: preserve 'use client' directive in universal registry items

Universal items (registry:file and registry:item) are framework-agnostic
components that can be installed without shadcn project initialization.
However, the RSC transformer was incorrectly removing 'use client'
directives from these files when config.rsc was false/undefined, breaking
client-side functionality.

This fix ensures transformers are skipped for universal items, preserving
their original content including 'use client' directives, while regular
shadcn components continue to have transformers applied as expected.

Changes:
- Skip all transformers for registry:file and registry:item types
- Add tests to verify 'use client' preservation in universal items
- Ensure regular components still have transformers applied

Fixes issue where universal items would lose 'use client' directives when
copied without a full shadcn project setup.

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-01-17 13:12:01 +04:00
github-actions[bot]
1e9e337923 chore(release): version packages (#9352)
* chore(release): version packages

* ci: deps

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shadcn <m@shadcn.com>
2026-01-16 18:07:12 +04:00
Neeraj Dalal
66d2400784 feat(icons): the icons we all love and adore - remixicon (#9156)
* feat: remixicon

* chore: update deps

* chore: update icon

* chore: fix issues

* chore: build registry

* chore: changeset

* deps

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-01-16 18:00:06 +04:00
shadcn
682c98989d feat: registry add command (#9351)
* feat: implement registry add

* chore: changeset

* fix: registries docs

* feat: update add command

* fix
2026-01-16 17:55:48 +04:00
shadcn
77d7b39ef7 chore: rebuild registry 2026-01-16 17:07:32 +04:00
shadcn
ff3c1e1d95 fix: input 2026-01-15 19:48:20 +04:00
shadcn
068f7c22aa feat: add field and hover-card 2026-01-15 17:32:16 +04:00
shadcn
b1b25fe15d feat: add empty 2026-01-15 16:51:57 +04:00
shadcn
689b4c6b41 feat: add date-picker 2026-01-15 16:32:35 +04:00
shadcn
66637058fc feat: dropdown-menu 2026-01-15 15:50:35 +04:00
shadcn
da07cf6ffe feat: add data-table dialog and drawer 2026-01-15 13:17:45 +04:00
shadcn
1a5b9ce036 feat: add context menu 2026-01-15 12:57:18 +04:00
shadcn
1ce874edd2 feat: add command 2026-01-15 12:34:24 +04:00
shadcn
45480505d8 feat: add combobox 2026-01-15 12:11:27 +04:00
shadcn
bd4ef8e08c feat: add collapsible 2026-01-15 11:25:35 +04:00
shadcn
5a897b7765 feat: add checkbox 2026-01-15 10:59:58 +04:00
shadcn
3f62e7dee0 fix: chart 2026-01-15 10:26:46 +04:00
shadcn
f2d4395233 feat: add carousel 2026-01-15 09:36:08 +04:00
shadcn
3ab7d04824 feat: add calendar and card 2026-01-15 08:54:40 +04:00
shadcn
58f73f62a0 fix 2026-01-14 22:05:15 +04:00
shadcn
d9061d64aa fix 2026-01-14 22:02:57 +04:00
shadcn
4784f264c5 fix 2026-01-14 21:37:16 +04:00
shadcn
7031141cf3 feat: add button 2026-01-14 21:30:23 +04:00
shadcn
d0fe494491 fix 2026-01-14 20:55:40 +04:00
shadcn
be0b798e21 feat: add breadcrumb 2026-01-14 20:46:23 +04:00
Huy Hoàng
5b3ba49aec fix(calendar): fix typo 'elative' to 'relative' in range_start classname (#9292)
Fixes #9278
2026-01-14 20:43:36 +04:00
shadcn
953107e7f9 feat: add badge 2026-01-14 20:01:05 +04:00
shadcn
c880796bf2 feat: add avatar 2026-01-14 19:26:01 +04:00
shadcn
48b069c453 feat: add aspect-ratio 2026-01-14 13:19:37 +04:00
shadcn
b5c7a014c8 fix 2026-01-14 12:59:48 +04:00
shadcn
aee10914fe feat: add more examples 2026-01-14 09:25:14 +04:00
shadcn
aadba2f859 Merge branch 'main' into shadcn/base-docs 2026-01-13 17:03:38 +04:00
shadcn
54edfd228d feat: add new registries (#9325)
* add new registries

* fix

* fix

* docs: add warning

* fix
2026-01-13 16:19:15 +04:00
Aniket Pawar
fd3e5515f3 feat: add @heroicons-animated to directory.json and registries.json (#9268)
* Add new registry for heroicons-animated

* Add '@heroicons-animated' collection to directory

Added new animated icon collection '@heroicons-animated' with homepage, URL, description, and logo.

* Update URL for @heroicons-animated registry

* Update directory.json

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-01-12 18:07:44 +04:00
Amarnath Dhumal
65ad910bca Add Chamaac registry (#9208)
Co-authored-by: shadcn <m@shadcn.com>
2026-01-12 18:05:47 +04:00
Md Kawsar Islam Yeasin
d4a1c89e8e feat: add neobrutalism to registry directory (#9168) 2026-01-12 18:01:59 +04:00
LN
78023693c6 Feat/add registry directory icons animated (#9143)
* feat: add new registry entry for icons-animated

* feat: add new registry entry for icons-animated with logo and description

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-01-12 18:01:37 +04:00
Aman Shakya
0fc52a7f4d Add new registry entry for @forgeui (#9074)
* added forgeui in registries

* Remove duplicate entries in registries.json

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-01-12 17:59:54 +04:00
shadcn
dced7f6045 fix 2026-01-08 22:25:29 +04:00
shadcn
cbe672151a chore: run format:write 2026-01-08 21:35:21 +04:00
shadcn
62aef1117f fix 2026-01-08 21:27:27 +04:00
shadcn
cbc9ed8688 fix 2026-01-08 15:26:02 +04:00
shadcn
b309b1a060 fix 2026-01-08 13:13:10 +04:00
shadcn
e003cf74d2 chore: remove claude files 2026-01-08 12:59:38 +04:00
shadcn
34f1061c6b fix 2026-01-08 12:54:08 +04:00
shadcn
ed28a348c7 fix 2026-01-08 12:05:23 +04:00
shadcn
38bcb3c2eb fix 2026-01-07 22:43:45 +04:00
shadcn
e9acf86c24 fix 2026-01-07 22:05:19 +04:00
shadcn
2f829db41d fix 2026-01-07 14:37:42 +04:00
shadcn
413dc4c01f feat: transform code for display 2026-01-06 16:58:56 +04:00
shadcn
eb098f87d2 feat: add base and radix docs 2026-01-06 16:43:01 +04:00
2481 changed files with 78753 additions and 9354 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
Fix: skip all transforms for universal registry items

View File

@@ -1,63 +0,0 @@
name: Add registry to directory
description: Add your registry to the directory
title: "[Registry Directory]: "
labels: ["registry", "directory"]
assignees: []
body:
- type: input
id: name
attributes:
label: Name
description: The name of your registry. This is also the namespace.
placeholder: e.g., "@acme"
validations:
required: true
- type: input
id: url
attributes:
label: URL
description: The URL to your registry index. Use {name} placeholder.
placeholder: https://ui.acme.com/r/{name}.json
validations:
required: true
- type: input
id: homepage
attributes:
label: Homepage
description: The URL to your registry homepage. This is where users can browse your registry.
placeholder: https://ui.acme.com
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Briefly describe what is your registry and what type of components or code it distributes.
placeholder:
validations:
required: true
- type: textarea
id: logo
attributes:
label: Logo
description: Add your SVG logo here.
placeholder:
validations:
required: true
- type: checkboxes
id: requirements
attributes:
label: Checklist
description: Verify that your registry meets the following requirements.
options:
- label: The registry must be open source and publicly accessible.
- label: The registry must be a valid JSON file that conforms to the [registry schema](https://ui.shadcn.com/docs/registry/registry-json) specification.
- label: The `files` array, if present on your registry items, must NOT include a `content` property.
- label: I've attached a square SVG logo to this issue
validations:
required: true

View File

@@ -1,10 +1,8 @@
"use client"
import * as React from "react"
import { IconMinus, IconPlus } from "@tabler/icons-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
import { Button } from "@/examples/radix/ui/button"
import { ButtonGroup } from "@/examples/radix/ui/button-group"
import {
Field,
FieldContent,
@@ -15,13 +13,11 @@ import {
FieldSeparator,
FieldSet,
FieldTitle,
} from "@/registry/new-york-v4/ui/field"
import { Input } from "@/registry/new-york-v4/ui/input"
import {
RadioGroup,
RadioGroupItem,
} from "@/registry/new-york-v4/ui/radio-group"
import { Switch } from "@/registry/new-york-v4/ui/switch"
} from "@/examples/radix/ui/field"
import { Input } from "@/examples/radix/ui/input"
import { RadioGroup, RadioGroupItem } from "@/examples/radix/ui/radio-group"
import { Switch } from "@/examples/radix/ui/switch"
import { IconMinus, IconPlus } from "@tabler/icons-react"
export function AppearanceSettings() {
const [gpuCount, setGpuCount] = React.useState(8)

View File

@@ -1,20 +1,8 @@
"use client"
import * as React from "react"
import {
ArchiveIcon,
ArrowLeftIcon,
CalendarPlusIcon,
ClockIcon,
ListFilterIcon,
MailCheckIcon,
MoreHorizontalIcon,
TagIcon,
Trash2Icon,
} from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
import { Button } from "@/examples/radix/ui/button"
import { ButtonGroup } from "@/examples/radix/ui/button-group"
import {
DropdownMenu,
DropdownMenuContent,
@@ -27,7 +15,18 @@ import {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
} from "@/examples/radix/ui/dropdown-menu"
import {
ArchiveIcon,
ArrowLeftIcon,
CalendarPlusIcon,
ClockIcon,
ListFilterIcon,
MailCheckIcon,
MoreHorizontalIcon,
TagIcon,
Trash2Icon,
} from "lucide-react"
export function ButtonGroupDemo() {
const [label, setLabel] = React.useState("personal")

View File

@@ -1,21 +1,20 @@
"use client"
import * as React from "react"
import { AudioLinesIcon, PlusIcon } from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
import { Button } from "@/examples/radix/ui/button"
import { ButtonGroup } from "@/examples/radix/ui/button-group"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/registry/new-york-v4/ui/input-group"
} from "@/examples/radix/ui/input-group"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
} from "@/examples/radix/ui/tooltip"
import { AudioLinesIcon, PlusIcon } from "lucide-react"
export function ButtonGroupInputGroup() {
const [voiceEnabled, setVoiceEnabled] = React.useState(false)

View File

@@ -1,10 +1,9 @@
"use client"
import { Button } from "@/examples/radix/ui/button"
import { ButtonGroup } from "@/examples/radix/ui/button-group"
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
export function ButtonGroupNested() {
return (
<ButtonGroup>

View File

@@ -1,14 +1,13 @@
import { BotIcon, ChevronDownIcon } from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
import { Button } from "@/examples/radix/ui/button"
import { ButtonGroup } from "@/examples/radix/ui/button-group"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
import { Separator } from "@/registry/new-york-v4/ui/separator"
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
} from "@/examples/radix/ui/popover"
import { Separator } from "@/examples/radix/ui/separator"
import { Textarea } from "@/examples/radix/ui/textarea"
import { BotIcon, ChevronDownIcon } from "lucide-react"
export function ButtonGroupPopover() {
return (

View File

@@ -1,11 +1,5 @@
import { PlusIcon } from "lucide-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Avatar, AvatarFallback, AvatarImage } from "@/examples/radix/ui/avatar"
import { Button } from "@/examples/radix/ui/button"
import {
Empty,
EmptyContent,
@@ -13,7 +7,8 @@ import {
EmptyHeader,
EmptyMedia,
EmptyTitle,
} from "@/registry/new-york-v4/ui/empty"
} from "@/examples/radix/ui/empty"
import { PlusIcon } from "lucide-react"
export function EmptyAvatarGroup() {
return (

View File

@@ -1,5 +1,5 @@
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import { Field, FieldLabel } from "@/registry/new-york-v4/ui/field"
import { Checkbox } from "@/examples/radix/ui/checkbox"
import { Field, FieldLabel } from "@/examples/radix/ui/field"
export function FieldCheckbox() {
return (

View File

@@ -1,5 +1,5 @@
import { Button } from "@/registry/new-york-v4/ui/button"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import { Button } from "@/examples/radix/ui/button"
import { Checkbox } from "@/examples/radix/ui/checkbox"
import {
Field,
FieldDescription,
@@ -8,16 +8,16 @@ import {
FieldLegend,
FieldSeparator,
FieldSet,
} from "@/registry/new-york-v4/ui/field"
import { Input } from "@/registry/new-york-v4/ui/input"
} from "@/examples/radix/ui/field"
import { Input } from "@/examples/radix/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york-v4/ui/select"
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
} from "@/examples/radix/ui/select"
import { Textarea } from "@/examples/radix/ui/textarea"
export function FieldDemo() {
return (

View File

@@ -1,5 +1,5 @@
import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import { Card, CardContent } from "@/examples/radix/ui/card"
import { Checkbox } from "@/examples/radix/ui/checkbox"
import {
Field,
FieldDescription,
@@ -8,7 +8,7 @@ import {
FieldLegend,
FieldSet,
FieldTitle,
} from "@/registry/new-york-v4/ui/field"
} from "@/examples/radix/ui/field"
const options = [
{

View File

@@ -1,13 +1,8 @@
"use client"
import { useState } from "react"
import {
Field,
FieldDescription,
FieldTitle,
} from "@/registry/new-york-v4/ui/field"
import { Slider } from "@/registry/new-york-v4/ui/slider"
import { Field, FieldDescription, FieldTitle } from "@/examples/radix/ui/field"
import { Slider } from "@/examples/radix/ui/slider"
export function FieldSlider() {
const [value, setValue] = useState([200, 800])

View File

@@ -1,4 +1,4 @@
import { FieldSeparator } from "@/registry/new-york-v4/ui/field"
import { FieldSeparator } from "@/examples/radix/ui/field"
import { AppearanceSettings } from "./appearance-settings"
import { ButtonGroupDemo } from "./button-group-demo"

View File

@@ -1,20 +1,19 @@
"use client"
import * as React from "react"
import { IconInfoCircle, IconStar } from "@tabler/icons-react"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/registry/new-york-v4/ui/input-group"
import { Label } from "@/registry/new-york-v4/ui/label"
} from "@/examples/radix/ui/input-group"
import { Label } from "@/examples/radix/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
} from "@/examples/radix/ui/popover"
import { IconInfoCircle, IconStar } from "@tabler/icons-react"
export function InputGroupButtonExample() {
const [isFavorite, setIsFavorite] = React.useState(false)

View File

@@ -1,12 +1,9 @@
import { IconCheck, IconInfoCircle, IconPlus } from "@tabler/icons-react"
import { ArrowUpIcon, Search } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
} from "@/examples/radix/ui/dropdown-menu"
import {
InputGroup,
InputGroupAddon,
@@ -14,13 +11,15 @@ import {
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@/registry/new-york-v4/ui/input-group"
import { Separator } from "@/registry/new-york-v4/ui/separator"
} from "@/examples/radix/ui/input-group"
import { Separator } from "@/examples/radix/ui/separator"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
} from "@/examples/radix/ui/tooltip"
import { IconCheck, IconInfoCircle, IconPlus } from "@tabler/icons-react"
import { ArrowUpIcon, Search } from "lucide-react"
export function InputGroupDemo() {
return (

View File

@@ -1,6 +1,4 @@
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
import { Button } from "@/registry/new-york-v4/ui/button"
import { Button } from "@/examples/radix/ui/button"
import {
Item,
ItemActions,
@@ -8,7 +6,8 @@ import {
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/registry/new-york-v4/ui/item"
} from "@/examples/radix/ui/item"
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
export function ItemDemo() {
return (

View File

@@ -1,24 +1,8 @@
"use client"
import { useMemo, useState } from "react"
import {
IconApps,
IconArrowUp,
IconAt,
IconBook,
IconCircleDashedPlus,
IconPaperclip,
IconPlus,
IconWorld,
IconX,
} from "@tabler/icons-react"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york-v4/ui/avatar"
import { Badge } from "@/registry/new-york-v4/ui/badge"
import { Avatar, AvatarFallback, AvatarImage } from "@/examples/radix/ui/avatar"
import { Badge } from "@/examples/radix/ui/badge"
import {
Command,
CommandEmpty,
@@ -26,7 +10,7 @@ import {
CommandInput,
CommandItem,
CommandList,
} from "@/registry/new-york-v4/ui/command"
} from "@/examples/radix/ui/command"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
@@ -39,25 +23,36 @@ import {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/registry/new-york-v4/ui/dropdown-menu"
import { Field, FieldLabel } from "@/registry/new-york-v4/ui/field"
} from "@/examples/radix/ui/dropdown-menu"
import { Field, FieldLabel } from "@/examples/radix/ui/field"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupTextarea,
} from "@/registry/new-york-v4/ui/input-group"
} from "@/examples/radix/ui/input-group"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york-v4/ui/popover"
import { Switch } from "@/registry/new-york-v4/ui/switch"
} from "@/examples/radix/ui/popover"
import { Switch } from "@/examples/radix/ui/switch"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
} from "@/examples/radix/ui/tooltip"
import {
IconApps,
IconArrowUp,
IconAt,
IconBook,
IconCircleDashedPlus,
IconPaperclip,
IconPlus,
IconWorld,
IconX,
} from "@tabler/icons-react"
const SAMPLE_DATA = {
mentionable: [

View File

@@ -1,5 +1,5 @@
import { Badge } from "@/registry/new-york-v4/ui/badge"
import { Spinner } from "@/registry/new-york-v4/ui/spinner"
import { Badge } from "@/examples/radix/ui/badge"
import { Spinner } from "@/examples/radix/ui/spinner"
export function SpinnerBadge() {
return (

View File

@@ -1,4 +1,4 @@
import { Button } from "@/registry/new-york-v4/ui/button"
import { Button } from "@/examples/radix/ui/button"
import {
Empty,
EmptyContent,
@@ -6,8 +6,8 @@ import {
EmptyHeader,
EmptyMedia,
EmptyTitle,
} from "@/registry/new-york-v4/ui/empty"
import { Spinner } from "@/registry/new-york-v4/ui/spinner"
} from "@/examples/radix/ui/empty"
import { Spinner } from "@/examples/radix/ui/spinner"
export function SpinnerEmpty() {
return (

View File

@@ -1,8 +1,6 @@
import { type Metadata } from "next"
import Image from "next/image"
import Link from "next/link"
import { PlusSignIcon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { Announcement } from "@/components/announcement"
import { ExamplesNav } from "@/components/examples-nav"
@@ -58,10 +56,7 @@ export default function IndexPage() {
<PageHeaderDescription>{description}</PageHeaderDescription>
<PageActions>
<Button asChild size="sm" className="h-[31px] rounded-lg">
<Link href="/create">
<HugeiconsIcon icon={PlusSignIcon} />
New Project
</Link>
<Link href="/docs/installation">Get Started</Link>
</Button>
<Button asChild size="sm" variant="ghost" className="rounded-lg">
<Link href="/docs/components">View Components</Link>

View File

@@ -2,7 +2,11 @@ import * as React from "react"
import { notFound } from "next/navigation"
import { cn } from "@/lib/utils"
import { ChartDisplay } from "@/components/chart-display"
import {
ChartDisplay,
getCachedRegistryItem,
getChartHighlightedCode,
} from "@/components/chart-display"
import { getActiveStyle } from "@/registry/_legacy-styles"
import { charts } from "@/app/(app)/charts/charts"
@@ -44,6 +48,26 @@ export default async function ChartPage({ params }: ChartPageProps) {
const chartList = charts[chartType]
const activeStyle = await getActiveStyle()
// Prefetch all chart data in parallel for better performance.
// Charts are rendered via iframes, so we only need the metadata and highlighted code.
const chartDataPromises = chartList.map(async (chart) => {
const registryItem = await getCachedRegistryItem(chart.id, activeStyle.name)
if (!registryItem) return null
const highlightedCode = await getChartHighlightedCode(
registryItem.files?.[0]?.content ?? ""
)
if (!highlightedCode) return null
return {
...registryItem,
highlightedCode,
fullWidth: chart.fullWidth,
}
})
const prefetchedCharts = await Promise.all(chartDataPromises)
return (
<div className="grid flex-1 gap-12 lg:gap-24">
<h2 className="sr-only">
@@ -51,16 +75,14 @@ export default async function ChartPage({ params }: ChartPageProps) {
</h2>
<div className="grid flex-1 scroll-mt-20 items-stretch gap-10 md:grid-cols-2 md:gap-6 lg:grid-cols-3 xl:gap-10">
{Array.from({ length: 12 }).map((_, index) => {
const chart = chartList[index]
const chart = prefetchedCharts[index]
return chart ? (
<ChartDisplay
key={chart.id}
name={chart.id}
styleName={activeStyle.name}
key={chart.name}
chart={chart}
style={activeStyle.name}
className={cn(chart.fullWidth && "md:col-span-2 lg:col-span-3")}
>
<chart.component />
</ChartDisplay>
/>
) : (
<div
key={`empty-${index}`}

View File

@@ -63,9 +63,8 @@ export default function ChartsLayout({
</PageHeader>
<PageNav id="charts">
<ChartsNav />
<ThemeSelector className="mr-4 hidden md:flex" />
</PageNav>
<div className="container-wrapper section-soft flex-1">
<div className="container-wrapper flex-1">
<div className="container pb-6">
<section className="theme-container">{children}</section>
</div>

View File

@@ -1,21 +1,15 @@
import Link from "next/link"
import { notFound } from "next/navigation"
import { mdxComponents } from "@/mdx-components"
import {
IconArrowLeft,
IconArrowRight,
IconArrowUpRight,
} from "@tabler/icons-react"
import fm from "front-matter"
import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react"
import { findNeighbour } from "fumadocs-core/page-tree"
import z from "zod"
import { source } from "@/lib/source"
import { absoluteUrl } from "@/lib/utils"
import { DocsBaseSwitcher } from "@/components/docs-base-switcher"
import { DocsCopyPage } from "@/components/docs-copy-page"
import { DocsTableOfContents } from "@/components/docs-toc"
import { OpenInV0Cta } from "@/components/open-in-v0-cta"
import { Badge } from "@/registry/new-york-v4/ui/badge"
import { Button } from "@/registry/new-york-v4/ui/button"
export const revalidate = false
@@ -86,126 +80,110 @@ export default async function Page(props: {
const doc = page.data
const MDX = doc.body
const neighbours = findNeighbour(source.pageTree, page.url)
const raw = await page.data.getText("raw")
const { attributes } = fm(raw)
const { links } = z
.object({
links: z
.object({
doc: z.string().optional(),
api: z.string().optional(),
})
.optional(),
})
.parse(attributes)
return (
<div className="flex items-stretch text-[1.05rem] sm:text-[15px] xl:w-full">
<div
data-slot="docs"
className="flex scroll-mt-24 items-stretch pb-8 text-[1.05rem] sm:text-[15px] xl:w-full"
>
<div className="flex min-w-0 flex-1 flex-col">
<div className="h-(--top-spacing) shrink-0" />
<div className="mx-auto flex w-full max-w-2xl min-w-0 flex-1 flex-col gap-8 px-4 py-6 text-neutral-800 md:px-0 lg:py-8 dark:text-neutral-300">
<div className="mx-auto flex w-full max-w-[40rem] min-w-0 flex-1 flex-col gap-6 px-4 py-6 text-neutral-800 md:px-0 lg:py-8 dark:text-neutral-300">
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-2">
<div className="flex items-start justify-between">
<h1 className="scroll-m-20 text-4xl font-semibold tracking-tight sm:text-3xl xl:text-4xl">
<h1 className="scroll-m-24 text-4xl font-semibold tracking-tight sm:text-3xl">
{doc.title}
</h1>
<div className="docs-nav bg-background/80 border-border/50 fixed inset-x-0 bottom-0 isolate z-50 flex items-center gap-2 border-t px-6 py-4 backdrop-blur-sm sm:static sm:z-0 sm:border-t-0 sm:bg-transparent sm:px-0 sm:pt-1.5 sm:backdrop-blur-none">
<div className="docs-nav bg-background/80 border-border/50 fixed inset-x-0 bottom-0 isolate z-50 flex items-center gap-2 border-t px-6 py-4 backdrop-blur-sm sm:static sm:z-0 sm:border-t-0 sm:bg-transparent sm:px-0 sm:py-1.5 sm:backdrop-blur-none">
<DocsCopyPage page={raw} url={absoluteUrl(page.url)} />
{neighbours.previous && (
<Button
variant="secondary"
size="icon"
className="extend-touch-target ml-auto size-8 shadow-none md:size-7"
asChild
>
<Link href={neighbours.previous.url}>
<IconArrowLeft />
<span className="sr-only">Previous</span>
</Link>
</Button>
)}
{neighbours.next && (
<Button
variant="secondary"
size="icon"
className="extend-touch-target size-8 shadow-none md:size-7"
asChild
>
<Link href={neighbours.next.url}>
<span className="sr-only">Next</span>
<IconArrowRight />
</Link>
</Button>
)}
<div className="ml-auto flex gap-2">
{neighbours.previous && (
<Button
variant="secondary"
size="icon"
className="extend-touch-target size-8 shadow-none md:size-7"
asChild
>
<Link href={neighbours.previous.url}>
<IconArrowLeft />
<span className="sr-only">Previous</span>
</Link>
</Button>
)}
{neighbours.next && (
<Button
variant="secondary"
size="icon"
className="extend-touch-target size-8 shadow-none md:size-7"
asChild
>
<Link href={neighbours.next.url}>
<span className="sr-only">Next</span>
<IconArrowRight />
</Link>
</Button>
)}
</div>
</div>
</div>
{doc.description && (
<p className="text-muted-foreground text-[1.05rem] text-balance sm:text-base">
<p className="text-muted-foreground text-[1.05rem] sm:text-base sm:text-balance md:max-w-[80%]">
{doc.description}
</p>
)}
</div>
{links ? (
<div className="flex items-center gap-2 pt-4">
{links?.doc && (
<Badge asChild variant="secondary" className="rounded-full">
<a href={links.doc} target="_blank" rel="noreferrer">
Docs <IconArrowUpRight />
</a>
</Badge>
)}
{links?.api && (
<Badge asChild variant="secondary" className="rounded-full">
<a href={links.api} target="_blank" rel="noreferrer">
API Reference <IconArrowUpRight />
</a>
</Badge>
)}
</div>
) : null}
</div>
<div className="w-full flex-1 *:data-[slot=alert]:first:mt-0">
<div className="w-full flex-1 pb-16 *:data-[slot=alert]:first:mt-0 sm:pb-0">
{params.slug &&
params.slug[0] === "components" &&
params.slug[1] &&
params.slug[2] && (
<DocsBaseSwitcher
base={params.slug[1]}
component={params.slug[2]}
className="mb-4"
/>
)}
<MDX components={mdxComponents} />
</div>
</div>
<div className="mx-auto hidden h-16 w-full max-w-2xl items-center gap-2 px-4 sm:flex md:px-0">
{neighbours.previous && (
<Button
variant="secondary"
size="sm"
asChild
className="shadow-none"
>
<Link href={neighbours.previous.url}>
<IconArrowLeft /> {neighbours.previous.name}
</Link>
</Button>
)}
{neighbours.next && (
<Button
variant="secondary"
size="sm"
className="ml-auto shadow-none"
asChild
>
<Link href={neighbours.next.url}>
{neighbours.next.name} <IconArrowRight />
</Link>
</Button>
)}
<div className="hidden h-16 w-full items-center gap-2 px-4 sm:flex sm:px-0">
{neighbours.previous && (
<Button
variant="secondary"
size="sm"
asChild
className="shadow-none"
>
<Link href={neighbours.previous.url}>
<IconArrowLeft /> {neighbours.previous.name}
</Link>
</Button>
)}
{neighbours.next && (
<Button
variant="secondary"
size="sm"
className="ml-auto shadow-none"
asChild
>
<Link href={neighbours.next.url}>
{neighbours.next.name} <IconArrowRight />
</Link>
</Button>
)}
</div>
</div>
</div>
<div className="sticky top-[calc(var(--header-height)+1px)] z-30 ml-auto hidden h-[calc(100svh-var(--footer-height)+2rem)] w-72 flex-col gap-4 overflow-hidden overscroll-none pb-8 xl:flex">
<div className="h-(--top-spacing) shrink-0" />
<div className="sticky top-[calc(var(--header-height)+1px)] z-30 ml-auto hidden h-[90svh] w-72 flex-col gap-4 overflow-hidden overscroll-none pb-8 lg:flex">
<div className="h-(--top-spacing) shrink-0"></div>
{doc.toc?.length ? (
<div className="no-scrollbar overflow-y-auto px-8">
<div className="no-scrollbar flex flex-col gap-8 overflow-y-auto px-8">
<DocsTableOfContents toc={doc.toc} />
<div className="h-12" />
</div>
) : null}
<div className="flex flex-1 flex-col gap-12 px-6">
<div className="hidden flex-1 flex-col gap-6 px-6 xl:flex">
<OpenInV0Cta />
</div>
</div>

View File

@@ -9,7 +9,10 @@ export default function DocsLayout({
}) {
return (
<div className="container-wrapper flex flex-1 flex-col px-2">
<SidebarProvider className="3xl:fixed:container 3xl:fixed:px-3 min-h-min flex-1 items-start px-0 [--sidebar-width:220px] [--top-spacing:0] lg:grid lg:grid-cols-[var(--sidebar-width)_minmax(0,1fr)] lg:[--sidebar-width:240px] lg:[--top-spacing:calc(var(--spacing)*4)]">
<SidebarProvider
className="3xl:fixed:container 3xl:fixed:px-3 min-h-min flex-1 items-start px-0 [--top-spacing:0] lg:grid lg:grid-cols-[var(--sidebar-width)_minmax(0,1fr)] lg:[--top-spacing:calc(var(--spacing)*4)]"
style={{ "--sidebar-width": "220px" } as React.CSSProperties}
>
<DocsSidebar tree={source.pageTree} />
<div className="h-full w-full">{children}</div>
</SidebarProvider>

View File

@@ -3,10 +3,23 @@ import { NextResponse, type NextRequest } from "next/server"
import { processMdxForLLMs } from "@/lib/llm"
import { source } from "@/lib/source"
import { getActiveStyle } from "@/registry/_legacy-styles"
import { getActiveStyle, type Style } from "@/registry/_legacy-styles"
export const revalidate = false
function getStyleFromSlug(slug: string[] | undefined, fallbackStyle: string) {
// Detect base from URL: /docs/components/base/... or /docs/components/radix/...
if (slug && slug[0] === "components" && slug[1]) {
if (slug[1] === "base") {
return "base-nova"
}
if (slug[1] === "radix") {
return "new-york-v4"
}
}
return fallbackStyle
}
export async function GET(
_req: NextRequest,
{ params }: { params: Promise<{ slug?: string[] }> }
@@ -19,9 +32,11 @@ export async function GET(
notFound()
}
const effectiveStyle = getStyleFromSlug(slug, activeStyle.name)
const processedContent = processMdxForLLMs(
await page.data.getText("raw"),
activeStyle.name
effectiveStyle as Style["name"]
)
return new NextResponse(processedContent, {

View File

@@ -45,6 +45,12 @@ const IconPhosphor = lazy(() =>
}))
)
const IconRemixicon = lazy(() =>
import("@/registry/icons/icon-remixicon").then((mod) => ({
default: mod.IconRemixicon,
}))
)
const PREVIEW_ICONS = {
lucide: [
"CopyIcon",
@@ -110,6 +116,22 @@ const PREVIEW_ICONS = {
"CaretDownIcon",
"CaretRightIcon",
],
remixicon: [
"RiFileCopyLine",
"RiErrorWarningLine",
"RiDeleteBinLine",
"RiShareLine",
"RiShoppingBagLine",
"RiMoreLine",
"RiLoaderLine",
"RiAddLine",
"RiSubtractLine",
"RiArrowLeftLine",
"RiArrowRightLine",
"RiCheckLine",
"RiArrowDownSLine",
"RiArrowRightSLine",
],
}
const logos = {
@@ -194,6 +216,17 @@ const logos = {
/>
</svg>
),
remixicon: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor"
>
<path d="M12 2C17.5228 2 22 6.47715 22 12C22 15.3137 19.3137 18 16 18C12.6863 18 10 15.3137 10 12C10 11.4477 9.55228 11 9 11C8.44772 11 8 11.4477 8 12C8 16.4183 11.5817 20 16 20C16.8708 20 17.7084 19.8588 18.4932 19.6016C16.7458 21.0956 14.4792 22 12 22C6.6689 22 2.3127 17.8283 2.0166 12.5713C2.23647 9.45772 4.83048 7 8 7C11.3137 7 14 9.68629 14 13C14 13.5523 14.4477 14 15 14C15.5523 14 16 13.5523 16 13C16 8.58172 12.4183 5 8 5C6.50513 5 5.1062 5.41032 3.90918 6.12402C5.72712 3.62515 8.67334 2 12 2Z" />
</svg>
),
}
export function IconLibraryPicker({
@@ -301,7 +334,9 @@ const IconLibraryPreview = memo(function IconLibraryPreview({
? IconTabler
: iconLibrary === "hugeicons"
? IconHugeicons
: IconPhosphor
: iconLibrary === "phosphor"
? IconPhosphor
: IconRemixicon
return (
<Suspense

View File

@@ -30,6 +30,12 @@ const IconPhosphor = lazy(() =>
}))
)
const IconRemixicon = lazy(() =>
import("@/registry/icons/icon-remixicon").then((mod) => ({
default: mod.IconRemixicon,
}))
)
export function IconPlaceholder({
...props
}: {
@@ -52,6 +58,9 @@ export function IconPlaceholder({
{iconLibrary === "phosphor" && (
<IconPhosphor name={iconName} {...props} />
)}
{iconLibrary === "remixicon" && (
<IconRemixicon name={iconName} {...props} />
)}
</Suspense>
)
}

View File

@@ -99,7 +99,7 @@ export function ItemPicker({
variant="outline"
aria-label="Select item"
size="sm"
className="data-popup-open:bg-muted dark:data-popup-open:bg-muted/50 bg-muted/50 sm:bg-background md:dark:bg-background border-foreground/10 dark:bg-muted/50 h-[calc(--spacing(13.5))] flex-1 touch-manipulation justify-between gap-2 rounded-xl pr-4! pl-2.5 text-left shadow-none select-none *:data-[slot=combobox-trigger-icon]:hidden sm:h-8 sm:max-w-56 sm:rounded-lg sm:pr-2! xl:max-w-md"
className="data-popup-open:bg-muted dark:data-popup-open:bg-muted/50 bg-muted/50 sm:bg-background md:dark:bg-background border-foreground/10 dark:bg-muted/50 h-[calc(--spacing(13.5))] flex-1 touch-manipulation justify-between gap-2 rounded-xl pr-4! pl-2.5 text-left shadow-none select-none *:data-[slot=combobox-trigger-icon]:hidden sm:h-8 sm:max-w-56 sm:rounded-lg sm:pr-2! xl:max-w-64"
/>
}
>
@@ -123,9 +123,9 @@ export function ItemPicker({
<HugeiconsIcon icon={Search01Icon} />
</ComboboxTrigger>
<ComboboxContent
className="ring-foreground/10 min-w-[calc(var(--available-width)---spacing(4))] translate-x-2 animate-none rounded-xl border-0 ring-1 data-open:animate-none sm:min-w-[calc(var(--anchor-width)+--spacing(7))] sm:translate-x-0"
className="ring-foreground/10 min-w-[calc(var(--available-width)---spacing(4))] translate-x-2 animate-none rounded-xl border-0 ring-1 data-open:animate-none sm:min-w-[calc(var(--anchor-width)+--spacing(7))] sm:translate-x-0 xl:w-96"
side="bottom"
align="center"
align="end"
>
<ComboboxInput
showTrigger={false}

View File

@@ -139,6 +139,7 @@ function PickerSubTrigger({
tabler="IconChevronRight"
hugeicons="ArrowRight01Icon"
phosphor="CaretRightIcon"
remixicon="RiArrowRightSLine"
className="ml-auto"
/>
</MenuPrimitive.SubmenuTrigger>
@@ -192,6 +193,7 @@ function PickerCheckboxItem({
tabler="IconCheck"
hugeicons="Tick02Icon"
phosphor="CheckIcon"
remixicon="RiCheckLine"
/>
</MenuPrimitive.CheckboxItemIndicator>
</span>
@@ -233,6 +235,7 @@ function PickerRadioItem({
tabler="IconCheck"
hugeicons="Tick02Icon"
phosphor="CheckIcon"
remixicon="RiCheckLine"
className="size-4 pointer-coarse:size-5"
/>
</MenuPrimitive.RadioItemIndicator>

View File

@@ -56,7 +56,7 @@ export function ShareButton() {
<Button
size="sm"
variant="outline"
className="rounded-lg shadow-none"
className="rounded-lg shadow-none lg:w-8 xl:w-fit"
onClick={handleCopy}
>
{hasCopied ? (
@@ -64,7 +64,7 @@ export function ShareButton() {
) : (
<HugeiconsIcon icon={Share03Icon} strokeWidth={2} />
)}
Share
<span className="lg:hidden xl:block">Share</span>
</Button>
</TooltipTrigger>
<TooltipContent>Copy Link</TooltipContent>

View File

@@ -14,7 +14,7 @@ import {
import { useDesignSystemSearchParams } from "@/app/(create)/lib/search-params"
export function V0Button({ className }: { className?: string }) {
const [params, setParams] = useDesignSystemSearchParams()
const [params] = useDesignSystemSearchParams()
const isMobile = useIsMobile()
const isMounted = useMounted()
@@ -32,7 +32,7 @@ export function V0Button({ className }: { className?: string }) {
size="sm"
variant={isMobile ? "default" : "outline"}
className={cn(
"w-24 rounded-lg shadow-none data-[variant=default]:h-[31px]",
"w-24 rounded-lg shadow-none data-[variant=default]:h-[31px] lg:w-8 xl:w-24",
className
)}
asChild
@@ -41,7 +41,8 @@ export function V0Button({ className }: { className?: string }) {
href={`${process.env.NEXT_PUBLIC_V0_URL}/chat/api/open?url=${encodeURIComponent(url)}&title=${params.item}`}
target="_blank"
>
Open in <Icons.v0 className="size-5" />
<span className="lg:hidden xl:block">Open in</span>
<Icons.v0 className="size-5" />
</a>
</Button>
</TooltipTrigger>

View File

@@ -4,7 +4,11 @@ import { ArrowLeftIcon } from "lucide-react"
import type { SearchParams } from "nuqs/server"
import { siteConfig } from "@/lib/config"
import { source } from "@/lib/source"
import { absoluteUrl } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { MainNav } from "@/components/main-nav"
import { MobileNav } from "@/components/mobile-nav"
import { ModeSwitcher } from "@/components/mode-switcher"
import { SiteConfig } from "@/components/site-config"
import { BASES } from "@/registry/config"
@@ -64,6 +68,7 @@ export default async function CreatePage({
const params = await loadDesignSystemSearchParams(searchParams)
const base = BASES.find((b) => b.name === params.base) ?? BASES[0]
const pageTree = source.pageTree
const items = await getItemsForBase(base.name)
const filteredItems = items
@@ -82,38 +87,34 @@ export default async function CreatePage({
<header className="sticky top-0 z-50 w-full">
<div className="container-wrapper 3xl:fixed:px-0 px-6">
<div className="3xl:fixed:container flex h-(--header-height) items-center **:data-[slot=separator]:!h-4">
<div className="flex items-center xl:w-1/3">
<div className="3xl:fixed:container flex h-(--header-height) items-center **:data-[slot=separator]:!h-4">
<MobileNav
tree={pageTree}
items={siteConfig.navItems}
className="flex lg:hidden"
/>
<Button
asChild
variant="outline"
size="sm"
className="rounded-lg shadow-none"
variant="ghost"
size="icon"
className="hidden size-8 lg:flex"
>
<Link href="/">
<ArrowLeftIcon />
Back
<Icons.logo className="size-5" />
<span className="sr-only">{siteConfig.name}</span>
</Link>
</Button>
<Separator
orientation="vertical"
className="mx-2 hidden sm:mx-4 lg:flex"
/>
<div className="text-muted-foreground hidden text-sm font-medium lg:flex">
New Project
</div>
<MainNav items={siteConfig.navItems} className="hidden lg:flex" />
</div>
<div className="fixed inset-x-0 bottom-0 ml-auto flex flex-1 items-center gap-2 px-4.5 pb-4 sm:static sm:justify-end sm:p-0 lg:ml-0 xl:justify-center">
<div className="fixed inset-x-0 bottom-0 ml-auto flex flex-1 items-center justify-end gap-2 px-4.5 pb-4 sm:static sm:p-0 lg:ml-0">
<ItemPicker items={filteredItems} />
<div className="items-center gap-0 sm:hidden">
<RandomButton />
<ResetButton />
</div>
<Separator
orientation="vertical"
className="mr-2 hidden sm:flex xl:hidden"
/>
<Separator orientation="vertical" className="mr-2 flex" />
</div>
<div className="ml-auto flex items-center gap-2 sm:ml-0 md:justify-end xl:ml-auto xl:w-1/3">
<div className="ml-auto flex items-center gap-2 sm:ml-0 md:justify-end">
<SiteConfig className="3xl:flex hidden" />
<Separator orientation="vertical" className="3xl:flex hidden" />
<ModeSwitcher />

View File

@@ -118,6 +118,7 @@ function DropdownMenuCheckboxes() {
tabler="IconUser"
hugeicons="UserIcon"
phosphor="UserIcon"
remixicon="RiUserLine"
/>
Profile
</DropdownMenuItem>
@@ -127,6 +128,7 @@ function DropdownMenuCheckboxes() {
tabler="IconCreditCard"
hugeicons="CreditCardIcon"
phosphor="CreditCardIcon"
remixicon="RiBankCardLine"
/>
Billing
</DropdownMenuItem>
@@ -136,6 +138,7 @@ function DropdownMenuCheckboxes() {
tabler="IconSettings"
hugeicons="SettingsIcon"
phosphor="GearIcon"
remixicon="RiSettings3Line"
/>
Settings
</DropdownMenuItem>
@@ -171,6 +174,7 @@ function DropdownMenuCheckboxes() {
tabler="IconLogout"
hugeicons="LogoutIcon"
phosphor="SignOutIcon"
remixicon="RiLogoutBoxLine"
/>
Sign Out
</DropdownMenuItem>
@@ -227,6 +231,7 @@ function DropdownMenuWithAvatar() {
tabler="IconChevronsUpDown"
hugeicons="ChevronUpDownIcon"
phosphor="CaretUpDownIcon"
remixicon="RiExpandUpDownLine"
className="text-muted-foreground ml-auto"
/>
</Button>
@@ -257,6 +262,7 @@ function DropdownMenuWithAvatar() {
tabler="IconSparkles"
hugeicons="SparklesIcon"
phosphor="SparklesIcon"
remixicon="RiSparklingLine"
/>
Upgrade to Pro
</DropdownMenuItem>
@@ -269,6 +275,7 @@ function DropdownMenuWithAvatar() {
tabler="IconBadgeCheck"
hugeicons="BadgeCheckIcon"
phosphor="CheckCircleIcon"
remixicon="RiVerifiedBadgeLine"
/>
Account
</DropdownMenuItem>
@@ -278,6 +285,7 @@ function DropdownMenuWithAvatar() {
tabler="IconCreditCard"
hugeicons="CreditCardIcon"
phosphor="CreditCardIcon"
remixicon="RiBankCardLine"
/>
Billing
</DropdownMenuItem>
@@ -287,6 +295,7 @@ function DropdownMenuWithAvatar() {
tabler="IconBell"
hugeicons="BellIcon"
phosphor="BellIcon"
remixicon="RiNotification3Line"
/>
Notifications
</DropdownMenuItem>
@@ -298,6 +307,7 @@ function DropdownMenuWithAvatar() {
tabler="IconLogout"
hugeicons="LogoutIcon"
phosphor="SignOutIcon"
remixicon="RiLogoutBoxLine"
/>
Sign Out
</DropdownMenuItem>
@@ -352,6 +362,7 @@ function DropdownMenuAvatarOnly() {
tabler="IconSparkles"
hugeicons="SparklesIcon"
phosphor="SparklesIcon"
remixicon="RiSparklingLine"
/>
Upgrade to Pro
</DropdownMenuItem>
@@ -364,6 +375,7 @@ function DropdownMenuAvatarOnly() {
tabler="IconBadgeCheck"
hugeicons="BadgeCheckIcon"
phosphor="CheckCircleIcon"
remixicon="RiVerifiedBadgeLine"
/>
Account
</DropdownMenuItem>
@@ -373,6 +385,7 @@ function DropdownMenuAvatarOnly() {
tabler="IconCreditCard"
hugeicons="CreditCardIcon"
phosphor="CreditCardIcon"
remixicon="RiBankCardLine"
/>
Billing
</DropdownMenuItem>
@@ -382,6 +395,7 @@ function DropdownMenuAvatarOnly() {
tabler="IconBell"
hugeicons="BellIcon"
phosphor="BellIcon"
remixicon="RiNotification3Line"
/>
Notifications
</DropdownMenuItem>
@@ -393,6 +407,7 @@ function DropdownMenuAvatarOnly() {
tabler="IconLogout"
hugeicons="LogoutIcon"
phosphor="SignOutIcon"
remixicon="RiLogoutBoxLine"
/>
Sign Out
</DropdownMenuItem>
@@ -411,6 +426,7 @@ function DropdownMenuIconColor() {
tabler="IconDots"
hugeicons="MoreHorizontalCircle01Icon"
phosphor="DotsThreeOutlineIcon"
remixicon="RiMoreLine"
/>
<span className="sr-only">Toggle menu</span>
</Button>
@@ -423,6 +439,7 @@ function DropdownMenuIconColor() {
tabler="IconPencil"
hugeicons="EditIcon"
phosphor="PencilIcon"
remixicon="RiPencilLine"
/>
Edit
</DropdownMenuItem>
@@ -432,6 +449,7 @@ function DropdownMenuIconColor() {
tabler="IconShare"
hugeicons="ShareIcon"
phosphor="ShareIcon"
remixicon="RiShareLine"
/>
Share
</DropdownMenuItem>
@@ -442,6 +460,7 @@ function DropdownMenuIconColor() {
tabler="IconTrash"
hugeicons="DeleteIcon"
phosphor="TrashIcon"
remixicon="RiDeleteBinLine"
/>
Delete
</DropdownMenuItem>

View File

@@ -1,5 +1,13 @@
import { cn } from "@/lib/utils"
export function ComponentPreview({ children }: { children: React.ReactNode }) {
return <div className={cn("bg-background")}>{children}</div>
return (
<div
className={cn(
"bg-background *:data-[slot=card]:has-[[data-slot=chart]]:shadow-none"
)}
>
{children}
</div>
)
}

View File

@@ -4,10 +4,16 @@ import { type Metadata } from "next"
import { notFound } from "next/navigation"
import { siteConfig } from "@/lib/config"
import { getRegistryComponent, getRegistryItem } from "@/lib/registry"
import {
getDemoItem,
getRegistryComponent,
getRegistryItem,
} from "@/lib/registry"
import { absoluteUrl } from "@/lib/utils"
import { getStyle, legacyStyles, type Style } from "@/registry/_legacy-styles"
import "@/styles/legacy-themes.css"
import { ComponentPreview } from "./component-preview"
export const revalidate = false
@@ -16,7 +22,12 @@ export const dynamicParams = false
const getCachedRegistryItem = React.cache(
async (name: string, styleName: Style["name"]) => {
return await getRegistryItem(name, styleName)
// Try registry item first, then fallback to demo item (for examples).
const item = await getRegistryItem(name, styleName)
if (item) {
return item
}
return await getDemoItem(name, styleName)
}
)
@@ -73,9 +84,54 @@ export async function generateMetadata({
export async function generateStaticParams() {
const { Index } = await import("@/registry/__index__")
// const { Index: BasesIndex } = await import("@/registry/bases/__index__")
const { ExamplesIndex } = await import("@/examples/__index__")
const params: Array<{ style: string; name: string }> = []
for (const style of legacyStyles) {
// Check if this is a base-prefixed style (e.g., base-nova, radix-nova).
const baseMatch = style.name.match(/^(base|radix)-/)
if (baseMatch) {
const baseName = baseMatch[1]
// Add examples from ExamplesIndex.
const examples = ExamplesIndex[baseName]
if (examples) {
for (const exampleName of Object.keys(examples)) {
if (exampleName.startsWith("sidebar-")) {
params.push({
style: style.name,
name: exampleName,
})
}
}
}
// // Add UI components from BasesIndex.
// const baseIndex = BasesIndex[baseName]
// if (baseIndex) {
// for (const itemName in baseIndex) {
// const item = baseIndex[itemName]
// if (
// [
// "registry:block",
// "registry:component",
// "registry:example",
// "registry:internal",
// ].includes(item.type)
// ) {
// params.push({
// style: style.name,
// name: item.name,
// })
// }
// }
// }
continue
}
// Handle legacy styles (e.g., new-york-v4).
if (!Index[style.name]) {
continue
}

View File

@@ -3,12 +3,27 @@ import { ArrowRightIcon } from "lucide-react"
import { Badge } from "@/registry/new-york-v4/ui/badge"
function BaseUILogo() {
return (
<svg width="17" height="24" viewBox="0 0 17 24" className="size-3">
<path
fill="currentColor"
d="M9.5001 7.01537C9.2245 6.99837 9 7.22385 9 7.49999V23C13.4183 23 17 19.4183 17 15C17 10.7497 13.6854 7.27351 9.5001 7.01537Z"
/>
<path
fill="currentColor"
d="M8 9.8V12V23C3.58172 23 0 19.0601 0 14.2V12V1C4.41828 1 8 4.93989 8 9.8Z"
/>
</svg>
)
}
export function Announcement() {
return (
<Badge asChild variant="secondary" className="bg-transparent">
<Link href="/docs/changelog">
<span className="flex size-2 rounded-full bg-blue-500" title="New" />
npx shadcn create <ArrowRightIcon />
<BaseUILogo />
Base UI Documentation <ArrowRightIcon />
</Link>
</Badge>
)

View File

@@ -5,6 +5,7 @@ import { type z } from "zod"
import { highlightCode } from "@/lib/highlight-code"
import { getRegistryItem } from "@/lib/registry"
import { cn } from "@/lib/utils"
import { ChartIframe } from "@/components/chart-iframe"
import { ChartToolbar } from "@/components/chart-toolbar"
import { type Style } from "@/registry/_legacy-styles"
@@ -12,50 +13,43 @@ export type Chart = z.infer<typeof registryItemSchema> & {
highlightedCode: string
}
export async function ChartDisplay({
name,
styleName,
children,
export function ChartDisplay({
chart,
style,
className,
}: {
name: string
styleName: Style["name"]
chart: Chart
style: string
} & React.ComponentProps<"div">) {
const chart = await getCachedRegistryItem(name, styleName)
const highlightedCode = await getChartHighlightedCode(
chart?.files?.[0]?.content ?? ""
)
if (!chart || !highlightedCode) {
return null
}
return (
<div
className={cn(
"themes-wrapper group relative flex flex-col overflow-hidden rounded-xl border transition-all duration-200 ease-in-out hover:z-30",
"themes-wrapper group relative flex flex-col overflow-hidden rounded-xl transition-all duration-200 ease-in-out hover:z-30",
className
)}
>
<ChartToolbar
chart={{ ...chart, highlightedCode }}
className="bg-card text-card-foreground relative z-20 flex justify-end border-b px-3 py-2.5"
>
{children}
</ChartToolbar>
<div className="relative z-10 [&>div]:rounded-none [&>div]:border-none [&>div]:shadow-none">
{children}
chart={chart}
className="relative z-20 flex justify-end px-3 py-2.5"
/>
<div className="bg-background relative z-10 overflow-hidden rounded-xl">
<ChartIframe
src={`/view/${style}/${chart.name}`}
height={460}
title={chart.name}
/>
</div>
</div>
)
}
const getCachedRegistryItem = React.cache(
// Exported for parallel prefetching in page components.
export const getCachedRegistryItem = React.cache(
async (name: string, styleName: Style["name"]) => {
return await getRegistryItem(name, styleName)
}
)
const getChartHighlightedCode = React.cache(async (content: string) => {
export const getChartHighlightedCode = React.cache(async (content: string) => {
return await highlightCode(content)
})

View File

@@ -0,0 +1,31 @@
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
export function ChartIframe({
src,
height,
title,
}: {
src: string
height: number
title: string
}) {
const [loaded, setLoaded] = React.useState(false)
return (
<iframe
src={src}
className={cn(
"w-full border-none transition-opacity duration-300",
loaded ? "opacity-100" : "opacity-0"
)}
height={height}
loading="lazy"
title={title}
onLoad={() => setLoaded(true)}
/>
)
}

View File

@@ -46,7 +46,7 @@ export function ChartToolbar({
}
function ChartTitle({ chart }: { chart: Chart }) {
if (chart.name.includes("charts-line")) {
if (chart.name.includes("chart-line")) {
return (
<>
<LineChartIcon /> Line Chart

View File

@@ -18,7 +18,7 @@ export function CodeTabs({ children }: React.ComponentProps<typeof Tabs>) {
onValueChange={(value) =>
setConfig({ ...config, installationType: value as "cli" | "manual" })
}
className="relative mt-6 w-full"
className="relative mt-6 w-full *:data-[slot=tabs-list]:gap-6"
>
{children}
</Tabs>

View File

@@ -1,15 +1,17 @@
"use client"
import * as React from "react"
import { useRouter } from "next/navigation"
import { usePathname, useRouter } from "next/navigation"
import { type DialogProps } from "@radix-ui/react-dialog"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { IconArrowRight } from "@tabler/icons-react"
import { useDocsSearch } from "fumadocs-core/search/client"
import { CornerDownLeftIcon, SquareDashedIcon } from "lucide-react"
import { CornerDownLeftIcon, SquareDashedIcon, XIcon } from "lucide-react"
import { type Color, type ColorPalette } from "@/lib/colors"
import { trackEvent } from "@/lib/events"
import { showMcpDocs } from "@/lib/flags"
import { getCurrentBase, getPagesFromFolder } from "@/lib/page-tree"
import { type source } from "@/lib/source"
import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
@@ -26,13 +28,13 @@ import {
} from "@/registry/new-york-v4/ui/command"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogPortal,
DialogTitle,
DialogTrigger,
} from "@/registry/new-york-v4/ui/dialog"
import { Kbd, KbdGroup } from "@/registry/new-york-v4/ui/kbd"
import { Kbd } from "@/registry/new-york-v4/ui/kbd"
import { Separator } from "@/registry/new-york-v4/ui/separator"
import { Spinner } from "@/registry/new-york-v4/ui/spinner"
@@ -49,7 +51,9 @@ export function CommandMenu({
navItems?: { href: string; label: string }[]
}) {
const router = useRouter()
const pathname = usePathname()
const [config] = useConfig()
const currentBase = getCurrentBase(pathname)
const [open, setOpen] = React.useState(false)
const [selectedType, setSelectedType] = React.useState<
"color" | "page" | "component" | "block" | null
@@ -138,10 +142,13 @@ export function CommandMenu({
[setSelectedType, setCopyPayload, packageManager]
)
const runCommand = React.useCallback((command: () => unknown) => {
setOpen(false)
command()
}, [])
const runCommand = React.useCallback(
(command: () => unknown) => {
setOpen(false)
command()
},
[setOpen]
)
React.useEffect(() => {
const down = (e: KeyboardEvent) => {
@@ -207,10 +214,7 @@ export function CommandMenu({
</div>
</Button>
</DialogTrigger>
<DialogContent
showCloseButton={false}
className="rounded-xl border-none bg-clip-padding p-2 pb-11 shadow-2xl ring-4 ring-neutral-200/80 dark:bg-neutral-900 dark:ring-neutral-800"
>
<DialogContent className="rounded-xl border-none bg-clip-padding p-2 pb-11 shadow-2xl ring-4 ring-neutral-200/80 dark:bg-neutral-900 dark:ring-neutral-800">
<DialogHeader className="sr-only">
<DialogTitle>Search documentation...</DialogTitle>
<DialogDescription>Search for a command to run...</DialogDescription>
@@ -269,40 +273,37 @@ export function CommandMenu({
className="!p-0 [&_[cmdk-group-heading]]:scroll-mt-16 [&_[cmdk-group-heading]]:!p-3 [&_[cmdk-group-heading]]:!pb-1"
>
{group.type === "folder" &&
group.children.map((item) => {
if (item.type === "page") {
const isComponent = item.url.includes("/components/")
getPagesFromFolder(group, currentBase).map((item) => {
const isComponent = item.url.includes("/components/")
if (!showMcpDocs && item.url.includes("/mcp")) {
return null
}
return (
<CommandMenuItem
key={item.url}
value={
item.name?.toString()
? `${group.name} ${item.name}`
: ""
}
keywords={isComponent ? ["component"] : undefined}
onHighlight={() =>
handlePageHighlight(isComponent, item)
}
onSelect={() => {
runCommand(() => router.push(item.url))
}}
>
{isComponent ? (
<div className="border-muted-foreground aspect-square size-4 rounded-full border border-dashed" />
) : (
<IconArrowRight />
)}
{item.name}
</CommandMenuItem>
)
if (!showMcpDocs && item.url.includes("/mcp")) {
return null
}
return null
return (
<CommandMenuItem
key={item.url}
value={
item.name?.toString()
? `${group.name} ${item.name}`
: ""
}
keywords={isComponent ? ["component"] : undefined}
onHighlight={() =>
handlePageHighlight(isComponent, item)
}
onSelect={() => {
runCommand(() => router.push(item.url))
}}
>
{isComponent ? (
<div className="border-muted-foreground aspect-square size-4 rounded-full border border-dashed" />
) : (
<IconArrowRight />
)}
{item.name}
</CommandMenuItem>
)
})}
</CommandGroup>
))}
@@ -523,3 +524,30 @@ function SearchResults({
</CommandGroup>
)
}
function DialogContent({
className,
children,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
}) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className="fixed inset-0 z-50 bg-black/50"
/>
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
className
)}
{...props}
>
{children}
</DialogPrimitive.Content>
</DialogPortal>
)
}

View File

@@ -3,9 +3,11 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york-v4/ui/button"
export function ComponentPreviewTabs({
className,
previewClassName,
align = "center",
hideCode = false,
chromeLessOnMobile = false,
@@ -13,16 +15,19 @@ export function ComponentPreviewTabs({
source,
...props
}: React.ComponentProps<"div"> & {
previewClassName?: string
align?: "center" | "start" | "end"
hideCode?: boolean
chromeLessOnMobile?: boolean
component: React.ReactNode
source: React.ReactNode
}) {
const [isMobileCodeVisible, setIsMobileCodeVisible] = React.useState(false)
return (
<div
className={cn(
"group relative mt-4 mb-12 flex flex-col gap-2 rounded-lg border",
"group relative mt-4 mb-12 flex flex-col gap-2 overflow-hidden rounded-xl border",
className
)}
{...props}
@@ -30,9 +35,10 @@ export function ComponentPreviewTabs({
<div data-slot="preview">
<div
data-align={align}
data-chromeless={chromeLessOnMobile}
className={cn(
"preview flex w-full justify-center data-[align=center]:items-center data-[align=end]:items-end data-[align=start]:items-start",
chromeLessOnMobile ? "sm:p-10" : "h-[450px] p-10"
"preview flex h-72 w-full justify-center p-10 data-[align=center]:items-center data-[align=end]:items-end data-[align=start]:items-start data-[chromeless=true]:h-auto data-[chromeless=true]:p-0",
previewClassName
)}
>
{component}
@@ -40,9 +46,32 @@ export function ComponentPreviewTabs({
{!hideCode && (
<div
data-slot="code"
className="overflow-hidden [&_[data-rehype-pretty-code-figure]]:!m-0 [&_[data-rehype-pretty-code-figure]]:rounded-t-none [&_[data-rehype-pretty-code-figure]]:border-t [&_pre]:max-h-[400px]"
data-mobile-code-visible={isMobileCodeVisible}
className="relative overflow-hidden data-[mobile-code-visible=false]:max-h-24 md:max-h-none data-[mobile-code-visible=false]:md:max-h-none [&_[data-rehype-pretty-code-figure]]:!m-0 [&_[data-rehype-pretty-code-figure]]:rounded-t-none [&_[data-rehype-pretty-code-figure]]:border-t [&_pre]:max-h-72"
>
{source}
{!isMobileCodeVisible && (
<div className="absolute inset-0 flex items-center justify-center md:hidden">
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(to top, var(--color-code), color-mix(in oklab, var(--color-code) 60%, transparent), transparent)",
}}
/>
<Button
type="button"
size="sm"
variant="outline"
className="bg-background text-foreground dark:bg-background dark:text-foreground relative z-10"
onClick={() => {
setIsMobileCodeVisible(true)
}}
>
View Code
</Button>
</div>
)}
</div>
)}
</div>

View File

@@ -1,45 +1,33 @@
import * as React from "react"
import Image from "next/image"
import { getRegistryComponent } from "@/lib/registry"
import { ComponentPreviewTabs } from "@/components/component-preview-tabs"
import { ComponentSource } from "@/components/component-source"
import { Index } from "@/registry/__index__"
import { type Style } from "@/registry/_legacy-styles"
export function ComponentPreview({
name,
styleName = "new-york-v4",
type,
className,
previewClassName,
align = "center",
hideCode = false,
chromeLessOnMobile = false,
styleName = "new-york-v4",
...props
}: React.ComponentProps<"div"> & {
name: string
styleName?: Style["name"]
styleName?: string
align?: "center" | "start" | "end"
description?: string
hideCode?: boolean
type?: "block" | "component" | "example"
chromeLessOnMobile?: boolean
previewClassName?: string
}) {
const Component = Index[styleName]?.[name]?.component
if (!Component) {
return (
<p className="text-muted-foreground mt-6 text-sm">
Component{" "}
<code className="bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm">
{name}
</code>{" "}
not found in registry.
</p>
)
}
if (type === "block") {
return (
<div className="relative aspect-[4/2.5] w-full overflow-hidden rounded-md border md:-mx-1">
<div className="relative aspect-[4/2.5] w-full overflow-hidden rounded-xl border md:-mx-1">
<Image
src={`/r/styles/new-york-v4/${name}-light.png`}
alt={name}
@@ -61,12 +49,27 @@ export function ComponentPreview({
)
}
const Component = getRegistryComponent(name, styleName)
if (!Component) {
return (
<p className="text-muted-foreground mt-6 text-sm">
Component{" "}
<code className="bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm">
{name}
</code>{" "}
not found in registry.
</p>
)
}
return (
<ComponentPreviewTabs
className={className}
previewClassName={previewClassName}
align={align}
hideCode={hideCode}
component={<Component />}
component={<DynamicComponent name={name} styleName={styleName} />}
source={
<ComponentSource
name={name}
@@ -79,3 +82,22 @@ export function ComponentPreview({
/>
)
}
function DynamicComponent({
name,
styleName,
}: {
name: string
styleName: string
}) {
const Component = React.useMemo(
() => getRegistryComponent(name, styleName),
[name, styleName]
)
if (!Component) {
return null
}
return React.createElement(Component)
}

View File

@@ -3,12 +3,12 @@ import path from "node:path"
import * as React from "react"
import { highlightCode } from "@/lib/highlight-code"
import { getRegistryItem } from "@/lib/registry"
import { getDemoItem, getRegistryItem } from "@/lib/registry"
import { formatCode } from "@/lib/rehype"
import { cn } from "@/lib/utils"
import { CodeCollapsibleWrapper } from "@/components/code-collapsible-wrapper"
import { CopyButton } from "@/components/copy-button"
import { getIconForLanguageExtension } from "@/components/icons"
import { type Style } from "@/registry/_legacy-styles"
export async function ComponentSource({
name,
@@ -24,7 +24,7 @@ export async function ComponentSource({
title?: string
language?: string
collapsible?: boolean
styleName?: Style["name"]
styleName?: string
}) {
if (!name && !src) {
return null
@@ -33,7 +33,9 @@ export async function ComponentSource({
let code: string | undefined
if (name) {
const item = await getRegistryItem(name, styleName)
const item =
(await getDemoItem(name, styleName)) ??
(await getRegistryItem(name, styleName))
code = item?.files?.[0]?.content
}
@@ -46,12 +48,7 @@ export async function ComponentSource({
return null
}
// Fix imports.
// Replace @/registry/${style}/ with @/components/.
code = code.replaceAll(`@/registry/${styleName}/`, "@/components/")
// Replace export default with export.
code = code.replaceAll("export default", "export")
code = await formatCode(code, styleName)
code = code.replaceAll("/* eslint-disable react/no-children-prop */\n", "")
const lang = language ?? title?.split(".").pop() ?? "tsx"

View File

@@ -1,20 +1,16 @@
import Link from "next/link"
import { PAGES_NEW } from "@/lib/docs"
import { source } from "@/lib/source"
import { getPagesFromFolder, type PageTreeFolder } from "@/lib/page-tree"
export function ComponentsList() {
const components = source.pageTree.children.find(
(page) => page.$id === "components"
)
if (components?.type !== "folder") {
return
}
const list = components.children.filter(
(component) => component.type === "page"
)
export function ComponentsList({
componentsFolder,
currentBase,
}: {
componentsFolder: PageTreeFolder
currentBase: string
}) {
const list = getPagesFromFolder(componentsFolder, currentBase)
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 md:gap-x-8 lg:gap-x-16 lg:gap-y-6 xl:gap-x-20">

View File

@@ -1,13 +1,11 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { IconCheck } from "@tabler/icons-react"
import { IconCheck, IconCopy, IconPlus } from "@tabler/icons-react"
import { cn } from "@/lib/utils"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { useConfig } from "@/hooks/use-config"
import { useIsMobile } from "@/hooks/use-mobile"
import { CopyButton } from "@/components/copy-button"
import { copyToClipboardWithMeta } from "@/components/copy-button"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Dialog,
@@ -29,77 +27,114 @@ import {
DrawerTitle,
DrawerTrigger,
} from "@/registry/new-york-v4/ui/drawer"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york-v4/ui/tabs"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
export function DirectoryAddButton({
registry,
}: {
registry: {
name: string
url: string
}
registry: { name: string }
}) {
const { copyToClipboard, isCopied } = useCopyToClipboard()
const isMobile = useIsMobile()
const [config, setConfig] = useConfig()
const [hasCopied, setHasCopied] = React.useState(false)
const [open, setOpen] = React.useState(false)
const isMobile = useIsMobile()
const jsonValue = `{
"registries": {
"${registry.name}": "${registry.url}"
}
}`
const packageManager = config.packageManager || "pnpm"
const commands = React.useMemo(() => {
return {
pnpm: `pnpm dlx shadcn@latest registry add ${registry.name}`,
npm: `npx shadcn@latest registry add ${registry.name}`,
yarn: `yarn dlx shadcn@latest registry add ${registry.name}`,
bun: `bunx --bun shadcn@latest registry add ${registry.name}`,
}
}, [registry.name])
const command = commands[packageManager]
React.useEffect(() => {
if (hasCopied) {
const timer = setTimeout(() => setHasCopied(false), 2000)
return () => clearTimeout(timer)
}
}, [hasCopied])
const handleCopy = React.useCallback(() => {
copyToClipboardWithMeta(command, {
name: "copy_registry_add_command",
properties: {
command,
registry: registry.name,
},
})
setHasCopied(true)
}, [command, registry.name])
const Trigger = (
<Button
size="sm"
variant="outline"
className="relative z-10"
onClick={() => setOpen(true)}
>
{isCopied ? (
<IconCheck />
) : (
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Model Context Protocol</title>
<path
d="M13.85 0a4.16 4.16 0 0 0-2.95 1.217L1.456 10.66a.835.835 0 0 0 0 1.18.835.835 0 0 0 1.18 0l9.442-9.442a2.49 2.49 0 0 1 3.541 0 2.49 2.49 0 0 1 0 3.541L8.59 12.97l-.1.1a.835.835 0 0 0 0 1.18.835.835 0 0 0 1.18 0l.1-.098 7.03-7.034a2.49 2.49 0 0 1 3.542 0l.049.05a2.49 2.49 0 0 1 0 3.54l-8.54 8.54a1.96 1.96 0 0 0 0 2.755l1.753 1.753a.835.835 0 0 0 1.18 0 .835.835 0 0 0 0-1.18l-1.753-1.753a.266.266 0 0 1 0-.394l8.54-8.54a4.185 4.185 0 0 0 0-5.9l-.05-.05a4.16 4.16 0 0 0-2.95-1.218c-.2 0-.401.02-.6.048a4.17 4.17 0 0 0-1.17-3.552A4.16 4.16 0 0 0 13.85 0m0 3.333a.84.84 0 0 0-.59.245L6.275 10.56a4.186 4.186 0 0 0 0 5.902 4.186 4.186 0 0 0 5.902 0L19.16 9.48a.835.835 0 0 0 0-1.18.835.835 0 0 0-1.18 0l-6.985 6.984a2.49 2.49 0 0 1-3.54 0 2.49 2.49 0 0 1 0-3.54l6.983-6.985a.835.835 0 0 0 0-1.18.84.84 0 0 0-.59-.245"
className="fill-foreground"
/>
</svg>
)}
MCP
<Button size="sm" variant="outline" className="relative z-10">
Add <IconPlus />
</Button>
)
const Content = (
<>
<figure
data-rehype-pretty-code-figure
className={cn(
"group relative mt-0",
!isMobile &&
"dark:bg-background dark:[&_[data-line]:not([data-highlighted-line]):before]:bg-background!"
)}
>
<CopyButton
value={jsonValue}
className="top-3 right-2"
tooltip="Copy Code"
/>
<div data-rehype-pretty-code-title>components.json</div>
<pre className="no-scrollbar min-w-0 overflow-x-auto px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0">
<code data-line-numbers data-language="json">
<span data-line>{"{"}</span>
<span data-line>{' "registries": {'}</span>
<span
data-line
data-highlighted-line
>{` "${registry.name}": "${registry.url}"`}</span>
<span data-line>{" }"}</span>
<span data-line>{"}"}</span>
</code>
</pre>
</figure>
</>
<Tabs
value={packageManager}
onValueChange={(value) => {
setConfig({
...config,
packageManager: value as "pnpm" | "npm" | "yarn" | "bun",
})
}}
className="gap-0 overflow-hidden rounded-lg border"
>
<div className="flex items-center gap-2 border-b p-2">
<TabsList className="*:data-[slot=tabs-trigger]:data-[state=active]:border-input h-auto rounded-none bg-transparent p-0 font-mono *:data-[slot=tabs-trigger]:border *:data-[slot=tabs-trigger]:border-transparent *:data-[slot=tabs-trigger]:pt-0.5 *:data-[slot=tabs-trigger]:shadow-none!">
<TabsTrigger value="pnpm">pnpm</TabsTrigger>
<TabsTrigger value="npm">npm</TabsTrigger>
<TabsTrigger value="yarn">yarn</TabsTrigger>
<TabsTrigger value="bun">bun</TabsTrigger>
</TabsList>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon-sm"
variant="ghost"
className="ml-auto size-7 rounded-lg"
onClick={handleCopy}
>
{hasCopied ? (
<IconCheck className="size-4" />
) : (
<IconCopy className="size-4" />
)}
<span className="sr-only">Copy command</span>
</Button>
</TooltipTrigger>
<TooltipContent>
{hasCopied ? "Copied!" : "Copy command"}
</TooltipContent>
</Tooltip>
</div>
{Object.entries(commands).map(([key, cmd]) => (
<TabsContent key={key} value={key} className="mt-0">
<div className="bg-surface text-surface-foreground px-3 py-3">
<div className="no-scrollbar overflow-x-auto">
<code className="font-mono text-sm whitespace-nowrap">{cmd}</code>
</div>
</div>
</TabsContent>
))}
</Tabs>
)
if (isMobile) {
@@ -108,20 +143,16 @@ export function DirectoryAddButton({
<DrawerTrigger asChild>{Trigger}</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Configure MCP</DrawerTitle>
<DrawerTitle>Add Registry</DrawerTitle>
<DrawerDescription>
Copy and paste the following code into your project&apos;s
components.json.
Run this command to add {registry.name} to your project.
</DrawerDescription>
</DrawerHeader>
<div className="px-6">{Content}</div>
<div className="px-4">{Content}</div>
<DrawerFooter>
<DrawerClose asChild>
<Button size="sm">Close</Button>
<Button size="sm">Done</Button>
</DrawerClose>
<Button size="sm" asChild variant="outline">
<Link href="/docs/mcp">Read the docs</Link>
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
@@ -131,22 +162,15 @@ export function DirectoryAddButton({
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>{Trigger}</DialogTrigger>
<DialogContent
className="rounded-xl border-none bg-clip-padding shadow-2xl ring-4 ring-neutral-200/80 sm:max-w-[600px] dark:bg-neutral-900 dark:ring-neutral-800"
onOpenAutoFocus={(e) => e.preventDefault()}
>
<DialogContent className="dialog-ring animate-none! rounded-xl sm:max-w-md">
<DialogHeader>
<DialogTitle>Configure MCP</DialogTitle>
<DialogTitle>Add Registry</DialogTitle>
<DialogDescription>
Copy and paste the following code into your project&apos;s
components.json.
Run this command to add {registry.name} to your project.
</DialogDescription>
</DialogHeader>
{Content}
<DialogFooter className="justify-between!">
<Button size="sm" asChild variant="ghost">
<Link href="/docs/mcp">Read the docs</Link>
</Button>
<DialogFooter>
<DialogClose asChild>
<Button size="sm">Done</Button>
</DialogClose>

View File

@@ -50,8 +50,10 @@ export function DirectoryList() {
href={getHomepageUrl(registry.homepage)}
target="_blank"
rel="noopener noreferrer external"
className="group flex items-center gap-1"
>
{registry.name}
{registry.name}{" "}
<IconArrowUpRight className="size-4 opacity-0 group-hover:opacity-100" />
</a>
</ItemTitle>
{registry.description && (
@@ -61,15 +63,6 @@ export function DirectoryList() {
)}
</ItemContent>
<ItemActions className="relative z-10 hidden self-start sm:flex">
<Button size="sm" variant="outline" asChild>
<a
href={getHomepageUrl(registry.homepage)}
target="_blank"
rel="noopener noreferrer external"
>
View <IconArrowUpRight />
</a>
</Button>
<DirectoryAddButton registry={registry} />
</ItemActions>
<ItemFooter className="justify-start pl-16 sm:hidden">

View File

@@ -0,0 +1,39 @@
import Link from "next/link"
import { cn } from "@/lib/utils"
import { BASES } from "@/registry/bases"
export function DocsBaseSwitcher({
base,
component,
className,
}: {
base: string
component: string
className?: string
}) {
const activeBase = BASES.find((baseItem) => base === baseItem.name)
return (
<div className={cn("inline-flex w-full items-center gap-6", className)}>
{BASES.map((baseItem) => (
<Link
key={baseItem.name}
href={`/docs/components/${baseItem.name}/${component}`}
data-active={base === baseItem.name}
className="text-muted-foreground hover:text-foreground data-[active=true]:text-foreground after:bg-foreground relative inline-flex items-center justify-center gap-1 pt-1 pb-0.5 text-base font-medium transition-colors after:absolute after:inset-x-0 after:bottom-[-4px] after:h-0.5 after:opacity-0 after:transition-opacity data-[active=true]:after:opacity-100"
>
{baseItem.title}
</Link>
))}
{activeBase?.meta?.logo && (
<div
className="text-muted-foreground ml-auto size-4 shrink-0 opacity-80 [&_svg]:size-4"
dangerouslySetInnerHTML={{
__html: activeBase.meta.logo,
}}
/>
)}
</div>
)
}

View File

@@ -183,7 +183,10 @@ export function DocsCopyPage({ page, url }: { page: string; url: string }) {
<DropdownMenuTrigger asChild className="hidden sm:flex">
{trigger}
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="shadow-none">
<DropdownMenuContent
align="end"
className="animate-none! rounded-lg shadow-none"
>
{Object.entries(menuItems).map(([key, value]) => (
<DropdownMenuItem key={key} asChild>
{value(url)}
@@ -193,13 +196,13 @@ export function DocsCopyPage({ page, url }: { page: string; url: string }) {
</DropdownMenu>
<Separator
orientation="vertical"
className="!bg-foreground/10 absolute top-0 right-8 z-0 !h-8 peer-focus-visible:opacity-0 sm:right-7 sm:!h-7"
className="!bg-foreground/5 absolute top-1 right-8 z-0 !h-6 peer-focus-visible:opacity-0 sm:right-7 sm:!h-5"
/>
<PopoverTrigger asChild className="flex sm:hidden">
{trigger}
</PopoverTrigger>
<PopoverContent
className="bg-background/70 dark:bg-background/60 w-52 !origin-center rounded-lg p-1 shadow-sm backdrop-blur-sm"
className="bg-background/70 dark:bg-background/60 w-52 !origin-center rounded-lg p-1 shadow-none backdrop-blur-sm"
align="start"
>
{Object.entries(menuItems).map(([key, value]) => (

View File

@@ -0,0 +1,179 @@
"use client"
import { IconCheck, IconCopy } from "@tabler/icons-react"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
function getPromptUrl(baseURL: string, url: string) {
return `${baseURL}?q=${encodeURIComponent(
`I'm looking at this shadcn/ui documentation: ${url}.
Help me understand how to use it. Be ready to explain concepts, give examples, or help debug based on it.
`
)}`
}
export function DocsPageLinks({ page, url }: { page: string; url: string }) {
const { copyToClipboard, isCopied } = useCopyToClipboard()
return (
<div className="flex flex-col gap-3 px-6">
<ul className="text-muted-foreground flex flex-col gap-2 text-[0.8rem]">
<li>
<button
onClick={() => copyToClipboard(page)}
className="hover:text-foreground inline-flex items-center gap-2 transition-colors"
>
{isCopied ? (
<IconCheck className="size-4" />
) : (
<IconCopy className="size-4" />
)}
Copy page
</button>
</li>
<li>
<a
href={`${url}.md`}
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground inline-flex items-center gap-2 transition-colors"
>
<svg strokeLinejoin="round" viewBox="0 0 22 16" className="size-4">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.5 2.25H2.5C1.80964 2.25 1.25 2.80964 1.25 3.5V12.5C1.25 13.1904 1.80964 13.75 2.5 13.75H19.5C20.1904 13.75 20.75 13.1904 20.75 12.5V3.5C20.75 2.80964 20.1904 2.25 19.5 2.25ZM2.5 1C1.11929 1 0 2.11929 0 3.5V12.5C0 13.8807 1.11929 15 2.5 15H19.5C20.8807 15 22 13.8807 22 12.5V3.5C22 2.11929 20.8807 1 19.5 1H2.5ZM3 4.5H4H4.25H4.6899L4.98715 4.82428L7 7.02011L9.01285 4.82428L9.3101 4.5H9.75H10H11V5.5V11.5H9V7.79807L7.73715 9.17572L7 9.97989L6.26285 9.17572L5 7.79807V11.5H3V5.5V4.5ZM15 8V4.5H17V8H19.5L17 10.5L16 11.5L15 10.5L12.5 8H15Z"
fill="currentColor"
/>
</svg>
View as Markdown
</a>
</li>
<li>
<a
href={getPromptUrl("https://v0.dev", url)}
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground inline-flex items-center gap-2 transition-colors"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 147 70"
className="size-4"
>
<path d="M56 50.203V14h14v46.156C70 65.593 65.593 70 60.156 70c-2.596 0-5.158-1-7-2.843L0 14h19.797L56 50.203ZM147 56h-14V23.953L100.953 56H133v14H96.687C85.814 70 77 61.186 77 50.312V14h14v32.156L123.156 14H91V0h36.312C138.186 0 147 8.814 147 19.688V56Z" />
</svg>
Open in v0
</a>
</li>
<li>
<a
href={getPromptUrl("https://chatgpt.com", url)}
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground inline-flex items-center gap-2 transition-colors"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className="size-4"
>
<path
d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08-4.778 2.758a.795.795 0 0 0-.393.681zm1.097-2.365 2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5Z"
fill="currentColor"
/>
</svg>
Open in ChatGPT
</a>
</li>
<li>
<a
href={getPromptUrl("https://claude.ai/new", url)}
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground inline-flex items-center gap-2 transition-colors"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className="size-4"
>
<path
d="m4.714 15.956 4.718-2.648.079-.23-.08-.128h-.23l-.79-.048-2.695-.073-2.337-.097-2.265-.122-.57-.121-.535-.704.055-.353.48-.321.685.06 1.518.104 2.277.157 1.651.098 2.447.255h.389l.054-.158-.133-.097-.103-.098-2.356-1.596-2.55-1.688-1.336-.972-.722-.491L2 6.223l-.158-1.008.655-.722.88.06.225.061.893.686 1.906 1.476 2.49 1.833.364.304.146-.104.018-.072-.164-.274-1.354-2.446-1.445-2.49-.644-1.032-.17-.619a2.972 2.972 0 0 1-.103-.729L6.287.133 6.7 0l.995.134.42.364.619 1.415L9.735 4.14l1.555 3.03.455.898.243.832.09.255h.159V9.01l.127-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.583.28.48.685-.067.444-.286 1.851-.558 2.903-.365 1.942h.213l.243-.242.983-1.306 1.652-2.064.728-.82.85-.904.547-.431h1.032l.759 1.129-.34 1.166-1.063 1.347-.88 1.142-1.263 1.7-.79 1.36.074.11.188-.02 2.853-.606 1.542-.28 1.84-.315.832.388.09.395-.327.807-1.967.486-2.307.462-3.436.813-.043.03.049.061 1.548.146.662.036h1.62l3.018.225.79.522.473.638-.08.485-1.213.62-1.64-.389-3.825-.91-1.31-.329h-.183v.11l1.093 1.068 2.003 1.81 2.508 2.33.127.578-.321.455-.34-.049-2.204-1.657-.85-.747-1.925-1.62h-.127v.17l.443.649 2.343 3.521.122 1.08-.17.353-.607.213-.668-.122-1.372-1.924-1.415-2.168-1.141-1.943-.14.08-.674 7.254-.316.37-.728.28-.607-.461-.322-.747.322-1.476.388-1.924.316-1.53.285-1.9.17-.632-.012-.042-.14.018-1.432 1.967-2.18 2.945-1.724 1.845-.413.164-.716-.37.066-.662.401-.589 2.386-3.036 1.439-1.882.929-1.086-.006-.158h-.055L4.138 18.56l-1.13.146-.485-.456.06-.746.231-.243 1.907-1.312Z"
fill="currentColor"
/>
</svg>
Open in Claude
</a>
</li>
<li>
<a
href={getPromptUrl("https://scira.ai/", url)}
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground inline-flex items-center gap-2 transition-colors"
>
<svg
width="910"
height="934"
viewBox="0 0 910 934"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="size-4"
>
<path
d="M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M516.774 304.217C510.299 275.491 498.208 252.087 480.335 234.214C462.462 216.341 439.058 204.251 410.333 197.775C439.059 191.3 462.462 179.209 480.335 161.336C498.208 143.463 510.299 120.06 516.774 91.334C523.25 120.059 535.34 143.463 553.213 161.336C571.086 179.209 594.49 191.3 623.216 197.775C594.49 204.251 571.086 216.341 553.213 234.214C535.34 252.087 523.25 275.491 516.774 304.217Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M857.5 508.116C763.259 497.644 710.903 445.288 700.432 351.047C689.961 445.288 637.605 497.644 543.364 508.116C637.605 518.587 689.961 570.943 700.432 665.184C710.903 570.943 763.259 518.587 857.5 508.116Z"
stroke="currentColor"
strokeWidth="20"
strokeLinejoin="round"
/>
<path
d="M700.432 615.957C691.848 589.05 678.575 566.357 660.383 548.165C642.191 529.973 619.499 516.7 592.593 508.116C619.499 499.533 642.191 486.258 660.383 468.066C678.575 449.874 691.848 427.181 700.432 400.274C709.015 427.181 722.289 449.874 740.481 468.066C758.673 486.258 781.365 499.533 808.271 508.116C781.365 516.7 758.673 529.973 740.481 548.165C722.289 566.357 709.015 589.05 700.432 615.957Z"
stroke="currentColor"
strokeWidth="20"
strokeLinejoin="round"
/>
<path
d="M889.949 121.237C831.049 114.692 798.326 81.9698 791.782 23.0692C785.237 81.9698 752.515 114.692 693.614 121.237C752.515 127.781 785.237 160.504 791.782 219.404C798.326 160.504 831.049 127.781 889.949 121.237Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M791.782 196.795C786.697 176.937 777.869 160.567 765.16 147.858C752.452 135.15 736.082 126.322 716.226 121.237C736.082 116.152 752.452 107.324 765.16 94.6152C777.869 81.9065 786.697 65.5368 791.782 45.6797C796.867 65.5367 805.695 81.9066 818.403 94.6152C831.112 107.324 847.481 116.152 867.338 121.237C847.481 126.322 831.112 135.15 818.403 147.858C805.694 160.567 796.867 176.937 791.782 196.795Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="8"
strokeLinejoin="round"
/>
<path
d="M760.632 764.337C720.719 814.616 669.835 855.1 611.872 882.692C553.91 910.285 490.404 924.255 426.213 923.533C362.022 922.812 298.846 907.419 241.518 878.531C184.19 849.643 134.228 808.026 95.4548 756.863C56.6815 705.7 30.1238 646.346 17.8129 583.343C5.50207 520.339 7.76433 455.354 24.4266 393.359C41.089 331.364 71.7099 274.001 113.947 225.658C156.184 177.315 208.919 139.273 268.117 114.442"
stroke="currentColor"
strokeWidth="30"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
Open in Scira
</a>
</li>
</ul>
</div>
)
}

View File

@@ -5,6 +5,7 @@ import { usePathname } from "next/navigation"
import { PAGES_NEW } from "@/lib/docs"
import { showMcpDocs } from "@/lib/flags"
import { getCurrentBase, getPagesFromFolder } from "@/lib/page-tree"
import type { source } from "@/lib/source"
import {
Sidebar,
@@ -18,11 +19,15 @@ import {
} from "@/registry/new-york-v4/ui/sidebar"
const TOP_LEVEL_SECTIONS = [
{ name: "Get Started", href: "/docs" },
{ name: "Introduction", href: "/docs" },
{
name: "Components",
href: "/docs/components",
},
{
name: "Installation",
href: "/docs/installation",
},
{
name: "Directory",
href: "/docs/directory",
@@ -31,6 +36,10 @@ const TOP_LEVEL_SECTIONS = [
name: "MCP Server",
href: "/docs/mcp",
},
{
name: "Registry",
href: "/docs/registry",
},
{
name: "Forms",
href: "/docs/forms",
@@ -48,10 +57,11 @@ export function DocsSidebar({
...props
}: React.ComponentProps<typeof Sidebar> & { tree: typeof source.pageTree }) {
const pathname = usePathname()
const currentBase = getCurrentBase(pathname)
return (
<Sidebar
className="sticky top-[calc(var(--header-height)+1px)] z-30 hidden h-[calc(100svh-var(--footer-height)-4rem)] overscroll-none bg-transparent lg:flex"
className="sticky top-[calc(var(--header-height)+1px)] z-30 hidden h-[calc(100svh-6rem)] overscroll-none bg-transparent lg:flex"
collapsible="none"
{...props}
>
@@ -102,37 +112,34 @@ export function DocsSidebar({
<SidebarGroupContent>
{item.type === "folder" && (
<SidebarMenu className="gap-0.5">
{item.children.map((item) => {
if (
!showMcpDocs &&
item.type === "page" &&
item.url?.includes("/mcp")
) {
{getPagesFromFolder(item, currentBase).map((page) => {
if (!showMcpDocs && page.url.includes("/mcp")) {
return null
}
if (EXCLUDED_PAGES.includes(page.url)) {
return null
}
return (
item.type === "page" &&
!EXCLUDED_PAGES.includes(item.url) && (
<SidebarMenuItem key={item.url}>
<SidebarMenuButton
asChild
isActive={item.url === pathname}
className="data-[active=true]:bg-accent data-[active=true]:border-accent 3xl:fixed:w-full 3xl:fixed:max-w-48 relative h-[30px] w-fit overflow-visible border border-transparent text-[0.8rem] font-medium after:absolute after:inset-x-0 after:-inset-y-1 after:z-0 after:rounded-md"
>
<Link href={item.url}>
<span className="absolute inset-0 flex w-(--sidebar-width) bg-transparent" />
{item.name}
{PAGES_NEW.includes(item.url) && (
<span
className="flex size-2 rounded-full bg-blue-500"
title="New"
/>
)}
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
)
<SidebarMenuItem key={page.url}>
<SidebarMenuButton
asChild
isActive={page.url === pathname}
className="data-[active=true]:bg-accent data-[active=true]:border-accent 3xl:fixed:w-full 3xl:fixed:max-w-48 relative h-[30px] w-fit overflow-visible border border-transparent text-[0.8rem] font-medium after:absolute after:inset-x-0 after:-inset-y-1 after:z-0 after:rounded-md"
>
<Link href={page.url}>
<span className="absolute inset-0 flex w-(--sidebar-width) bg-transparent" />
{page.name}
{PAGES_NEW.includes(page.url) && (
<span
className="flex size-2 rounded-full bg-blue-500"
title="New"
/>
)}
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
)
})}
</SidebarMenu>

View File

@@ -107,7 +107,7 @@ export function DocsTableOfContents({
return (
<div className={cn("flex flex-col gap-2 p-4 pt-0 text-sm", className)}>
<p className="text-muted-foreground bg-background sticky top-0 h-6 text-xs">
<p className="text-muted-foreground bg-background sticky top-0 h-6 text-xs font-medium">
On This Page
</p>
{toc.map((item) => (

View File

@@ -2,10 +2,11 @@
import * as React from "react"
import Link, { type LinkProps } from "next/link"
import { useRouter } from "next/navigation"
import { usePathname, useRouter } from "next/navigation"
import { PAGES_NEW } from "@/lib/docs"
import { showMcpDocs } from "@/lib/flags"
import { getCurrentBase, getPagesFromFolder } from "@/lib/page-tree"
import { type source } from "@/lib/source"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york-v4/ui/button"
@@ -16,11 +17,15 @@ import {
} from "@/registry/new-york-v4/ui/popover"
const TOP_LEVEL_SECTIONS = [
{ name: "Get Started", href: "/docs" },
{ name: "Introduction", href: "/docs" },
{
name: "Components",
href: "/docs/components",
},
{
name: "Installation",
href: "/docs/installation",
},
{
name: "Directory",
href: "/docs/directory",
@@ -29,6 +34,10 @@ const TOP_LEVEL_SECTIONS = [
name: "MCP Server",
href: "/docs/mcp",
},
{
name: "Registry",
href: "/docs/registry",
},
{
name: "Forms",
href: "/docs/forms",
@@ -49,6 +58,8 @@ export function MobileNav({
className?: string
}) {
const [open, setOpen] = React.useState(false)
const pathname = usePathname()
const currentBase = getCurrentBase(pathname)
return (
<Popover open={open} onOpenChange={setOpen}>
@@ -125,31 +136,30 @@ export function MobileNav({
<div className="flex flex-col gap-8">
{tree?.children?.map((group, index) => {
if (group.type === "folder") {
const pages = getPagesFromFolder(group, currentBase)
return (
<div key={index} className="flex flex-col gap-4">
<div className="text-muted-foreground text-sm font-medium">
{group.name}
</div>
<div className="flex flex-col gap-3">
{group.children.map((item) => {
if (item.type === "page") {
if (!showMcpDocs && item.url.includes("/mcp")) {
return null
}
return (
<MobileLink
key={`${item.url}-${index}`}
href={item.url}
onOpenChange={setOpen}
className="flex items-center gap-2"
>
{item.name}{" "}
{PAGES_NEW.includes(item.url) && (
<span className="flex size-2 rounded-full bg-blue-500" />
)}
</MobileLink>
)
{pages.map((item) => {
if (!showMcpDocs && item.url.includes("/mcp")) {
return null
}
return (
<MobileLink
key={`${item.url}-${index}`}
href={item.url}
onOpenChange={setOpen}
className="flex items-center gap-2"
>
{item.name}{" "}
{PAGES_NEW.includes(item.url) && (
<span className="flex size-2 rounded-full bg-blue-500" />
)}
</MobileLink>
)
})}
</div>
</div>

View File

@@ -23,7 +23,7 @@ function PageHeaderHeading({
return (
<h1
className={cn(
"text-primary leading-tighter max-w-2xl text-4xl font-semibold tracking-tight text-balance lg:leading-[1.1] lg:font-semibold xl:text-5xl xl:tracking-tight",
"text-primary leading-tighter max-w-2xl text-4xl font-semibold tracking-tight text-balance lg:leading-[1.1] lg:font-semibold xl:text-5xl xl:tracking-tighter",
className
)}
{...props}

View File

@@ -11,7 +11,7 @@ import {
} from "@/registry/new-york-v4/ui/input-group"
export const SearchDirectory = () => {
const { query, setQuery } = useSearchRegistry()
const { query, registries, setQuery } = useSearchRegistry()
const onQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
@@ -29,6 +29,12 @@ export const SearchDirectory = () => {
value={query}
onChange={onQueryChange}
/>
<InputGroupAddon align="inline-end">
<span className="text-muted-foreground tabular-nums sm:text-xs">
{registries.length}{" "}
{registries.length === 1 ? "registry" : "registries"}
</span>
</InputGroupAddon>
<InputGroupAddon
align="inline-end"
data-disabled={!query.length}

View File

@@ -2,7 +2,7 @@ import { siteConfig } from "@/lib/config"
export function SiteFooter() {
return (
<footer className="group-has-[.section-soft]/body:bg-surface/40 3xl:fixed:bg-transparent group-has-[.docs-nav]/body:pb-20 group-has-[[data-slot=designer]]/body:hidden group-has-[.docs-nav]/body:sm:pb-0 dark:bg-transparent">
<footer className="group-has-[.section-soft]/body:bg-surface/40 3xl:fixed:bg-transparent group-has-[.docs-nav]/body:pb-20 group-has-[[data-slot=designer]]/body:hidden group-has-[[data-slot=docs]]/body:hidden group-has-[.docs-nav]/body:sm:pb-0 dark:bg-transparent dark:group-has-[.section-soft]/body:bg-surface/40">
<div className="container-wrapper px-4 xl:px-6">
<div className="flex h-(--footer-height) items-center justify-between">
<div className="text-muted-foreground w-full px-1 text-center text-xs leading-loose sm:text-sm">

View File

@@ -1,4 +1,4 @@
const SHOW = false
const SHOW = true
export function TailwindIndicator({
forceMount = false,

View File

@@ -4,6 +4,50 @@ description: Latest updates and announcements.
toc: false
---
## January 2026 - Base UI Documentation
We've shipped full documentation for Base UI components.
<div className="bg-muted/50 mt-6 flex w-full items-center justify-center rounded-lg border py-32">
<svg width="17" height="24" viewBox="0 0 17 24" className="size-12">
<path
fill="currentColor"
d="M9.5001 7.01537C9.2245 6.99837 9 7.22385 9 7.49999V23C13.4183 23 17 19.4183 17 15C17 10.7497 13.6854 7.27351 9.5001 7.01537Z"
/>
<path
fill="currentColor"
d="M8 9.8V12V23C3.58172 23 0 19.0601 0 14.2V12V1C4.41828 1 8 4.93989 8 9.8Z"
/>
</svg>
</div>
When we launched `npx shadcn create` in December, we introduced the ability to choose between Radix and Base UI as your component library. Today, we're following up with complete documentation for all Base UI components.
### What's New
- **Full Base UI docs** - Every component now has dedicated documentation for Base UI, covering usage, props, and examples.
- **Rebuilt examples** - All component examples have been rebuilt for both Radix and Base UI. Switch between them to see the implementation differences.
- **Side-by-side comparison** - The docs make it easy to compare how components work across both libraries.
### Same Abstraction, Different Primitives
The goal remains the same: give you a consistent API regardless of which primitive library you choose. The components look and behave the same way. Only the underlying implementation changes.
```tsx
// Works the same whether you're using Radix or Base UI.
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
```
If you're starting a new project, run `npx shadcn create` and pick your preferred library. The CLI handles the rest.
<Button asChild size="sm">
<Link href="/create" className="mt-6 no-underline!">
Try shadcn/create
</Link>
</Button>
---
## December 2025 - npx shadcn create
From the very first commit, the goal of shadcn/ui was to make it customizable.

View File

@@ -3,12 +3,22 @@ title: Registry Directory
description: Discover community registries for shadcn/ui components and blocks.
---
import { TriangleAlertIcon } from "lucide-react"
These registries are built into the CLI with no additional configuration required. To add a component, run: `npx shadcn add @<registry>/<component>`.
<DirectoryList />
<Callout
type="warning"
className="border-amber-200 bg-amber-50 font-semibold dark:border-amber-900 dark:bg-amber-950"
>
Community registries are maintained by third-party developers. Always review
code on installation to ensure it meets your security and quality standards.
</Callout>
Don't see a registry? Learn how to [add it here](/docs/registry/registry-index).
<DirectoryList />
## Documentation
You can use the `shadcn` CLI to run your own code registry. Running your own registry allows you to distribute your custom components, hooks, pages, config, rules and other files to any project.

View File

@@ -108,6 +108,7 @@ Here's the list of variables available for customization:
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
@@ -142,6 +143,7 @@ Here's the list of variables available for customization:
--accent: oklch(0.371 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
@@ -218,6 +220,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
@@ -252,6 +255,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
@@ -295,6 +299,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.97 0.001 106.424);
--accent-foreground: oklch(0.216 0.006 56.043);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.923 0.003 48.717);
--input: oklch(0.923 0.003 48.717);
--ring: oklch(0.709 0.01 56.259);
@@ -329,6 +334,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.268 0.007 34.298);
--accent-foreground: oklch(0.985 0.001 106.423);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.553 0.013 58.071);
@@ -372,6 +378,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--ring: oklch(0.705 0.015 286.067);
@@ -406,6 +413,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.274 0.006 286.033);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.552 0.016 285.938);
@@ -449,6 +457,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.967 0.003 264.542);
--accent-foreground: oklch(0.21 0.034 264.665);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.928 0.006 264.531);
--input: oklch(0.928 0.006 264.531);
--ring: oklch(0.707 0.022 261.325);
@@ -483,6 +492,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.278 0.033 256.848);
--accent-foreground: oklch(0.985 0.002 247.839);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.551 0.027 264.364);
@@ -526,6 +536,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.968 0.007 247.896);
--accent-foreground: oklch(0.208 0.042 265.755);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.929 0.013 255.508);
--input: oklch(0.929 0.013 255.508);
--ring: oklch(0.704 0.04 256.788);
@@ -560,6 +571,7 @@ For reference, here's a list of the base colors that are available.
--accent: oklch(0.279 0.041 260.031);
--accent-foreground: oklch(0.984 0.003 247.858);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.551 0.027 264.364);

View File

@@ -1,76 +0,0 @@
---
title: Accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
component: true
links:
doc: https://www.radix-ui.com/docs/primitives/components/accordion
api: https://www.radix-ui.com/docs/primitives/components/accordion#api-reference
---
<ComponentPreview
name="accordion-demo"
className="[&_.preview>[data-orientation=vertical]]:sm:max-w-[80%] **:[.preview]:min-h-[400px]"
description="An accordion with three items"
align="start"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add accordion
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-accordion
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="accordion" title="components/ui/accordion.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
```
```tsx showLineNumbers
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>
```

View File

@@ -1,88 +0,0 @@
---
title: Alert Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
featured: true
component: true
links:
doc: https://www.radix-ui.com/docs/primitives/components/alert-dialog
api: https://www.radix-ui.com/docs/primitives/components/alert-dialog#api-reference
---
<ComponentPreview
name="alert-dialog-demo"
title="An alert dialog with cancel and continue buttons."
description="An alert dialog with cancel and continue buttons."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add alert-dialog
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-alert-dialog
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="alert-dialog" title="components/ui/alert-dialog.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
```
```tsx showLineNumbers
<AlertDialog>
<AlertDialogTrigger>Open</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
```

View File

@@ -1,59 +0,0 @@
---
title: Alert
description: Displays a callout for user attention.
component: true
---
<ComponentPreview
name="alert-demo"
title="An alert with an icon, title and description."
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add alert
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="alert" title="components/ui/alert.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
```
```tsx showLineNumbers
<Alert variant="default | destructive">
<Terminal />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components and dependencies to your app using the cli.
</AlertDescription>
</Alert>
```

View File

@@ -1,64 +0,0 @@
---
title: Aspect Ratio
description: Displays content within a desired ratio.
component: true
links:
doc: https://www.radix-ui.com/docs/primitives/components/aspect-ratio
api: https://www.radix-ui.com/docs/primitives/components/aspect-ratio#api-reference
---
<ComponentPreview
name="aspect-ratio-demo"
title="Aspect Ratio"
description="A component that displays an image with a 16:9 aspect ratio."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add aspect-ratio
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-aspect-ratio
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="aspect-ratio" title="components/ui/aspect-ratio.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import { AspectRatio } from "@/components/ui/aspect-ratio"
```
```tsx showLineNumbers
<AspectRatio ratio={16 / 9}>
<Image src="..." alt="Image" className="rounded-md object-cover" />
</AspectRatio>
```

View File

@@ -1,65 +0,0 @@
---
title: Avatar
description: An image element with a fallback for representing the user.
component: true
links:
doc: https://www.radix-ui.com/docs/primitives/components/avatar
api: https://www.radix-ui.com/docs/primitives/components/avatar#api-reference
---
<ComponentPreview
name="avatar-demo"
title="Avatar"
description="An avatar with a fallback."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add avatar
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-avatar
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="avatar" title="components/ui/avatar.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
```
```tsx showLineNumbers
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
```

View File

@@ -1,71 +0,0 @@
---
title: Badge
description: Displays a badge or a component that looks like a badge.
component: true
---
<ComponentPreview
name="badge-demo"
title="Badge"
description="A default badge"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add badge
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="badge" title="components/ui/badge.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx
import { Badge } from "@/components/ui/badge"
```
```tsx
<Badge variant="default | outline | secondary | destructive">Badge</Badge>
```
### Link
You can use the `asChild` prop to make another component look like a badge. Here's an example of a link that looks like a badge.
```tsx showLineNumbers
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
export function LinkAsBadge() {
return (
<Badge asChild>
<Link href="/">Badge</Link>
</Badge>
)
}
```

View File

@@ -0,0 +1,142 @@
---
title: Accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
base: base
component: true
links:
doc: https://base-ui.com/react/components/accordion
api: https://base-ui.com/react/components/accordion#api-reference
---
<ComponentPreview
styleName="base-nova"
name="accordion-demo"
align="start"
previewClassName="*:data-[slot=accordion]:max-w-sm h-[300px]"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add accordion
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="accordion"
title="components/ui/accordion.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
```
```tsx showLineNumbers
<Accordion defaultValue={["item-1"]}>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>
```
## Examples
### Basic
A basic accordion that shows one item at a time. The first item is open by default.
<ComponentPreview
styleName="base-nova"
name="accordion-basic"
align="start"
previewClassName="*:data-[slot=accordion]:max-w-sm h-[300px]"
/>
### Multiple
Use the `multiple` prop to allow multiple items to be open at the same time.
<ComponentPreview
styleName="base-nova"
name="accordion-multiple"
align="start"
previewClassName="*:data-[slot=accordion]:max-w-sm h-[450px]"
/>
### Disabled
Use the `disabled` prop on `AccordionItem` to disable individual items.
<ComponentPreview
styleName="base-nova"
name="accordion-disabled"
align="start"
previewClassName="*:data-[slot=accordion]:max-w-sm h-[300px]"
/>
### Borders
Add `border` to the `Accordion` and `border-b last:border-b-0` to the `AccordionItem` to add borders to the items.
<ComponentPreview
styleName="base-nova"
name="accordion-borders"
align="start"
previewClassName="*:data-[slot=accordion]:max-w-sm h-[300px]"
/>
### Card
Wrap the `Accordion` in a `Card` component.
<ComponentPreview
styleName="base-nova"
name="accordion-card"
align="start"
previewClassName="*:data-[slot=accordion]:max-w-sm h-[435px]"
/>
## API Reference
See the [Base UI](https://base-ui.com/react/components/accordion#api-reference) documentation for more information.

View File

@@ -0,0 +1,159 @@
---
title: Alert Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
featured: true
base: base
component: true
links:
doc: https://base-ui.com/react/components/alert-dialog
api: https://base-ui.com/react/components/alert-dialog#api-reference
---
<ComponentPreview
styleName="base-nova"
name="alert-dialog-demo"
previewClassName="h-56"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add alert-dialog
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="alert-dialog"
title="components/ui/alert-dialog.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
```
```tsx showLineNumbers
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
Show Dialog
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account
from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
```
## Examples
### Basic
A basic alert dialog with a title, description, and cancel and continue buttons.
<ComponentPreview
styleName="base-nova"
name="alert-dialog-basic"
previewClassName="h-56"
/>
### Small
Use the `size="sm"` prop to make the alert dialog smaller.
<ComponentPreview
styleName="base-nova"
name="alert-dialog-small"
previewClassName="h-56"
/>
### Media
Use the `AlertDialogMedia` component to add a media element such as an icon or image to the alert dialog.
<ComponentPreview
styleName="base-nova"
name="alert-dialog-media"
previewClassName="h-56"
/>
### Small with Media
Use the `size="sm"` prop to make the alert dialog smaller and the `AlertDialogMedia` component to add a media element such as an icon or image to the alert dialog.
<ComponentPreview
styleName="base-nova"
name="alert-dialog-small-media"
previewClassName="h-56"
/>
### Destructive
Use the `AlertDialogAction` component to add a destructive action button to the alert dialog.
<ComponentPreview
styleName="base-nova"
name="alert-dialog-destructive"
previewClassName="h-56"
/>
## API Reference
### size
Use the `size` props on the `AlertDialogContent` component to control the size of the alert dialog. It accepts the following values:
| Prop | Type | Default |
| ------ | ------------------- | ----------- |
| `size` | `"default" \| "sm"` | `"default"` |
For more information about the other components and their props, see the [Base UI documentation](https://base-ui.com/react/components/alert-dialog#api-reference).

View File

@@ -0,0 +1,144 @@
---
title: Alert
description: Displays a callout for user attention.
base: base
component: true
---
<ComponentPreview
styleName="base-nova"
name="alert-demo"
previewClassName="h-auto sm:h-72 p-6"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add alert
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="alert" title="components/ui/alert.tsx" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Alert,
AlertAction,
AlertDescription,
AlertTitle,
} from "@/components/ui/alert"
```
```tsx showLineNumbers
<Alert>
<InfoIcon />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components and dependencies to your app using the cli.
</AlertDescription>
<AlertAction>
<Button variant="outline">Enable</Button>
</AlertAction>
</Alert>
```
## Examples
### Basic
A basic alert with an icon, title and description.
<ComponentPreview
styleName="base-nova"
name="alert-basic"
previewClassName="h-auto sm:h-72 p-6"
/>
### Destructive
Use `variant="destructive"` to create a destructive alert.
<ComponentPreview
styleName="base-nova"
name="alert-destructive"
previewClassName="h-auto sm:h-72 p-6"
/>
### Action
Use `AlertAction` to add a button or other action element to the alert.
<ComponentPreview
styleName="base-nova"
name="alert-action"
previewClassName="h-auto sm:h-72 p-6"
/>
### Custom Colors
You can customize the alert colors by adding custom classes such as `bg-amber-50 dark:bg-amber-950` to the `Alert` component.
<ComponentPreview
styleName="base-nova"
name="alert-colors"
previewClassName="h-auto sm:h-72 p-6"
/>
## API Reference
### Alert
The `Alert` component displays a callout for user attention.
| Prop | Type | Default |
| --------- | ---------------------------- | ----------- |
| `variant` | `"default" \| "destructive"` | `"default"` |
### AlertTitle
The `AlertTitle` component displays the title of the alert.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### AlertDescription
The `AlertDescription` component displays the description or content of the alert.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### AlertAction
The `AlertAction` component displays an action element (like a button) positioned absolutely in the top-right corner of the alert.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |

View File

@@ -0,0 +1,93 @@
---
title: Aspect Ratio
description: Displays content within a desired ratio.
base: base
component: true
---
<ComponentPreview name="aspect-ratio-demo" styleName="base-nova" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add aspect-ratio
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="aspect-ratio"
title="components/ui/aspect-ratio.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import { AspectRatio } from "@/components/ui/aspect-ratio"
```
```tsx showLineNumbers
<AspectRatio ratio={16 / 9}>
<Image src="..." alt="Image" className="rounded-md object-cover" />
</AspectRatio>
```
## Examples
### Square
A square aspect ratio component using the `ratio={1 / 1}` prop. This is useful for displaying images in a square format.
<ComponentPreview name="aspect-ratio-square" styleName="base-nova" />
### Portrait
A portrait aspect ratio component using the `ratio={9 / 16}` prop. This is useful for displaying images in a portrait format.
<ComponentPreview
styleName="base-nova"
name="aspect-ratio-portrait"
previewClassName="h-96"
/>
## API Reference
### AspectRatio
The `AspectRatio` component displays content within a desired ratio.
| Prop | Type | Default | Required |
| ----------- | -------- | ------- | -------- |
| `ratio` | `number` | - | Yes |
| `className` | `string` | - | No |
For more information, see the [Base UI documentation](https://base-ui.com/react/components/aspect-ratio#api-reference).

View File

@@ -0,0 +1,185 @@
---
title: Avatar
description: An image element with a fallback for representing the user.
base: base
component: true
links:
doc: https://base-ui.com/react/components/avatar
api: https://base-ui.com/react/components/avatar#api-reference
---
<ComponentPreview
styleName="base-nova"
name="avatar-demo"
previewClassName="h-72"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add avatar
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="avatar"
title="components/ui/avatar.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
```
```tsx showLineNumbers
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
```
## Examples
### Basic
A basic avatar component with an image and a fallback.
<ComponentPreview styleName="base-nova" name="avatar-basic" />
### Badge
Use the `AvatarBadge` component to add a badge to the avatar. The badge is positioned at the bottom right of the avatar.
<ComponentPreview styleName="base-nova" name="avatar-badge" />
Use the `className` prop to add custom styles to the badge such as custom colors, sizes, etc.
```tsx showLineNumbers
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
<AvatarBadge className="bg-green-600 dark:bg-green-800" />
</Avatar>
```
### Badge with Icon
You can also use an icon inside `<AvatarBadge>`.
<ComponentPreview styleName="base-nova" name="avatar-badge-icon" />
### Avatar Group
Use the `AvatarGroup` component to add a group of avatars.
<ComponentPreview styleName="base-nova" name="avatar-group" />
### Avatar Group Count
Use `<AvatarGroupCount>` to add a count to the group.
<ComponentPreview styleName="base-nova" name="avatar-group-count" />
### Avatar Group with Icon
You can also use an icon inside `<AvatarGroupCount>`.
<ComponentPreview styleName="base-nova" name="avatar-group-count-icon" />
### Sizes
Use the `size` prop to change the size of the avatar.
<ComponentPreview styleName="base-nova" name="avatar-size" />
### Dropdown
You can use the `Avatar` component as a trigger for a dropdown menu.
<ComponentPreview styleName="base-nova" name="avatar-dropdown" />
## API Reference
### Avatar
The `Avatar` component is the root component that wraps the avatar image and fallback.
| Prop | Type | Default |
| ----------- | --------------------------- | ----------- |
| `size` | `"default" \| "sm" \| "lg"` | `"default"` |
| `className` | `string` | - |
### AvatarImage
The `AvatarImage` component displays the avatar image. It accepts all Base UI Avatar Image props.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `src` | `string` | - |
| `alt` | `string` | - |
| `className` | `string` | - |
### AvatarFallback
The `AvatarFallback` component displays a fallback when the image fails to load. It accepts all Base UI Avatar Fallback props.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### AvatarBadge
The `AvatarBadge` component displays a badge indicator on the avatar, typically positioned at the bottom right.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### AvatarGroup
The `AvatarGroup` component displays a group of avatars with overlapping styling.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### AvatarGroupCount
The `AvatarGroupCount` component displays a count indicator in an avatar group, typically showing the number of additional avatars.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
For more information about Base UI Avatar props, see the [Base UI documentation](https://base-ui.com/react/components/avatar#api-reference).

View File

@@ -0,0 +1,97 @@
---
title: Badge
description: Displays a badge or a component that looks like a badge.
base: base
component: true
---
<ComponentPreview styleName="base-nova" name="badge-demo" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add badge
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="badge"
title="components/ui/badge.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx
import { Badge } from "@/components/ui/badge"
```
```tsx
<Badge variant="default | outline | secondary | destructive">Badge</Badge>
```
## Examples
### Variants
Use the `variant` prop to change the variant of the badge.
<ComponentPreview styleName="base-nova" name="badge-variants" />
### With Icon
You can render an icon inside the badge. Use `data-icon="inline-start"` to render the icon on the left and `data-icon="inline-end"` to render the icon on the right.
<ComponentPreview styleName="base-nova" name="badge-icon" />
### With Spinner
You can render a spinner inside the badge. Remember to add the `data-icon="inline-start"` or `data-icon="inline-end"` prop to the spinner.
<ComponentPreview styleName="base-nova" name="badge-spinner" />
### Link
Use the `render` prop to render a link as a badge.
<ComponentPreview styleName="base-nova" name="badge-link" />
### Custom Colors
You can customize the colors of a badge by adding custom classes such as `bg-green-50 dark:bg-green-800` to the `Badge` component.
<ComponentPreview styleName="base-nova" name="badge-colors" />
## API Reference
### Badge
The `Badge` component displays a badge or a component that looks like a badge.
| Prop | Type | Default |
| ----------- | ----------------------------------------------------------------------------- | ----------- |
| `variant` | `"default" \| "secondary" \| "destructive" \| "outline" \| "ghost" \| "link"` | `"default"` |
| `className` | `string` | - |

View File

@@ -0,0 +1,176 @@
---
title: Breadcrumb
description: Displays the path to the current resource using a hierarchy of links.
base: base
component: true
---
<ComponentPreview
styleName="base-nova"
name="breadcrumb-demo"
previewClassName="p-2"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add breadcrumb
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="breadcrumb"
title="components/ui/breadcrumb.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
```
```tsx showLineNumbers
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink render={<a href="/" />}>Home</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink render={<a href="/components" />}>
Components
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
```
## Examples
### Basic
A basic breadcrumb with a home link and a components link.
<ComponentPreview styleName="base-nova" name="breadcrumb-basic" />
### Custom separator
Use a custom component as `children` for `<BreadcrumbSeparator />` to create a custom separator.
<ComponentPreview styleName="base-nova" name="breadcrumb-separator" />
### Dropdown
You can compose `<BreadcrumbItem />` with a `<DropdownMenu />` to create a dropdown in the breadcrumb.
<ComponentPreview styleName="base-nova" name="breadcrumb-dropdown" />
### Collapsed
We provide a `<BreadcrumbEllipsis />` component to show a collapsed state when the breadcrumb is too long.
<ComponentPreview
styleName="base-nova"
name="breadcrumb-ellipsis"
previewClassName="p-2"
/>
### Link component
To use a custom link component from your routing library, you can use the `render` prop on `<BreadcrumbLink />`.
<ComponentPreview styleName="base-nova" name="breadcrumb-link" />
## API Reference
### Breadcrumb
The `Breadcrumb` component is the root navigation element that wraps all breadcrumb components.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### BreadcrumbList
The `BreadcrumbList` component displays the ordered list of breadcrumb items.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### BreadcrumbItem
The `BreadcrumbItem` component wraps individual breadcrumb items.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### BreadcrumbLink
The `BreadcrumbLink` component displays a clickable link in the breadcrumb.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### BreadcrumbPage
The `BreadcrumbPage` component displays the current page in the breadcrumb (non-clickable).
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### BreadcrumbSeparator
The `BreadcrumbSeparator` component displays a separator between breadcrumb items. You can pass custom children to override the default separator icon.
| Prop | Type | Default |
| ----------- | ----------------- | ------- |
| `children` | `React.ReactNode` | - |
| `className` | `string` | - |
### BreadcrumbEllipsis
The `BreadcrumbEllipsis` component displays an ellipsis indicator for collapsed breadcrumb items.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |

View File

@@ -1,17 +1,18 @@
---
title: Button Group
description: A container that groups related buttons together with consistent styling.
base: base
component: true
---
<ComponentPreview name="button-group-demo" />
<ComponentPreview styleName="base-nova" name="button-group-demo" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
@@ -24,17 +25,21 @@ npx shadcn@latest add button-group
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slot
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="button-group" title="components/ui/button-group.tsx" />
<ComponentSource
name="button-group"
title="components/ui/button-group.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -85,19 +90,19 @@ import {
Set the `orientation` prop to change the button group layout.
<ComponentPreview name="button-group-orientation" />
<ComponentPreview styleName="base-nova" name="button-group-orientation" />
### Size
Control the size of buttons using the `size` prop on individual buttons.
<ComponentPreview name="button-group-size" />
<ComponentPreview styleName="base-nova" name="button-group-size" />
### Nested
Nest `<ButtonGroup>` components to create button groups with spacing.
<ComponentPreview name="button-group-nested" />
<ComponentPreview styleName="base-nova" name="button-group-nested" />
### Separator
@@ -105,43 +110,43 @@ The `ButtonGroupSeparator` component visually divides buttons within a group.
Buttons with variant `outline` do not need a separator since they have a border. For other variants, a separator is recommended to improve the visual hierarchy.
<ComponentPreview name="button-group-separator" />
<ComponentPreview styleName="base-nova" name="button-group-separator" />
### Split
Create a split button group by adding two buttons separated by a `ButtonGroupSeparator`.
<ComponentPreview name="button-group-split" />
<ComponentPreview styleName="base-nova" name="button-group-split" />
### Input
Wrap an `Input` component with buttons.
<ComponentPreview name="button-group-input" />
<ComponentPreview styleName="base-nova" name="button-group-input" />
### Input Group
Wrap an `InputGroup` component to create complex input layouts.
<ComponentPreview name="button-group-input-group" />
<ComponentPreview styleName="base-nova" name="button-group-input-group" />
### Dropdown Menu
Create a split button group with a `DropdownMenu` component.
<ComponentPreview name="button-group-dropdown" />
<ComponentPreview styleName="base-nova" name="button-group-dropdown" />
### Select
Pair with a `Select` component.
<ComponentPreview name="button-group-select" />
<ComponentPreview styleName="base-nova" name="button-group-select" />
### Popover
Use with a `Popover` component.
<ComponentPreview name="button-group-popover" />
<ComponentPreview styleName="base-nova" name="button-group-popover" />
## API Reference

View File

@@ -0,0 +1,153 @@
---
title: Button
description: Displays a button or a component that looks like a button.
featured: true
base: base
component: true
---
<ComponentPreview styleName="base-nova" name="button-demo" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add button
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="button"
title="components/ui/button.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx
import { Button } from "@/components/ui/button"
```
```tsx
<Button variant="outline">Button</Button>
```
## Cursor
Tailwind v4 [switched](https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor) from `cursor: pointer` to `cursor: default` for the button component.
If you want to keep the `cursor: pointer` behavior, add the following code to your CSS file:
```css showLineNumbers title="globals.css"
@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}
```
## Examples
### Size
Use the `size` prop to change the size of the button.
<ComponentPreview styleName="base-nova" name="button-size" />
### Default
<ComponentPreview styleName="base-nova" name="button-default" />
### Outline
<ComponentPreview styleName="base-nova" name="button-outline" />
### Secondary
<ComponentPreview styleName="base-nova" name="button-secondary" />
### Ghost
<ComponentPreview styleName="base-nova" name="button-ghost" />
### Destructive
<ComponentPreview styleName="base-nova" name="button-destructive" />
### Link
<ComponentPreview styleName="base-nova" name="button-link" />
### Icon
<ComponentPreview styleName="base-nova" name="button-icon" />
### With Icon
Remember to add the `data-icon="inline-start"` or `data-icon="inline-end"` attribute to the icon for the correct spacing.
<ComponentPreview styleName="base-nova" name="button-with-icon" />
### Rounded
Use the `rounded-full` class to make the button rounded.
<ComponentPreview styleName="base-nova" name="button-rounded" />
### Spinner
Render a `<Spinner />` component inside the button to show a loading state. Remember to add the `data-icon="inline-start"` or `data-icon="inline-end"` attribute to the spinner for the correct spacing.
<ComponentPreview styleName="base-nova" name="button-spinner" />
### Button Group
To create a button group, use the `ButtonGroup` component. See the [Button Group](/docs/components/base/button-group) documentation for more details.
<ComponentPreview styleName="base-nova" name="button-group-demo" />
### Link
You can use the `render` prop on `<Button />` to make another component look like a button. Here's an example of a link that looks like a button.
<ComponentPreview styleName="base-nova" name="button-render" />
## API Reference
### Button
The `Button` component is a wrapper around the `button` element that adds a variety of styles and functionality.
| Prop | Type | Default |
| --------- | ------------------------------------------------------------------------------------ | ----------- |
| `variant` | `"default" \| "outline" \| "ghost" \| "destructive" \| "secondary" \| "link"` | `"default"` |
| `size` | `"default" \| "xs" \| "sm" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg"` | `"default"` |

View File

@@ -1,15 +1,16 @@
---
title: Calendar
description: A date field component that allows users to enter and edit date.
description: A calendar component that allows users to select a date or a range of dates.
base: base
component: true
links:
doc: https://react-day-picker.js.org
---
<ComponentPreview
styleName="base-nova"
name="calendar-demo"
title="Calendar"
description="A calendar showing the current date."
previewClassName="h-96"
/>
## Blocks
@@ -23,7 +24,7 @@ See all calendar blocks in the [Blocks Library](/blocks/calendar) page.
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
@@ -36,7 +37,7 @@ npx shadcn@latest add calendar
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
@@ -50,7 +51,11 @@ The `Calendar` component uses the `Button` component. Make sure you have it inst
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="calendar" title="components/ui/calendar.tsx" />
<ComponentSource
name="calendar"
title="components/ui/calendar.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -85,13 +90,9 @@ See the [React DayPicker](https://react-day-picker.js.org) documentation for mor
The `Calendar` component is built on top of [React DayPicker](https://react-day-picker.js.org).
## Customization
See the [React DayPicker](https://react-day-picker.js.org/docs/customization) documentation for more information on how to customize the `Calendar` component.
## Date Picker
You can use the `<Calendar>` component to build a date picker. See the [Date Picker](/docs/components/date-picker) page for more information.
You can use the `<Calendar>` component to build a date picker. See the [Date Picker](/docs/components/base/date-picker) page for more information.
## Persian / Hijri / Jalali Calendar
@@ -103,9 +104,11 @@ To use the Persian calendar, edit `components/ui/calendar.tsx` and replace `reac
```
<ComponentPreview
styleName="base-nova"
name="calendar-hijri"
title="Persian / Hijri / Jalali Calendar"
description="A Persian calendar."
previewClassName="h-[400px]"
/>
## Selected Date (With TimeZone)
@@ -138,53 +141,65 @@ export function CalendarWithTimezone() {
## Examples
### Range Calendar
### Basic
A basic calendar component. We used `className="rounded-lg border"` to style the calendar.
<ComponentPreview
name="calendar-05"
title="Range Calendar"
description="A calendar showing the current date and range selection."
className="**:[.preview]:h-auto lg:**:[.preview]:h-[450px]"
styleName="base-nova"
name="calendar-basic"
previewClassName="h-96"
/>
### Range Calendar
Use the `mode="range"` prop to enable range selection.
<ComponentPreview
styleName="base-nova"
name="calendar-range"
previewClassName="h-[36rem] md:h-96"
/>
### Month and Year Selector
Use `captionLayout="dropdown"` to show month and year dropdowns.
<ComponentPreview
name="calendar-13"
title="Month and Year Selector"
description="A calendar with month and year dropdowns."
styleName="base-nova"
name="calendar-caption"
previewClassName="h-96"
/>
### Date of Birth Picker
### Presets
<ComponentPreview
name="calendar-22"
title="Date of Birth Picker"
description="A calendar with date of birth picker."
styleName="base-nova"
name="calendar-presets"
previewClassName="h-[650px]"
/>
### Date and Time Picker
<ComponentPreview
name="calendar-24"
title="Date and Time Picker"
description="A calendar with date and time picker."
styleName="base-nova"
name="calendar-time"
previewClassName="h-[600px]"
/>
### Natural Language Picker
This component uses the `chrono-node` library to parse natural language dates.
### Booked dates
<ComponentPreview
name="calendar-29"
title="Natural Language Picker"
description="A calendar with natural language picker."
styleName="base-nova"
name="calendar-booked-dates"
previewClassName="h-96"
/>
### Custom Cell Size
<ComponentPreview
name="calendar-18"
styleName="base-nova"
name="calendar-custom-days"
title="Custom Cell Size"
description="A calendar with custom cell size that's responsive."
className="**:[.preview]:h-[560px]"
@@ -212,6 +227,16 @@ Or use fixed values:
/>
```
### Week Numbers
Use `showWeekNumber` to show week numbers.
<ComponentPreview
styleName="base-nova"
name="calendar-week-numbers"
previewClassName="h-96"
/>
## Upgrade Guide
### Tailwind v4
@@ -448,7 +473,7 @@ function CalendarDayButton({
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
className={cn(
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70",
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md [&>span]:text-xs [&>span]:opacity-70",
defaultClassNames.day,
className
)}
@@ -476,24 +501,6 @@ npx shadcn@latest add calendar-02
This will install the latest version of the calendar blocks.
## Changelog
## API Reference
### 2025-10-26 Fixed day radius with week numbers
We have fixed an issue where the selected day's left border radius was not applied correctly when week numbers were displayed. The fix ensures that when `showWeekNumber` is enabled, the first day (which is the second child due to the week number column) correctly receives the rounded left border.
To apply this fix, edit `components/ui/calendar.tsx` and update the `day` class in `classNames`:
```tsx showLineNumbers title="components/ui/calendar.tsx" {5-7}
classNames={{
// ... other classNames
day: cn(
"relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
props.showWeekNumber
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
defaultClassNames.day
),
// ... other classNames
}}
```
See the [React DayPicker](https://react-day-picker.js.org) documentation for more information on the `Calendar` component API.

View File

@@ -0,0 +1,159 @@
---
title: Card
description: Displays a card with header, content, and footer.
base: base
component: true
---
<ComponentPreview
name="card-demo"
styleName="base-nova"
previewClassName="h-[30rem]"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add card
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="card"
title="components/ui/card.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
```
```tsx showLineNumbers
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card Description</CardDescription>
<CardAction>Card Action</CardAction>
</CardHeader>
<CardContent>
<p>Card Content</p>
</CardContent>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</Card>
```
## Examples
### Size
Use the `size="sm"` prop to set the size of the card to small. The small size variant uses smaller spacing.
<ComponentPreview
styleName="base-nova"
name="card-small"
previewClassName="h-96"
/>
### Image
Add an image before the card header to create a card with an image.
<ComponentPreview
styleName="base-nova"
name="card-image"
previewClassName="h-[32rem]"
/>
## API Reference
### Card
The `Card` component is the root container for card content.
| Prop | Type | Default |
| ----------- | ------------------- | ----------- |
| `size` | `"default" \| "sm"` | `"default"` |
| `className` | `string` | - |
### CardHeader
The `CardHeader` component is used for a title, description, and optional action.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### CardTitle
The `CardTitle` component is used for the card title.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### CardDescription
The `CardDescription` component is used for helper text under the title.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### CardAction
The `CardAction` component places content in the top-right of the header (for example, a button or a badge).
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### CardContent
The `CardContent` component is used for the main card body.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |
### CardFooter
The `CardFooter` component is used for actions and secondary content at the bottom of the card.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | - |

View File

@@ -1,6 +1,7 @@
---
title: Carousel
description: A carousel with motion and swipe built using Embla.
base: base
component: true
links:
doc: https://www.embla-carousel.com/get-started/react
@@ -8,9 +9,9 @@ links:
---
<ComponentPreview
styleName="base-nova"
name="carousel-demo"
title="Carousel"
description="A carousel with 5 items and a previous and next button."
previewClassName="h-80 sm:h-[32rem]"
/>
## About
@@ -22,7 +23,7 @@ The carousel component is built using the [Embla Carousel](https://www.embla-car
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
@@ -36,7 +37,7 @@ npx shadcn@latest add carousel
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
@@ -46,7 +47,11 @@ npm install embla-carousel-react
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="carousel" title="components/ui/carousel.tsx" />
<ComponentSource
name="carousel"
title="components/ui/carousel.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -86,11 +91,7 @@ import {
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
<ComponentPreview
name="carousel-size"
title="Carousel"
description="A carousel with 3 active items of equal size."
/>
<ComponentPreview styleName="base-nova" name="carousel-size" />
```tsx showLineNumbers {4-6}
// 33% of the carousel width.
@@ -118,21 +119,7 @@ To set the size of the items, you can use the `basis` utility class on the `<Car
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
<Callout className="mt-6">
**Why:** I tried to use the `gap` property or a `grid` layout on the `
<CarouselContent />` but it required a lot of math and mental effort to get the
spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
use.
You can always adjust this in your own project if you need to.
</Callout>
<ComponentPreview
name="carousel-spacing"
title="Carousel"
description="A carousel with 3 items with a spacing of 1rem."
/>
<ComponentPreview styleName="base-nova" name="carousel-spacing" />
```tsx showLineNumbers /-ml-4/ /pl-4/
<Carousel>
@@ -159,9 +146,9 @@ You can always adjust this in your own project if you need to.
Use the `orientation` prop to set the orientation of the carousel.
<ComponentPreview
styleName="base-nova"
name="carousel-orientation"
title="Carousel"
description="A vertical carousel."
previewClassName="h-[32rem]"
/>
```tsx showLineNumbers /vertical | horizontal/
@@ -198,9 +185,9 @@ You can pass options to the carousel using the `opts` prop. See the [Embla Carou
Use a state and the `setApi` props to get an instance of the carousel API.
<ComponentPreview
styleName="base-nova"
name="carousel-api"
title="Carousel"
description="A carousel with a slide counter."
previewClassName="sm:h-[32rem]"
/>
```tsx showLineNumbers {1,4,22}
@@ -293,9 +280,11 @@ export function Example() {
```
<ComponentPreview
styleName="base-nova"
name="carousel-plugin"
title="Carousel"
description="A carousel with the autoplay plugin."
previewClassName="sm:h-[32rem]"
/>
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.
## API Reference
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on props and plugins.

View File

@@ -1,6 +1,7 @@
---
title: Chart
description: Beautiful charts. Built using Recharts. Copy and paste into your apps.
base: base
component: true
---
@@ -11,7 +12,8 @@ component: true
</Callout>
<ComponentPreview
name="chart-bar-interactive"
styleName="base-nova"
name="chart-demo"
className="theme-blue [&_.preview]:h-auto [&_.preview]:p-0 [&_.preview]:lg:min-h-[404px] [&_.preview>div]:w-full [&_.preview>div]:border-none [&_.preview>div]:shadow-none"
hideCode
/>
@@ -51,57 +53,23 @@ We do not wrap Recharts. This means you're not locked into an abstraction. When
## Installation
<Callout className="mt-4">
**Note:** If you are using charts with **React 19** or the **Next.js 15**, see the note [here](/docs/react-19#recharts).
</Callout>
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
<Steps>
<Step>Run the following command to install `chart.tsx`</Step>
```bash
npx shadcn@latest add chart
```
<Step>Add the following colors to your CSS file</Step>
```css title="app/globals.css" showLineNumbers
@layer base {
:root {
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
}
}
```
</Steps>
</TabsContent>
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
@@ -111,7 +79,11 @@ npm install recharts
<Step>Copy and paste the following code into `components/ui/chart.tsx`.</Step>
<ComponentSource name="chart" title="components/ui/chart.tsx" />
<ComponentSource
name="chart"
title="components/ui/chart.tsx"
styleName="base-nova"
/>
<Step>Add the following colors to your CSS file</Step>
@@ -197,11 +169,10 @@ You can now build your chart using Recharts components.
</Callout>
<ComponentSource name="chart-bar-demo" title="components/example-chart.tsx" />
<ComponentPreview
name="chart-bar-demo"
className="[&_.preview]:min-h-[250px] [&_.preview]:p-4"
styleName="base-nova"
name="chart-example"
previewClassName="h-80"
/>
</Steps>
@@ -210,7 +181,7 @@ You can now build your chart using Recharts components.
Let's add a grid to the chart.
<Steps>
<Steps className="mb-0 pt-2">
<Step>Import the `CartesianGrid` component.</Step>
@@ -231,8 +202,9 @@ import { Bar, BarChart, CartesianGrid } from "recharts"
```
<ComponentPreview
name="chart-bar-demo-grid"
className="[&_.preview]:min-h-[250px] [&_.preview]:p-4"
styleName="base-nova"
name="chart-example-grid"
previewClassName="h-80"
/>
</Steps>
@@ -241,7 +213,7 @@ import { Bar, BarChart, CartesianGrid } from "recharts"
To add an x-axis to the chart, we'll use the `XAxis` component.
<Steps>
<Steps className="mb-0 pt-2">
<Step>Import the `XAxis` component.</Step>
@@ -269,8 +241,9 @@ import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
```
<ComponentPreview
name="chart-bar-demo-axis"
className="[&_.preview]:min-h-[250px] [&_.preview]:p-4"
styleName="base-nova"
name="chart-example-axis"
previewClassName="h-80"
/>
</Steps>
@@ -281,7 +254,7 @@ So far we've only used components from Recharts. They look great out of the box
To add a tooltip, we'll use the custom `ChartTooltip` and `ChartTooltipContent` components from `chart`.
<Steps>
<Steps className="mb-0 pt-2">
<Step>Import the `ChartTooltip` and `ChartTooltipContent` components.</Step>
@@ -310,8 +283,9 @@ import { ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
```
<ComponentPreview
name="chart-bar-demo-tooltip"
className="[&_.preview]:min-h-[250px] [&_.preview]:p-4"
styleName="base-nova"
name="chart-example-tooltip"
previewClassName="h-80"
/>
Hover to see the tooltips. Easy, right? Two components, and we've got a beautiful tooltip.
@@ -322,7 +296,7 @@ Hover to see the tooltips. Easy, right? Two components, and we've got a beautifu
We'll do the same for the legend. We'll use the `ChartLegend` and `ChartLegendContent` components from `chart`.
<Steps>
<Steps className="mb-0 pt-2">
<Step>Import the `ChartLegend` and `ChartLegendContent` components.</Step>
@@ -352,8 +326,9 @@ import { ChartLegend, ChartLegendContent } from "@/components/ui/chart"
```
<ComponentPreview
name="chart-bar-demo-legend"
className="[&_.preview]:min-h-[250px] [&_.preview]:p-4"
styleName="base-nova"
name="chart-example-legend"
previewClassName="h-80"
/>
</Steps>
@@ -398,11 +373,11 @@ Charts have built-in support for theming. You can use css variables (recommended
### CSS Variables
<Steps>
<Steps className="mb-0 pt-2">
<Step>Define your colors in your css file</Step>
```css {6-7,14-15} title="app/globals.css" showLineNumbers
```css title="app/globals.css" showLineNumbers
@layer base {
:root {
--chart-1: oklch(0.646 0.222 41.116);
@@ -418,7 +393,7 @@ Charts have built-in support for theming. You can use css variables (recommended
<Step>Add the color to your `chartConfig`</Step>
```tsx {4,8} showLineNumbers
```tsx title="components/example-chart.tsx" showLineNumbers
const chartConfig = {
desktop: {
label: "Desktop",
@@ -437,12 +412,24 @@ const chartConfig = {
You can also define your colors directly in the chart config. Use the color format you prefer.
```tsx showLineNumbers
```tsx title="components/example-chart.tsx" showLineNumbers
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
},
mobile: {
label: "Mobile",
color: "hsl(220, 98%, 61%)",
},
tablet: {
label: "Tablet",
color: "oklch(0.5 0.2 240)",
},
laptop: {
label: "Laptop",
color: "var(--chart-2)",
},
} satisfies ChartConfig
```
@@ -458,7 +445,7 @@ To use the theme colors in your chart, reference the colors using the format `va
#### Chart Data
```tsx showLineNumbers
```tsx title="components/example-chart.tsx" showLineNumbers
const chartData = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
@@ -467,7 +454,7 @@ const chartData = [
#### Tailwind
```tsx
```tsx title="components/example-chart.tsx"
<LabelList className="fill-[--color-desktop]" />
```
@@ -475,11 +462,7 @@ const chartData = [
A chart tooltip contains a label, name, indicator and value. You can use a combination of these to customize your tooltip.
<ComponentPreview
name="chart-tooltip-demo"
className="[&_.preview]:py-0"
hideCode
/>
<ComponentPreview styleName="base-nova" name="chart-tooltip" hideCode />
You can turn on/off any of these using the `hideLabel`, `hideIndicator` props and customize the indicator style using the `indicator` prop.
@@ -487,11 +470,11 @@ Use `labelKey` and `nameKey` to use a custom key for the tooltip label and name.
Chart comes with the `<ChartTooltip>` and `<ChartTooltipContent>` components. You can use these two components to add custom tooltips to your chart.
```tsx
```tsx title="components/example-chart.tsx"
import { ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
```
```tsx
```tsx title="components/example-chart.tsx"
<ChartTooltip content={<ChartTooltipContent />} />
```
@@ -536,7 +519,7 @@ const chartConfig = {
} satisfies ChartConfig
```
```tsx
```tsx title="components/example-chart.tsx"
<ChartTooltip
content={<ChartTooltipContent labelKey="visitors" nameKey="browser" />}
/>
@@ -548,11 +531,11 @@ This will use `Total Visitors` for label and `Chrome` and `Safari` for the toolt
You can use the custom `<ChartLegend>` and `<ChartLegendContent>` components to add a legend to your chart.
```tsx
```tsx title="components/example-chart.tsx"
import { ChartLegend, ChartLegendContent } from "@/components/ui/chart"
```
```tsx
```tsx title="components/example-chart.tsx"
<ChartLegend content={<ChartLegendContent />} />
```
@@ -582,7 +565,7 @@ const chartConfig = {
} satisfies ChartConfig
```
```tsx
```tsx title="components/example-chart.tsx"
<ChartLegend content={<ChartLegendContent nameKey="browser" />} />
```
@@ -594,6 +577,6 @@ You can turn on the `accessibilityLayer` prop to add an accessible layer to your
This prop adds keyboard access and screen reader support to your charts.
```tsx
```tsx title="components/example-chart.tsx"
<LineChart accessibilityLayer />
```

View File

@@ -0,0 +1,127 @@
---
title: Checkbox
description: A control that allows the user to toggle between checked and not checked.
base: base
component: true
links:
doc: https://base-ui.com/react/components/checkbox
api: https://base-ui.com/react/components/checkbox#api-reference
---
<ComponentPreview
styleName="base-nova"
name="checkbox-demo"
previewClassName="h-80"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add checkbox
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="checkbox"
title="components/ui/checkbox.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx
import { Checkbox } from "@/components/ui/checkbox"
```
```tsx
<Checkbox />
```
## Checked State
Use `defaultChecked` for uncontrolled checkboxes, or `checked` and
`onCheckedChange` to control the state.
```tsx showLineNumbers
import * as React from "react"
export function Example() {
const [checked, setChecked] = React.useState(false)
return <Checkbox checked={checked} onCheckedChange={setChecked} />
}
```
## Invalid State
Set `aria-invalid` on the checkbox and `data-invalid` on the field wrapper to
show the invalid styles.
<ComponentPreview styleName="base-nova" name="checkbox-invalid" />
## Examples
### Basic
Pair the checkbox with `Field` and `FieldLabel` for proper layout and labeling.
<ComponentPreview styleName="base-nova" name="checkbox-basic" />
### Description
Use `FieldContent` and `FieldDescription` for helper text.
<ComponentPreview styleName="base-nova" name="checkbox-description" />
### Disabled
Use the `disabled` prop to prevent interaction and add the `data-disabled` attribute to the `<Field>` component for disabled styles.
<ComponentPreview styleName="base-nova" name="checkbox-disabled" />
### Group
Use multiple fields to create a checkbox list.
<ComponentPreview styleName="base-nova" name="checkbox-group" />
### Table
<ComponentPreview
styleName="base-nova"
name="checkbox-table"
previewClassName="p-4 md:p-8"
/>
## API Reference
See the [Base UI](https://base-ui.com/react/components/checkbox#api-reference) documentation for more information.

View File

@@ -0,0 +1,123 @@
---
title: Collapsible
description: An interactive component which expands/collapses a panel.
base: base
component: true
featured: true
links:
doc: https://base-ui.com/react/components/collapsible
api: https://base-ui.com/react/components/collapsible#api-reference
---
<ComponentPreview styleName="base-nova" name="collapsible-demo" align="start" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add collapsible
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="collapsible"
title="components/ui/collapsible.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
```
```tsx showLineNumbers
<Collapsible>
<CollapsibleTrigger>Can I use this in my project?</CollapsibleTrigger>
<CollapsibleContent>
Yes. Free to use for personal and commercial projects. No attribution
required.
</CollapsibleContent>
</Collapsible>
```
## Controlled State
Use the `open` and `onOpenChange` props to control the state.
```tsx showLineNumbers
import * as React from "react"
export function Example() {
const [open, setOpen] = React.useState(false)
return (
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
<CollapsibleContent>Content</CollapsibleContent>
</Collapsible>
)
}
```
## Examples
### Basic
<ComponentPreview
styleName="base-nova"
name="collapsible-basic"
align="start"
/>
### Settings Panel
Use a trigger button to reveal additional settings.
<ComponentPreview styleName="base-nova" name="collapsible-settings" />
### File Tree
Use nested collapsibles to build a file tree.
<ComponentPreview
styleName="base-nova"
name="collapsible-file-tree"
previewClassName="h-[36rem]"
/>
## API Reference
See the [Base UI](https://base-ui.com/react/components/collapsible#api-reference) documentation for more information.

View File

@@ -0,0 +1,261 @@
---
title: Combobox
description: Autocomplete input with a list of suggestions.
base: base
component: true
links:
doc: https://base-ui.com/react/components/combobox
api: https://base-ui.com/react/components/combobox#api-reference
---
<ComponentPreview
styleName="base-nova"
name="combobox-demo"
description="A combobox with a list of frameworks."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add combobox
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="combobox"
title="components/ui/combobox.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
```
```tsx showLineNumbers
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
export function ExampleCombobox() {
return (
<Combobox items={frameworks}>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}
```
## Custom Items
Use `itemToStringValue` when your items are objects.
```tsx showLineNumbers
import * as React from "react"
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
type Framework = {
label: string
value: string
}
const frameworks: Framework[] = [
{ label: "Next.js", value: "next" },
{ label: "SvelteKit", value: "sveltekit" },
{ label: "Nuxt", value: "nuxt" },
]
export function ExampleComboboxCustomItems() {
return (
<Combobox
items={frameworks}
itemToStringValue={(framework) => framework.label}
>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(framework) => (
<ComboboxItem key={framework.value} value={framework}>
{framework.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}
```
## Multiple Selection
Use `multiple` with chips for multi-select behavior.
```tsx showLineNumbers
import * as React from "react"
import {
Combobox,
ComboboxChip,
ComboboxChips,
ComboboxChipsInput,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxValue,
} from "@/components/ui/combobox"
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
export function ExampleComboboxMultiple() {
const [value, setValue] = React.useState<string[]>([])
return (
<Combobox
items={frameworks}
multiple
value={value}
onValueChange={setValue}
>
<ComboboxChips>
<ComboboxValue>
{value.map((item) => (
<ComboboxChip key={item}>{item}</ComboboxChip>
))}
</ComboboxValue>
<ComboboxChipsInput placeholder="Add framework" />
</ComboboxChips>
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}
```
## Examples
### Basic
A simple combobox with a list of frameworks.
<ComponentPreview styleName="base-nova" name="combobox-basic" />
### Multiple
A combobox with multiple selection using `multiple` and `ComboboxChips`.
<ComponentPreview styleName="base-nova" name="combobox-multiple" />
### Clear Button
Use the `showClear` prop to show a clear button.
<ComponentPreview styleName="base-nova" name="combobox-clear" />
### Groups
Use `ComboboxGroup` and `ComboboxSeparator` to group items.
<ComponentPreview styleName="base-nova" name="combobox-groups" />
### Custom Items
You can render custom component inside `ComboboxItem`.
<ComponentPreview styleName="base-nova" name="combobox-custom" />
### Invalid
Use the `aria-invalid` prop to make the combobox invalid.
<ComponentPreview styleName="base-nova" name="combobox-invalid" />
### Disabled
Use the `disabled` prop to disable the combobox.
<ComponentPreview styleName="base-nova" name="combobox-disabled" />
### Auto Highlight
Use the `autoHighlight` prop automatically highlight the first item on filter.
<ComponentPreview styleName="base-nova" name="combobox-auto-highlight" />
### Popup
You can trigger the combobox from a button or any other component by using the `render` prop. Move the `ComboboxInput` inside the `ComboboxContent`.
<ComponentPreview styleName="base-nova" name="combobox-popup" />
### Input Group
You can add an addon to the combobox by using the `InputGroupAddon` component inside the `ComboboxInput`.
<ComponentPreview styleName="base-nova" name="combobox-input-group" />
## API Reference
See the [Base UI](https://base-ui.com/react/components/combobox#api-reference) documentation for more information.

View File

@@ -0,0 +1,125 @@
---
title: Command
description: Command menu for search and quick actions.
base: base
component: true
links:
doc: https://github.com/dip/cmdk
---
<ComponentPreview
styleName="base-nova"
name="command-demo"
align="start"
previewClassName="h-[24.5rem]"
/>
## About
The `<Command />` component uses the [`cmdk`](https://github.com/dip/cmdk) component by [Dip](https://www.dip.org/).
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add command
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install cmdk
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="command"
title="components/ui/command.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command"
```
```tsx showLineNumbers
<Command className="max-w-sm rounded-lg border">
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>Calendar</CommandItem>
<CommandItem>Search Emoji</CommandItem>
<CommandItem>Calculator</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>Profile</CommandItem>
<CommandItem>Billing</CommandItem>
<CommandItem>Settings</CommandItem>
</CommandGroup>
</CommandList>
</Command>
```
## Examples
### Basic
A simple command menu in a dialog.
<ComponentPreview styleName="base-nova" name="command-basic" />
### Shortcuts
<ComponentPreview styleName="base-nova" name="command-shortcuts" />
### Groups
A command menu with groups, icons and separators.
<ComponentPreview styleName="base-nova" name="command-groups" />
### Scrollable
Scrollable command menu with multiple items.
<ComponentPreview styleName="base-nova" name="command-scrollable" />
## API Reference
See the [cmdk](https://github.com/dip/cmdk) documentation for more information.

View File

@@ -0,0 +1,140 @@
---
title: Context Menu
description: Displays a menu of actions triggered by a right click.
base: base
component: true
links:
doc: https://base-ui.com/react/components/context-menu
api: https://base-ui.com/react/components/context-menu#api-reference
---
<ComponentPreview
styleName="base-nova"
name="context-menu-demo"
description="A context menu with sub menu items."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add context-menu
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="context-menu"
title="components/ui/context-menu.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
```
```tsx showLineNumbers
<ContextMenu>
<ContextMenuTrigger>Right click here</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>Profile</ContextMenuItem>
<ContextMenuItem>Billing</ContextMenuItem>
<ContextMenuItem>Team</ContextMenuItem>
<ContextMenuItem>Subscription</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
```
## Examples
### Basic
A simple context menu with a few actions.
<ComponentPreview styleName="base-nova" name="context-menu-basic" />
### Submenu
Use `ContextMenuSub` to nest secondary actions.
<ComponentPreview styleName="base-nova" name="context-menu-submenu" />
### Shortcuts
Add `ContextMenuShortcut` to show keyboard hints.
<ComponentPreview styleName="base-nova" name="context-menu-shortcuts" />
### Groups
Group related actions and separate them with dividers.
<ComponentPreview styleName="base-nova" name="context-menu-groups" />
### Icons
Combine icons with labels for quick scanning.
<ComponentPreview styleName="base-nova" name="context-menu-icons" />
### Checkboxes
Use `ContextMenuCheckboxItem` for toggles.
<ComponentPreview styleName="base-nova" name="context-menu-checkboxes" />
### Radio
Use `ContextMenuRadioItem` for exclusive choices.
<ComponentPreview styleName="base-nova" name="context-menu-radio" />
### Destructive
Use `variant="destructive"` to style the menu item as destructive.
<ComponentPreview styleName="base-nova" name="context-menu-destructive" />
### Sides
Control submenu placement with side and align props.
<ComponentPreview styleName="base-nova" name="context-menu-sides" />
## API Reference
See the [Base UI](https://base-ui.com/react/components/context-menu#api-reference) documentation for more information.

View File

@@ -1,12 +1,18 @@
---
title: Data Table
description: Powerful table and datagrids built using TanStack Table.
base: base
component: true
links:
doc: https://tanstack.com/table/v8/docs/introduction
---
<ComponentPreview name="data-table-demo" className="[&_.preview]:items-start" />
<ComponentPreview
styleName="radix-nova"
name="data-table-demo"
align="start"
previewClassName="items-start h-[28rem] px-4 md:px-8"
/>
## Introduction
@@ -102,7 +108,7 @@ I'm using a Next.js example here but this works for any other React framework.
Let's start by building a basic table.
<Steps>
<Steps className="mb-0 pt-2">
### Column Definitions
@@ -279,7 +285,7 @@ export default async function DemoPage() {
Let's format the amount cell to display the dollar amount. We'll also align the cell to the right.
<Steps>
<Steps className="mb-0 pt-2">
### Update columns definition
@@ -311,7 +317,7 @@ You can use the same approach to format other cells and headers.
Let's add row actions to our table. We'll use a `<Dropdown />` component for this.
<Steps>
<Steps className="mb-0 pt-2">
### Update columns definition
@@ -375,7 +381,7 @@ You can access the row data using `row.original` in the `cell` function. Use thi
Next, we'll add pagination to our table.
<Steps>
<Steps className="mb-0 pt-2">
### Update `<DataTable>`
@@ -461,7 +467,7 @@ See [Reusable Components](#reusable-components) section for a more advanced pagi
Let's make the email column sortable.
<Steps>
<Steps className="mb-0 pt-2">
### Update `<DataTable>`
@@ -543,7 +549,7 @@ This will automatically sort the table (asc and desc) when the user toggles on t
Let's add a search input to filter emails in our table.
<Steps>
<Steps className="mb-0 pt-2">
### Update `<DataTable>`
@@ -618,7 +624,7 @@ Filtering is now enabled for the `email` column. You can add filters to other co
Adding column visibility is fairly simple using `@tanstack/react-table` visibility API.
<Steps>
<Steps className="mb-0 pt-2">
### Update `<DataTable>`
@@ -731,7 +737,7 @@ This adds a dropdown menu that you can use to toggle column visibility.
Next, we're going to add row selection to our table.
<Steps>
<Steps className="mb-0 pt-2">
### Update column definitions
@@ -856,7 +862,10 @@ export const columns = [
Add pagination controls to your table including page size and selection count.
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-pagination.tsx" />
<ComponentSource
src="/app/(app)/examples/tasks/components/data-table-pagination.tsx"
styleName="radix-nova"
/>
```tsx
<DataTablePagination table={table} />
@@ -866,7 +875,10 @@ Add pagination controls to your table including page size and selection count.
A component to toggle column visibility.
<ComponentSource src="/app/(app)/examples/tasks/components/data-table-view-options.tsx" />
<ComponentSource
src="/app/(app)/examples/tasks/components/data-table-view-options.tsx"
styleName="radix-nova"
/>
```tsx
<DataTableViewOptions table={table} />

View File

@@ -0,0 +1,97 @@
---
title: Date Picker
description: A date picker component with range and presets.
base: base
component: true
---
<ComponentPreview styleName="base-nova" name="date-picker-demo" />
## Installation
The Date Picker is built using a composition of the `<Popover />` and the `<Calendar />` components.
See installation instructions for the [Popover](/docs/components/base/popover#installation) and the [Calendar](/docs/components/base/calendar#installation) components.
## Usage
```tsx showLineNumbers title="components/example-date-picker.tsx"
"use client"
import * as React from "react"
import { format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function DatePickerDemo() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger
render={
<Button
variant="outline"
data-empty={!date}
className="data-[empty=true]:text-muted-foreground justify-start text-left font-normal"
/>
}
>
<CalendarIcon />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar mode="single" selected={date} onSelect={setDate} />
</PopoverContent>
</Popover>
)
}
```
See the [React DayPicker](https://react-day-picker.js.org) documentation for more information.
## Examples
### Basic
A basic date picker component.
<ComponentPreview styleName="base-nova" name="date-picker-basic" />
### Range Picker
A date picker component for selecting a range of dates.
<ComponentPreview styleName="base-nova" name="date-picker-range" />
### Date of Birth
A date picker component for selecting a date of birth. This component includes a dropdown caption layout for date and month selection.
<ComponentPreview styleName="base-nova" name="date-picker-dob" />
### Input
A date picker component with an input field for selecting a date.
<ComponentPreview styleName="base-nova" name="date-picker-input" />
### Time Picker
A date picker component with a time input field for selecting a time.
<ComponentPreview styleName="base-nova" name="date-picker-time" />
### Natural Language Picker
This component uses the `chrono-node` library to parse natural language dates.
<ComponentPreview styleName="base-nova" name="date-picker-natural-language" />

View File

@@ -0,0 +1,116 @@
---
title: Dialog
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
featured: true
base: base
component: true
links:
doc: https://base-ui.com/react/components/dialog
api: https://base-ui.com/react/components/dialog#api-reference
---
<ComponentPreview
styleName="base-nova"
name="dialog-demo"
description="A dialog for editing profile details."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add dialog
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="dialog"
title="components/ui/dialog.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
```
```tsx showLineNumbers
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
```
## Examples
### Custom Close Button
Replace the default close control with your own button.
<ComponentPreview styleName="base-nova" name="dialog-close-button" />
### No Close Button
Use `showCloseButton={false}` to hide the close button.
<ComponentPreview styleName="base-nova" name="dialog-no-close-button" />
### Sticky Footer
Keep actions visible while the content scrolls.
<ComponentPreview styleName="base-nova" name="dialog-sticky-footer" />
### Scrollable Content
Long content can scroll while the header stays in view.
<ComponentPreview styleName="base-nova" name="dialog-scrollable-content" />
## API Reference
See the [Base UI](https://base-ui.com/react/components/dialog#api-reference) documentation for more information.

View File

@@ -1,12 +1,13 @@
---
title: Drawer
description: A drawer component for React.
base: base
component: true
links:
doc: https://vaul.emilkowal.ski/getting-started
---
<ComponentPreview name="drawer-demo" description="A drawer component." />
<ComponentPreview styleName="base-nova" name="drawer-demo" />
## About
@@ -17,7 +18,7 @@ Drawer is built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilk
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
@@ -30,7 +31,7 @@ npx shadcn@latest add drawer
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
@@ -40,7 +41,11 @@ npm install vaul
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="drawer" title="components/ui/drawer.tsx" />
<ComponentSource
name="drawer"
title="components/ui/drawer.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -85,8 +90,24 @@ import {
## Examples
### Scrollable Content
Keep actions visible while the content scrolls.
<ComponentPreview styleName="base-nova" name="drawer-scrollable-content" />
### Sides
Use the `direction` prop to set the side of the drawer. Available options are `top`, `right`, `bottom`, and `left`.
<ComponentPreview styleName="base-nova" name="drawer-sides" />
### Responsive Dialog
You can combine the `Dialog` and `Drawer` components to create a responsive dialog. This renders a `Dialog` component on desktop and a `Drawer` on mobile.
<ComponentPreview name="drawer-dialog" />
<ComponentPreview styleName="base-nova" name="drawer-dialog" />
## API Reference
See the [Vaul documentation](https://vaul.emilkowal.ski/components/drawer) for the full API reference.

View File

@@ -0,0 +1,165 @@
---
title: Dropdown Menu
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
featured: true
base: base
component: true
links:
doc: https://base-ui.com/react/components/dropdown-menu
api: https://base-ui.com/react/components/dropdown-menu#api-reference
---
<ComponentPreview
styleName="base-nova"
name="dropdown-menu-demo"
description="A dropdown menu with icons, shortcuts and sub menu items."
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add dropdown-menu
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="dropdown-menu"
title="components/ui/dropdown-menu.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
```
```tsx showLineNumbers
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Open
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuGroup>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuSeparator />
</DropdownMenuGroup>
<DropdownMenuGroup>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuItem>Subscription</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
```
## Examples
### Basic
A basic dropdown menu with labels and separators.
<ComponentPreview styleName="base-nova" name="dropdown-menu-basic" />
### Submenu
Use `DropdownMenuSub` to nest secondary actions.
<ComponentPreview styleName="base-nova" name="dropdown-menu-submenu" />
### Shortcuts
Add `DropdownMenuShortcut` to show keyboard hints.
<ComponentPreview styleName="base-nova" name="dropdown-menu-shortcuts" />
### Icons
Combine icons with labels for quick scanning.
<ComponentPreview styleName="base-nova" name="dropdown-menu-icons" />
### Checkboxes
Use `DropdownMenuCheckboxItem` for toggles.
<ComponentPreview styleName="base-nova" name="dropdown-menu-checkboxes" />
### Checkboxes Icons
Add icons to checkbox items.
<ComponentPreview styleName="base-nova" name="dropdown-menu-checkboxes-icons" />
### Radio Group
Use `DropdownMenuRadioGroup` for exclusive choices.
<ComponentPreview styleName="base-nova" name="dropdown-menu-radio-group" />
### Radio Icons
Show radio options with icons.
<ComponentPreview styleName="base-nova" name="dropdown-menu-radio-icons" />
### Destructive
Use `variant="destructive"` for irreversible actions.
<ComponentPreview styleName="base-nova" name="dropdown-menu-destructive" />
### Avatar
An account switcher dropdown triggered by an avatar.
<ComponentPreview styleName="base-nova" name="dropdown-menu-avatar" />
### Complex
A richer example combining groups, icons, and submenus.
<ComponentPreview styleName="base-nova" name="dropdown-menu-complex" />
## API Reference
See the [Base UI documentation](https://base-ui.com/react/components/dropdown-menu) for the full API reference.

View File

@@ -1,17 +1,22 @@
---
title: Empty
description: Use the Empty component to display an empty state.
base: base
component: true
---
<ComponentPreview name="empty-demo" className="[&_.preview]:p-0" />
<ComponentPreview
styleName="base-nova"
name="empty-demo"
previewClassName="h-96 p-0"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
@@ -24,11 +29,15 @@ npx shadcn@latest add empty
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="empty" title="components/ui/empty.tsx" />
<ComponentSource
name="empty"
title="components/ui/empty.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -73,33 +82,50 @@ import {
Use the `border` utility class to create an outline empty state.
<ComponentPreview
styleName="base-nova"
name="empty-outline"
className="[&_.preview]:p-6 md:[&_.preview]:p-10"
previewClassName="h-96 p-6 md:p-10"
/>
### Background
Use the `bg-*` and `bg-gradient-*` utilities to add a background to the empty state.
<ComponentPreview name="empty-background" className="[&_.preview]:p-0" />
<ComponentPreview
styleName="base-nova"
name="empty-background"
previewClassName="h-96 p-0"
/>
### Avatar
Use the `EmptyMedia` component to display an avatar in the empty state.
<ComponentPreview name="empty-avatar" className="[&_.preview]:p-0" />
<ComponentPreview
styleName="base-nova"
name="empty-avatar"
previewClassName="h-96 p-0"
/>
### Avatar Group
Use the `EmptyMedia` component to display an avatar group in the empty state.
<ComponentPreview name="empty-avatar-group" className="[&_.preview]:p-0" />
<ComponentPreview
styleName="base-nova"
name="empty-avatar-group"
previewClassName="h-96 p-0"
/>
### InputGroup
You can add an `InputGroup` component to the `EmptyContent` component.
<ComponentPreview name="empty-input-group" className="[&_.preview]:p-0" />
<ComponentPreview
styleName="base-nova"
name="empty-input-group"
previewClassName="h-96 p-0"
/>
## API Reference

View File

@@ -1,12 +1,14 @@
---
title: Field
description: Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
base: base
component: true
---
<ComponentPreview
styleName="base-nova"
name="field-demo"
className="[&_.preview]:h-[800px] [&_.preview]:p-6 md:[&_.preview]:h-[850px]"
previewClassName="h-[800px] p-6 md:h-[850px]"
/>
## Installation
@@ -14,7 +16,7 @@ component: true
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
@@ -27,11 +29,15 @@ npx shadcn@latest add field
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="field" title="components/ui/field.tsx" />
<ComponentSource
name="field"
title="components/ui/field.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -106,47 +112,55 @@ See the [Form](/docs/forms) documentation for building forms with the `Field` co
### Input
<ComponentPreview name="field-input" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-input" />
### Textarea
<ComponentPreview name="field-textarea" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-textarea" />
### Select
<ComponentPreview name="field-select" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-select" />
### Slider
<ComponentPreview name="field-slider" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-slider" />
### Fieldset
<ComponentPreview name="field-fieldset" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-fieldset" />
### Checkbox
<ComponentPreview name="field-checkbox" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview
styleName="base-nova"
name="field-checkbox"
previewClassName="h-[32rem]"
/>
### Radio
<ComponentPreview name="field-radio" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-radio" />
### Switch
<ComponentPreview name="field-switch" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-switch" />
### Choice Card
Wrap `Field` components inside `FieldLabel` to create selectable field groups. This works with `RadioItem`, `Checkbox` and `Switch` components.
<ComponentPreview name="field-choice-card" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview styleName="base-nova" name="field-choice-card" />
### Field Group
Stack `Field` components with `FieldGroup`. Add `FieldSeparator` to divide them.
<ComponentPreview name="field-group" className="!mb-4 [&_.preview]:p-6" />
<ComponentPreview
styleName="base-nova"
name="field-group"
previewClassName="h-96"
/>
## Responsive Layout
@@ -155,8 +169,9 @@ Stack `Field` components with `FieldGroup`. Add `FieldSeparator` to divide them.
- **Responsive fields:** Set `orientation="responsive"` for automatic column layouts inside container-aware parents. Apply `@container/field-group` classes on `FieldGroup` to switch orientations at specific breakpoints.
<ComponentPreview
styleName="base-nova"
name="field-responsive"
className="!mb-4 [&_.preview]:h-[650px] [&_.preview]:p-6 [&_.preview]:md:h-[500px] [&_.preview]:md:p-10"
previewClassName="h-[650px] p-6 md:h-[500px] md:p-10"
/>
## Validation and Errors

View File

@@ -0,0 +1,125 @@
---
title: Hover Card
description: For sighted users to preview content available behind a link.
base: base
component: true
links:
doc: https://base-ui.com/react/components/hover-card
api: https://base-ui.com/react/components/hover-card#api-reference
---
<ComponentPreview
styleName="base-nova"
name="hover-card-demo"
previewClassName="h-80"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add hover-card
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="hover-card"
title="components/ui/hover-card.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
```
```tsx showLineNumbers
<HoverCard>
<HoverCardTrigger>Hover</HoverCardTrigger>
<HoverCardContent>
The React Framework created and maintained by @vercel.
</HoverCardContent>
</HoverCard>
```
## Trigger Delays
Use `delay` and `closeDelay` on the trigger to control when the card opens and
closes.
```tsx showLineNumbers
<HoverCard>
<HoverCardTrigger delay={100} closeDelay={200}>
Hover
</HoverCardTrigger>
<HoverCardContent>Content</HoverCardContent>
</HoverCard>
```
## Positioning
Use the `side` and `align` props on `HoverCardContent` to control placement.
```tsx showLineNumbers
<HoverCard>
<HoverCardTrigger>Hover</HoverCardTrigger>
<HoverCardContent side="top" align="start">
Content
</HoverCardContent>
</HoverCard>
```
## Examples
### Basic
<ComponentPreview
styleName="base-nova"
name="hover-card-demo"
previewClassName="h-80"
/>
### Sides
<ComponentPreview
styleName="base-nova"
name="hover-card-sides"
previewClassName="h-[22rem]"
/>
## API Reference
See the [Base UI](https://base-ui.com/react/components/hover-card#api-reference) documentation for more information.

View File

@@ -0,0 +1,295 @@
---
title: Input Group
description: Add addons, buttons, and helper content to inputs.
base: base
component: true
---
import { IconInfoCircle } from "@tabler/icons-react"
<ComponentPreview
styleName="base-nova"
name="input-group-demo"
previewClassName="h-[26rem]"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add input-group
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="input-group"
title="components/ui/input-group.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"
```
```tsx showLineNumbers
<InputGroup>
<InputGroupInput placeholder="Search..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
```
## Align
Use the `align` prop on `InputGroupAddon` to position the addon relative to the input.
<Callout>
For proper focus management, `InputGroupAddon` should always be placed after
`InputGroupInput` or `InputGroupTextarea` in the DOM. Use the `align` prop to
visually position the addon.
</Callout>
### inline-start
Use `align="inline-start"` to position the addon at the start of the input. This is the default.
<ComponentPreview
styleName="base-nova"
name="input-group-inline-start"
previewClassName="h-48"
/>
### inline-end
Use `align="inline-end"` to position the addon at the end of the input.
<ComponentPreview
styleName="base-nova"
name="input-group-inline-end"
previewClassName="h-48"
/>
### block-start
Use `align="block-start"` to position the addon above the input.
<ComponentPreview
styleName="base-nova"
name="input-group-block-start"
previewClassName="h-96"
/>
### block-end
Use `align="block-end"` to position the addon below the input.
<ComponentPreview
styleName="base-nova"
name="input-group-block-end"
previewClassName="h-[26rem]"
/>
## Examples
### Icon
<ComponentPreview
styleName="base-nova"
name="input-group-icon"
previewClassName="h-80"
/>
### Text
<ComponentPreview
styleName="base-nova"
name="input-group-text"
previewClassName="h-80"
/>
### Button
<ComponentPreview
styleName="base-nova"
name="input-group-button"
previewClassName="h-72"
/>
### Kbd
<ComponentPreview
styleName="base-nova"
name="input-group-kbd"
previewClassName="h-40"
/>
### Dropdown
<ComponentPreview
styleName="base-nova"
name="input-group-dropdown"
previewClassName="h-56"
/>
### Spinner
<ComponentPreview
styleName="base-nova"
name="input-group-spinner"
previewClassName="h-80"
/>
### Textarea
<ComponentPreview
styleName="base-nova"
name="input-group-textarea"
previewClassName="h-96"
/>
### Custom Input
Add the `data-slot="input-group-control"` attribute to your custom input for automatic focus state handling.
Here's an example of a custom resizable textarea from a third-party library.
<ComponentPreview
styleName="base-nova"
name="input-group-custom"
previewClassName="h-56"
/>
## API Reference
### InputGroup
The main component that wraps inputs and addons.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | |
```tsx
<InputGroup>
<InputGroupInput />
<InputGroupAddon />
</InputGroup>
```
### InputGroupAddon
Displays icons, text, buttons, or other content alongside inputs.
<Callout icon={<IconInfoCircle />} title="Focus Navigation">
For proper focus navigation, the `InputGroupAddon` component should be placed
after the input. Set the `align` prop to position the addon.
</Callout>
| Prop | Type | Default |
| ----------- | ---------------------------------------------------------------- | ---------------- |
| `align` | `"inline-start" \| "inline-end" \| "block-start" \| "block-end"` | `"inline-start"` |
| `className` | `string` | |
```tsx
<InputGroupAddon align="inline-end">
<SearchIcon />
</InputGroupAddon>
```
**For `<InputGroupInput />`, use the `inline-start` or `inline-end` alignment. For `<InputGroupTextarea />`, use the `block-start` or `block-end` alignment.**
The `InputGroupAddon` component can have multiple `InputGroupButton` components and icons.
```tsx
<InputGroupAddon>
<InputGroupButton>Button</InputGroupButton>
<InputGroupButton>Button</InputGroupButton>
</InputGroupAddon>
```
### InputGroupButton
Displays buttons within input groups.
| Prop | Type | Default |
| ----------- | ----------------------------------------------------------------------------- | --------- |
| `size` | `"xs" \| "icon-xs" \| "sm" \| "icon-sm"` | `"xs"` |
| `variant` | `"default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "link"` | `"ghost"` |
| `className` | `string` | |
```tsx
<InputGroupButton>Button</InputGroupButton>
<InputGroupButton size="icon-xs" aria-label="Copy">
<CopyIcon />
</InputGroupButton>
```
### InputGroupInput
Replacement for `<Input />` when building input groups. This component has the input group styles pre-applied and uses the unified `data-slot="input-group-control"` for focus state handling.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | |
All other props are passed through to the underlying `<Input />` component.
```tsx
<InputGroup>
<InputGroupInput placeholder="Enter text..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
```
### InputGroupTextarea
Replacement for `<Textarea />` when building input groups. This component has the textarea group styles pre-applied and uses the unified `data-slot="input-group-control"` for focus state handling.
| Prop | Type | Default |
| ----------- | -------- | ------- |
| `className` | `string` | |
All other props are passed through to the underlying `<Textarea />` component.
```tsx
<InputGroup>
<InputGroupTextarea placeholder="Enter message..." />
<InputGroupAddon align="block-end">
<InputGroupButton>Send</InputGroupButton>
</InputGroupAddon>
</InputGroup>
```

View File

@@ -0,0 +1,147 @@
---
title: Input OTP
description: Accessible one-time password component with copy paste functionality.
base: base
component: true
links:
doc: https://input-otp.rodz.dev
---
<ComponentPreview styleName="base-nova" name="input-otp-demo" />
## About
Input OTP is built on top of [input-otp](https://github.com/guilhermerodz/input-otp) by [@guilherme_rodz](https://twitter.com/guilherme_rodz).
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add input-otp
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install input-otp
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="input-otp"
title="components/ui/input-otp.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"
```
```tsx showLineNumbers
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
```
## Pattern
Use the `pattern` prop to define a custom pattern for the OTP input.
```tsx showLineNumbers {1,5}
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
;<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
...
</InputOTP>
```
<ComponentPreview styleName="base-nova" name="input-otp-pattern" />
## Examples
### Separator
Use the `<InputOTPSeparator />` component to add a separator between input groups.
<ComponentPreview styleName="base-nova" name="input-otp-separator" />
### Disabled
Use the `disabled` prop to disable the input.
<ComponentPreview styleName="base-nova" name="input-otp-disabled" />
### Controlled
Use the `value` and `onChange` props to control the input value.
<ComponentPreview styleName="base-nova" name="input-otp-controlled" />
### Invalid
Use `aria-invalid` on the slots to show an error state.
<ComponentPreview styleName="base-nova" name="input-otp-invalid" />
### Four Digits
A common pattern for PIN codes. This uses the `pattern={REGEXP_ONLY_DIGITS}` prop.
<ComponentPreview styleName="base-nova" name="input-otp-four-digits" />
### Alphanumeric
Use `REGEXP_ONLY_DIGITS_AND_CHARS` to accept both letters and numbers.
<ComponentPreview styleName="base-nova" name="input-otp-alphanumeric" />
### Form
<ComponentPreview
styleName="base-nova"
name="input-otp-form"
previewClassName="h-[30rem]"
/>
## API Reference
See the [input-otp](https://input-otp.rodz.dev) documentation for more information.

View File

@@ -0,0 +1,190 @@
---
title: Input
description: A text input component for forms and user data entry with built-in styling and accessibility features.
base: base
component: true
---
<ComponentPreview
styleName="base-nova"
name="input-demo"
previewClassName="*:max-w-xs"
/>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add input
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="input"
title="components/ui/input.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx
import { Input } from "@/components/ui/input"
```
```tsx
<Input />
```
## Examples
### Basic
<ComponentPreview
styleName="base-nova"
name="input-basic"
previewClassName="*:max-w-xs"
/>
### Field
Use `Field`, `FieldLabel`, and `FieldDescription` to create an input with a
label and description.
<ComponentPreview
styleName="base-nova"
name="input-field"
previewClassName="*:max-w-xs"
/>
### Field Group
Use `FieldGroup` to show multiple `Field` blocks and to build forms.
<ComponentPreview
styleName="base-nova"
name="input-fieldgroup"
previewClassName="*:max-w-xs"
/>
### Disabled
Use the `disabled` prop to disable the input. To style the disabled state, add the `data-disabled` attribute to the `Field` component.
<ComponentPreview
styleName="base-nova"
name="input-disabled"
previewClassName="*:max-w-xs"
/>
### Invalid
Use the `aria-invalid` prop to mark the input as invalid. To style the invalid state, add the `data-invalid` attribute to the `Field` component.
<ComponentPreview
styleName="base-nova"
name="input-invalid"
previewClassName="*:max-w-xs"
/>
### File
Use the `type="file"` prop to create a file input.
<ComponentPreview
styleName="base-nova"
name="input-file"
previewClassName="*:max-w-xs"
/>
### Inline
Use `Field` with `orientation="horizontal"` to create an inline input.
Pair with `Button` to create a search input with a button.
<ComponentPreview
styleName="base-nova"
name="input-inline"
previewClassName="*:max-w-xs"
/>
### Grid
Use a grid layout to place multiple inputs side by side.
<ComponentPreview
styleName="base-nova"
name="input-grid"
previewClassName="p-6"
/>
### Required
Use the `required` attribute to indicate required inputs.
<ComponentPreview
styleName="base-nova"
name="input-required"
previewClassName="*:max-w-xs"
/>
### Badge
Use `Badge` in the label to highlight a recommended field.
<ComponentPreview
styleName="base-nova"
name="input-badge"
previewClassName="*:max-w-xs"
/>
### Input Group
To add icons, text, or buttons inside an input, use the `InputGroup` component. See the [Input Group](/docs/components/input-group) component for more examples.
<ComponentPreview
styleName="base-nova"
name="input-input-group"
previewClassName="*:max-w-xs"
/>
### Button Group
To add buttons to an input, use the `ButtonGroup` component. See the [Button Group](/docs/components/button-group) component for more examples.
<ComponentPreview
styleName="base-nova"
name="input-button-group"
previewClassName="*:max-w-xs"
/>
### Form
A full form example with multiple inputs, a select, and a button.
<ComponentPreview
styleName="base-nova"
name="input-form"
previewClassName="h-[32rem]"
/>

View File

@@ -0,0 +1,276 @@
---
title: Item
description: A versatile component for displaying content with media, title, description, and actions.
base: base
component: true
---
<ComponentPreview styleName="base-nova" name="item-demo" />
The `Item` component is a straightforward flex container that can house nearly any type of content. Use it to display a title, description, and actions. Group it with the `ItemGroup` component to create a list of items.
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add item
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="item"
title="components/ui/item.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"
```
```tsx showLineNumbers
<Item>
<ItemMedia variant="icon">
<Icon />
</ItemMedia>
<ItemContent>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</ItemContent>
<ItemActions>
<Button>Action</Button>
</ItemActions>
</Item>
```
## Item vs Field
Use `Field` if you need to display a form input such as a checkbox, input, radio, or select.
If you only need to display content such as a title, description, and actions, use `Item`.
## Variant
Use the `variant` prop to change the visual style of the item.
<ComponentPreview
styleName="base-nova"
name="item-variant"
previewClassName="h-96"
/>
## Size
Use the `size` prop to change the size of the item. Available sizes are `default`, `sm`, and `xs`.
<ComponentPreview
styleName="base-nova"
name="item-size"
previewClassName="h-96"
/>
## Examples
### Icon
Use `ItemMedia` with `variant="icon"` to display an icon.
<ComponentPreview styleName="base-nova" name="item-icon" />
### Avatar
You can use `ItemMedia` with `variant="avatar"` to display an avatar.
<ComponentPreview styleName="base-nova" name="item-avatar" />
### Image
Use `ItemMedia` with `variant="image"` to display an image.
<ComponentPreview styleName="base-nova" name="item-image" />
### Group
Use `ItemGroup` to group related items together.
<ComponentPreview
styleName="base-nova"
name="item-group"
previewClassName="h-96"
/>
### Header
Use `ItemHeader` to add a header above the item content.
<ComponentPreview
styleName="base-nova"
name="item-header"
previewClassName="h-96"
/>
### Link
Use the `render` prop to render the item as a link. The hover and focus states will be applied to the anchor element.
<ComponentPreview styleName="base-nova" name="item-link" />
```tsx showLineNumbers
<Item render={<a href="/dashboard" />}>
<ItemMedia variant="icon">
<HomeIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Dashboard</ItemTitle>
<ItemDescription>Overview of your account and activity.</ItemDescription>
</ItemContent>
</Item>
```
### Dropdown
<ComponentPreview styleName="base-nova" name="item-dropdown" />
## API Reference
### Item
The main component for displaying content with media, title, description, and actions.
| Prop | Type | Default |
| --------- | ----------------------------------- | ----------- |
| `variant` | `"default" \| "outline" \| "muted"` | `"default"` |
| `size` | `"default" \| "sm" \| "xs"` | `"default"` |
| `render` | `React.ReactElement` | |
### ItemGroup
A container that groups related items together with consistent styling.
```tsx
<ItemGroup>
<Item />
<Item />
</ItemGroup>
```
### ItemSeparator
A separator between items in a group.
```tsx
<ItemGroup>
<Item />
<ItemSeparator />
<Item />
</ItemGroup>
```
### ItemMedia
Use `ItemMedia` to display media content such as icons, images, or avatars.
| Prop | Type | Default |
| --------- | -------------------------------- | ----------- |
| `variant` | `"default" \| "icon" \| "image"` | `"default"` |
```tsx
<ItemMedia variant="icon">
<Icon />
</ItemMedia>
```
```tsx
<ItemMedia variant="image">
<img src="..." alt="..." />
</ItemMedia>
```
### ItemContent
Wraps the title and description of the item.
```tsx
<ItemContent>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</ItemContent>
```
### ItemTitle
Displays the title of the item.
```tsx
<ItemTitle>Item Title</ItemTitle>
```
### ItemDescription
Displays the description of the item.
```tsx
<ItemDescription>Item description</ItemDescription>
```
### ItemActions
Container for action buttons or other interactive elements.
```tsx
<ItemActions>
<Button>Action</Button>
</ItemActions>
```
### ItemHeader
Displays a header above the item content.
```tsx
<Item>
<ItemHeader>Header</ItemHeader>
<ItemContent>...</ItemContent>
</Item>
```
### ItemFooter
Displays a footer below the item content.
```tsx
<Item>
<ItemContent>...</ItemContent>
<ItemFooter>Footer</ItemFooter>
</Item>
```

View File

@@ -1,17 +1,18 @@
---
title: Kbd
description: Used to display textual user input from keyboard.
base: base
component: true
---
<ComponentPreview name="kbd-demo" />
<ComponentPreview styleName="base-nova" name="kbd-demo" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
@@ -24,11 +25,15 @@ npx shadcn@latest add kbd
<TabsContent value="manual">
<Steps>
<Steps className="mb-0 pt-2">
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="kbd" title="components/ui/kbd.tsx" />
<ComponentSource
name="kbd"
title="components/ui/kbd.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
@@ -54,25 +59,25 @@ import { Kbd } from "@/components/ui/kbd"
Use the `KbdGroup` component to group keyboard keys together.
<ComponentPreview name="kbd-group" />
<ComponentPreview styleName="base-nova" name="kbd-group" />
### Button
Use the `Kbd` component inside a `Button` component to display a keyboard key inside a button.
<ComponentPreview name="kbd-button" />
<ComponentPreview styleName="base-nova" name="kbd-button" />
### Tooltip
You can use the `Kbd` component inside a `Tooltip` component to display a tooltip with a keyboard key.
<ComponentPreview name="kbd-tooltip" />
<ComponentPreview styleName="base-nova" name="kbd-tooltip" />
### Input Group
You can use the `Kbd` component inside a `InputGroupAddon` component to display a keyboard key inside an input group.
<ComponentPreview name="kbd-input-group" />
<ComponentPreview styleName="base-nova" name="kbd-input-group" />
## API Reference

View File

@@ -0,0 +1,90 @@
---
title: Label
description: Renders an accessible label associated with controls.
base: base
component: true
links:
doc: https://base-ui.com/react/components/label
api: https://base-ui.com/react/components/label#api-reference
---
<ComponentPreview styleName="base-nova" name="label-demo" />
<Callout>
For form fields, use the [Field](/docs/components/base/field) component which
includes built-in label, description, and error handling.
</Callout>
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add label
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="label"
title="components/ui/label.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx
import { Label } from "@/components/ui/label"
```
```tsx
<Label htmlFor="email">Your email address</Label>
```
## Label in Field
For form fields, use the [Field](/docs/components/base/field) component which
includes built-in `FieldLabel`, `FieldDescription`, and `FieldError` components.
```tsx
<Field>
<FieldLabel htmlFor="email">Your email address</FieldLabel>
<Input id="email" />
</Field>
```
<ComponentPreview
styleName="base-nova"
name="field-demo"
previewClassName="h-[44rem]"
/>
## API Reference
See the [Base UI Label](https://base-ui.com/react/components/label#api-reference) documentation for more information.

View File

@@ -0,0 +1,117 @@
---
title: Menubar
description: A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
base: base
component: true
links:
doc: https://base-ui.com/react/components/menubar
api: https://base-ui.com/react/components/menubar#api-reference
---
<ComponentPreview styleName="base-nova" name="menubar-demo" />
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">Command</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn@latest add menubar
```
</TabsContent>
<TabsContent value="manual">
<Steps className="mb-0 pt-2">
<Step>Install the following dependencies:</Step>
```bash
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="menubar"
title="components/ui/menubar.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
```tsx showLineNumbers
import {
Menubar,
MenubarContent,
MenubarGroup,
MenubarItem,
MenubarMenu,
MenubarSeparator,
MenubarShortcut,
MenubarTrigger,
} from "@/components/ui/menubar"
```
```tsx showLineNumbers
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarGroup>
<MenubarItem>
New Tab <MenubarShortcut>⌘T</MenubarShortcut>
</MenubarItem>
<MenubarItem>New Window</MenubarItem>
</MenubarGroup>
<MenubarSeparator />
<MenubarGroup>
<MenubarItem>Share</MenubarItem>
<MenubarItem>Print</MenubarItem>
</MenubarGroup>
</MenubarContent>
</MenubarMenu>
</Menubar>
```
## Examples
### Checkbox
Use `MenubarCheckboxItem` for toggleable options.
<ComponentPreview styleName="base-nova" name="menubar-checkbox" />
### Radio
Use `MenubarRadioGroup` and `MenubarRadioItem` for single-select options.
<ComponentPreview styleName="base-nova" name="menubar-radio" />
### Submenu
Use `MenubarSub`, `MenubarSubTrigger`, and `MenubarSubContent` for nested menus.
<ComponentPreview styleName="base-nova" name="menubar-submenu" />
### With Icons
<ComponentPreview styleName="base-nova" name="menubar-icons" />
## API Reference
See the [Base UI Menubar](https://base-ui.com/react/components/menubar#api-reference) documentation.

View File

@@ -0,0 +1,64 @@
{
"title": "Base UI",
"pages": [
"accordion",
"alert",
"alert-dialog",
"aspect-ratio",
"avatar",
"badge",
"breadcrumb",
"button",
"button-group",
"calendar",
"card",
"carousel",
"chart",
"checkbox",
"collapsible",
"combobox",
"command",
"context-menu",
"data-table",
"date-picker",
"dialog",
"drawer",
"dropdown-menu",
"empty",
"field",
"form",
"hover-card",
"input",
"input-group",
"input-otp",
"item",
"kbd",
"label",
"menubar",
"native-select",
"navigation-menu",
"pagination",
"popover",
"progress",
"radio-group",
"resizable",
"scroll-area",
"select",
"separator",
"sheet",
"sidebar",
"skeleton",
"slider",
"sonner",
"spinner",
"switch",
"table",
"tabs",
"textarea",
"toast",
"toggle",
"toggle-group",
"tooltip",
"typography"
]
}

Some files were not shown because too many files have changed in this diff Show More