mirror of
https://github.com/different-ai/openwork
synced 2026-04-25 17:15:34 +02:00
475 lines
11 KiB
Markdown
475 lines
11 KiB
Markdown
# 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.
|