From 898740ce43a453682c87af5f0bff522f507e85b5 Mon Sep 17 00:00:00 2001 From: Benoit TRAVERS Date: Thu, 16 Jul 2026 01:34:24 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(design):=20Beetroot=20Atlas=20rebrand?= =?UTF-8?q?=20=E2=80=94=20theme,=20landing,=20and=20engraved=20logos=20(v3?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A botanical atlas: the beetroot as a fine-line engraving on warm ivory paper. Hairline rules, double-rule plate frames, numbered plates, a high-contrast serif, and near-zero motion — premium through restraint. Replaces the rejected Riso (#27) and Soft (#28) directions. - theme: ivory "atlas" canonical scheme (aubergine dark on .dark), Fraunces + Switzer + JetBrains Mono replace Montserrat, hairline shape language, no glow/float/gradient-text. Semantic token API and the one-knob --accent contract unchanged (minor bump, changeset included). - landing: Catalogue — left-biased serif hero beside a framed engraving (Pl. I — Beta vulgaris, with root cross-section), inventory band, four numbered package plates (Pl. II–V) with live stars and copyable installs, numbered principles (I–III), ruled prose columns, hairline inspirations index, quiet typographic close, mast-headed footer. All copy preserved. - logos: all five marks + favicon redrawn as hand-tinted botanical engravings (beetroot, rabbit, hourglass, no-throw ring, shield). - design.md: the locked design system for future pages. --- .changeset/beetroot-atlas-rebrand.md | 10 + .hallmark/log.json | 38 ++ apps/website/.vitepress/theme/Landing.vue | 622 ++++++++++-------- apps/website/public/favicon.svg | 39 +- .../public/logos/amqp-contract-dark.svg | 21 +- .../public/logos/amqp-contract-light.svg | 21 +- apps/website/public/logos/btravstack-dark.svg | 28 +- .../website/public/logos/btravstack-light.svg | 28 +- apps/website/public/logos/demesne-dark.svg | 31 +- apps/website/public/logos/demesne-light.svg | 31 +- .../public/logos/temporal-contract-dark.svg | 39 +- .../public/logos/temporal-contract-light.svg | 39 +- apps/website/public/logos/unthrown-dark.svg | 32 +- apps/website/public/logos/unthrown-light.svg | 32 +- design.md | 140 ++++ packages/theme/src/fonts.css | 10 +- packages/theme/src/style.css | 179 +++-- packages/theme/src/tokens.css | 233 +++---- 18 files changed, 1052 insertions(+), 521 deletions(-) create mode 100644 .changeset/beetroot-atlas-rebrand.md create mode 100644 .hallmark/log.json create mode 100644 design.md diff --git a/.changeset/beetroot-atlas-rebrand.md b/.changeset/beetroot-atlas-rebrand.md new file mode 100644 index 0000000..2bed52b --- /dev/null +++ b/.changeset/beetroot-atlas-rebrand.md @@ -0,0 +1,10 @@ +--- +"@btravstack/theme": minor +--- + +Beetroot Atlas rebrand — a botanical atlas: engraved, warm, exact. + +- Light warm-ivory paper (`oklch(95.5% 0.015 85)`) with sepia ink is now the canonical scheme; dark becomes the "aubergine" scheme on `.dark`. +- Typography: Fraunces (display serif) + Switzer (body) + JetBrains Mono (code) replace Montserrat. +- Shape language: hairline rules, near-sharp corners, no decorative shadows, solid (non-frosted) navbar, solid ink hero name, still hero mark (no float, no glow). Motion reduced to near-zero. +- Non-breaking: all semantic tokens keep their names and the one-knob rebrand contract is unchanged — consumer sites still override only `--accent` (`:root:root { --accent: … }`) and every derived shade follows via `color-mix`. The new ivory paper is hue-neutral, so per-site accents (orange/indigo/green/violet) harmonise without changes. diff --git a/.hallmark/log.json b/.hallmark/log.json new file mode 100644 index 0000000..22543f7 --- /dev/null +++ b/.hallmark/log.json @@ -0,0 +1,38 @@ +[ + { + "date": "2026-07-16", + "scope": "app", + "macrostructure": "Catalogue", + "theme": "custom", + "theme_axes": "light / high-contrast-serif / chromatic-other (beetroot-pink ~353)", + "vibe": "botanical atlas, engraved plates, warm ivory, exact", + "enrichment": "Tier-B hand-built SVG engravings (plates + full logo set)", + "nav": "N9 edge-aligned", + "footer": "Ft1 mast-headed", + "brief": "btravstack org rebrand v3 — Beetroot Atlas (editorial botanical), replaces rejected v1 Riso + v2 Soft" + }, + { + "date": "2026-07-16", + "scope": "app", + "macrostructure": "Bento Grid", + "theme": "custom", + "theme_axes": "light / rounded-sans / chromatic-other (beetroot-pink ~353)", + "vibe": "warm, rounded, charming beetroot, soft-but-exact", + "enrichment": "H9 original gradient mascot (Tier-B, restored)", + "nav": "N1b", + "footer": "Ft8 marquee", + "brief": "btravstack org rebrand v2 — Beetroot Soft (REJECTED by user; PR #28)" + }, + { + "date": "2026-07-16", + "scope": "app", + "macrostructure": "Manifesto", + "theme": "custom", + "theme_axes": "light / display-heavy / chromatic-other (beetroot-pink ~353)", + "vibe": "beetroot risograph, contract-as-document, raw, technical", + "enrichment": "Tier-B hand-built SVG (flat riso logo set)", + "nav": "N7 brutal slab", + "footer": "Ft4 dense colophon", + "brief": "btravstack org rebrand v1 — Beetroot Riso (REJECTED by user; PR #27)" + } +] diff --git a/apps/website/.vitepress/theme/Landing.vue b/apps/website/.vitepress/theme/Landing.vue index 5c7c877..61c736c 100644 --- a/apps/website/.vitepress/theme/Landing.vue +++ b/apps/website/.vitepress/theme/Landing.vue @@ -10,9 +10,9 @@ function toggleAppearance() { } const principles = [ - { num: "01", title: "The signature is the doc", body: "A contract or a function signature should be enough to understand a system. Declare the interface once and read everything from it — no digging into implementations." }, - { num: "02", title: "Fail fast, everywhere", body: "The sooner the error, the better the code. Anticipated failures are values in the type, malformed data stops at the boundary, missing wiring is a compile error." }, - { num: "03", title: "Coherence at a high level", body: "Contracts pin down how the pieces fit — publisher and consumer, workflow and client, module and dependency can't drift apart while implementations change underneath." }, + { num: "I.", title: "The signature is the doc", body: "A contract or a function signature should be enough to understand a system. Declare the interface once and read everything from it — no digging into implementations." }, + { num: "II.", title: "Fail fast, everywhere", body: "The sooner the error, the better the code. Anticipated failures are values in the type, malformed data stops at the boundary, missing wiring is a compile error." }, + { num: "III.", title: "Coherence at a high level", body: "Contracts pin down how the pieces fit — publisher and consumer, workflow and client, module and dependency can't drift apart while implementations change underneath." }, ]; const aiCards = [ @@ -42,6 +42,8 @@ interface Project { docs: string; } +const plates = ["II", "III", "IV", "V"]; + const projects: Project[] = [ { tag: "Messaging", name: "amqp-contract", pkg: "@amqp-contract/contract", @@ -106,354 +108,406 @@ onMounted(() => { diff --git a/apps/website/public/favicon.svg b/apps/website/public/favicon.svg index b7615ac..2c95385 100644 --- a/apps/website/public/favicon.svg +++ b/apps/website/public/favicon.svg @@ -1,12 +1,27 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/amqp-contract-dark.svg b/apps/website/public/logos/amqp-contract-dark.svg index 6f5c947..069d821 100644 --- a/apps/website/public/logos/amqp-contract-dark.svg +++ b/apps/website/public/logos/amqp-contract-dark.svg @@ -1 +1,20 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/amqp-contract-light.svg b/apps/website/public/logos/amqp-contract-light.svg index 2aa8ee6..7d7307d 100644 --- a/apps/website/public/logos/amqp-contract-light.svg +++ b/apps/website/public/logos/amqp-contract-light.svg @@ -1 +1,20 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/btravstack-dark.svg b/apps/website/public/logos/btravstack-dark.svg index 89f4f6c..e8bdd3e 100644 --- a/apps/website/public/logos/btravstack-dark.svg +++ b/apps/website/public/logos/btravstack-dark.svg @@ -1 +1,27 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/btravstack-light.svg b/apps/website/public/logos/btravstack-light.svg index 89f4f6c..d332b04 100644 --- a/apps/website/public/logos/btravstack-light.svg +++ b/apps/website/public/logos/btravstack-light.svg @@ -1 +1,27 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/demesne-dark.svg b/apps/website/public/logos/demesne-dark.svg index d8db6ab..4a5e891 100644 --- a/apps/website/public/logos/demesne-dark.svg +++ b/apps/website/public/logos/demesne-dark.svg @@ -1 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/demesne-light.svg b/apps/website/public/logos/demesne-light.svg index 0301a24..f5d740d 100644 --- a/apps/website/public/logos/demesne-light.svg +++ b/apps/website/public/logos/demesne-light.svg @@ -1 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/temporal-contract-dark.svg b/apps/website/public/logos/temporal-contract-dark.svg index 374fc70..962b904 100644 --- a/apps/website/public/logos/temporal-contract-dark.svg +++ b/apps/website/public/logos/temporal-contract-dark.svg @@ -1 +1,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/temporal-contract-light.svg b/apps/website/public/logos/temporal-contract-light.svg index 0c88007..ef7f1b4 100644 --- a/apps/website/public/logos/temporal-contract-light.svg +++ b/apps/website/public/logos/temporal-contract-light.svg @@ -1 +1,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/unthrown-dark.svg b/apps/website/public/logos/unthrown-dark.svg index b49df8a..a897b72 100644 --- a/apps/website/public/logos/unthrown-dark.svg +++ b/apps/website/public/logos/unthrown-dark.svg @@ -1 +1,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/unthrown-light.svg b/apps/website/public/logos/unthrown-light.svg index 9d0f2b3..ab574c9 100644 --- a/apps/website/public/logos/unthrown-light.svg +++ b/apps/website/public/logos/unthrown-light.svg @@ -1 +1,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design.md b/design.md new file mode 100644 index 0000000..c4490a0 --- /dev/null +++ b/design.md @@ -0,0 +1,140 @@ +# Design — btravstack · "Beetroot Atlas" + +A locked design system for the btravstack org (landing + `@btravstack/theme` + +the four docs sites). Every page redesign reads this file before emitting code. +Do not regenerate per page — extend or amend this file when the system needs to +grow. + +**The idea.** A botanical atlas. The beetroot (btrav ~ betterave) is a +fine-line engraving on warm ivory paper — hand-tinted linework, hairline rules, +double-rule plate frames, numbered plates, a high-contrast serif. Premium +through restraint: almost no motion, letterspaced small caps, generous +whitespace. Supersedes the rejected "Beetroot Riso" (brutalist) and +"Beetroot Soft" (playful) systems — no hard slabs, no pills, no cards, no +mascot bobbing. + +## Genre + +editorial — premium register. Vibe: *"botanical atlas, engraved plates, warm +ivory, exact"*. + +## Macrostructure family + +- Marketing pages (landing): **Catalogue** — left-biased hero (serif headline + + framed engraving plate), an inventory band, a uniform grid of numbered + double-rule plates (Pl. I is the beetroot; Pl. II–V the packages), then + editorial sections: numbered principles (I–III), ruled prose columns, a + hairline index, a quiet typographic close. +- Docs sites (VitePress content): default VitePress layout wearing the theme + tokens — Fraunces headings, hairline feature frames, still (no float, no + glow), near-sharp corners. +- Nav: **N9 edge-aligned** — wordmark hard-left, toggle + text CTA hard-right. +- Footer: **Ft1 mast-headed** — single band: wordmark, tagline, small links, + MIT line. + +## Theme + +Light "atlas ivory" is canonical; dark "aubergine" via `.dark`. +Only lightness/chroma move between schemes. + +| Token | Light (canonical) | Dark — aubergine | +| --- | --- | --- | +| `--bg` (paper) | `oklch(95.5% 0.015 85)` warm ivory | `oklch(17.5% 0.020 340)` | +| `--card` | `oklch(93% 0.018 85)` | `oklch(21% 0.022 340)` | +| `--text` (ink) | `oklch(25% 0.020 30)` sepia-black | `oklch(93% 0.008 60)` warm cream | +| `--accent` | **#E0589A** = `oklch(65.5% 0.182 353)` — THE knob | same | +| `--text-accent` | accent darkened 30% (AA on ivory) | `var(--accent)` | +| rules | hairlines (22–38% ink mix); plates use double frames | same recipe | +| focus ring | accent darkened 18% — instant, never animated | same | + +**One-knob rebrand is contractual.** Docs sites override only `--accent` +(`:root:root { --accent: … }`); every derived shade flows via `color-mix`. +amqp `#FF6600` · temporal `#6B76F2` · unthrown `#46B86C` · demesne `#B45CE0`. +Note: the paper is hue-neutral warm ivory (hue 85), so it harmonises with any +per-site accent. + +Accent discipline: text-level only (labels, underlines, plate numbers); fills +appear only on docs-site brand buttons. Washes ≤ 13% opacity. + +## Typography + +- Display: **Fraunces** (variable, optical size), weight 550–650, roman, + sentence case. Never italic headers, never all-caps headlines — all-caps is + reserved for small-caps labels (`Pl. II — Messaging`) at 12px, tracked 0.12em. +- Body: **Switzer** (Fontshare), 400–600. +- Mono (outlier): **JetBrains Mono** — code, package identifiers, star counts. +- Hero headline ≤ 50 chars at `--fs-hero: clamp(36px, 4.8vw, 54px)`. +- 2+1 rule: exactly these three families. + +## Spacing / shape + +Named px scale in `packages/theme/src/tokens.css` (`--space-1…6`). Corners: +near-sharp (3–8px; plates fully square). Shadows: none in the design language +(a 1px hairline "rest" shadow at most; toast excepted). No glassmorphism, no +frosted nav — atlas paper is solid. + +**The plate device** is the system's signature: 1px outer rule + 5px gutter + +1px inner rule, square corners, small-caps caption with an accent plate number. +Use it for framed content, never for generic cards. + +## Motion + +- Easings: `--ease: cubic-bezier(0.16, 1, 0.3, 1)`; durations ≤ 0.18s. +- Stance: near-none. Hover = underline/color shifts only. No floats, no lifts, + no marquees, no scroll reveals. The copy toast is the only moving element. +- Reduced-motion: transitions collapse; toast becomes opacity-only. + +## Microinteractions stance + +- Silent success (copy button → small mono toast, 1.6s). +- Hover tooltips 800ms · focus 0ms. Focus rings instant, 2px `--focus`. + +## CTA voice + +- Primary: typographic — Fraunces 600 + 2px accent underline + arrow glyph. + No filled buttons on marketing pages. +- Docs sites keep VitePress's filled brand button (accent fill, ink text, + 4px radius) for functional CTAs. + +## Logos + +All five marks are **fine-line botanical engravings**: sepia-ink linework +(cream-ink on dark), hand-tint fills (soft beet pink, soft leaf green), hatch +shading, hand-drawn irregularity. Concepts: beetroot (org, Pl. I with root +cross-section), rabbit (amqp), hourglass with beet sand (temporal), no-throw +ring (unthrown), double-frame shield (demesne). Per-scheme files swap ink + +tint values only. Never flatten to hard-edge marks; never revert to gradients. + +## What pages MUST share + +- The wordmark voice (Fraunces 600, "btrav" in `--text-accent` + "stack" in ink). +- The one-knob accent architecture; accent as text/underline, not fills. +- The three fonts, hairline-rule language, the plate device, small-caps labels. +- The stillness — motion stays near-zero everywhere. + +## What pages MAY differ on + +- Macrostructure within the family (a future page may be Long Document or + Conversational FAQ — still wearing this theme). +- Per-site accent hue (docs sites). +- Docs sites keep VitePress's functional layout; only the dress is shared. + +## Exports + +### tokens.css +The canonical export lives at `packages/theme/src/tokens.css` (published as +`@btravstack/theme`). It is the single source of truth; do not duplicate here. + +### Quick-reference (other projects) +```css +:root { + --color-paper: oklch(95.5% 0.015 85); + --color-ink: oklch(25% 0.020 30); + --color-accent: #E0589A; + --font-display: "Fraunces", serif; + --font-body: "Switzer", sans-serif; + --font-mono: "JetBrains Mono", monospace; + --ease: cubic-bezier(0.16, 1, 0.3, 1); + --rule-hair: 1px; +} +``` diff --git a/packages/theme/src/fonts.css b/packages/theme/src/fonts.css index 39e3d6d..995c66c 100644 --- a/packages/theme/src/fonts.css +++ b/packages/theme/src/fonts.css @@ -1,6 +1,8 @@ /* - * btravstack web fonts. Montserrat (display/UI) + JetBrains Mono (code). - * Loaded from Google Fonts so the system stays zero-asset and portable; - * the weights here match the --fw-* tokens in tokens.css. + * btravstack web fonts — the "Beetroot Atlas" pairing (2+1 rule). + * Fraunces (display serif, optical size + 500–700) — atlas headings + * Switzer (body/UI, 400–600, Fontshare) — refined neutral + * JetBrains Mono (code + labels) */ -@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=JetBrains+Mono:wght@400;500;600&display=swap'); +@import url('https://api.fontshare.com/v2/css?f[]=switzer@400,500,600&display=swap'); diff --git a/packages/theme/src/style.css b/packages/theme/src/style.css index 3afaf6d..b2a9a27 100644 --- a/packages/theme/src/style.css +++ b/packages/theme/src/style.css @@ -1,13 +1,16 @@ +/* Hallmark · genre: editorial (premium) · design-system: design.md · designed-as-app + * scope: VitePress chrome mapping for all btravstack sites (docs + landing shell) + */ + /* - * @btravstack/theme — VitePress style entry. + * @btravstack/theme — VitePress style entry ("Beetroot Atlas"). * * Maps the btravstack design tokens onto VitePress's own `--vp-*` variables so * every btravstack site (the website + the docs sites) shares one look. * Because the semantic tokens (--bg, --text, --text-accent, …) flip per scheme * in tokens.css, this mapping is written once and adapts automatically: - * - dark → the btravstack plum surfaces, pink links - * - light → clean warm-white surfaces, brand darkened to deep beetroot - * (--text-accent) so links/headings pass WCAG-AA on white + * - light → warm ivory paper, sepia ink, deep-beet links (WCAG-AA) + * - dark → aubergine surfaces, warm cream ink, vibrant pink links * * The `:root:root` selector raises specificity to (0,2,0) so the mapping wins * over VitePress's own `:root` / `.dark` defaults regardless of load order. @@ -19,7 +22,7 @@ /* Surfaces / text / dividers — scheme-aware via the semantic tokens */ --vp-c-bg: var(--bg); --vp-c-bg-alt: var(--card); - --vp-c-bg-soft: var(--card-soft); + --vp-c-bg-soft: var(--card); --vp-c-bg-elv: var(--card); --vp-c-text-1: var(--text); @@ -33,20 +36,19 @@ /* Brand — links/active use accent-as-TEXT (darkens on light for AA). All shades derive from --accent, so a site override rebrands everything. */ --vp-c-brand-1: var(--text-accent); - --vp-c-brand-2: var(--accent-soft); + --vp-c-brand-2: var(--accent); --vp-c-brand-3: var(--accent-deep); - --vp-c-brand-soft: color-mix(in srgb, var(--accent) 16%, transparent); + --vp-c-brand-soft: color-mix(in srgb, var(--accent) 14%, transparent); --vp-c-brand: var(--text-accent); /* legacy alias */ - /* Inline code — accent-as-text so it stays readable on the (scheme-aware) - code inset: pink on dark, deep beetroot on the light code wash */ + /* Inline code — accent-as-text on the scheme-aware code inset */ --vp-code-color: var(--text-accent); --vp-code-bg: var(--code-bg); --vp-font-family-base: var(--sans); --vp-font-family-mono: var(--mono); - /* Solid brand buttons — dark ink on the pink FILL, readable in both schemes */ + /* Solid brand buttons — dark ink on the accent FILL, readable both schemes */ --vp-button-brand-bg: var(--accent); --vp-button-brand-text: var(--accent-contrast); --vp-button-brand-border: transparent; @@ -65,105 +67,91 @@ --vp-custom-block-warning-border: var(--accent-line); --vp-custom-block-warning-text: var(--text-accent); - /* Home hero: gradient name + accent glow behind the logo */ - --vp-home-hero-name-color: transparent; - --vp-home-hero-name-background: linear-gradient(120deg, var(--text-accent), var(--accent-soft)); - --vp-home-hero-image-background-image: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 30%, transparent), transparent 72%); - --vp-home-hero-image-filter: blur(8px); + /* Home hero: solid ink name in the atlas serif — no gradient, no glow */ + --vp-home-hero-name-color: var(--vp-c-text-1); + --vp-home-hero-name-background: none; + --vp-home-hero-image-background-image: none; + --vp-home-hero-image-filter: none; } -/* ── Playful flourishes — shared across every btravstack site ───── */ - -/* Home hero: a gently floating logo. The beetroot glow lives BEHIND the hero - image itself (--vp-home-hero-image-background-image, set above) so it tracks - the logo wherever the layout places it. We deliberately do NOT add a separate - centered ::before glow: the landing page uses its own bespoke layout (not - .VPHome), and on the docs sites the default home hero is left-aligned with the - image on the right — a hard-centered glow there just floats, disconnected. */ -.VPHome { overflow-x: clip; } -.VPHero .image-container { animation: btv-floaty 6s ease-in-out infinite; } -.VPHero .image-src { filter: drop-shadow(0 18px 40px color-mix(in srgb, var(--accent-deep) 45%, transparent)); } -@keyframes btv-floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } - -/* Navbar logo: VitePress's 24px mark reads as a speck next to the wordmark. - Size it up through VitePress's own variable (--vp-nav-logo-height) rather than - overriding `.logo { height }` — VitePress sets the height from a scoped - `:deep(.logo)` selector that out-specifies a plain `.VPNavBarTitle .logo`, so - the variable is the only reliable lever. Spacing stays on the logo's default - margin (no extra `gap`, which would compound with it). - Light/dark switching is left to VitePress: with `logo: { light, dark }` per - repo, its native .VPImage class toggle already shows the right artwork. */ -:root:root { --vp-nav-logo-height: 34px; } -@media (max-width: 768px) { :root:root { --vp-nav-logo-height: 28px; } } - -/* Home hero: tighten the display type. (The hero `image:` is one file per repo, - set in index.md frontmatter; for a crisp mark in both schemes make it - scheme-aware there — hero.image.light / hero.image.dark.) */ -.VPHero .name { letter-spacing: -1.5px; } -.VPHero .text { letter-spacing: -0.5px; } - -/* Feature cards: hairline border + a soft lift on hover, echoing the landing - page's package cards; brand-tinted icon chip. */ +/* ── Atlas chrome — shared across every btravstack site ────────── */ + +/* No horizontal scroll, ever (clip — not hidden — so sticky keeps working). */ +html, body { overflow-x: clip; } + +/* Display serif on the big headings; body stays Switzer for reading. */ +.VPHero .name, +.VPHero .text, +.VPFeature .title, +.vp-doc h1, .vp-doc h2, .vp-doc h3 { + font-family: var(--display); + letter-spacing: var(--tracking-tight); + font-weight: 600; +} +.VPHero .name { font-weight: 650; } +.VPNavBar .title { font-family: var(--display); font-weight: 600; } + +/* Navbar: solid ivory paper with a hairline — atlas pages don't frost. */ +.VPNav, .VPNavBar { background: var(--bg) !important; } +.VPNavBar { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; } +.VPNavBar .content-body { background: transparent !important; } +.VPNavBar:not(.home) { border-bottom: 1px solid var(--border); } + +/* Hero mark: still, engraved — no float, no glow. A fine rule beneath. */ +.VPHome { overflow-x: clip; position: relative; } +.VPHero .image-src { filter: none; } + +/* Navbar logo sizing via VitePress's own variable. */ +:root:root { --vp-nav-logo-height: 30px; } +@media (max-width: 768px) { :root:root { --vp-nav-logo-height: 25px; } } + +/* Feature cards: hairline frames, sharp-ish corners, quiet hover. */ .VPFeature { - border: 1px solid var(--vp-c-divider); + border: 1px solid var(--border); border-radius: var(--radius-lg); - transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; -} -.VPFeature:hover { - transform: translateY(-4px); - border-color: var(--vp-c-border); - box-shadow: var(--shadow-card); + transition: border-color var(--speed) var(--ease); } +.VPFeature:hover { border-color: var(--border-2); } .VPFeature .icon { border-radius: var(--radius) !important; - background: var(--vp-c-brand-soft) !important; - border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); + background: var(--accent-wash) !important; + border: 1px solid var(--accent-line); } -.VPFeature .title { letter-spacing: -0.3px; } -/* Brand buttons: springy hover */ -.VPButton.brand { transition: filter 0.15s, transform 0.15s; } -.VPButton.brand:hover { filter: brightness(1.07); transform: translateY(-1px); } +/* Brand buttons: quiet press, no bounce. */ +.VPButton { border-radius: var(--radius) !important; font-weight: 600; } +.VPButton.brand { transition: background-color var(--speed-fast) var(--ease); } +.VPButton.brand:active { transform: translateY(1px); } -/* Rounder, pinker code */ -.vp-doc :not(pre) > code { border-radius: 7px; } +/* Code + admonitions: eased corners, inset paper. */ +.vp-doc :not(pre) > code { border-radius: var(--radius-sm); } .vp-doc div[class*="language-"] { border-radius: var(--radius); } .vp-doc .custom-block { border-radius: var(--radius); } -/* ── Home hero: a bigger mark + a large accent glow shape ───────── - The default hero only has a small glow hugging the logo; the docs - mockups show a broad accent wash bleeding off the top-right. */ -/* position: relative only — the existing `.VPHome { overflow-x: clip }` above - handles the glow's horizontal bleed; a full `overflow: clip` here would also - clip vertical content like the feature-card hover-lift shadows. */ -.VPHome { position: relative; } -.VPHome::before { - content: ""; - position: absolute; top: -40px; right: -120px; z-index: 0; - width: 620px; height: 620px; max-width: 85vw; - background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%); - pointer-events: none; -} -.VPHome .VPHero, -.VPHome .VPFeatures { position: relative; z-index: 1; } +/* Hero mark sizing on docs homes (all project logos are square). */ @media (min-width: 960px) { - .VPHero .image-container { width: 340px; height: 340px; } + .VPHero .image-container { width: 300px; height: 300px; } .VPHero .image-bg { width: 100%; height: 100%; } - /* the mark ships at a small intrinsic size, so stretch it explicitly - (all project logos are square) rather than relying on max-width */ .VPHero .image-src { - width: 300px; - height: 300px; - max-width: 300px; - max-height: 300px; + width: 260px; + height: 260px; + max-width: 260px; + max-height: 260px; object-fit: contain; } } +/* Focus is a first-class state — instant, visible, never animated. */ +:focus-visible { + outline: 2px solid var(--focus); + outline-offset: 2px; + transition: none; +} + /* ── "Part of btravstack" org-attribution strip (layout-bottom slot) ── */ .btv-partof { - border-top: 1px solid var(--vp-c-divider); - background: var(--vp-c-bg-alt); + border-top: 1px solid var(--border); + background: var(--bg); padding: 16px 24px; display: flex; justify-content: center; @@ -173,22 +161,17 @@ align-items: center; gap: 8px; font-size: 13px; - font-weight: 600; + font-weight: 500; color: var(--vp-c-text-3); text-decoration: none; - transition: color 0.15s; + transition: color var(--speed-fast) var(--ease); } -.btv-partof-link:hover { color: var(--vp-c-text-2); } +.btv-partof-link:hover { color: var(--vp-c-text-1); } .btv-partof-link:focus-visible { - outline: 2px solid var(--accent); + outline: 2px solid var(--focus); outline-offset: 3px; - border-radius: 4px; color: var(--vp-c-text-1); } .btv-partof-mark { display: inline-flex; line-height: 0; } -.btv-partof-word { font-weight: 700; letter-spacing: -0.3px; color: var(--vp-c-text-2); } -.btv-partof-pink { color: var(--bt-pink); } - -@media (prefers-reduced-motion: reduce) { - .VPHero .image-container { animation: none; } -} +.btv-partof-word { font-family: var(--display); font-weight: 600; color: var(--vp-c-text-2); } +.btv-partof-pink { color: var(--text-accent); } diff --git a/packages/theme/src/tokens.css b/packages/theme/src/tokens.css index 5167a3a..d280edb 100644 --- a/packages/theme/src/tokens.css +++ b/packages/theme/src/tokens.css @@ -1,19 +1,29 @@ +/* Hallmark · genre: editorial (premium) · macrostructure: Catalogue · design-system: design.md · designed-as-app + * theme: custom · vibe: "botanical atlas, engraved plates, warm ivory, exact" + * paper: oklch(95.5% 0.015 85) · accent: #E0589A (oklch(65.5% 0.182 353)) + * display: Fraunces 550–650 (roman) · body: Switzer · outlier: JetBrains Mono + * axes: light / high-contrast-serif / chromatic-other (beetroot-pink ~353°) + * nav: N9 edge-aligned · footer: Ft1 mast-headed · studied: no · context: user-provided + * pre-emit critique: P5 H4 E4 S5 R5 V5 + */ + /* - * btravstack design tokens — the single source of truth. + * btravstack design tokens — the single source of truth ("Beetroot Atlas"). * - * Mirrors the btravstack Design System (tokens/colors.css, themes.css, - * typography.css, spacing.css). Reference the SEMANTIC tokens (--bg, --text, - * --accent, --text-accent, --radius, --space-*) in app code; the raw --bt-* - * palette underneath is the provenance, exposed so adapters (e.g. VitePress) - * can remap freely. + * A botanical atlas: warm ivory paper, sepia ink, hairline rules and + * double-rule plate frames, a high-contrast serif, and the beetroot as a + * fine-line engraving. Premium through restraint — almost no motion, no + * decorative shadows, letterspaced small caps. Reference the SEMANTIC tokens + * (--bg, --text, --accent, --text-accent, --radius, --space-*) in app code; + * the raw --bt-* palette underneath is the provenance. * - * Dark is the canonical default. The light scheme overrides the semantic - * tokens; it activates on `:root:not(.dark)` (VitePress's appearance system) - * as well as an explicit `.light` / `[data-theme="light"]`. + * LIGHT "atlas ivory" is the canonical scheme. The dark "aubergine" scheme + * overrides the semantic tokens on `.dark` / `[data-theme="dark"]` + * (VitePress's appearance system). * - * Fills vs text: keep --accent / --green for FILLS (buttons, glow, washes); + * Fills vs text: keep --accent / --green for FILLS (docs buttons, washes); * use --text-accent / --text-green for COLORED TEXT and icons — light mode - * darkens those to deep beetroot so links/labels pass WCAG-AA on white. + * darkens those to deep beetroot so links/labels pass WCAG-AA on ivory. */ :root { /* ── Brand palette (raw beetroot) ─────────────────────────────── */ @@ -25,98 +35,99 @@ --bt-leaf: #3DAE62; /* center leaf */ --bt-leaf-dark: #2C8B4E; /* side leaves */ --bt-green: #46B86C; /* "success" green (checks) */ - --bt-cream: #F5EFF3; /* near-white text */ - --bt-ink: #1a0c14; /* near-black, for text on accent */ + --bt-cream: #F5F0E5; /* atlas ivory */ + --bt-ink: #2B1E1C; /* sepia-black engraving ink */ - /* ── Surfaces (dark scheme — the canonical btravstack look) ───── */ - --bg: #15101c; - --bg-grad: #1b1326; /* radial hero glow inner stop */ - --card: #1d1726; - --card-soft: #241c31; + /* ── Surfaces (LIGHT scheme — "atlas ivory", canonical) ──────── */ + --bg: oklch(95.5% 0.015 85); /* warm ivory ≈ #f5f0e5 */ + --bg-grad: oklch(94% 0.018 85); /* legacy alias — flat, no glow */ + --card: oklch(93% 0.018 85); /* ≈ #ede7db */ + --card-soft: oklch(90.5% 0.020 85); /* ≈ #e6dfd1 */ - /* ── Borders / dividers ──────────────────────────────────────── */ - --border: rgba(255, 255, 255, 0.08); - --border-2: rgba(255, 255, 255, 0.12); + /* ── Rules — hairlines; plates draw their own double frames ──── */ + --border: color-mix(in srgb, var(--text) 22%, transparent); + --border-2: color-mix(in srgb, var(--text) 38%, transparent); /* ── Text ────────────────────────────────────────────────────── */ - --text: #F5EFF3; - --muted: #AEA1BC; - --faint: #7E7290; + --text: oklch(25% 0.020 30); /* sepia-black ≈ #2b1e1c */ + --muted: oklch(42% 0.018 35); /* ≈ #564a46, ~7:1 on ivory */ + --faint: oklch(50% 0.015 35); /* ≈ #6b605d, ~4.9:1 on ivory */ - /* ── Accent — the single brand knob (v1.4.0) ───────────────────── + /* ── Accent — the single brand knob ────────────────────────────── Set ONE value, `--accent`, and the whole theme rebrands: the deep, hover, soft and wash shades below all derive from it via color-mix, - so a consumer site needs only `:root { --accent: #FF6600 }`. The - beetroot pink here is the default (the btravstack org brand); the - docs sites override it with their own accent. - (Optional finer knobs: override --text-accent for a hand-tuned - light-mode AA link color, or --accent-contrast for ink on the fill.) */ + so a consumer site needs only `:root:root { --accent: #FF6600 }`. + The beetroot pink here is the default (the btravstack org brand); + the docs sites override it with their own accent. */ --accent: #E0589A; /* ← the knob */ - --accent-contrast: #1a0c14; /* readable text on an accent fill */ - --accent-deep: color-mix(in srgb, var(--accent), #000 42%); /* deep root — active/press */ - --accent-hover: color-mix(in srgb, var(--accent), #000 18%); /* button hover fill */ - --accent-soft: color-mix(in srgb, var(--accent) 62%, #fff); /* highlight vein / brand-2 */ - --accent-rgb: 224, 88, 154; /* legacy: prefer color-mix over rgba(var(--accent-rgb),…) */ + --accent-contrast: #2B1E1C; /* readable text on an accent fill */ + --accent-deep: color-mix(in srgb, var(--accent), #000 42%); /* active/press */ + --accent-hover: color-mix(in srgb, var(--accent), #000 16%); /* fill hover */ + --accent-soft: color-mix(in srgb, var(--accent) 62%, #fff); /* hand-tint */ + --accent-rgb: 224, 88, 154; /* legacy: prefer color-mix */ --green: #46B86C; --green-rgb: 70, 184, 108; - /* ── Accent-as-TEXT (split from fill; light darkens it for AA) ─── */ - --text-accent: var(--accent); /* on dark plum the accent reads fine as text */ - --text-green: #46B86C; + /* ── Accent-as-TEXT (split from fill; light darkens it for AA) ── */ + --text-accent: color-mix(in srgb, var(--accent), #000 30%); /* deep beet, AA on ivory */ + --text-green: #15683A; - /* Display accent — the accent as the LARGE display wordmark color in - both schemes (large text only needs 3:1, so it can stay fully vibrant). */ - --display-accent: var(--accent); + /* Display accent — the accent for LARGE display moments only. */ + --display-accent: color-mix(in srgb, var(--accent), #000 18%); - /* ── Glass (frosted sticky headers) ──────────────────────────── */ - --glass: rgba(21, 16, 28, 0.66); + /* ── Focus — instant, never animated ─────────────────────────── */ + --focus: color-mix(in srgb, var(--accent), #000 18%); + + /* ── Header surface (solid — atlas paper doesn't frost) ──────── */ + --glass: var(--bg); /* ── Accent washes / lines / code inset ──────────────────────── */ - --accent-wash: color-mix(in srgb, var(--accent) 10%, transparent); - --accent-wash-2: color-mix(in srgb, var(--accent) 16%, transparent); - --accent-line: color-mix(in srgb, var(--accent) 25%, transparent); - --code-bg: rgba(0, 0, 0, 0.35); /* inline code inset */ + --accent-wash: color-mix(in srgb, var(--accent) 8%, transparent); + --accent-wash-2: color-mix(in srgb, var(--accent) 13%, transparent); + --accent-line: color-mix(in srgb, var(--accent-deep) 35%, transparent); + --code-bg: color-mix(in srgb, var(--text) 5%, var(--bg)); - /* ── Typography: families ────────────────────────────────────── */ - --sans: 'Montserrat', 'Segoe UI', system-ui, sans-serif; - --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; + /* ── Typography: families (2+1 — see design.md) ──────────────── */ + --display: 'Fraunces', ui-serif, Georgia, serif; + --sans: 'Switzer', 'Segoe UI', system-ui, sans-serif; + --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; /* ── Typography: weights ─────────────────────────────────────── */ --fw-medium: 500; --fw-semibold: 600; --fw-bold: 700; - --fw-extrabold: 800; + --fw-extrabold: 700; /* atlas caps at 700 — engravings, not posters */ /* ── Typography: display / heading scale (clamped, fluid) ─────── */ - --fs-hero: clamp(52px, 9vw, 92px); - --fs-h1: clamp(34px, 6vw, 56px); - --fs-h2: clamp(28px, 4vw, 40px); - --fs-h3: 23px; - --fs-h4: 18px; + --fs-hero: clamp(36px, 4.8vw, 54px); + --fs-h1: clamp(32px, 5vw, 48px); + --fs-h2: clamp(24px, 3vw, 31px); + --fs-h3: 21px; + --fs-h4: 17px; /* ── Typography: body / UI scale ─────────────────────────────── */ - --fs-lead: clamp(20px, 2.6vw, 26px); + --fs-lead: clamp(18px, 2.1vw, 21px); --fs-body-lg: 17px; --fs-body: 15px; --fs-body-sm: 14px; --fs-caption: 13px; - --fs-eyebrow: 13px; + --fs-eyebrow: 12px; /* ── Typography: tracking + line heights ─────────────────────── */ - --tracking-hero: -2.5px; - --tracking-tight: -1px; - --tracking-snug: -0.5px; - --tracking-eyebrow: 1.5px; - --lh-tight: 1.08; - --lh-snug: 1.3; - --lh-body: 1.6; - - /* ── Radii ───────────────────────────────────────────────────── */ - --radius-sm: 9px; - --radius: 12px; - --radius-lg: 16px; - --radius-xl: 20px; - --radius-2xl: 24px; + --tracking-hero: -0.015em; + --tracking-tight: -0.01em; + --tracking-snug: -0.005em; + --tracking-eyebrow: 0.12em; + --lh-tight: 1.12; + --lh-snug: 1.35; + --lh-body: 1.65; + + /* ── Radii — near-sharp; plates are square, docs slightly eased ─ */ + --radius-sm: 3px; + --radius: 4px; + --radius-lg: 6px; + --radius-xl: 8px; + --radius-2xl: 8px; --radius-pill: 999px; /* ── Spacing scale ───────────────────────────────────────────── */ @@ -127,51 +138,47 @@ --space-5: 22px; --space-6: 28px; - /* ── Shadows ─────────────────────────────────────────────────── */ - --shadow-card: 0 24px 60px -28px rgba(0, 0, 0, 0.8); - --shadow-accent: 0 12px 30px -10px color-mix(in srgb, var(--accent) 60%, transparent); - --shadow-toast: 0 18px 40px -16px rgba(0, 0, 0, 0.8); + /* ── Shadows — paper doesn't float; keep them almost nothing ─── */ + --shadow-card: 0 1px 0 color-mix(in srgb, var(--text) 12%, transparent); + --shadow-accent: none; + --shadow-toast: 0 6px 20px -10px color-mix(in srgb, var(--text) 45%, transparent); /* ── Layout & motion ─────────────────────────────────────────── */ - --container: 1180px; - --speed-fast: 0.15s; - --speed: 0.2s; - --ease: ease; + --container: 1120px; + --speed-fast: 0.12s; + --speed: 0.18s; + --ease: cubic-bezier(0.16, 1, 0.3, 1); } /* - * Light scheme — warm, faintly beetroot-tinted near-whites. Only the semantic - * tokens flip; the raw --bt-* palette is unchanged. Brand FILLS stay vibrant - * pink (dark ink on top); accent-as-TEXT darkens to deep beetroot for AA. + * Dark scheme — "aubergine": the atlas printed in negative. Only the semantic + * tokens flip; the raw --bt-* palette never changes. Brand FILLS stay vibrant + * (dark ink on top); accent-as-TEXT reads fine on aubergine. */ -:root:not(.dark), -[data-theme="light"], -.light { - --bg: #FBF7F9; - --bg-grad: #FCEDF4; - --card: #FFFFFF; - --card-soft: #F0E6EC; - - --border: rgba(26, 12, 20, 0.16); - --border-2: rgba(26, 12, 20, 0.24); - - --text: #1F0E18; - --muted: #5E5159; /* ~7.1:1 on white */ - --faint: #766678; /* ~5.0:1 on white (was #978996, 3.1:1 — failed AA) */ - - /* --accent (the FILL) is NOT overridden here — it stays the site's vibrant - accent in both schemes (dark ink sits on top). Only accent-as-TEXT darkens - for WCAG-AA on white. */ - --text-accent: color-mix(in srgb, var(--accent), #000 22%); /* darkened accent, ~AA on white */ - --text-green: #15683A; /* ~5.2:1 on white — affirmative text */ - - --accent-wash: color-mix(in srgb, var(--accent) 12%, transparent); - --accent-wash-2: color-mix(in srgb, var(--accent) 16%, transparent); - --accent-line: color-mix(in srgb, var(--accent-deep) 40%, transparent); - --glass: rgba(255, 255, 255, 0.72); - --code-bg: color-mix(in srgb, var(--accent) 8%, #fff); - - --shadow-card: 0 18px 44px -26px color-mix(in srgb, var(--accent-deep) 55%, transparent); - --shadow-accent: 0 12px 26px -10px color-mix(in srgb, var(--accent) 45%, transparent); - --shadow-toast: 0 18px 40px -16px rgba(40, 16, 30, 0.18); +.dark, +[data-theme="dark"] { + --bg: oklch(17.5% 0.020 340); /* aubergine ≈ #170d13 */ + --bg-grad: oklch(19.5% 0.021 340); + --card: oklch(21% 0.022 340); /* ≈ #1f141b */ + --card-soft: oklch(24.5% 0.022 340); /* ≈ #281c24 */ + + --border: color-mix(in srgb, var(--text) 18%, transparent); + --border-2: color-mix(in srgb, var(--text) 32%, transparent); + + --text: oklch(93% 0.008 60); /* warm cream ≈ #ece7e3 */ + --muted: oklch(75% 0.012 55); /* ≈ #b4aca7 */ + --faint: oklch(63% 0.012 50); /* ≈ #908783 */ + + --text-accent: var(--accent); + --text-green: #46B86C; + --display-accent: var(--accent); + + --glass: var(--bg); + --accent-wash: color-mix(in srgb, var(--accent) 10%, transparent); + --accent-wash-2: color-mix(in srgb, var(--accent) 15%, transparent); + --accent-line: color-mix(in srgb, var(--accent) 28%, transparent); + --code-bg: rgba(0, 0, 0, 0.35); + + --shadow-card: 0 1px 0 rgba(0, 0, 0, 0.5); + --shadow-toast: 0 6px 22px -10px rgba(0, 0, 0, 0.7); } From c0cc2451ef1ba13d6946ec1a6c45ea640a9573a8 Mon Sep 17 00:00:00 2001 From: Benoit TRAVERS Date: Thu, 16 Jul 2026 01:37:27 +0200 Subject: [PATCH 2/2] fix(logos): center the beet inside the unthrown ring The engraved beet sat ~15px right of the ring center and its leaves collided with the ring stroke. Recentered the figure (and dropped the third leaf that skewed the bounding box) so the mark reads cleanly at navbar sizes too. --- apps/website/public/logos/unthrown-dark.svg | 38 ++++++-------------- apps/website/public/logos/unthrown-light.svg | 38 ++++++-------------- 2 files changed, 22 insertions(+), 54 deletions(-) diff --git a/apps/website/public/logos/unthrown-dark.svg b/apps/website/public/logos/unthrown-dark.svg index a897b72..4eb68f9 100644 --- a/apps/website/public/logos/unthrown-dark.svg +++ b/apps/website/public/logos/unthrown-dark.svg @@ -1,31 +1,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/apps/website/public/logos/unthrown-light.svg b/apps/website/public/logos/unthrown-light.svg index ab574c9..dc6d76e 100644 --- a/apps/website/public/logos/unthrown-light.svg +++ b/apps/website/public/logos/unthrown-light.svg @@ -1,31 +1,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + +