@loke/design-system
Version:
A design system with individually importable components
417 lines (335 loc) • 12.4 kB
Markdown
---
name: getting-started
description: >
Set up /design-system in a new app. Install @loke/design-system,
/icons, @loke/ui. Configure Tailwind v4 with block, :root
CSS variables, directive pointing to the design system package.
Add TooltipProvider. Set up .dark class dark mode. Granular subpath
imports from /design-system/<component>. Import stylesheet via
/design-system/styles. Activate when setting up or configuring the
design system in a new or existing project.
type: lifecycle
library: '/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 /design-system @loke/icons /ui
```
### 2. Configure Tailwind CSS
Create or update your app's CSS entry point. The consumer must duplicate the `` block and `:root`/`.dark` variables, then add an `` directive pointing to the design system package so Tailwind v4 does not purge DS classes.
```css
/* src/styles.css */
"tailwindcss";
"tailwindcss-animate";
/* Point Tailwind at the DS package so its classes are not purged */
"../../../packages/design-system"; /* monorepo path -- see note below */
-variant dark (&:is(.dark *));
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;
accordion-down {
from { height: 0; }
to { height: var(--loke-accordion-content-height); }
}
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);
}
base {
* {
border-border outline-ring/50;
}
body {
bg-background text-foreground;
}
button {
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
-variant dark (&:is(.dark *));
```
This means any element inside a `.dark` ancestor gets dark styles applied.
### directive
**Monorepo (workspace package):**
```css
"../../../packages/design-system";
```
**External npm dependency:**
```css
"../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 /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 -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 directive in consumer Tailwind CSS
```css
/* Wrong -- DS utility classes get purged at build time */
"tailwindcss";
inline {
/* ... */
}
```
```css
/* Correct -- monorepo */
"tailwindcss";
"../../../packages/design-system";
inline {
/* ... */
}
```
```css
/* Correct -- external npm */
"tailwindcss";
"../node_modules/@loke/design-system";
inline {
/* ... */
}
```
---
## See also
- `theming/SKILL.md` -- customizing colors and dark mode tokens
- `overlay-composition/SKILL.md` -- overlays require "use client" and TooltipProvider