Skip to content
9 changes: 0 additions & 9 deletions packages/ui/script/colors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,7 @@
--error-text: #9a2818;
--diff-add: rgba(45, 157, 90, 0.1);
--diff-del: rgba(210, 74, 58, 0.1);
--shadow-raised: 0 6px 24px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-floating: 0 8px 24px rgba(0, 0, 0, 0.09), 0 1px 3px rgba(0, 0, 0, 0.07);
--shadow-modal: 0 16px 48px rgba(0, 0, 0, 0.11), 0 4px 12px rgba(0, 0, 0, 0.07);
--ring-base: 0 0 0 1px rgba(11, 6, 0, 0.18);
--shadow-xs-border-focus: 0 0 0 1px var(--brand-primary), 0 0 0 3px rgba(255, 89, 16, 0.2);
--shadow-xs-border-select: 0 0 0 1px var(--brand-primary);
--shadow-xs-border-hover: 0 0 0 1px rgba(11, 6, 0, 0.28);
--shadow-xs-border-critical-base: 0 0 0 1px var(--error);
--shadow-xs-border-critical-focus: 0 0 0 1px var(--error), 0 0 0 3px rgba(210, 74, 58, 0.2);
--icon-agent-plan-base: #a753ae;
--icon-agent-docs-base: #fcb239;
--icon-agent-ask-base: #2090f5;
Expand Down Expand Up @@ -96,7 +88,6 @@
--syntax-diff-add: #3a8437;
--syntax-diff-delete: #ca2d17;
--syntax-diff-unknown: #ff0000;
--text-mix-blend-mode: multiply;
--button-brand-base: #ff5910;
--button-brand-hover: #e04e0e;
--icon-success-base: #2d9d5a;
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/checkbox.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
[data-slot="checkbox-checkbox-description"] {
color: var(--fg-base);
font-family: var(--font-family-sans);
font-size: 12px;
font-size: var(--font-size-x-small);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-normal);
letter-spacing: var(--letter-spacing-normal);
Expand All @@ -77,7 +77,7 @@
[data-slot="checkbox-checkbox-error"] {
color: var(--error-text);
font-family: var(--font-family-sans);
font-size: 12px;
font-size: var(--font-size-x-small);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-normal);
letter-spacing: var(--letter-spacing-normal);
Expand Down
27 changes: 8 additions & 19 deletions packages/ui/src/components/icon.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,18 @@
align-items: center;
justify-content: center;
flex-shrink: 0;
/* resize: both; */
aspect-ratio: 1/1;
color: var(--icon-base);

&[data-size="small"] {
width: 16px;
height: 16px;
}

&[data-size="normal"] {
width: 20px;
height: 20px;
}
&[data-color="strong"] { color: var(--icon-strong); }
&[data-color="weak"] { color: var(--icon-weak); }
&[data-color="disabled"] { color: var(--icon-disabled); }

&[data-size="medium"] {
width: 24px;
height: 24px;
}

&[data-size="large"] {
width: 24px;
height: 24px;
}
/* size="small" is the standard chrome size (16px); normal/medium/large for special contexts */
&[data-size="small"] { width: 16px; height: 16px; }
&[data-size="normal"] { width: 20px; height: 20px; }
&[data-size="medium"] { width: 24px; height: 24px; }
&[data-size="large"] { width: 24px; height: 24px; }

