# OpenWork Design System This document turns the visual direction in `DESIGN-LANGUAGE.md` into an implementation system that can unify: - `apps/app` (OpenWork app) - `ee/apps/den-web` (OpenWork Cloud / Den web surfaces) - `ee/apps/landing` (marketing + product storytelling) The goal is not to create three similar styles. The goal is one OpenWork design system with a few environment-specific expressions. --- ## 1. Why this exists Today the product already has the beginnings of a system, but it is split across: - app-specific CSS variables in `apps/app/src/app/index.css` - Tailwind theme setup in `apps/app/tailwind.config.ts` - Radix color tokens in `apps/app/src/styles/colors.css` - repeated utility-class decisions across app, Cloud, and landing That creates three problems: 1. the app and Cloud can feel related but not identical 2. visual decisions are made at the screen level instead of the system level 3. tokens, primitives, and page composition rules are not clearly separated This file defines the missing structure. --- ## 2. System model OpenWork should use a three-layer design system: ### Layer 1: Foundations Raw design tokens: - color - typography - spacing - radius - shadow - motion These are the only values components should depend on directly. ### Layer 2: Semantic tokens Product-meaning tokens: - `surface.page` - `surface.panel` - `surface.sidebar` - `text.primary` - `text.secondary` - `border.subtle` - `action.primary.bg` - `state.hover` - `state.selected` These should map foundation tokens into product meaning. ### Layer 3: Component primitives Reusable building blocks: - Button - Card - Input - Modal shell - Sidebar shell - List row - Status pill - Section header - Empty state Pages should mostly compose these primitives, not invent their own visual logic. --- ## 3. Relationship to existing docs - `DESIGN-LANGUAGE.md` = visual philosophy and qualitative rules - `DESIGN-SYSTEM.md` = implementation structure and migration plan If there is a conflict: 1. `DESIGN-LANGUAGE.md` decides what the product should feel like 2. `DESIGN-SYSTEM.md` decides how to encode that in tokens and primitives --- ## 4. Core principle: one system, three expressions OpenWork has three main UI contexts: 1. **App expression** — denser, flatter, operational 2. **Cloud expression** — still operational, slightly more editorial and roomy 3. **Landing expression** — more atmospheric, but still clearly the same product family These should differ mostly in: - spacing density - shell scale - amount of atmosphere - page composition They should **not** differ in: - brand color logic - button language - border philosophy - type hierarchy - selection behavior --- ## 5. Canonical token architecture We should converge on a small token set that works everywhere. ### 5.1 Foundation color tokens Use Radix as the raw palette source, but not as the public API for product styling. Raw palette source: - Radix gray/slate/sage for neutrals - Radix red/amber/green/blue for semantic states ### 5.2 Semantic color tokens Canonical semantic token set: - `--ow-color-page` - `--ow-color-surface` - `--ow-color-surface-subtle` - `--ow-color-surface-sidebar` - `--ow-color-border` - `--ow-color-border-strong` - `--ow-color-text` - `--ow-color-text-muted` - `--ow-color-text-subtle` - `--ow-color-accent` - `--ow-color-accent-hover` - `--ow-color-hover` - `--ow-color-active` - `--ow-color-success` - `--ow-color-warning` - `--ow-color-danger` These should become the shared API across app and Cloud. ### 5.3 Current mapping from app tokens Existing app tokens already point in the right direction: - `--dls-app-bg` -> `--ow-color-page` - `--dls-surface` -> `--ow-color-surface` - `--dls-sidebar` -> `--ow-color-surface-sidebar` - `--dls-border` -> `--ow-color-border` - `--dls-text-primary` -> `--ow-color-text` - `--dls-text-secondary` -> `--ow-color-text-muted` - `--dls-accent` -> `--ow-color-accent` - `--dls-accent-hover` -> `--ow-color-accent-hover` We should migrate by aliasing first, not by breaking everything at once. --- ## 6. Typography system Typography should be systemized into roles, not ad hoc text sizes. ### Roles - **display** — rare marketing or hero usage - **headline** — page and section headers - **title** — card and object titles - **body** — default reading text - **meta** — labels, helper copy, secondary information - **micro** — pills, badges, tiny metadata ### Shared rules - one main sans family across product surfaces - medium weight does the majority of hierarchy work - muted text is the default support color - avoid large type jumps inside the app --- ## 7. Spacing system OpenWork should use a consistent spacing scale instead of one-off values. Recommended base scale: - 4 - 8 - 12 - 16 - 20 - 24 - 32 - 40 - 48 - 64 ### Usage guidance - micro control padding: 8–12 - row padding: 12–16 - card padding: 20–24 - major section padding: 32–48 - page rhythm: 48–64 on roomy surfaces, 24–32 in dense app surfaces --- ## 8. Radius system Canonical radius roles: - `--ow-radius-control` — small controls and rows - `--ow-radius-card` — cards and panels - `--ow-radius-shell` — sidebars, large grouped containers, modal shells - `--ow-radius-pill` — buttons, tabs, chips Suggested mapping: - control: 12px - card: 16px - shell: 24px–32px - pill: 9999px --- ## 9. Shadow system Shadow should be a named system with very few levels. - `--ow-shadow-none` - `--ow-shadow-control` - `--ow-shadow-card` - `--ow-shadow-shell` Default behavior: - app: mostly `none` or `control` - Cloud: mostly `none`, `control`, occasional `card` - landing: selective `card` or `shell` --- ## 10. Component primitive families We should explicitly define a small primitive set shared across product surfaces. ### 10.1 Action primitives - Primary button - Secondary button - Ghost button - Destructive button - Segmented pill / tab item ### 10.2 Structure primitives - Page shell - Sidebar shell - Card - Quiet card - Modal shell - Section divider ### 10.3 Input primitives - Text input - Textarea - Select - Checkbox/radio treatment - Inline field group ### 10.4 Navigation primitives - Sidebar row - List row - Topbar item - Breadcrumb / section tab ### 10.5 Feedback primitives - Status pill - Banner - Empty state - Toast --- ## 11. System-first implementation rules ### Rule 1: prefer semantic tokens over raw utility colors Prefer: - `bg-[var(--ow-color-surface)]` - `text-[var(--ow-color-text-muted)]` Over: - `bg-white` - `text-gray-500` Raw grays are still acceptable for temporary legacy usage, but new primitives should use semantic tokens. ### Rule 2: page code should not define new visual language Page files can compose primitives and choose layouts. They should not invent new button styles, new shadow rules, or new selection patterns. ### Rule 3: Radix stays underneath the system Radix is the palette source. OpenWork tokens are the product API. ### Rule 4: app and Cloud should share primitives even if frameworks differ Even when implementations differ, the primitive names and behaviors should match. Example: - `Button` in app - `Button` in den-web Both should resolve to the same token logic and visual rules. --- ## 12. Migration strategy Do not redesign everything at once. Use this sequence. ### Phase 1: lock the foundations 1. create canonical semantic tokens 2. alias current app tokens to the new token names 3. document primitive families and approved variants ### Phase 2: unify the most reused primitives Start with: 1. Button 2. Card 3. Input 4. Sidebar row 5. Modal shell These give the largest visual consistency gain. ### Phase 3: unify shell patterns Standardize: - page background - sidebar shell - panel/card shell - list row selection - headers and section spacing ### Phase 4: refactor high-traffic screens Prioritize: - workspace/session surfaces in `apps/app` - Cloud dashboard shells in `ee/apps/den-web` - share/package/connect flows in `apps/app` ### Phase 5: remove local style drift As primitives stabilize: - reduce repeated one-off class recipes - replace raw gray classes in repeated patterns - collapse duplicate card/button/input styles into primitives --- ## 13. Recommended initial source of truth files If we implement this system, the likely canonical files should be: - `DESIGN-LANGUAGE.md` — philosophy - `DESIGN-SYSTEM.md` — system rules and migration plan - `apps/app/src/app/index.css` — initial token host for app runtime - `apps/app/tailwind.config.ts` — Tailwind token exposure - `apps/app/src/app/components/button.tsx` — canonical action primitive start - `apps/app/src/app/components/card.tsx` — canonical surface primitive start - `apps/app/src/app/components/text-input.tsx` — canonical field primitive start Later, a shared package may make sense, but not before the token model is stable. --- ## 14. Recommended file plan for the next step The smallest safe implementation path is: ### Step A Introduce canonical `--ow-*` aliases in `apps/app/src/app/index.css` without removing `--dls-*` yet. ### Step B Refactor `Button`, `Card`, and `TextInput` to consume shared semantic tokens. ### Step C Use the Den dashboard shell as the reference for: - sidebar shell - row selection - neutral panel rhythm ### Step D Restyle one OpenWork app screen fully using the system to prove the direction. Recommended pilot screens: - `apps/app/src/app/pages/settings.tsx` - session/workspace sidebar surfaces - share workspace modal --- ## 15. What a successful system looks like We will know this is working when: 1. app, Cloud, and landing feel obviously from the same product family 2. a new screen can be built mostly from existing primitives 3. visual changes happen by adjusting tokens or primitives, not by editing many pages 4. selection, buttons, cards, and inputs behave consistently everywhere 5. raw color classes become uncommon outside truly local exceptions --- ## 16. Anti-goals This system should not: - introduce a trendy visual reboot disconnected from the current product - replace the OpenWork mood described in `DESIGN-LANGUAGE.md` - depend on a large new dependency just to manage styling - force a shared package too early - block incremental improvements until a perfect system exists The correct approach is a strong design system built through small, boring, compounding steps. --- ## 17. Immediate next recommendation If continuing from this doc, the best next change is: 1. add `--ow-*` semantic token aliases in `apps/app/src/app/index.css` 2. standardize `Button`, `Card`, and `TextInput` 3. then restyle one app shell to match the calmer Den dashboard direction That gives a real system foothold without a broad rewrite.