Files
ms-haushalt/src/styles/global.css
T
FloandClaude 04a46caf19 feat(icicle): refine layout, labels, and zoom UX
- Sticky labels: each bar's title slides down inside its rect under
  the sticky timeline so it stays visible while any part of the rect
  is in view, then slides off when the rect's bottom passes.
- Auto-wrap long bar names into multiple <tspan> lines so labels
  no longer overrun their box.
- Drop browser-native <title> tooltips; the custom tooltip remains.
- Demote depth-1 nameSize to depth-2 size when a bar shows its name
  but not its euro amount, so it doesn't outshout column-2 titles.
- Collapse the depth-4 column when the zoomed-in scope has no
  Breakdown items; surviving columns expand to fill the canvas.
- Tighter year-budget centering, runtime gap (GAP_Y) instead of
  d3 partition padding, gutter masks for year-frame strokes.
- Two-hue OKLCH palette tuned so all labels can stay a single colour
  per flow (light on Aufwendungen, dark on Erträge).
- Sidebar: clickable Aufw/Ertr/Saldo to swap flow; Beschreibung +
  collapsed Erläuterungen extracted from PG sections.
- Sticky timeline axis with active-year tick in the active flow's
  colour; URL state sync for path/year/flow.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-07 16:29:04 +02:00

202 lines
7.1 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.
/* ──────────────────────────────────────────────────────────────────────────
ms-haushalt · global tokens, reset, and base typography.
See .impeccable.md for the design context this expresses.
────────────────────────────────────────────────────────────────────────── */
@import "@fontsource-variable/geist";
@import "@fontsource-variable/geist-mono";
/* Figtree Variable — display + body face. SIL OFL, served from
public/fonts. Roman + italic axes, weight 300900. */
@font-face {
font-family: "Figtree Variable";
src: url("/fonts/Figtree-VariableFont_wght.woff2") format("woff2-variations");
font-weight: 300 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Figtree Variable";
src: url("/fonts/Figtree-Italic-VariableFont_wght.woff2") format("woff2-variations");
font-weight: 300 900;
font-style: italic;
font-display: swap;
}
:root {
/* ── Color (OKLCH, paper-feeling, two-hue flow palette) ───────────── */
/* Background: warm uncoated stock, never pure white. Tinted toward the
Aufwendungen hue family by 0.005-0.01 chroma per the impeccable
"tint neutrals toward brand hue" principle. */
--paper: oklch(98.4% 0.006 60);
--paper-deep: oklch(96.0% 0.008 60);
/* Ink: warmed near-black. Same principle inverted. */
--ink: oklch(20% 0.02 50);
--ink-soft: oklch(38% 0.02 55);
--ink-mute: oklch(58% 0.015 60);
/* Hairlines & faint surfaces. */
--rule: oklch(86% 0.012 60);
--rule-soft: oklch(92% 0.008 60);
/* Two-hue flow palette per design brief §9.5: deep purple for
Aufwendungen (money leaving), orange for Erträge (money coming in).
The two ranges sit on opposite sides of the label-contrast
threshold so all labels on each flow's icicle can be a single
colour: light text on the dark purples, dark text on the light
oranges. Mirrors the JS PALETTE in src/lib/colors.ts. */
--flow-aufwand-h: 295; /* deep plum-purple */
--flow-aufwand-c: 0.19;
--flow-ertrag-h: 55; /* warm orange / amber */
--flow-ertrag-c: 0.15;
/* Solid ink-on-paper renders of each flow accent (used for active
toggle state, breadcrumb separators, slider handle, etc.). The
accent uses Ldark / Llight values that match the bar palette's
darkest-purple and lightest-readable-orange respectively. */
--flow-aufwand: oklch(34% var(--flow-aufwand-c) var(--flow-aufwand-h));
--flow-ertrag: oklch(68% var(--flow-ertrag-c) var(--flow-ertrag-h));
/* ── Spacing (4pt scale, semantic names) ──────────────────────────── */
--space-2xs: 4px;
--space-xs: 8px;
--space-sm: 12px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
--space-2xl: 48px;
--space-3xl: 64px;
--space-4xl: 96px;
/* ── Type scale ───────────────────────────────────────────────────── */
/* Display sizes are fluid (editorial, not app-chrome), per the
typography rule. Body and UI sizes are fixed rem. The display
range is intentionally tighter than the previous version — the
hero needs to stay above the fold on a typical 13" laptop. */
--size-display-1: clamp(2.625rem, 5.5vw + 1rem, 5.25rem); /* hero stack */
--size-display-2: clamp(1.75rem, 2.5vw + 0.5rem, 2.5rem); /* sidebar heading */
--size-display-3: 1.375rem; /* secondary */
--size-body: 1rem; /* 16px */
--size-body-lg: 1.0625rem;/* 17px lede */
--size-caption: 0.8125rem;/* 13px */
--size-eyebrow: 0.6875rem;/* 11px small caps */
--size-micro: 0.75rem; /* 12px sidebar dt labels */
/* ── Faces ────────────────────────────────────────────────────────── */
/* Display + body: Figtree (variable, SIL OFL, served from
public/fonts). Numbers: Geist Mono. The `--face-serif` /
`--face-sans` token names are legacy aliases — both now point to
Figtree since it carries display and body alike. */
--face-display: "Figtree Variable", -apple-system, BlinkMacSystemFont,
"Segoe UI", system-ui, sans-serif;
--face-serif: var(--face-display);
--face-sans: var(--face-display);
--face-mono: "Geist Mono Variable", "Geist Variable", ui-monospace,
"SF Mono", "Menlo", monospace;
/* ── Layout rhythm ────────────────────────────────────────────────── */
--measure: 65ch;
--page-max: 1280px;
--page-pad-x: clamp(1rem, 4vw, 3rem);
}
/* ── Reset ──────────────────────────────────────────────────────────── */
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
font-feature-settings:
"kern" 1,
"liga" 1,
"calt" 1,
"ss01" 1;
/* German-specific OpenType: lining figures for tabular data, oldstyle
elsewhere. Enabled per-element where useful. */
}
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: var(--face-serif);
font-size: var(--size-body);
line-height: 1.55;
font-feature-settings:
"kern" 1,
"liga" 1,
"calt" 1;
/* Slight optical correction for warmly-tinted paper background. */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
p {
margin: 0 0 var(--space-md);
max-width: var(--measure);
}
h1, h2, h3, h4 {
margin: 0;
font-family: var(--face-serif);
font-weight: 800;
line-height: 0.95;
letter-spacing: -0.01em;
font-variant-ligatures: discretionary-ligatures;
}
a {
color: inherit;
text-decoration-thickness: 1px;
text-underline-offset: 0.18em;
}
button {
font: inherit;
color: inherit;
background: none;
border: 0;
padding: 0;
cursor: pointer;
}
/* ── Utility patterns we use repeatedly ─────────────────────────────── */
.eyebrow {
font-family: var(--face-sans);
font-size: var(--size-eyebrow);
font-weight: 500;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-mute);
}
.caption {
font-family: var(--face-sans);
font-size: var(--size-caption);
color: var(--ink-soft);
line-height: 1.4;
}
.tabular {
font-family: var(--face-mono);
font-variant-numeric: tabular-nums lining-nums;
font-feature-settings: "tnum" 1, "lnum" 1;
letter-spacing: -0.01em;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
transition-duration: 0.001ms !important;
}
}