---
name: Drift
status: final
sources:
  - {planning_artifacts}/prds/drift-2026-03-12/prd.md
updated: 2026-04-02
---

# Drift — Experience Spine

> Illustrative example. Single-surface responsive web. shadcn/ui on Next.js + Tailwind. Paired with `design-example-shadcn.md` (Drift DESIGN.md). Demonstrates: component-library inheritance, keyboard-first interaction primitives, the "shadcn + brand-layer" pattern that covers most modern web SaaS.

## Foundation

Single-surface responsive web. shadcn/ui on Next.js 15+ with Tailwind CSS. The component library does most of the work; brand discipline is "respect the defaults except where the brand layer overrides them." `DESIGN.md` is the visual identity reference and names the override surface; this spine is the experience. Single-tenant per project; users can belong to multiple projects but each project is a self-contained workspace.

## Information Architecture

| Surface | Reached from | Purpose |
|---|---|---|
| Today | App open / `g t` | Current focus, in-progress tasks pulled from all projects |
| Projects | Sidebar / `g p` | List of active and archived projects |
| Project detail | Projects row / `g 1`–`g 9` | Tasks in this project, organized by lane |
| Search | `⌘K` / `Ctrl+K` | Command palette — surface, navigate, act |
| Settings | Avatar menu | Account, theme, keyboard shortcuts, billing |

Sidebar collapses to icons on `md`; becomes a `Sheet` on `sm`. Modal stacks one level deep (e.g., open `Dialog` on top of a surface, never on top of another dialog).

→ Composition reference: `mockups/today.html`, `mockups/project-detail.html`, `mockups/command-palette.html`. Spine wins on conflict.

## Voice and Tone

Microcopy. Brand voice and aesthetic posture live in `DESIGN.md`.

| Do | Don't |
|---|---|
| "What are you working on?" | "Let's get productive! 🚀" |
| "3 tasks in motion" | "You have 3 active items." |
| "Closed. Nice work." | "Task completed successfully ✓" |
| "Nothing in motion. Pick something." | "No active tasks. Click below to get started!" |
| Manager-facing: counts and verbs. Employee-facing: same. | Different tone per audience — Drift talks to everyone the same way. |

## Component Patterns

Behavioral. Visual specs live in `DESIGN.md.Components` (or in shadcn defaults, when inherited).

| Component | Use | Behavioral rules |
|---|---|---|
| Task row | Projects, Today | Click anywhere on row opens edit dialog. Checkbox toggles done state with optimistic update. Hover reveals quick-actions (`focus`, `defer`, `archive`). |
| Focus card | Today, Project detail | At most one focus card per surface — the task or project marked with `focus` state. `f` keyboard shortcut sets focus on the active row. |
| Command palette | Global (⌘K) | Fuzzy search across all projects, tasks, and commands. `Enter` fires the highlighted result. `→` previews a result. Escape closes. |
| Project header | Project detail | Inline-editable title (click to edit, blur to save). Status pill: active / archived / done. |
| Empty state | Anywhere | shadcn's empty pattern + one Drift-specific sentence. `display-sm` for the headline, body text below, single primary action. |

## State Patterns

