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.
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.
Package at a Glance
| Detail | Value |
|---|---|
| Package name | @leavelocalhost/ui |
| Component style | shadcn new-york |
| Icon library | Lucide React |
| CSS framework | Tailwind CSS 4 via @tailwindcss/postcss |
| Color format | OKLCH CSS custom properties |
| Radius | 0.375rem base with scaled variants |
| Fonts | Inter (sans), Source Serif 4 (serif), JetBrains Mono (mono) |
What Lives Here
The UI package owns everything that is shared across apps:
- Components — pre-styled, accessible primitives (Button, Card, Dialog,
Sidebar, etc.) exported individually from
@leavelocalhost/ui/<component>. - Design tokens — CSS custom properties for colors, radii, shadows, and
typography defined in
src/globals.css. - Utilities — the
cn()helper for merging Tailwind classes,callAll()for composing event handlers, anduseDoubleCheck()for destructive-action confirmation UX. - Hooks —
useIsMobile()for responsive behavior at the 768 px breakpoint.
What Belongs Elsewhere
- Product-only UI — components that only appear in the authenticated app
belong in
apps/app/src/componentsor co-located_componentsdirectories. - Marketing-only UI — landing-page sections, pricing cards, and other
public-facing components belong in
apps/marketing.
Included Components
The package exports the following components via individual entry points:
| Component | Import path | Radix primitive |
|---|---|---|
| Avatar | @leavelocalhost/ui/avatar | @radix-ui/react-avatar |
| Badge | @leavelocalhost/ui/badge | — |
| Button | @leavelocalhost/ui/button | @radix-ui/react-slot |
| Card | @leavelocalhost/ui/card | — |
| Dialog | @leavelocalhost/ui/dialog | @radix-ui/react-dialog |
| Dropdown Menu | @leavelocalhost/ui/dropdown-menu | @radix-ui/react-dropdown-menu |
| Input | @leavelocalhost/ui/input | — |
| Input OTP | @leavelocalhost/ui/input-otp | input-otp |
| Label | @leavelocalhost/ui/label | @radix-ui/react-label |
| Logo | @leavelocalhost/ui/logo | — |
| Scroll Area | @leavelocalhost/ui/scroll-area | @radix-ui/react-scroll-area |
| Select | @leavelocalhost/ui/select | @radix-ui/react-select |
| Separator | @leavelocalhost/ui/separator | @radix-ui/react-separator |
| Sheet | @leavelocalhost/ui/sheet | @radix-ui/react-dialog |
| Sidebar | @leavelocalhost/ui/sidebar | composite |
| Skeleton | @leavelocalhost/ui/skeleton | — |
| Switch | @leavelocalhost/ui/switch | @radix-ui/react-switch |
| Table | @leavelocalhost/ui/table | — |
| Tabs | @leavelocalhost/ui/tabs | @radix-ui/react-tabs |
| Tooltip | @leavelocalhost/ui/tooltip | @radix-ui/react-tooltip |
| Upload Input | @leavelocalhost/ui/upload-input | @xixixao/uploadstuff |
| Icons | @leavelocalhost/ui/icons | — |
Importing Components
Every component is exported through a dedicated entry point, not a barrel
index.ts. Import from the specific path:
import { Button } from "@leavelocalhost/ui/button";
import { cn } from "@leavelocalhost/ui/utils";Do not use deep imports into src/. The exports map in package.json
defines the public API.
Utility Exports
cn(...inputs)
Merges class names with clsx and tailwind-merge. Use this everywhere you
compose Tailwind classes:
import { cn } from "@leavelocalhost/ui/utils";
<div className={cn("p-4", isActive && "bg-primary")} />;callAll(...fns)
Calls multiple event handlers in sequence. Useful when composing behavior from different sources:
import { callAll } from "@leavelocalhost/ui/utils";
<button onClick={callAll(onTrack, onClose)} />;useDoubleCheck()
Returns { doubleCheck, getButtonProps } to implement two-click confirmation
for destructive actions. On first click the button enters a "confirm" state; on
second click the action fires. Pressing Escape or blurring the button resets.
import { useDoubleCheck } from "@leavelocalhost/ui/utils";
const { doubleCheck, getButtonProps } = useDoubleCheck();
<Button
variant={doubleCheck ? "destructive" : "ghost"}
{...getButtonProps({ onClick: handleDelete })}
>
{doubleCheck ? "Are you sure?" : "Delete"}
</Button>;useIsMobile()
Returns true when the viewport is narrower than 768 px. Used by the Sidebar
component to switch between desktop and mobile layouts:
import { useIsMobile } from "@leavelocalhost/ui/use-mobile";Next Reads
- shadcn/ui — adding and customizing shadcn components.
- Theming — design tokens and CSS custom properties.
- Dark Mode — how dark mode works.
- Internationalization (i18n) — internationalization setup.
- Customizing the Dashboard — modifying the sidebar, header, and navigation.
Notification Emails
The starter includes two categories of notification emails: subscription notifications and sensitive action verification codes.
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.