# SKILL.md — Monolith Design System
Version: 1.0.0

Behavioural rules for Claude when working within the Monolith design system. Read this alongside DESIGN.md.

---

## Decision Framework

Before generating any UI, ask:
1. What is the user trying to accomplish?
2. What is the simplest component that achieves it?
3. Does this match an existing pattern in DESIGN.md?

Prefer existing patterns. Only introduce a new component type when no existing pattern fits.

---

## Component Selection

### Modal vs. Drawer vs. Page

- **Modal** — confirmations, short forms (≤4 fields), destructive action warnings. Max width: `max-w-md`.
- **Drawer** — filters, settings panels, long forms on mobile. Slides from the right. Full height.
- **New page** — anything requiring more than 6 fields, multi-step flows, or content that benefits from a URL.

Never put a table or data-heavy content inside a modal.

### Icon vs. Label vs. Icon+Label

- **Icon only** — only when the action is universally understood (close X, search, back arrow) and the element has a tooltip or `aria-label`.
- **Label only** — for nav links and primary CTAs.
- **Icon + Label** — for secondary actions in toolbars or when reinforcing meaning helps (e.g., Upload icon + "Upload files").

### Empty State vs. Skeleton vs. Spinner

- **Skeleton** — when layout is known and data load time is < 2 seconds. Match the skeleton shape exactly to the content.
- **Spinner** — for actions with unknown duration (form submit, file upload). Place inline near the action, not full-screen.
- **Empty state** — when a list or table has no data. Always include: an icon, a heading, a one-sentence explanation, and a CTA to add the first item.

---

## Responsive Strategy

### Breakpoints

Use Tailwind's default breakpoints:
- `sm`: 640px — single-column → multi-column transitions
- `md`: 768px — show/hide secondary nav, expand sidebars
- `lg`: 1024px — desktop layouts, full sidebar, multi-column grids
- `xl`: 1280px — wider grids (4-col), expanded tables

### Layout Rules

- Mobile: single column. Stack everything vertically.
- Tablet (sm/md): 2-column grids permitted. Sidebar becomes a collapsible drawer.
- Desktop (lg+): multi-column layouts. Sidebar can be persistent (sticky).
- Navigation: hamburger below `md`, full nav at `md` and above.
- Tables: on mobile, either horizontally scroll (`overflow-x-auto`) or convert to stacked card layout. Never truncate table data on mobile.

### What Collapses on Mobile

- Sidebars → bottom sheet or hidden behind a toggle
- Table columns → show only the 2–3 most important columns; add a "See all" expand
- Multi-step forms → show one step at a time
- Secondary nav links → move into a hamburger menu
- Metadata rows → stack vertically instead of inline

---

## State Handling

### Loading

```tsx
{/* Skeleton for a card */}
<div className="bg-[#0D0D0D] border border-[#1C1C1C] rounded-sm animate-pulse">
  <div className="aspect-video bg-[#161616]" />
  <div className="p-4 space-y-2">
    <div className="h-3 bg-[#161616] rounded-sm w-3/4" />
    <div className="h-3 bg-[#161616] rounded-sm w-1/2" />
  </div>
</div>
```

Rules:
- Skeleton bg: `#161616` (one step above surface)
- Never use a spinner for page-level loading — use skeletons
- Animate with `animate-pulse` only — not `animate-spin` on skeleton elements

### Empty

```tsx
<div className="flex flex-col items-center justify-center py-24 text-center">
  <Icon size={32} className="text-[#2A2A2A] mb-4" />
  <p className="text-sm font-medium text-[#505050] mb-1">No items yet</p>
  <p className="text-xs text-[#404040] mb-6">One-sentence explanation of why it's empty.</p>
  <button className="...primary button...">Add first item</button>
</div>
```

### Error

```tsx
<p role="alert" className="text-xs font-mono text-red-500 mt-1">
  Error message here.
</p>
```

Rules:
- Inline field errors: beneath the field, `font-mono text-xs text-red-500`
- Page-level errors: use a banner at the top of the content area, not a modal
- Always include `role="alert"` so screen readers announce errors immediately
- Never use red backgrounds — red text on dark surface only

### Success

```tsx
<p className="text-xs font-mono text-green-500 mt-1">
  Saved successfully.
</p>
```

- Inline success: same pattern as error but `text-green-500`
- Page-level success: a dismissible banner, auto-dismisses after 4 seconds
- Never redirect automatically on success unless the current page is now invalid