| State | Surface | Treatment |
|---|---|---|
| Cold app load | Today | shadcn `Skeleton` rows (4-6) match expected layout. Resolves on data. |
| No focus | Today | `display-sm`: "Nothing in motion. Pick something." Below: list of in-progress tasks from all projects. |
| Empty project | Project detail | `display-sm`: "{Project title} is empty." Body: "Add a first task to get going." Single primary button. |
| Command palette no matches | ⌘K | "No matches. Start typing a task or project name, or pick an action below." Followed by 4-5 common commands. |
| Offline | Global (status bar) | shadcn `Toast` once: "You're offline. Changes will sync when you reconnect." Local writes continue. |
| Permission denied | Projects (others' private) | Surface hidden from sidebar. No "blocked" screen. |
| Stale data | Project detail | If background refresh detects changes, shadcn `Toast`: "Updated by {user_name}. Refresh." Manual refresh, no auto. |

## Interaction Primitives

**Keyboard-first.** Drift's primary audience is developers and power users; the keyboard surface is the product, the mouse is fallback.

- `⌘K` / `Ctrl+K` — Command palette (universal)
- `g t` / `g p` — Go to Today / Projects (vim-style)
- `g 1`–`g 9` — Go to project by sidebar position
- `f` — Set focus on highlighted task/project
- `c` — Create new task (context-aware: in the active project)
- `Esc` — Close dialogs, exit edit mode, clear command palette
- `/` — Focus search in current surface

**Mouse:** click to act, drag deferred to v2. Hover reveals row actions on `md+` (touch users tap to reveal).

**Banned everywhere:** infinite scroll (pagination only), drag-to-reorder in v1, hover-only affordances on `sm` viewports, modal stacks > 1 level deep.

## Accessibility Floor

Behavioral. Visual contrast lives in `DESIGN.md` (inherits shadcn's WCAG AA-compliant defaults; brand overrides verified to maintain ratios).

- WCAG 2.2 AA across the responsive web surface.
- Screen reader announces page surface on navigation: "Today, focus surface" / "Project: {name}, task list, {N} tasks."
- Keyboard shortcuts available without modifier on most surfaces (vim-style `g t` etc.) — users with motor-control limitations get the same surface as power users.
- `Tab` order matches reading order on every surface. `Esc` always closes the topmost modal/popover.
- Command palette is fully keyboard-operable; results announce as they update via `aria-live`.
- Focus rings inherit shadcn's `ring` token — visible at AA contrast against `background`.

## Responsive & Platform

| Breakpoint | Behavior |
|---|---|
| `≥ lg` (1024px+) | Sidebar visible. Today is a 2-column layout: focus + in-motion list. |
| `md` (768–1023px) | Sidebar collapses to icons. Today stacks to single column. |
| `< md` (`sm`) | Sidebar becomes a `Sheet` triggered from top bar. Command palette opens fullscreen. |

Drift is responsive web, not a native mobile app. The product works on phones for read + simple-edit, but the primary surface is desktop / laptop.

## Inspiration & Anti-patterns

- **Lifted from Linear:** the keyboard-first discipline. `⌘K` is the command center; vim-style nav (`g t`); no drag for primary navigation; status pill vocabulary.
- **Lifted from Notion:** inline-editable titles. Click-to-edit on project header, blur to save. No edit/view mode toggle.
- **Lifted from shadcn:** the entire surface vocabulary. Drift's brand is *what we add to shadcn*, not a from-scratch design system. This is a deliberate posture, not a shortcut.
- **Rejected — Streaks, badges, achievement notifications:** Drift is a tool, not a habit app. Task closure is its own reward; no celebratory animation, no "🎉 5-day streak!" toast.
- **Rejected — AI-suggested next tasks:** Drift surfaces what's in motion, doesn't tell the user what to work on. The user picks focus; the tool surfaces consequences.
- **Rejected — Multi-column kanban as default project view:** lists are linear; kanban hides progress behind columns. Optional v2; not the default.

## Key Flows

### Flow 1 — Morning focus (Sarah, solo founder, 8:45am Tuesday)

1. Sarah opens Drift in a browser tab.
2. App loads Today. `display-sm`: "Welcome back, Sarah." Focus card shows yesterday's marked task — "Finish landing page hero copy" — still in motion.
3. She hits `⌘K`, types "ship hero", sees the matching task and presses Enter to open it.
4. Inline edit: she updates the task description with two new bullets. Tab + Tab triggers save.
5. **Climax:** Sarah closes the dialog. Today re-renders: focus card still shows the hero task, but now with the updated body text visible at a glance. She doesn't have to navigate anywhere — the surface that greeted her now reflects the work she just did. She picks up her coffee and starts writing.

Failure: data save fails → shadcn `Toast` (destructive variant): "Couldn't save. Trying again." Inline edit retained; another `Enter` retries.

### Flow 2 — Async handoff (Devon and Mara, small remote team, mid-afternoon)

1. Devon finishes wiring up the auth flow and marks the task `done`.
2. Mara, time-zoned three hours ahead and online during overlap, opens Drift.
3. Today loads; her focus is on her own front-end work, but the in-motion list shows Devon's auth task now marked done and one task below it newly assigned to her — "Wire up post-auth redirect" — that Devon set during checkout.
4. She hits `f` on the row to mark it as her focus, then `Enter` to open it.
5. **Climax:** The focus card swaps. Her surface now shows the post-auth redirect task as the live thing she's working on; the projects sidebar shows the Auth project highlighted; the command palette `⌘K` defaults its first result to "Go to Auth project." The state of the team's progress is *embedded in her surface* — no Slack thread to scroll, no status doc to read.

Failure: Devon hadn't actually assigned the follow-up — Mara mis-assigned to herself. She hits `Esc`, `f` again to unfocus, and reassigns to Devon. No "are you sure" dialog; Drift trusts the user.
