shadcn/ui
Leave Localhost uses shadcn/ui as its component foundation. Components live in the shared packages/ui package and are configured with the new-york style, Lucide icons, and CSS variables for theming.
Leave Localhost uses shadcn/ui as its component
foundation. Components live in the shared packages/ui package and are
configured with the new-york style, Lucide icons, and CSS variables
for theming.
Configuration
The shadcn configuration file is at packages/ui/components.json:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"iconLibrary": "lucide",
"tailwind": {
"config": "",
"css": "src/globals.css",
"baseColor": "gray",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"hooks": "@/hooks",
"lib": "@/lib",
"ui": "@/components",
"utils": "@/utils"
}
}Key decisions:
style: "new-york"— the more refined shadcn variant with smaller elements and tighter spacing.rsc: true— components are React Server Component safe by default. Those that require client state add"use client"explicitly.cssVariables: true— all colors resolve through CSS custom properties, enabling runtime theming and dark mode.
Adding a New Component
Run the shadcn CLI from the UI package root:
bunx --bun shadcn@latest add <component> --cwd packages/uiThe CLI reads components.json, writes the component file to
src/components/, and installs any missing Radix dependencies.
After adding a component:
-
Export it from
packages/ui/package.jsonunder"exports":"./accordion": "./src/components/accordion.tsx" -
Import it in your app by the new entry point:
import { Accordion } from "@leavelocalhost/ui/accordion"; -
Run typechecks to verify the new export resolves:
bun run typecheck
Updating An Existing Component
Refresh one component at a time so generated diffs stay reviewable:
bunx --bun shadcn@latest add <component> --overwrite --cwd packages/uiThen inspect the diff before keeping it:
git diff -- packages/ui/src/components/<component>.tsx packages/ui/package.jsonCheck generated imports. Components should import local helpers through the UI
package layout, usually ../utils, not app-local aliases from apps/app or
apps/marketing.
If the refreshed component adds a new dependency, keep it in
packages/ui/package.json. If it adds a new shared component file, add an
entry to the exports map before importing it from an app.
After Adding Components
Run the focused verification first:
bun --cwd packages/ui lint
bun --cwd packages/ui typecheckThen run the affected app checks:
bun run lint
bun run typecheck
bun --cwd apps/app build
bun --cwd apps/marketing buildUse the app build that matches the component's consumers if only one app imports the new shared component.
Customizing Existing Components
shadcn components are copied into the project, not installed as a dependency. This means you own the source code and can modify it freely.
Common customization patterns:
- Add a variant — extend the
cva()call. For example, theButtoncomponent defines variants fordefault,destructive,outline,secondary,ghost, andlink. Add a new variant key and its Tailwind classes. - Change sizes — modify the
sizevariants in thecva()definition. TheButtonships withdefault,xs,sm,lg,icon,icon-xs,icon-sm, andicon-lg. - Adjust styling — edit the Tailwind classes directly in the component file.
All components use the
cn()utility to merge default and consumer-provided classes, so overrides from the call site work naturally.
Component Architecture
shadcn components in this project follow a consistent pattern:
import { cn } from "../utils";
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn("rounded-xl border bg-card p-6", className)}
{...props}
/>
);
}data-slotattributes identify component parts for debugging and styling.cn()merges default styles with consumer overrides.React.ComponentProps<>provides full HTML attribute forwarding.asChildvia@radix-ui/react-slotlets you swap the rendered element (e.g. rendering aButtonas a Next.jsLink).
Variant-Driven Components
Components with multiple visual states use class-variance-authority:
import { cva, type VariantProps } from "class-variance-authority";
const buttonVariants = cva("base-classes", {
variants: {
variant: {
default: "bg-primary text-primary-foreground",
destructive: "bg-destructive text-destructive-foreground",
},
size: {
default: "h-9 px-4",
sm: "h-8 px-3",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});This approach makes variants type-safe and composable. Import
buttonVariants if you need to apply the same styles outside of the Button
component.
The Sidebar System
The Sidebar is the most complex component in the package. It provides a complete dashboard navigation system with:
SidebarProvider— context for open/collapsed state, persisted via a cookie (sidebar_state).Sidebar— the main container withvariant(sidebar,floating,inset),side(left,right), andcollapsible(offcanvas,icon,none) props.SidebarMenuButton— navigation items withisActivestate,tooltipsupport when collapsed, andasChildfor routing integration.SidebarTrigger— toggle button with⌘Bkeyboard shortcut.- Mobile — on viewports below 768 px, the sidebar renders as a
Sheet(slide-over drawer).
See Customizing the Dashboard for guidance on modifying the sidebar layout.
Tips
- Don't import from
src/— always use the package entry points (@leavelocalhost/ui/button, etc.). Theexportsmap inpackage.jsonis the public API. - Keep shared vs. app-specific separate — if a component is only used in
apps/app, put it inapps/app/src/components. Move it topackages/uionly when both apps need it. - Check
cn()merging —tailwind-mergeresolves class conflicts intelligently (e.g.,bg-red-500overridesbg-blue-500). Usecn()for any conditional class composition.
UI Overview
Leave Localhost ships a shared UI package at packages/ui that both the product app (apps/app) and the marketing site (apps/marketing) import from. The package is built on shadcn/ui, Radix primitives, Tailwind CSS v4, and class-variance-authority for variant-driven styling.
Theming
Leave Localhost uses CSS custom properties for all design tokens. Colors are defined in the OKLCH color space for perceptual uniformity, and every token is referenced through Tailwind CSS 4's @theme block so you can use them as regular Tailwind utilities.