### Disabled

```tsx
<button disabled className="... opacity-40 cursor-not-allowed">
```

- Disabled opacity: `opacity-40`
- Always pair `disabled` attribute with `cursor-not-allowed`
- Never hide disabled elements — opacity communicates state, visibility communicates existence

---

## Accessibility Defaults

### Focus

All interactive elements must have a visible focus ring:

```tsx
className="focus:outline-none focus:ring-1 focus:ring-[#F0F0F0]/30"
```

- Use `focus-visible:` variants to show ring only on keyboard navigation
- Never remove focus outlines without a replacement
- Focus ring colour: `#F0F0F0` at 30% opacity

### ARIA

- Modals: `role="dialog"` + `aria-modal="true"` + `aria-labelledby` pointing to the modal title
- Alerts/errors: `role="alert"` (announces immediately) or `role="status"` (announces politely)
- Icon-only buttons: always include `aria-label`
- Toggle buttons: use `aria-pressed="true/false"`
- Loading states: `aria-busy="true"` on the container being loaded
- Navigation landmarks: `<nav>`, `<main>`, `<footer>` — use semantic HTML, not `<div role="navigation">`

### Colour Contrast

- Primary text (`#F0F0F0`) on background (`#080808`): passes AAA
- Secondary text (`#808080`) on background: passes AA for large text, AA for normal at ≥14px
- Muted text (`#505050`): use only for decorative/non-essential content — fails AA for small text
- Never use muted text for required labels or error messages

### Keyboard Navigation

- All interactive elements must be reachable by Tab
- Modals must trap focus while open — use a focus trap library
- Dropdowns: open on Enter/Space, close on Escape, navigate items with arrow keys
- Skip-to-content link: first focusable element on every page

---

## Code Conventions

### Tailwind Class Ordering

Follow this order within a `className`:
1. Layout (display, position, width, height)
2. Flexbox/Grid
3. Spacing (margin, padding)
4. Typography
5. Background
6. Border
7. Effects (shadow, opacity)
8. Transitions
9. Pseudo-classes (hover:, focus:)

Example:
```tsx
className="flex items-center gap-3 px-4 py-2 text-sm text-[#808080] bg-[#0D0D0D] border border-[#1C1C1C] rounded-sm hover:text-[#F0F0F0] hover:border-[#2A2A2A] transition-colors"
```

### Component Naming

- PascalCase for component files and exports: `ListingCard.tsx`
- kebab-case for route segments: `app/listings/[id]/page.tsx`
- camelCase for props, variables, and functions
- Prefix client components with nothing — mark with `'use client'` at the top instead
- Suffix server action files with `.actions.ts`

### File Structure

```
components/
  ui/          # Generic, reusable (Button, Input, Modal)
  listings/    # Domain-specific (ListingCard, ListingFilters)
  buyer/       # Role-specific (BuyButton, DownloadList)
  seller/      # Role-specific (UploadForm, EarningsSummary)
app/
  (marketing)/ # Public pages (home, listings, guide, legal)
  (auth)/      # Auth pages (login, register)
  (dashboard)/ # Authenticated pages (account, seller)
  api/         # Route handlers
```

### Props Discipline

- Never pass more than 6 props to a component — extract a type or split the component
- Use explicit types, not `any`
- Prefer `children` over `content` or `label` props for rich content
- Colocate types with the component file unless shared across 3+ files

### When to Use Client Components

Use `'use client'` only when the component needs:
- `useState` or `useReducer`
- `useEffect`
- Browser event handlers (`onClick` on dynamic data, `onChange`, etc.)
- Browser APIs (`window`, `document`, `localStorage`)

Everything else should be a server component. If only a small part of a component needs interactivity, extract that part into a child client component.

---

## Writing UX Copy

- Button labels: verb + noun. "Upload files" not "Upload". "View listing" not "View".
- Error messages: say what happened and what to do. "Payment failed — check your card details and try again." Not "An error occurred."
- Empty states: explain why it's empty, not just that it is. "No listings yet — create one to start selling."
- Confirmation dialogs: state the consequence, not the action. "This will permanently delete the listing and all associated files." Not "Are you sure?"
- Loading messages: use past tense for instant feedback. "Saved." "Uploaded." Not "Saving..." unless the operation takes > 1 second.
