Tighten typography and XWorkmate layout defaults

This commit is contained in:
Haitao Pan 2026-03-12 19:23:24 +08:00
parent 3bdcc0b147
commit 17206d02f8
4 changed files with 129 additions and 28 deletions

View File

@ -0,0 +1,40 @@
# Skill: UI Typography Consistency
## When to Use
- Global typography tuning
- New page or component design
- Sidebar, form, dialog, and dashboard cleanup
- UI refresh work that should feel closer to ChatGPT Web reading density
## Defaults
- Base body and message text: `16px` with `line-height: 1.5`
- Sidebar, helper text, and secondary descriptions: `14px`
- Metadata, timestamps, and micro labels: `12px` to `13px`
- Primary page headings: `24px` to `28px`
- Text color: use deep gray such as `#0d0d0d`, not pure black
- Font stack: default to the project sans font and keep it consistent across all pages
## Steps
1. Start from the global tokens in [`/Users/shenlan/workspaces/cloud-neutral-toolkit/console.svc.plus/src/app/globals.css`](/Users/shenlan/workspaces/cloud-neutral-toolkit/console.svc.plus/src/app/globals.css).
2. Keep body copy at `16px` unless the surface is explicitly metadata-heavy.
3. Use `14px` for sidebars, helper text, chip labels, and supporting descriptions.
4. Reserve `12px` to `13px` for timestamps, disclaimers, and low-emphasis metadata.
5. Tighten layout density together with typography: if text gets smaller, reduce padding and corner radius proportionally.
6. Verify at least one dense workspace page and one marketing or document page after the change.
## Do
- Do keep line height readable at `1.5` for paragraphs, lists, inputs, and buttons.
- Do make typography decisions in the global design tokens first.
- Do keep container radius and spacing visually consistent with the text scale.
- Do prefer neutral grays for body text and muted text over saturated colors.
## Do Not
- Do not mix multiple body font sizes on the same page without a clear hierarchy.
- Do not use oversized rounded corners on dense workflow surfaces.
- Do not rely on one-off component overrides when the issue is global.
- Do not use pure black text for normal reading surfaces.

View File

@ -11,19 +11,19 @@
--app-shell-nav-offset: 5.5rem;
/* Light theme defaults */
--color-background: #f4f6fb;
--color-background-muted: #e7ecf6;
--color-background: #f7f7f8;
--color-background-muted: #ececef;
--color-surface: #ffffff;
--color-surface-elevated: rgba(255, 255, 255, 0.96);
--color-surface-translucent: rgba(255, 255, 255, 0.88);
--color-surface-muted: #f1f4fb;
--color-surface-hover: #f0f4ff;
--color-surface-border: #d6e0ff;
--color-surface-border-strong: #b4c5ff;
--color-text: #1e2e55;
--color-heading: #2e3a59;
--color-text-muted: #4a5672;
--color-text-subtle: #61708c;
--color-surface-translucent: rgba(255, 255, 255, 0.92);
--color-surface-muted: #f3f4f6;
--color-surface-hover: #f7f8fb;
--color-surface-border: #e5e7eb;
--color-surface-border-strong: #d1d5db;
--color-text: #0d0d0d;
--color-heading: #111827;
--color-text-muted: #4b5563;
--color-text-subtle: #6b7280;
--color-text-inverse: #f8fbff;
--color-primary: #3366ff;
--color-primary-hover: #4d7aff;
@ -45,31 +45,92 @@
--color-info: #3366ff;
--color-info-muted: #f0f4ff;
--color-info-foreground: #254edb;
--color-overlay: rgba(30, 46, 85, 0.45);
--color-overlay: rgba(17, 24, 39, 0.42);
--color-ring: #d6e0ff;
--color-focus: rgba(51, 102, 255, 0.35);
--color-divider: rgba(15, 23, 42, 0.08);
--color-divider: rgba(17, 24, 39, 0.08);
--color-badge-surface: #e5e7eb;
--color-badge-muted: #f3f4f6;
--color-badge-foreground: #1f2937;
--gradient-app-from: #f5f8ff;
--gradient-app-via: #eef3ff;
--gradient-app-to: #f4f9ff;
--gradient-app-from: #fafafa;
--gradient-app-via: #f4f5f7;
--gradient-app-to: #f7f7f8;
--gradient-primary-from: #3366ff;
--gradient-primary-to: #254edb;
--shadow-sm: 0 1px 3px rgba(30, 46, 85, 0.08), 0 1px 2px rgba(30, 46, 85, 0.04);
--shadow-md: 0 12px 32px rgba(30, 46, 85, 0.12);
--shadow-sm: 0 1px 2px rgba(17, 24, 39, 0.06), 0 1px 3px rgba(17, 24, 39, 0.04);
--shadow-md: 0 10px 24px rgba(17, 24, 39, 0.08);
--radius-lg: 1rem;
--radius-xl: 1.5rem;
--radius-lg: 0.875rem;
--radius-xl: 1.125rem;
--radius-pill: 999px;
}
html {
font-family: var(--font-geist-sans);
font-size: 16px;
}
body {
font-family: var(--font-geist-sans);
font-size: 1rem;
line-height: 1.5;
background-color: var(--color-background);
color: var(--color-text);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
transition: background-color 150ms ease, color 150ms ease;
}
button,
input,
textarea,
select {
font: inherit;
}
p,
li,
label,
input,
textarea,
select,
button {
line-height: 1.5;
}
@layer base {
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--color-heading);
font-weight: 600;
line-height: 1.25;
letter-spacing: -0.02em;
}
h1 {
font-size: 1.75rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
small {
font-size: 0.8125rem;
line-height: 1.4;
}
::selection {
background: rgba(51, 102, 255, 0.18);
}
}

