Leave Localhost logoLeave LocalhostDocs
UI

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

DetailValue
Package name@leavelocalhost/ui
Component styleshadcn new-york
Icon libraryLucide React
CSS frameworkTailwind CSS 4 via @tailwindcss/postcss
Color formatOKLCH CSS custom properties
Radius0.375rem base with scaled variants
FontsInter (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, and useDoubleCheck() for destructive-action confirmation UX.
  • HooksuseIsMobile() 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/components or co-located _components directories.
  • 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:

ComponentImport pathRadix 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-otpinput-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/sidebarcomposite
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

On this page