Files
learnharness/app/globals.css
T
amosblomqvist 5e6d5f124c Typography round 2 (owner): lh-input (caret at the font-metric floor) + 14px session titles
- lh-input: lh-prose's 1.5 reading leading made typing carets tower
  (~25px box on 16.5px text, ~2.1× the glyphs — the caret fills the line
  box in Chromium). Inputs are not paragraphs: 1.25 leading drops the
  caret to ~21px, the font-metric floor (as close to the glyphs as
  Chromium will draw). Swapped on every typing surface — composer
  (chat + home), the thinking field, answer + editable-Other inputs, the
  model search; rendered prose (message.tsx container, option
  descriptions, chat.tsx pre-wrap) keeps lh-prose 1.5. Same family and
  size as prose; only leading differs.
- Session titles: 14px (text-base) — Source Sans at 12.5px read as
  small as the 11px mono meta under them; the title now holds its rank.
  Live: title "Source Sans 3" 14px vs meta "JetBrains Mono" 11px, row
  52px; composer line box 20.625px (was 24.75px).

Gates: typecheck, lint, 469/469.
2026-09-17 13:15:35 +03:00

295 lines
9.0 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@import "tailwindcss";
@custom-variant dark (&:is(.dark *));
/*
* THE SKIN — wave 5. Design tokens, the single place to restyle the app.
*
* Mathematical soundness, per the owner's brief:
* - Tailwind's built-in 0.25rem (4px) spacing scale IS the rhythm — every
* padding/margin/gap in the app uses whole units of a 4px base.
* - The type scale below is a declared modular ladder (each --text-* token
* carries its own line-height); components never invent pixel sizes.
* - Radii ladder is 4 / 8 / 12 / 16px multiples of the 4px base.
* - Emphasis colors live in tokens (--lh-ink-strong / --lh-ink-italic) so a
* future settings UI reassigns them (configurable per the owner).
*/
/* Palette (owner's hexes): main #141414, raised #1C1C1C, edges #434343.
Mono chrome = JetBrains Mono; prose = Source Sans 3; titles = CMU Serif. */
:root {
--radius: 8px;
--background: #141414;
--foreground: #e6e6e6;
--card: #1c1c1c;
--card-foreground: #e6e6e6;
--popover: #1c1c1c;
--popover-foreground: #e6e6e6;
--primary: #e6e6e6;
--primary-foreground: #141414;
--secondary: #1c1c1c;
--secondary-foreground: #e6e6e6;
--muted: #1c1c1c;
--muted-foreground: #8d8d8d;
--accent: #2a2a2a;
--accent-foreground: #e6e6e6;
--destructive: oklch(0.704 0.191 22.216);
--border: #434343;
--input: #434343;
--ring: #8d8d8d;
/* Emphasis coloring in teaching prose — ready for the settings UI. */
--lh-ink-strong: #c96b4a;
--lh-ink-italic: #6fa9d6;
--lh-prose-size: 16.5px;
--lh-prose-leading: 1.5;
}
/* The app is always dark (<html class="dark">); mirror the same values so
the block stays coherent under Tailwind's variant. */
.dark {
--background: #141414;
--foreground: #e6e6e6;
--card: #1c1c1c;
--card-foreground: #e6e6e6;
--popover: #1c1c1c;
--popover-foreground: #e6e6e6;
--primary: #e6e6e6;
--primary-foreground: #141414;
--secondary: #1c1c1c;
--secondary-foreground: #e6e6e6;
--muted: #1c1c1c;
--muted-foreground: #8d8d8d;
--accent: #2a2a2a;
--accent-foreground: #e6e6e6;
--destructive: oklch(0.704 0.191 22.216);
--border: #434343;
--input: #434343;
--ring: #8d8d8d;
--lh-ink-strong: #c96b4a;
--lh-ink-italic: #6fa9d6;
--lh-prose-size: 16.5px;
--lh-prose-leading: 1.5;
}
/* CMU Serif bold (titles) — bundled local file, no CDN dependency. */
@font-face {
font-family: "lh-cmu";
src: url("/fonts/cmunbx.ttf") format("truetype");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@theme inline {
/* Fonts: chrome = JetBrains Mono, prose = Source Sans 3, titles = CMU
Serif bold. The JetBrains/SourceSans variables attach in app/layout.tsx.
(owner, typography round) The Tailwind defaults are ALIASED to the app
fonts: `font-mono`/`font-sans` utilities would otherwise fall to the
SYSTEM stacks — a fourth font the app never chose — and streamdown's
code-block chrome + mermaid-error + demo-card all use them. */
--font-ui: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace;
--font-prose: var(--font-sans3), ui-sans-serif, system-ui, sans-serif;
--font-title: "lh-cmu", serif;
--font-mono: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace;
--font-sans: var(--font-sans3), ui-sans-serif, system-ui, sans-serif;
/* The modular type ladder — each size carries its own line-height.
Component sizes must pull from this ladder. */
--text-xxs: 10px;
--text-xxs--line-height: 1.4;
--text-xs: 11px;
--text-xs--line-height: 1.4;
--text-sm: 12.5px;
--text-sm--line-height: 1.4;
--text-base: 14px;
--text-base--line-height: 1.45;
--text-lg: 16px;
--text-lg--line-height: 1.45;
--text-xl: 20px;
--text-xl--line-height: 1.4;
--text-2xl: 25px;
--text-2xl--line-height: 1.3;
--text-3xl: 32px;
--text-3xl--line-height: 1.25;
/* Radii ladder — 4/8/12/16 multiples of the 4px base. */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius: 8px;
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
}
* {
border-color: var(--color-border);
}
body {
background: var(--color-background);
color: var(--color-foreground);
font-family: var(--font-ui);
}
/* Teaching prose (the markdown renderer's container gets .lh-prose):
Source Sans 3 with the size/leading tokens; emphasis color bound to
the settings-ready tokens. */
.lh-prose {
font-family: var(--font-prose);
font-size: var(--lh-prose-size);
line-height: var(--lh-prose-leading);
}
.lh-prose :where(strong, b) {
color: var(--lh-ink-strong);
font-weight: 700;
}
.lh-prose :where(em, i) {
color: var(--lh-ink-italic);
}
/* lh-input (owner, typography round 2): lh-prose's 1.5 READING leading
makes typing carets tower over the glyphs — the caret fills the line
box (24.75px at 16.5px text, ~2.1× the glyph height). Inputs are not
paragraphs: a tighter 1.25 leading brings the caret to the font-metric
floor (~21px, as close to the glyphs as Chromium will draw it) while
wrapped lines in growing textareas still read fine. Rendered prose keeps
1.5 — the two share family and size, they differ only in leading. */
.lh-input {
font-family: var(--font-prose);
font-size: var(--lh-prose-size);
line-height: 1.25;
}
/* Thin dark native scrollbar (app-wide): dark thumb, transparent track. */
* {
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-muted-foreground);
}
/* Dock variant swap — quick fade/rise on content change; the shell's HEIGHT
morph is separate (WAAPI in DockOverlay). Re-applied via class toggling, no
remount, so collapsed-question state survives. */
@keyframes dock-variant-in {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.dock-variant {
animation: dock-variant-in 160ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* ============================================================
* W3 — presentation fixes (appended 2026-08-22)
* ============================================================ */
/* Streamdown's internal UI chrome (code blocks, tables, mermaid containers,
copy/download buttons) is styled with Tailwind utilities living in its dist
* bundle. Tailwind v4 never scans node_modules, so without this line those
* classes are dead — code blocks and mermaid containers rendered unstyled.
* (Streamdown README, "Tailwind CSS setup".) */
@source "../node_modules/streamdown/dist/*.js";
/* Token the streamdown utilities reference (code-block/mermaid chrome uses
* bg-sidebar / border-sidebar): map it onto the app's card surface. */
@theme inline {
--color-sidebar: var(--card);
}
/* Mermaid diagrams: keep wide charts inside the content column. Mermaid
* inlines a px max-width on the svg, so !important is required to win. */
[data-streamdown="mermaid"] svg {
max-width: 100% !important;
height: auto !important;
}
/* Display math: long equations scroll instead of overflowing the column. */
.katex-display {
overflow-x: auto;
overflow-y: hidden;
}
/* ============================================================
* Session status — the braille spinner (the one working indicator)
* ============================================================ */
/* (owner, minimal-color round) Phase color-coding is RETIRED: phases only
advance (every session ends in Teaching), so phase-as-signal was
decoration; the --phase-* tokens and every phase-dot state are gone.
What state deserved — "a turn is in flight" — is carried by MOTION
alone: the terminal braille spinner (components/braille-spinner.tsx),
monochrome, existing only while true. */
/* One keyframes, all ten frames: each frame <g> holds opacity 1 for its
10% slice of the 800ms cycle and 0 elsewhere; frames stagger via
positive animation-delay (frame k starts at k×80ms). Static contexts
(SSR, reduced motion) show frame 0 — see the base rules below. */
@keyframes braille-frame {
0%,
10% {
opacity: 1;
}
10.001%,
100% {
opacity: 0;
}
}
.braille-frame {
animation: braille-frame 800ms linear infinite;
opacity: 0;
}
/* Static fallback: frame 0 (⠋) is the glyph a non-animated context shows. */
.braille-frame[data-frame="0"] {
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.braille-frame {
animation: none;
}
}