UNPKG

@loke/design-system

Version:

A design system with individually importable components

417 lines (335 loc) 12.4 kB
--- name: getting-started description: > Set up @loke/design-system in a new app. Install @loke/design-system, @loke/icons, @loke/ui. Configure Tailwind v4 with @theme block, :root CSS variables, @source directive pointing to the design system package. Add TooltipProvider. Set up .dark class dark mode. Granular subpath imports from @loke/design-system/<component>. Import stylesheet via @loke/design-system/styles. Activate when setting up or configuring the design system in a new or existing project. type: lifecycle library: '@loke/design-system' library_version: '2.0.0-rc.6' sources: - 'LOKE/merchant-frontends:packages/design-system/package.json' - 'LOKE/merchant-frontends:packages/design-system/src/styles/index.css' - 'LOKE/merchant-frontends:packages/design-system/src/styles/theme.css' - 'LOKE/merchant-frontends:packages/design-system/build.ts' - 'LOKE/merchant-frontends:packages/design-system/CHANGELOG.md' - 'LOKE/merchant-frontends:apps/office/src/styles.css' --- ## Setup ### 1. Install packages ```bash bun add @loke/design-system @loke/icons @loke/ui ``` ### 2. Configure Tailwind CSS Create or update your app's CSS entry point. The consumer must duplicate the `@theme` block and `:root`/`.dark` variables, then add an `@source` directive pointing to the design system package so Tailwind v4 does not purge DS classes. ```css /* src/styles.css */ @import "tailwindcss"; @plugin "tailwindcss-animate"; /* Point Tailwind at the DS package so its classes are not purged */ @source "../../../packages/design-system"; /* monorepo path -- see note below */ @custom-variant dark (&:is(.dark *)); @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-sans); --font-mono: var(--font-mono); --color-sidebar-ring: var(--sidebar-ring); --color-sidebar-border: var(--sidebar-border); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar: var(--sidebar); --color-chart-5: var(--chart-5); --color-chart-4: var(--chart-4); --color-chart-3: var(--chart-3); --color-chart-2: var(--chart-2); --color-chart-1: var(--chart-1); --color-ring: var(--ring); --color-input: var(--input); --color-border: var(--border); --color-destructive-foreground: var(--destructive-foreground); --color-destructive: var(--destructive); --color-accent-foreground: var(--accent-foreground); --color-accent: var(--accent); --color-muted-foreground: var(--muted-foreground); --color-muted: var(--muted); --color-secondary-foreground: var(--secondary-foreground); --color-secondary: var(--secondary); --color-primary-foreground: var(--primary-foreground); --color-primary: var(--primary); --color-popover-foreground: var(--popover-foreground); --color-popover: var(--popover); --color-card-foreground: var(--card-foreground); --color-card: var(--card); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); --animate-accordion-down: accordion-down 0.2s ease-out; --animate-accordion-up: accordion-up 0.2s ease-out; @keyframes accordion-down { from { height: 0; } to { height: var(--loke-accordion-content-height); } } @keyframes accordion-up { from { height: var(--loke-accordion-content-height); } to { height: 0; } } } :root { --background: oklch(96.47% 0.0167 274.82); --foreground: oklch(27.73% 0.0061 258.36); --card: oklch(99.16% 0.0029 247.85); --card-foreground: oklch(37.39% 0.0822 285.49); --popover: oklch(99.16% 0.0029 247.85); --popover-foreground: oklch(37.1% 0.0722 285.35); --primary: oklch(51.4% 0.2276 276.98); --primary-foreground: oklch(99.17% 0.0028 325.6); --secondary: oklch(72.29% 0.1438 163.11); --secondary-foreground: oklch(99.78% 0.0068 115.7); --muted: oklch(96.71% 0.0029 264.54); --muted-foreground: oklch(55.13% 0.0233 264.36); --accent: oklch(96.71% 0.0029 264.54); --accent-foreground: oklch(21.03% 0.0318 264.65); --destructive: oklch(58.52% 0.18 24.61); --destructive-foreground: oklch(98.48% 0.0213 193.18); --border: oklch(90.58% 0.013831 272.4947); --input: oklch(87.53% 0.0142 268.67); --ring: oklch(74.83% 0.1308 254.23); --radius: 0.5rem; --chart-1: oklch(0.62 0.15 162); --chart-2: oklch(0.58 0.14 198); --chart-3: oklch(0.62 0.16 53); --chart-4: oklch(0.58 0.22 285); --chart-5: oklch(0.58 0.22 16); --sidebar-ring: oklch(74.83% 0.1308 254.23); --sidebar-border: oklch(28.01% 0.0249 258.35); --sidebar-accent-foreground: oklch(72.47% 0.1495 160.94); --sidebar-accent: oklch(28.01% 0.0249 258.35); --sidebar-foreground: oklch(95.05% 0.0041 286.32); --sidebar: oklch(21.03% 0.0316 264.78); --brand-50: oklch(96% 0.04 276.98); --brand-100: oklch(92% 0.08 276.98); --brand-200: oklch(83% 0.13 276.98); --brand-300: oklch(74% 0.18 276.98); --brand-400: oklch(63% 0.21 276.98); --brand-500: oklch(51.4% 0.2276 276.98); --brand-600: oklch(42% 0.2 276.98); --brand-700: oklch(33% 0.17 276.98); --brand-800: oklch(24% 0.13 276.98); --brand-900: oklch(15% 0.09 276.98); --brand-950: oklch(5% 0.05 276.98); } .dark { --background: oklch(15% 0.02 274.82); --foreground: oklch(90% 0.01 258.36); --card: oklch(20% 0.01 247.85); --card-foreground: oklch(85% 0.04 283.75); --popover: oklch(18% 0.015 247.85); --popover-foreground: oklch(92% 0.07 285.35); --primary: oklch(60% 0.25 276.98); --primary-foreground: oklch(15% 0.01 325.6); --secondary: oklch(55% 0.16 163.11); --secondary-foreground: oklch(15% 0.02 115.7); --muted: oklch(25% 0.01 264.54); --muted-foreground: oklch(75% 0.03 264.36); --accent: oklch(30% 0.05 264.54); --accent-foreground: oklch(95% 0.04 264.65); --destructive: oklch(60% 0.22 24.61); --destructive-foreground: oklch(15% 0.02 193.18); --border: oklch(30% 0.02 272.49); --input: oklch(28% 0.02 268.67); --ring: oklch(65% 0.18 254.23); --chart-1: oklch(0.70 0.13 162); --chart-2: oklch(0.68 0.12 198); --chart-3: oklch(0.68 0.14 53); --chart-4: oklch(0.66 0.18 285); --chart-5: oklch(0.65 0.19 16); --sidebar-ring: oklch(65% 0.18 254.23); --sidebar-border: oklch(28.01% 0.0249 258.35); --sidebar-accent-foreground: oklch(55% 0.16 163.11); --sidebar-accent: oklch(28.01% 0.0249 258.35); --sidebar-foreground: oklch(97% 0.01 286.32); --sidebar: oklch(20% 0.01 247.85); } @layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } button { @apply cursor-pointer; } } ``` ### 3. Import the stylesheet and add TooltipProvider Import the DS stylesheet at your app entry point and wrap your tree with `TooltipProvider`. ```tsx // src/main.tsx (or app entry) import "@loke/design-system/styles"; import { TooltipProvider } from "@loke/design-system/tooltip"; export default function App() { return ( <TooltipProvider> {/* your app */} </TooltipProvider> ); } ``` --- ## Core Patterns ### Subpath imports Every component lives at its own subpath. There is no barrel export. ```tsx import { Button } from "@loke/design-system/button"; import { Input } from "@loke/design-system/input"; import { Dialog, DialogContent, DialogTitle } from "@loke/design-system/dialog"; import { cn } from "@loke/design-system/cn"; import { Badge } from "@loke/design-system/badge"; import { Tooltip, TooltipContent, TooltipTrigger } from "@loke/design-system/tooltip"; ``` Full list of available subpaths (from `package.json` exports): `accordion` `alert` `alert-dialog` `avatar` `badge` `box` `button` `calendar` `card` `checkbox` `cn` `collapsible` `columns` `command` `date-picker` `dialog` `dropdown-menu` `form` `heading` `inline` `input` `label` `max-width-wrapper` `page-layout` `pagination` `popover` `radio-group` `responsive` `select` `separator` `sheet` `sidebar` `skeleton` `slot` `spinner` `stack` `switch` `table` `tabs` `text` `textarea` `toast` `tooltip` `use-mobile` ### Stylesheet import ```ts import "@loke/design-system/styles"; ``` Import once at the app entry point, before any component imports. ### Dark mode Dark mode is class-based. Toggle by adding/removing the `dark` class on `<html>` or `<body>`. ```ts document.documentElement.classList.toggle("dark"); ``` The Tailwind custom variant is configured as: ```css @custom-variant dark (&:is(.dark *)); ``` This means any element inside a `.dark` ancestor gets dark styles applied. ### @source directive **Monorepo (workspace package):** ```css @source "../../../packages/design-system"; ``` **External npm dependency:** ```css @source "../node_modules/@loke/design-system"; ``` Adjust the relative path to match your app's location relative to the DS package root. --- ## Common Mistakes ### 1. CRITICAL: Importing from barrel path instead of subpath ```tsx // Wrong -- no barrel export exists import { Button } from "@loke/design-system"; // Correct import { Button } from "@loke/design-system/button"; ``` ### 2. CRITICAL: Using shadcn-style import paths ```tsx // Wrong -- this is a shadcn/ui pattern, not used here import { Button } from "@/components/ui/button"; // Correct import { Button } from "@loke/design-system/button"; ``` ### 3. CRITICAL: Missing stylesheet import ```tsx // Wrong -- components render unstyled import { Button } from "@loke/design-system/button"; // Correct -- import the stylesheet once at app entry import "@loke/design-system/styles"; import { Button } from "@loke/design-system/button"; ``` ### 4. HIGH: Using overlay components in server components ```tsx // Wrong -- Dialog, Popover, Tooltip, Sheet etc. use browser APIs // app/page.tsx (React Server Component) import { Dialog } from "@loke/design-system/dialog"; export default function Page() { return <Dialog>...</Dialog>; // runtime error } ``` ```tsx // Correct -- add "use client" boundary // app/components/my-dialog.tsx "use client"; import { Dialog, DialogContent, DialogTrigger } from "@loke/design-system/dialog"; export function MyDialog() { return ( <Dialog> <DialogTrigger>Open</DialogTrigger> <DialogContent>Content</DialogContent> </Dialog> ); } ``` ```tsx // app/page.tsx import { MyDialog } from "./components/my-dialog"; export default function Page() { return <MyDialog />; } ``` ### 5. HIGH: Importing primitives from @loke/design-system instead of @loke/ui ```tsx // Wrong -- arrow, focus-scope, popper, portal, presence, roving-focus // are internal primitives not exported from @loke/design-system import { Presence } from "@loke/design-system/presence"; import { FocusScope } from "@loke/design-system/focus-scope"; import { Portal } from "@loke/design-system/portal"; // Correct -- primitives live in @loke/ui import { Presence } from "@loke/ui/presence"; import { FocusScope } from "@loke/ui/focus-scope"; import { Portal } from "@loke/ui/portal"; ``` > Fixed in v2.0.0-rc.5. Agents trained on older code may still generate this pattern. ### 6. HIGH: Hallucinating @radix-ui/react-* imports ```tsx // Wrong -- @radix-ui packages are internal deps, not consumer-facing import * as DialogPrimitive from "@radix-ui/react-dialog"; import * as PopoverPrimitive from "@radix-ui/react-popover"; // Correct -- use the DS exports directly import { Dialog, DialogContent, DialogTrigger } from "@loke/design-system/dialog"; import { Popover, PopoverContent, PopoverTrigger } from "@loke/design-system/popover"; ``` ### 7. HIGH: Missing @source directive in consumer Tailwind CSS ```css /* Wrong -- DS utility classes get purged at build time */ @import "tailwindcss"; @theme inline { /* ... */ } ``` ```css /* Correct -- monorepo */ @import "tailwindcss"; @source "../../../packages/design-system"; @theme inline { /* ... */ } ``` ```css /* Correct -- external npm */ @import "tailwindcss"; @source "../node_modules/@loke/design-system"; @theme inline { /* ... */ } ``` --- ## See also - `theming/SKILL.md` -- customizing colors and dark mode tokens - `overlay-composition/SKILL.md` -- overlays require "use client" and TooltipProvider