mirror of
https://github.com/different-ai/openwork
synced 2026-04-25 17:15:34 +02:00
new design
This commit is contained in:
474
DESIGN-SYSTEM.md
Normal file
474
DESIGN-SYSTEM.md
Normal file
@@ -0,0 +1,474 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user