English | 中文
[The token system has been replaced—the table in § 1 is retained only for historical reference] The
--bg-*/--text-*/--accenttoken families documented here and their host package,packages/client/web-ui, were retired during the plugin refactor. The sole current token source ispackages/client/ui-theme/src/styles/, which defines the--dsw-*system (a static color scale plus a semantic alias layer, with dark-mode overrides underbody[data-ds-dark-theme]). The sheet is authoritative and component audits use it as the baseline. The following rules remain in force: CSS Modules + clsx, no component library, no Tailwind, no hard-coded color values in components, pair every font size with a line height, use spacing in multiples of 4, and do not put monospace last in the code font stack.Status: formerly a “living document” that evolved with
packages/client/web-ui. The visual baseline came from empirical study of the deepseekchat frontend repository. The web-styling-system RFC owns the framework decisions and engineering constraints; this document does not repeat their rationale.
All tokens live in packages/client/web-ui/src/style/global.css: :root contains the light-theme values, and the [data-theme='dark'] block overrides the same variables (columns that were not complete are marked as placeholders). Component CSS references tokens only and contains no literal color values.
| token | Light value | Dark value (placeholder) | Purpose |
|---|---|---|---|
--bg-base |
#ffffff |
#151517 |
Page background |
--bg-layer |
#ffffff |
#232324 |
Floating layer/panel |
--bg-sidebar |
#f9fafb |
#1b1b1c |
Sidebar background |
--text-primary |
#0f1115 |
#f9fafb |
Body text |
--text-secondary |
#61666b |
#cfd3d6 |
Secondary text |
--text-tertiary |
#81858c |
#adb2b8 |
Supporting/descriptive text |
--border-l1 |
rgba(0,0,0,.04) |
rgba(255,255,255,.06) |
Subtle separator (sidebar right edge) |
--border-l2 |
rgba(0,0,0,.1) |
rgba(255,255,255,.12) |
Standard border |
--hover-bg |
rgba(38,49,72,.06) |
rgba(255,255,255,.08) |
Hover-state background |
--active-bg |
rgba(38,49,72,.1) |
rgba(255,255,255,.14) |
Pressed/active-state background |
--accent |
#3964fe |
#5686fe |
Brand blue (deepseek-500; one step lighter in dark mode) |
--accent-soft |
#edf3fe |
#28313f |
Soft brand background (emphasis blocks) |
--accent-item |
#e4edfd |
#35363a |
Selected sidebar-item background |
--bubble-bg |
#edf3fe |
#2c2c2e |
User-message bubble background |
--ok / --error / --warn |
#22c55e / #ec1313 / #f59e0b |
Same values | Semantic status colors |
--text-on-solid |
#ffffff |
Same value | Text on solid backgrounds (accent/error badges, etc.) |
--ok-soft / --error-soft |
#e6faed / #fee2e2 |
#233c2c / #570c0c |
Soft semantic status backgrounds (badges); green-100/red-100, with the 900 shades in dark mode |
--color-frame-mux / --color-frame-host |
#8250df / #0969da |
Same values | RPC debugger direction colors (project-specific, not part of the baseline) |
--frame-mux-soft / --frame-host-soft |
rgba(130,80,223,.1) / rgba(9,105,218,.1) |
Same colors at .24 |
Soft direction-color backgrounds (badges) |
--scroll-color / --scroll-color-hover |
rgba(0,0,0,.08) / .15 |
rgba(255,255,255,.15) / .24 |
Scrollbar colors (for .scrollable only) |
| token | Value | Description |
|---|---|---|
--font-ui |
Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif |
Body-text stack |
--font-mono |
Menlo, Monaco, Consolas, 'JetBrains Mono', 'Courier New', sans-serif |
Code stack; do not put monospace last (prevents SimSun fallback for Chinese on Windows) |
--fw-strong |
600 |
Unified bold weight |
--ease |
cubic-bezier(.4,0,.2,1) |
Sole easing curve |
--dur / --dur-fast / --dur-slow |
.2s / .1s / .3s |
Three transition durations |
--radius-s / --radius-m / --radius-l / --radius-bubble / --radius-xl |
8px / 12px / 16px / 22px / 24px |
Semantic radius steps: small controls / list items and blocks inside panels / floating layers / bubbles / input cards (same as the baseline inputWrapper); use 999px directly for pills |
--shadow-panel |
0 0 1px rgba(0,0,0,.2), 0 0 4px rgba(0,0,0,.02), 0 12px 32px rgba(0,0,0,.08) |
Floating-layer shadow (baseline lv3) |
--shadow-float |
0 0 1px rgba(0,0,0,.24), 0 4px 12px rgba(0,0,0,.06), 0 16px 48px rgba(0,0,0,.16) |
Strong floating panel (enhanced lv3, such as the RPC debugger overlay) |
--shadow-card |
0 4px 10px rgba(0,0,0,.02), 0 2px 4px rgba(0,0,0,.04); dark value none |
Subtle input-card shadow (baseline: borders plus a subtle shadow distinguish same-color light surfaces; a lighter background distinguishes dark surfaces, with the shadow disabled) |
Font sizes and spacing are not tokenized (matching the baseline repository's decision): components specify font sizes in px and always pair them with line heights. Common pairs are 16/24 (bubbles), 14/22 (UI default), and 12/18 (supporting text); spacing uses multiples of 4.
260px + 1px right border (--border-l1); background --bg-sidebar.40px, radius --radius-m, font size 14px; hover background --hover-bg or a sidebar-specific gray; selected items use --accent-item without changing text color.--text-tertiary / sticky at the top (using the sidebar background to cover scrolling content).max-width: 840px, reduced to 712px below 1024px.--bubble-bg, radius --radius-bubble, padding 10px 16px, font size 16px/24px, and max-width: calc(100% - 88px); assistant messages are a plain document flow without a background.opacity: 0 by default; fades in when its parent is hovered or contains focus (--dur + --ease).--radius-xl, border --border-l2, background --bg-base, shadow --shadow-card; two internal vertical sections = textarea (16px/24px, minimum 2 lines, maximum 14 lines = 336px, auto-growing through a mirror div) + action row (a 34px primary round button nested at bottom right); focus does not change the border or shadow (matching the baseline).--accent background with a white ↑ “Send” arrow; while running it changes in place to an accent ■ “Stop” icon on --accent-soft (the same color family, not a warning, and not red). Input is locked while running (decision 3, replacing the earlier hover-menu design): the textarea is disabled (gray, with draft content still visible), there is no queue/interjection menu, and Stop is the only action. When the turn ends, input unlocks and regains focus. Enter sends; Ctrl/Meta+Enter inserts a newline (the keyboard path is disabled with the locked input while running).scrollbar-gutter: stable so they do not consume layout space (always use .scrollable; see § 3.9).| Symbol | Quadrant | Badge colors |
|---|---|---|
↑ |
client-request (unary outbound) | --accent / --accent-soft |
↓ |
server-response (unary response) | ok --ok/--ok-soft, error --error/--error-soft |
⇟ |
server-request (SSE frame push) | mux --color-frame-mux/--frame-mux-soft, host --color-frame-host/--frame-host-soft |
⇞ |
client-response (SSE-side response) | --accent/--accent-soft at reduced opacity |
[data-theme] selectors; dark-mode differences belong only in the global.css token table. If a theme must change a non-token value such as a gradient endpoint, define a local CSS variable in the component and have the theme block override only that variable (a variable bridge)..active .show) and attach them with clsx: clsx(styles.x, cond && styles.active, className).className and merge it into the root element.composes; share through tokens and extracted components.:global only to pierce third-party or cross-package class names; do not use it to define new global classes.var(--dur*) var(--ease) and transition only opacity / transform / background color / shadow. Wrap hover-only reveal elements in @media (hover: hover)..scrollable utility class from global.css to every scroll container; do not write ::-webkit-scrollbar inside components.style={{'--x': v}}), while rules remain in CSS; do not assemble style objects in TSX to branch by theme or state.--text-primary/secondary/tertiary levels for gray text; do not add another gray.src/style/global.css always uses this section order: ① token table (:root + [data-theme='dark']), ② global foundations (box-sizing, body, button reset), ③ global utility classes (.scrollable, etc.; keep the total in single digits).*.module.css beside the component with the same name; use one module file per component.css-modules.d.ts wildcard declaration. Reassess introducing tcm to generate exact .css.d.ts files only after the component count exceeds 20.-- variable that has not been added to the table (except for component-local variable bridges).[data-theme='dark'] overrides every placeholder column in § 1, compare the RPC panel, sidebar, and conversation stream manually or with screenshots. Acceptance requires all three to match and no component-level theme selector to remain.| Date | Deviation | Rationale |
|---|---|---|
| (none) |