shadcn/ui Components
What We Did
Section titled “What We Did”Replaced the placeholder UI components in @repo/ui with a full shadcn/ui component library using the new Base UI primitives. This provides a complete, accessible, and customizable design system with Tailwind CSS v4 and oklch color space theming.
Why This Approach
Section titled “Why This Approach”Key reasons:
- Base UI primitives: shadcn’s new
@base-ui/reactprovides unstyled, accessible components that are more lightweight than Radix UI - oklch color space: Modern color system with better perceptual uniformity for consistent light/dark themes
- Tailwind CSS v4: Latest version with native CSS variables and improved performance
- Monorepo-ready: Components live in
@repo/uiand can be consumed by any app in the workspace - Full customization: shadcn components are copied into your codebase, not imported from node_modules
Alternatives considered:
- Radix UI + old shadcn: More mature but heavier; Base UI is the future direction
- Headless UI: Less comprehensive component set
- Build from scratch: Too time-consuming; shadcn provides battle-tested patterns
Commands Used
Section titled “Commands Used”# Install dependenciesbun install
# Verify types compileturbo check-types --filter=@repo/ui --filter=web
# Run development serverturbo dev --filter=webImplementation Details
Section titled “Implementation Details”Package Structure
Section titled “Package Structure”packages/ui/├── components.json # shadcn configuration├── package.json # Dependencies├── src/│ ├── components/│ │ └── ui/│ │ ├── alert-dialog.tsx│ │ ├── badge.tsx│ │ ├── button.tsx│ │ ├── card.tsx│ │ ├── combobox.tsx│ │ ├── dropdown-menu.tsx│ │ ├── field.tsx│ │ ├── index.ts # Barrel export│ │ ├── input-group.tsx│ │ ├── input.tsx│ │ ├── label.tsx│ │ ├── select.tsx│ │ ├── separator.tsx│ │ └── textarea.tsx│ ├── lib/│ │ └── utils.ts # cn() utility│ └── styles/│ └── globals.css # Tailwind themePackage Exports
Section titled “Package Exports”The @repo/ui package exports are configured in package.json:
{ "exports": { "./components/ui/*": "./src/components/ui/*.tsx", "./components/ui": "./src/components/ui/index.ts", "./lib/utils": "./src/lib/utils.ts", "./styles/globals.css": "./src/styles/globals.css" }}Components Configuration
Section titled “Components Configuration”The components.json file configures shadcn for the monorepo:
{ "$schema": "https://ui.shadcn.com/schema.json", "style": "base-vega", "rsc": true, "tsx": true, "tailwind": { "config": "", "css": "src/styles/globals.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, "iconLibrary": "lucide", "aliases": { "components": "@repo/ui/components", "utils": "@repo/ui/lib/utils", "ui": "@repo/ui/components/ui" }}Theme Configuration
Section titled “Theme Configuration”The theme uses oklch color space for better color consistency. Key variables in globals.css:
@import "tailwindcss";@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --color-primary: var(--primary); /* ... mapped from CSS variables to Tailwind */}
:root { --background: oklch(1 0 0); --foreground: oklch(0.145 0 0); --primary: oklch(0.67 0.16 58); /* ... full light theme */}
.dark { --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); --primary: oklch(0.77 0.16 70); /* ... full dark theme */}Apps/Web Integration
Section titled “Apps/Web Integration”The web app imports the theme in app/globals.css:
@import "tailwindcss";@import "@repo/ui/styles/globals.css";And configures Next.js to transpile the package in next.config.js:
const nextConfig = { transpilePackages: ["@repo/ui"],}Key Dependencies
Section titled “Key Dependencies”Added to packages/ui/package.json:
@base-ui/react: ^1.1.0 - Unstyled, accessible UI primitives from shadcnclass-variance-authority: ^0.7.0 - Type-safe variant stylingclsx: ^2.1.1 - Conditional class namestailwind-merge: ^2.3.0 - Merge Tailwind classes without conflictslucide-react: ^0.468.0 - Icon librarytw-animate-css: ^1.3.4 - Animation utilities
Added to apps/web/package.json:
tailwindcss: ^4.1.0 - Tailwind CSS v4@tailwindcss/postcss: ^4.1.0 - PostCSS plugin for Tailwind
Integration with Existing Code
Section titled “Integration with Existing Code”Importing Components
Section titled “Importing Components”Components can be imported individually or from the barrel export:
// Individual imports (tree-shakeable)import { Button } from "@repo/ui/components/ui/button"import { Card, CardHeader, CardTitle } from "@repo/ui/components/ui/card"
// Barrel importimport { Button, Card, Input } from "@repo/ui/components/ui"Using the cn() Utility
Section titled “Using the cn() Utility”The cn() function merges Tailwind classes safely:
import { cn } from "@repo/ui/lib/utils"
;<div className={cn("base-class", conditional && "conditional-class", className)} />Example Usage
Section titled “Example Usage”import { Button } from "@repo/ui/components/ui/button"import { Card, CardContent, CardHeader, CardTitle } from "@repo/ui/components/ui/card"import { Input } from "@repo/ui/components/ui/input"
export function MyForm() { return ( <Card> <CardHeader> <CardTitle>Create Item</CardTitle> </CardHeader> <CardContent> <form className="flex gap-2"> <Input placeholder="Enter title..." className="flex-1" /> <Button type="submit">Create</Button> </form> </CardContent> </Card> )}Context for AI
Section titled “Context for AI”When working with the UI components:
- Component location: All shadcn components are in
packages/ui/src/components/ui/ - Import paths: Use
@repo/ui/components/ui/buttonnot@/components/ui/button - Internal imports: Components import utils with relative paths
../../lib/utils - Base UI: Components use
@base-ui/reactprimitives, not Radix UI - Dark mode: Uses
.darkclass selector, configured via@custom-variant dark - Adding components: Can use
bunx shadcn@latest add <component>but will need to update import paths
Available Components
Section titled “Available Components”| Component | Description |
|---|---|
AlertDialog | Modal dialog for important actions |
Badge | Status indicators and labels |
Button | Primary interactive element (6 variants) |
Card | Container with header/content/footer |
Combobox | Searchable select with autocomplete |
DropdownMenu | Context menus and action menus |
Field | Form field wrapper with label/error |
Input | Text input field |
InputGroup | Input with addons (icons, buttons) |
Label | Form label component |
Select | Dropdown select component |
Separator | Visual divider |
Textarea | Multi-line text input |
Outcomes
Section titled “Outcomes”Before
Section titled “Before”- 3 placeholder components (button, card, code) with no styling
- No design system or theme
- Inline styles in page components
- 13 fully-styled, accessible components
- Complete oklch-based theme with light/dark mode
- Consistent design language across the app
- Type-safe component variants
Testing/Verification
Section titled “Testing/Verification”# Type checkturbo check-types --filter=@repo/ui --filter=web
# Run dev serverturbo dev --filter=webExpected results:
- Page renders at http://localhost:3000 with styled components
- Cards have subtle shadows and rounded corners
- Button has primary color styling
- Dark mode works automatically (via prefers-color-scheme)
- Form inputs have proper focus states
Next Steps
Section titled “Next Steps”- Add more shadcn components as needed (Dialog, Toast, Form, etc.)
- Consider adding a theme switcher for manual dark mode toggle
- Set up Storybook for component documentation
- Add component tests with Testing Library