View File

@ -13,7 +13,7 @@ export default function XWorkmatePage() {
const defaults = getConsoleIntegrationDefaults();
return (
<div className="h-[calc(100vh-var(--app-shell-nav-offset))] w-full p-4">
<div className="h-[calc(100vh-var(--app-shell-nav-offset))] w-full">
<Suspense fallback={<XWorkmateLoading />}>
<XWorkmateWorkspacePage defaults={defaults} />
</Suspense>

View File

@ -1893,7 +1893,7 @@ export function XWorkmateWorkspacePage({
}
return (
<div className="relative h-full min-h-0 overflow-hidden rounded-[var(--radius-xl)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] shadow-[var(--shadow-md)]">
<div className="relative h-full min-h-0 overflow-hidden rounded-[var(--radius-lg)] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] shadow-[var(--shadow-md)]">
{sidebarState === "hidden" ? (
<div className="pointer-events-none absolute inset-y-0 left-0 z-10 flex items-start p-4">
<button
@ -1911,7 +1911,7 @@ export function XWorkmateWorkspacePage({
{sidebarState !== "hidden" ? (
<aside
className={cn(
"flex h-full shrink-0 flex-col border-r border-[color:var(--color-surface-border)] bg-[var(--color-background)]/90 px-2.5 py-3 backdrop-blur transition-[width] duration-200",
"flex h-full shrink-0 flex-col border-r border-[color:var(--color-surface-border)] bg-[var(--color-background)]/90 px-2 py-2.5 backdrop-blur transition-[width] duration-200",
collapsed ? "w-[88px]" : "w-[292px]",
)}
>
@ -2027,8 +2027,8 @@ export function XWorkmateWorkspacePage({
<section className="flex min-w-0 flex-1 flex-col">
<header
className={cn(
"border-b border-[color:var(--color-surface-border)] px-4",
activeSection === "assistant" ? "py-2.5" : "py-4",
"border-b border-[color:var(--color-surface-border)] px-3.5",
activeSection === "assistant" ? "py-2" : "py-3",
)}
>
<div
@ -2109,7 +2109,7 @@ export function XWorkmateWorkspacePage({
</div>
{activeDefinition.tabs.length > 0 ? (
<div className="mt-4 flex flex-wrap gap-2">
<div className="mt-3 flex flex-wrap gap-2">
{activeDefinition.tabs.map((tab) => (
<button
key={tab.key}
@ -2131,11 +2131,11 @@ export function XWorkmateWorkspacePage({
<div
className={cn(
"min-h-0 flex-1 p-4",
"min-h-0 flex-1 p-3",
activeSection === "assistant" ? "overflow-hidden" : "overflow-auto",
)}
>
<div className={cn("space-y-4", activeSection === "assistant" ? "h-full min-h-0" : "")}>
<div className={cn("space-y-3", activeSection === "assistant" ? "h-full min-h-0" : "")}>
{renderSectionOverview()}
{renderMainContent()}
</div>