diff --git a/packages/app/AGENTS.md b/packages/app/AGENTS.md
index 2af9fb558b8..41cdb347daf 100644
--- a/packages/app/AGENTS.md
+++ b/packages/app/AGENTS.md
@@ -19,6 +19,12 @@
- Always prefer `createStore` over multiple `createSignal` calls
+## Typography
+
+- Use `--line-height-compact` (`16px`) for `13px` compact UI text and `--line-height-base` (`20px`) for body text.
+- Do not use `leading-none`, `line-height: 1`, or a `13px` line height for normal text. Inter descenders clip inside truncation and overflow containers.
+- Keep control and row heights explicit. Fix font metrics directly rather than using transforms, negative margins, or clip-padding compensation.
+
## Localization
- NEVER hardcode user-visible English strings in production code. ALWAYS use an i18n key for visible copy, placeholders, accessible labels, tooltips, menus, dialogs, toasts, empty states, and displayed errors.
diff --git a/packages/app/e2e/regression/session-timeline-notices.spec.ts b/packages/app/e2e/regression/session-timeline-notices.spec.ts
index ae1e9ffdc3d..bc420ff26c0 100644
--- a/packages/app/e2e/regression/session-timeline-notices.spec.ts
+++ b/packages/app/e2e/regression/session-timeline-notices.spec.ts
@@ -86,10 +86,12 @@ test("shows a delegating row while subagent input streams", async ({ page }) =>
const delegating = page.locator('[data-component="task-tool-delegating"]')
await expect(delegating).toBeVisible()
- await expect(delegating.locator('[data-component="text-shimmer"]')).toHaveAttribute(
+ const shimmer = delegating.locator('[data-component="text-shimmer"]')
+ await expect(shimmer).toHaveAttribute(
"aria-label",
"Delegating agent...",
)
+ await expect(shimmer).toHaveCSS("line-height", "16px")
const icon = delegating.locator('[data-slot="icon-svg"]')
await expect(icon.locator('use[href="#opencode-v2-icon-subagent"]')).toBeVisible()
await expect(icon).toHaveCSS("color", "rgb(174, 174, 174)")
@@ -127,11 +129,11 @@ test("renders the moved location notice in its compact timeline style", async ({
await expect(notice).toHaveCSS("padding-bottom", "4px")
await expect(label).toHaveCSS("font-size", "13px")
await expect(label).toHaveCSS("font-weight", "530")
- await expect(label).toHaveCSS("line-height", "13px")
+ await expect(label).toHaveCSS("line-height", "16px")
await expect(label).toHaveCSS("color", "rgb(128, 128, 128)")
await expect(value).toHaveCSS("font-size", "13px")
await expect(value).toHaveCSS("font-weight", "440")
- await expect(value).toHaveCSS("line-height", "13px")
+ await expect(value).toHaveCSS("line-height", "16px")
await expect(value).toHaveCSS("color", "rgb(128, 128, 128)")
await expect(value).toHaveCSS("text-overflow", "ellipsis")
await expect(value).toHaveCSS("white-space", "nowrap")
diff --git a/packages/app/e2e/regression/session-timeline-tool-projection.spec.ts b/packages/app/e2e/regression/session-timeline-tool-projection.spec.ts
index ec745c44b82..ab8af77c726 100644
--- a/packages/app/e2e/regression/session-timeline-tool-projection.spec.ts
+++ b/packages/app/e2e/regression/session-timeline-tool-projection.spec.ts
@@ -121,19 +121,20 @@ test("labels skill tools from IDs and result metadata", async ({ page }) => {
messages: [
userMessage(),
assistantMessage([
- toolPart(pending, "skill", "running", { id: "sample-skill" }),
+ toolPart(pending, "skill", "running", { id: "frontend-design" }),
toolPart(completed, "skill", "completed", { id: "opencode" }, { metadata: { name: "OpenCode" } }),
]),
],
})
for (const [id, name] of [
- [pending, "sample-skill"],
+ [pending, "frontend-design"],
[completed, "OpenCode"],
] as const) {
const skill = page.locator(`[data-timeline-part-id="${id}"]`)
const loaded = skill.locator('[data-component="tool-loaded-item"]')
await expect(loaded).toHaveAttribute("aria-label", `Loaded ${name} skill`)
+ await expect(loaded).toHaveCSS("line-height", "16px")
await expect(loaded.locator('[data-slot="tool-loaded-label"]')).toHaveText("Loaded")
await expect(loaded.locator('[data-slot="tool-loaded-kind"]')).toHaveText("skill")
await expect(loaded.locator('[data-component="text-shimmer"]')).toHaveAttribute("aria-label", name)
diff --git a/packages/app/src/home/sessions/view.tsx b/packages/app/src/home/sessions/view.tsx
index a4515e31e87..60a4c8f4320 100644
--- a/packages/app/src/home/sessions/view.tsx
+++ b/packages/app/src/home/sessions/view.tsx
@@ -509,7 +509,7 @@ function HomeSessionsEmpty(props: { onNewSession?: () => void; language: ReturnT
diff --git a/packages/app/src/new-session/view.tsx b/packages/app/src/new-session/view.tsx
index a639f9d26b2..fe1e3b374d9 100644
--- a/packages/app/src/new-session/view.tsx
+++ b/packages/app/src/new-session/view.tsx
@@ -139,7 +139,7 @@ function ProviderTip() {
>