[data-slot="icon-svg"] {
width: 100%;
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/src/components/keybind.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
background: var(--surface-base);
box-shadow: var(--shadow-xxs-border);

/* text-13-regular */
font-family: var(--font-family-sans);
font-size: 12px;
font-size: var(--font-size-x-small);
font-weight: var(--font-weight-regular);
line-height: 1;
color: var(--fg-weak);
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/components/message-part.css
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@
[data-component="markdown"] {
margin-top: 16px;
font-style: normal;
font-size: 13px;
font-size: var(--font-size-small);
color: var(--fg-weak);

strong,
Expand Down Expand Up @@ -407,7 +407,7 @@
[data-slot="bash-pre"] code {
font-family: var(--font-family-mono);
font-feature-settings: var(--font-family-mono--font-feature-settings);
font-size: 13px;
font-size: var(--font-size-small);
line-height: var(--line-height-large);
white-space: pre-wrap;
overflow-wrap: anywhere;
Expand Down Expand Up @@ -1154,7 +1154,7 @@
display: flex;
flex-direction: column;
gap: 2px;
font-size: 13px;
font-size: var(--font-size-small);

[data-slot="question-text"] {
color: var(--fg-weak);
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/radio-group.css
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
--radio-group-padding: 2px;

[data-slot="radio-group-item-label"] {
font-size: 12px;
font-size: var(--font-size-x-small);
}

[data-slot="radio-group-item-control"] {
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/switch.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
[data-slot="switch-description"] {
color: var(--fg-base);
font-family: var(--font-family-sans);
font-size: 12px;
font-size: var(--font-size-x-small);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-normal);
letter-spacing: var(--letter-spacing-normal);
Expand All @@ -75,7 +75,7 @@
[data-slot="switch-error"] {
color: var(--error-text);
font-family: var(--font-family-sans);
font-size: 12px;
font-size: var(--font-size-x-small);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-normal);
letter-spacing: var(--letter-spacing-normal);
Expand Down
9 changes: 0 additions & 9 deletions packages/ui/src/styles/tailwind/colors.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,7 @@
--color-error-text: var(--error-text);
--color-diff-add: var(--diff-add);
--color-diff-del: var(--diff-del);
--color-shadow-raised: var(--shadow-raised);
--color-shadow-floating: var(--shadow-floating);
--color-shadow-modal: var(--shadow-modal);
--color-ring-base: var(--ring-base);
--color-shadow-xs-border-focus: var(--shadow-xs-border-focus);
--color-shadow-xs-border-select: var(--shadow-xs-border-select);
--color-shadow-xs-border-hover: var(--shadow-xs-border-hover);
--color-shadow-xs-border-critical-base: var(--shadow-xs-border-critical-base);
--color-shadow-xs-border-critical-focus: var(--shadow-xs-border-critical-focus);
--color-icon-agent-plan-base: var(--icon-agent-plan-base);
--color-icon-agent-docs-base: var(--icon-agent-docs-base);
--color-icon-agent-ask-base: var(--icon-agent-ask-base);
Expand Down Expand Up @@ -101,7 +93,6 @@
--color-syntax-diff-add: var(--syntax-diff-add);
--color-syntax-diff-delete: var(--syntax-diff-delete);
--color-syntax-diff-unknown: var(--syntax-diff-unknown);
--color-text-mix-blend-mode: var(--text-mix-blend-mode);
--color-button-brand-base: var(--button-brand-base);
--color-button-brand-hover: var(--button-brand-hover);
--color-icon-success-base: var(--icon-success-base);
Expand Down
8 changes: 3 additions & 5 deletions packages/ui/src/styles/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
--surface-raised: #ffffff;
--surface-sunken: #faf9f7; /* light alias → bg-cream; no third neutral tier (L9) */
--sidebar: #faf9f7;
--surface-base-active: rgba(0, 0, 0, 0.051);

/* ────────── Light ink · STANDARDS L11 (4 tiers) ────────── */
--fg-strong: #1a1613;
Expand Down Expand Up @@ -219,9 +220,6 @@
0 6.38px 14.177px 0 rgba(0, 0, 0, 0.05),
0 3.127px 6.95px 0 rgba(0, 0, 0, 0.06),
0 1.237px 2.748px 0 rgba(0, 0, 0, 0.09);
/* Active-press overlay on base surface */
--surface-base-active: rgba(0, 0, 0, 0.051);

/* Text mix blend mode utility — controls antialiasing on light surfaces */
--text-mix-blend-mode: multiply;

Expand Down Expand Up @@ -285,6 +283,7 @@
/* Brand-tinted interactive · dark (warm orange muted) */
--surface-interactive-base: #2b1f19;
--surface-interactive-hover: #362619;
--surface-base-active: rgba(255, 255, 255, 0.059);

/* Shadow · L18: dark elevation via L10 surfaces; drop shadows forbidden */
--shadow-raised: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
Expand Down Expand Up @@ -372,7 +371,6 @@
--syntax-diff-delete: #faa494;
--syntax-diff-unknown: #ff0000;

--surface-base-active: rgba(255, 255, 255, 0.059);
--shadow-xxs-border: 0 0 0 0.5px var(--border-base);
--shadow-xs-border-base:
0 0 0 1px var(--border-base),
Expand Down Expand Up @@ -414,6 +412,7 @@

--surface-interactive-base: #2b1f19;
--surface-interactive-hover: #362619;
--surface-base-active: rgba(255, 255, 255, 0.059);

--shadow-raised: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
--shadow-floating: 0 1px 0 rgba(255, 255, 255, 0.05) inset;
Expand Down Expand Up @@ -500,7 +499,6 @@
--syntax-diff-delete: #faa494;
--syntax-diff-unknown: #ff0000;

--surface-base-active: rgba(255, 255, 255, 0.059);
--shadow-xxs-border: 0 0 0 0.5px var(--border-base);
--shadow-xs-border-base:
0 0 0 1px var(--border-base),
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/theme/themes/pawwork.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"sidebar": "#faf9f7",
"surface-interactive-base": "#fdf6f0",
"surface-interactive-hover": "#fbece0",
"surface-base-active": "rgba(0, 0, 0, 0.051)",
"fg-strong": "#1a1613",
"fg-base": "#4a4138",
"fg-weak": "#8c817a",
Expand Down Expand Up @@ -75,6 +76,7 @@
"sidebar": "#15120f",
"surface-interactive-base": "#2b1f19",
"surface-interactive-hover": "#362619",
"surface-base-active": "rgba(255, 255, 255, 0.059)",
"fg-strong": "#f3ede4",
"fg-base": "#c5beb4",
"fg-weak": "#8a8278",
Expand Down
70 changes: 70 additions & 0 deletions packages/ui/test/colors-generation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* colors-generation.test.ts
*
* Verifies that tailwind/colors.css is in sync with script/colors.txt.
* For every --X token defined in colors.txt there must be a corresponding
* --color-X: var(--X) entry in colors.css, and vice versa (no orphan entries).
*
* This catches stale artifacts: if colors.txt is updated but colors.css is not
* regenerated, a Tailwind utility like `bg-surface-base-active` would silently
* resolve to the wrong value.
*/

import { expect, test } from "bun:test"
import { readFileSync } from "fs"
import { join } from "path"

const ROOT = join(import.meta.dirname, "..")
const COLORS_TXT = readFileSync(join(ROOT, "script/colors.txt"), "utf-8")
const COLORS_CSS = readFileSync(join(ROOT, "src/styles/tailwind/colors.css"), "utf-8")

/** Extract token names from colors.txt (e.g. "--brand-primary: ..." → "brand-primary") */
function parseTxtTokens(txt: string): string[] {
return txt
.split("\n")
.filter((l) => l.trim().startsWith("--"))
.map((l) => l.trim().split(":")[0].trim().substring(2))
.filter(Boolean)
}

/** Extract token names from colors.css @theme block (e.g. "--color-brand-primary: var(--brand-primary)" → "brand-primary") */
function parseCssTokens(css: string): string[] {
const entries: string[] = []
const re = /--color-([^\s:]+)\s*:\s*var\(--\1\)/g
let m: RegExpExecArray | null
while ((m = re.exec(css)) !== null) {
entries.push(m[1])
}
return entries
}
Comment thread
Astro-Han marked this conversation as resolved.

const txtTokens = parseTxtTokens(COLORS_TXT)
const cssTokens = parseCssTokens(COLORS_CSS)

test("colors.txt has no duplicate token names", () => {
const dupes = txtTokens.filter((t, i) => txtTokens.indexOf(t) !== i)
expect(dupes, `duplicate tokens in colors.txt: ${dupes.join(", ")}`).toEqual([])
})

test("colors.css has no duplicate token entries", () => {
const dupes = cssTokens.filter((t, i) => cssTokens.indexOf(t) !== i)
expect(dupes, `duplicate entries in colors.css: ${dupes.join(", ")}`).toEqual([])
})

test("every colors.txt token has a --color-X: var(--X) entry in colors.css", () => {
const cssSet = new Set(cssTokens)
const missing = txtTokens.filter((t) => !cssSet.has(t))
expect(
missing,
`tokens in colors.txt missing from colors.css: ${missing.join(", ")}`,
).toEqual([])
})

test("every colors.css entry has a corresponding token in colors.txt", () => {
const txtSet = new Set(txtTokens)
const orphan = cssTokens.filter((t) => !txtSet.has(t))
expect(
orphan,
`tokens in colors.css not present in colors.txt: ${orphan.join(", ")}`,
).toEqual([])
})
Loading