diff --git a/.changeset/beetroot-stack-rebrand.md b/.changeset/beetroot-stack-rebrand.md new file mode 100644 index 0000000..ec3a2b1 --- /dev/null +++ b/.changeset/beetroot-stack-rebrand.md @@ -0,0 +1,11 @@ +--- +"@btravstack/theme": minor +--- + +Beetroot Stack rebrand — studied from tanstack.com: a near-neutral black canvas where each package glows in its own accent. + +- Near-black canvas (`oklch(13% 0.006 310)`) is the canonical dark-default; the light scheme remains the daylight fallback. +- Typography: Geist (display 700/800 + body — weight contrast does the branding) + JetBrains Mono replace Montserrat. +- The multi-accent stack: new `--pkg-amqp` / `--pkg-temporal` / `--pkg-unthrown` / `--pkg-demesne` tokens expose the official per-package accents so the landing (and any consumer) can show the stack in color; the org beetroot pink stays the `--accent` knob for wordmark, links and CTAs. +- Elevation through surface lightness (no hairline card borders), solid ink hero name (no gradient text), floating hero mark kept as the one character moment, no glow washes. +- Non-breaking: all semantic tokens keep their names and the one-knob rebrand contract is unchanged — each docs site still overrides only `--accent` with its package color, which is exactly the studied per-library accent system. diff --git a/.hallmark/log.json b/.hallmark/log.json new file mode 100644 index 0000000..6a84823 --- /dev/null +++ b/.hallmark/log.json @@ -0,0 +1,50 @@ +[ + { + "date": "2026-07-16", + "scope": "app", + "macrostructure": "Ecosystem Index", + "theme": "studied-DNA (source: https://tanstack.com/)", + "theme_axes": "dark / geometric-sans / multi-accent (org beetroot + per-package)", + "vibe": "neutral black canvas, colorful stack, real numbers", + "enrichment": "H9 original gradient mascot (Tier-B, kept)", + "nav": "N1b", + "footer": "Ft3 index (genuine hub)", + "brief": "btravstack org rebrand v5 \u2014 Beetroot Stack, studied from tanstack.com (user's stated inspiration); reworked onto design/beetroot-nocturne (PR #30)" + }, + { + "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 \u2014 Beetroot Atlas (LIKED by user; PR #29, branch design/beetroot-atlas)" + }, + { + "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 \u2014 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 \u2014 Beetroot Riso (REJECTED by user; PR #27)" + } +] \ No newline at end of file diff --git a/apps/website/.vitepress/config.ts b/apps/website/.vitepress/config.ts index 73261e0..6d24505 100644 --- a/apps/website/.vitepress/config.ts +++ b/apps/website/.vitepress/config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "vitepress"; -const SITE_TITLE = "btravstack"; +const SITE_TITLE = "BtravStack"; const SITE_DESCRIPTION = "Type-safe building blocks for the TypeScript backend: contracts for AMQP & Temporal, typed dependency injection, and errors as values."; const SITE_URL = "https://btravstack.github.io"; @@ -46,7 +46,7 @@ export default defineConfig({ "meta", { property: "og:image:alt", - content: "btravstack — type-safe building blocks for the TypeScript backend", + content: "BtravStack — type-safe building blocks for the TypeScript backend", }, ], @@ -59,7 +59,7 @@ export default defineConfig({ "meta", { name: "twitter:image:alt", - content: "btravstack — type-safe building blocks for the TypeScript backend", + content: "BtravStack — type-safe building blocks for the TypeScript backend", }, ], @@ -94,7 +94,7 @@ export default defineConfig({ JSON.stringify({ "@context": "https://schema.org", "@type": "ItemList", - name: "btravstack packages", + name: "BtravStack packages", itemListElement: [ { "@type": "ListItem", diff --git a/apps/website/.vitepress/theme/Landing.vue b/apps/website/.vitepress/theme/Landing.vue index 5c7c877..8486f22 100644 --- a/apps/website/.vitepress/theme/Landing.vue +++ b/apps/website/.vitepress/theme/Landing.vue @@ -1,5 +1,5 @@ diff --git a/apps/website/public/favicon.svg b/apps/website/public/favicon.svg index b7615ac..f98ed29 100644 --- a/apps/website/public/favicon.svg +++ b/apps/website/public/favicon.svg @@ -1,12 +1,25 @@ - - - - - - - - - - - - \ 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..b4c2412 100644 --- a/apps/website/public/logos/amqp-contract-dark.svg +++ b/apps/website/public/logos/amqp-contract-dark.svg @@ -1 +1,25 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/amqp-contract-light.svg b/apps/website/public/logos/amqp-contract-light.svg index 2aa8ee6..74ebe82 100644 --- a/apps/website/public/logos/amqp-contract-light.svg +++ b/apps/website/public/logos/amqp-contract-light.svg @@ -1 +1,25 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/btravstack-dark.svg b/apps/website/public/logos/btravstack-dark.svg index 89f4f6c..44db1a4 100644 --- a/apps/website/public/logos/btravstack-dark.svg +++ b/apps/website/public/logos/btravstack-dark.svg @@ -1 +1,25 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/btravstack-light.svg b/apps/website/public/logos/btravstack-light.svg index 89f4f6c..44db1a4 100644 --- a/apps/website/public/logos/btravstack-light.svg +++ b/apps/website/public/logos/btravstack-light.svg @@ -1 +1,25 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/demesne-dark.svg b/apps/website/public/logos/demesne-dark.svg index d8db6ab..76eeeac 100644 --- a/apps/website/public/logos/demesne-dark.svg +++ b/apps/website/public/logos/demesne-dark.svg @@ -1 +1,20 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/demesne-light.svg b/apps/website/public/logos/demesne-light.svg index 0301a24..706cb57 100644 --- a/apps/website/public/logos/demesne-light.svg +++ b/apps/website/public/logos/demesne-light.svg @@ -1 +1,20 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/temporal-contract-dark.svg b/apps/website/public/logos/temporal-contract-dark.svg index 374fc70..c39db23 100644 --- a/apps/website/public/logos/temporal-contract-dark.svg +++ b/apps/website/public/logos/temporal-contract-dark.svg @@ -1 +1,21 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/temporal-contract-light.svg b/apps/website/public/logos/temporal-contract-light.svg index 0c88007..7cd4493 100644 --- a/apps/website/public/logos/temporal-contract-light.svg +++ b/apps/website/public/logos/temporal-contract-light.svg @@ -1 +1,21 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/unthrown-dark.svg b/apps/website/public/logos/unthrown-dark.svg index b49df8a..09e0d5e 100644 --- a/apps/website/public/logos/unthrown-dark.svg +++ b/apps/website/public/logos/unthrown-dark.svg @@ -1 +1,26 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/logos/unthrown-light.svg b/apps/website/public/logos/unthrown-light.svg index 9d0f2b3..72c5146 100644 --- a/apps/website/public/logos/unthrown-light.svg +++ b/apps/website/public/logos/unthrown-light.svg @@ -1 +1,26 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design.md b/design.md new file mode 100644 index 0000000..8d37b71 --- /dev/null +++ b/design.md @@ -0,0 +1,150 @@ +# Design — btravstack · "Beetroot Stack" (studied DNA) + +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.** Studied from the user's stated inspiration. The org is a +*colorful stack of distinct tools* on a near-neutral black canvas: the +beetroot pink is the ORG accent (wordmark, links, CTAs, mascot), while each +package glows in **its own accent** — amqp orange, temporal indigo, unthrown +green, demesne violet. Weight-contrast typography (one grotesque at 400 vs +800), elevation through surface lightness, real numbers in the hero, and the +gradient beetroot mascot as the one character moment. + +## Provenance + +Extracted from `https://tanstack.com/` on 2026-07-16 as a **public reference +for the user's own brand** (user-stated inspiration). Sources: page HTML via +WebFetch + own headless screenshot (so the rhythm pass is covered). The DNA is +structural — macrostructure, multi-accent model, type roles, stats-hero; +tokens below are btravstack's own, not the source's. Do not copy TanStack +copy, artwork, or exact hues. + +## Genre + +atmospheric (studied register — dark, quiet, confident; no blooms). + +## Macrostructure family + +- Marketing pages (landing): **Ecosystem Index** — wordmark-led hero with the + mascot beside and a **real-stats strip** beneath (live GitHub stars, live + npm downloads, package count — never invented numbers), then the stack as + per-package-colored elevated panels, philosophy panels, "why now" cluster, + inspirations, closing CTA. +- Docs sites (VitePress content): default VitePress layout wearing the theme + tokens — floating hero mark with a subtle halo, elevated feature cards, + the site's own package accent everywhere via the one-knob override. +- Nav: **N1b three-section** — wordmark · centered links · toggle + GitHub. +- Footer: **Ft3 index** — justified: the landing is a genuine hub for four + docs sites. Brand column + Docs column + GitHub column + MIT line. No + social row, no Legal filler. + +## Theme + +**Dark near-black is canonical**; light is the daylight fallback. + +| Token | Dark (canonical) | Light | +| --- | --- | --- | +| `--bg` (canvas) | `oklch(13% 0.006 310)` ≈ #080709 | `#FAF9FA` | +| `--card` | `oklch(17% 0.007 310)` — elevation 1 | `#FFFFFF` | +| `--card-soft` | `oklch(20.5% 0.008 310)` — elevation 2 | `#F0EDF0` | +| `--text` | `oklch(95% 0.004 310)` | `#1B1218` | +| `--accent` (org) | **#E0589A** — THE knob | same (text darkens for AA) | +| `--pkg-amqp` | `#FF6600` | darken 30% at point of use | +| `--pkg-temporal` | `#6B76F2` | darken 30% at point of use | +| `--pkg-unthrown` | `#46B86C` | darken 30% at point of use | +| `--pkg-demesne` | `#B45CE0` | darken 30% at point of use | + +**The multi-accent rule (the studied signature).** The canvas and chrome are +near-neutral; color belongs to the *products*. Package names, category dots +and panel hover-glows use the package's own accent. The org accent never +paints package content, and package accents never paint org chrome. Each docs +site keeps overriding only `--accent` with its package color — that IS this +system. + +**Honest numbers.** The stats strip may only show live-fetched values +(GitHub API, npm downloads API) or real baselines; unfetched values render +as `—`, never a made-up figure. + +## Typography + +- Display + body: **Geist** — weight contrast does the branding (display + 700/800, tracking −0.03em; body 400/500). Sentence case; never italic + headers; never gradient text. +- Mono (outlier): **JetBrains Mono** — code, identifiers, tags, tabular stats. +- Wordmark: Geist 800, "btrav" in `--display-accent` + "stack" in ink. + +## Spacing / shape + +Named px scale in `packages/theme/src/tokens.css` (`--space-1…6`). Corners: +10–14px cards, 999px only where legacy pills remain. **No hairline card +borders** — elevation via `--card` / `--card-soft`; 1px rules only as +page-level dividers (stats strip, footer). Shadows: darkness at rest; +per-package color glow on package-panel hover only. + +## Motion + +- Easings: `--ease: cubic-bezier(0.16, 1, 0.3, 1)`; durations ≤ 0.2s. +- Three primitives: mascot bob (character moment), panel hover lift (+ the + package-colored shadow), copy toast. No blooms, no scroll reveals, no + marquees. Reduced-motion: bob stops, transitions collapse. + +## Microinteractions stance + +- Silent success (copy button → small mono toast, 1.6s). +- Hover tooltips 800ms · focus 0ms. Focus rings instant, 2px accent. + +## CTA voice + +- Primary: accent-filled rounded rectangle (10px radius), ink text, Geist 700. +- Secondary: outlined, transparent. + +## Logos + +The original soft gradient marks stay — mascot, rabbit, hourglass, no-throw, +shield. They read perfectly on the near-black canvas and each sits inside its +package-colored panel context. + +## What pages MUST share + +- The wordmark voice and the one-knob accent architecture. +- The neutral-canvas / colored-products split (the multi-accent rule). +- Geist + JetBrains Mono, elevation-not-borders, honest numbers. +- Exactly one character moment (the floating mascot) per page. + +## What pages MAY differ on + +- Macrostructure within the family for future marketing pages. +- Per-site accent hue (docs sites) — that's the system working as designed. +- 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(13% 0.006 310); + --color-ink: oklch(95% 0.004 310); + --color-accent: #E0589A; /* org */ + --pkg-amqp: #FF6600; + --pkg-temporal: #6B76F2; + --pkg-unthrown: #46B86C; + --pkg-demesne: #B45CE0; + --font-display: "Geist", sans-serif; /* 800 for display */ + --font-body: "Geist", sans-serif; + --font-mono: "JetBrains Mono", monospace; + --ease: cubic-bezier(0.16, 1, 0.3, 1); +} +``` + +## Notes — anti-patterns not to carry over from the source + +- No embedded AI prompt-box gimmick. +- No partner/sponsor tier walls (no such content). +- No invented statistics — live APIs or `—`. diff --git a/packages/theme/src/fonts.css b/packages/theme/src/fonts.css index 39e3d6d..9e0f4b0 100644 --- a/packages/theme/src/fonts.css +++ b/packages/theme/src/fonts.css @@ -1,6 +1,7 @@ /* - * 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 Stack" pairing (studied DNA). + * Geist (display 700/800 + body 400/500 — weight contrast does the branding) + * JetBrains Mono (code + labels + tabular stats) + * Loaded from Google Fonts so the system stays zero-asset and portable. */ -@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=Geist:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap'); diff --git a/packages/theme/src/style.css b/packages/theme/src/style.css index 3afaf6d..86adaaf 100644 --- a/packages/theme/src/style.css +++ b/packages/theme/src/style.css @@ -1,13 +1,16 @@ +/* Hallmark · genre: atmospheric · 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 Stack"). * * 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 + * - dark → the near-black stack canvas, per-site accent links (canonical) + * - light → warm near-white surfaces, deep-beet links (WCAG-AA) * * 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. @@ -38,8 +41,7 @@ --vp-c-brand-soft: color-mix(in srgb, var(--accent) 16%, 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); @@ -65,92 +67,67 @@ --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%); + /* Home hero: solid ink name (no gradient text) + a warm bloom that tracks + the floating mark wherever the layout places it. */ + --vp-home-hero-name-color: var(--vp-c-text-1); + --vp-home-hero-name-background: none; + --vp-home-hero-image-background-image: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 18%, transparent), transparent 72%); --vp-home-hero-image-filter: blur(8px); } -/* ── Playful flourishes — shared across every btravstack site ───── */ +/* ── Nocturne chrome — shared across every btravstack site ─────── */ + +/* No horizontal scroll, ever (clip — not hidden — so sticky keeps working). */ +html, body { overflow-x: clip; } -/* 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; } +/* Weighty display voice on the big headings. */ +.VPHero .name, +.VPHero .text { font-family: var(--display); letter-spacing: var(--tracking-hero); } +.VPHero .name { font-weight: 700; } +.VPFeature .title { letter-spacing: var(--tracking-snug); } +.VPNavBar .title { font-weight: 700; } + +/* Home hero: the gently floating mark — the one character moment per page. */ +.VPHome { overflow-x: clip; position: relative; } +.VPHero .image-container { animation: btv-floaty 6s var(--ease) 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. */ +@keyframes btv-floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } } + +/* Navbar logo sizing via VitePress's own variable. */ :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. */ +/* Feature cards: elevation through lightness + a warm glow on hover. */ .VPFeature { - border: 1px solid var(--vp-c-divider); + border: 1px solid transparent; border-radius: var(--radius-lg); - transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; + transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease); } .VPFeature:hover { - transform: translateY(-4px); - border-color: var(--vp-c-border); - box-shadow: var(--shadow-card); + transform: translateY(-3px); + box-shadow: var(--shadow-accent); } .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); + 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: confident pill, quiet press. */ +.VPButton { font-weight: 600; } +.VPButton.brand { transition: background-color var(--speed-fast) var(--ease), transform var(--speed-fast) var(--ease); } +.VPButton.brand:hover { transform: translateY(-1px); } +.VPButton.brand:active { transform: translateY(0); } -/* Rounder, pinker code */ +/* Rounded code + admonitions. */ .vp-doc :not(pre) > code { border-radius: 7px; } .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-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; @@ -160,10 +137,17 @@ } } +/* 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(--card); padding: 16px 24px; display: flex; justify-content: center; @@ -176,18 +160,18 @@ font-weight: 600; 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); } +.btv-partof-pink { color: var(--text-accent); } @media (prefers-reduced-motion: reduce) { .VPHero .image-container { animation: none; } diff --git a/packages/theme/src/tokens.css b/packages/theme/src/tokens.css index 5167a3a..8d34140 100644 --- a/packages/theme/src/tokens.css +++ b/packages/theme/src/tokens.css @@ -1,23 +1,33 @@ +/* Hallmark · genre: atmospheric (studied) · macrostructure: Ecosystem Index · design-system: design.md · designed-as-app + * theme: studied-DNA (source: https://tanstack.com/) · studied: yes · DNA-source: url + screenshot + * paper: oklch(13% 0.006 310) near-neutral black · org accent: #E0589A · per-package accents on neutral canvas + * observed-DNA: neutral-black canvas · multi-accent per library · heavy geometric grotesque 800 · real-stats hero + * display: Geist 800 · body: Geist 400 · outlier: JetBrains Mono + * nav: N1b three-section · footer: Ft3 index (genuine hub) · context: user-provided + * pre-emit critique: P5 H4 E4 S5 R4 V4 + */ + /* - * btravstack design tokens — the single source of truth. + * btravstack design tokens — the single source of truth ("Beetroot Stack"). * - * 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. + * Studied from tanstack.com: a near-neutral black canvas where each package + * glows in ITS OWN accent — the org reads as a colorful stack of distinct + * tools, not a monochrome brand. The beetroot pink is the ORG accent + * (wordmark, links, org CTAs); the per-package accents (amqp orange, + * temporal indigo, unthrown green, demesne violet) carry the packages. + * Elevation through lightness, weight-contrast typography, real numbers. * - * 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"]`. + * 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 `.light` / `[data-theme=light]`. * - * Fills vs text: keep --accent / --green for FILLS (buttons, glow, 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. + * Fills vs text: keep --accent / --green for FILLS (buttons, washes); use + * --text-accent / --text-green for COLORED TEXT and icons — light mode + * darkens those for WCAG-AA on white. */ :root { - /* ── Brand palette (raw beetroot) ─────────────────────────────── */ - --bt-pink: #E0589A; /* beetroot flesh — primary accent */ + /* ── Brand palette (raw beetroot + the stack accents) ────────── */ + --bt-pink: #E0589A; /* beetroot flesh — org accent */ --bt-pink-soft: #EE9CC4; /* highlight veins */ --bt-magenta: #CE3D80; /* mid root */ --bt-deep: #8E1A52; /* dark root */ @@ -26,60 +36,69 @@ --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-ink: #16060E; /* near-black, for text on accent */ + + /* The stack — official per-package accents (docs sites set these as + their own --accent; the landing shows them side by side). */ + --pkg-amqp: #FF6600; + --pkg-temporal: #6B76F2; + --pkg-unthrown: #46B86C; + --pkg-demesne: #B45CE0; - /* ── Surfaces (dark scheme — the canonical btravstack look) ───── */ - --bg: #15101c; - --bg-grad: #1b1326; /* radial hero glow inner stop */ - --card: #1d1726; - --card-soft: #241c31; + /* ── Surfaces (DARK scheme — canonical: near-neutral black) ──── */ + --bg: oklch(13% 0.006 310); /* ≈ #080709 — the canvas */ + --bg-grad: oklch(15% 0.007 310); /* legacy alias */ + --card: oklch(17% 0.007 310); /* elevation step 1 ≈ #100f12 */ + --card-soft: oklch(20.5% 0.008 310); /* elevation step 2 ≈ #18161a */ - /* ── Borders / dividers ──────────────────────────────────────── */ - --border: rgba(255, 255, 255, 0.08); - --border-2: rgba(255, 255, 255, 0.12); + /* ── Dividers — subtle; elevation carries structure ──────────── */ + --border: color-mix(in srgb, var(--text) 8%, transparent); + --border-2: color-mix(in srgb, var(--text) 14%, transparent); /* ── Text ────────────────────────────────────────────────────── */ - --text: #F5EFF3; - --muted: #AEA1BC; - --faint: #7E7290; + --text: oklch(95% 0.004 310); + --muted: oklch(72% 0.006 310); + --faint: oklch(58% 0.006 310); - /* ── Accent — the single brand knob (v1.4.0) ───────────────────── + /* ── Org 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 org default; each docs site overrides + it with its package accent — that per-site color IS the studied + multi-accent system. */ --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: #16060E; /* 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); /* highlight vein */ + --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 */ + /* ── Accent-as-TEXT (split from fill; light darkens it for AA) ── */ + --text-accent: var(--accent); /* on near-black the accent reads fine as text */ --text-green: #46B86C; - /* 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 — the accent as the LARGE display wordmark color. */ --display-accent: var(--accent); - /* ── Glass (frosted sticky headers) ──────────────────────────── */ - --glass: rgba(21, 16, 28, 0.66); + /* ── Focus — instant, never animated ─────────────────────────── */ + --focus: var(--accent); - /* ── 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 */ + /* ── Glass (frosted sticky header) ───────────────────────────── */ + --glass: color-mix(in srgb, var(--bg) 78%, transparent); + + /* ── Washes / lines / code inset ─────────────────────────────── */ + --accent-wash: color-mix(in srgb, var(--accent) 9%, transparent); + --accent-wash-2: color-mix(in srgb, var(--accent) 15%, transparent); + --accent-line: color-mix(in srgb, var(--accent) 26%, transparent); + --code-bg: rgba(0, 0, 0, 0.45); /* inline code inset */ - /* ── Typography: families ────────────────────────────────────── */ - --sans: 'Montserrat', 'Segoe UI', system-ui, sans-serif; - --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; + /* ── Typography: families (weight contrast does the branding) ── */ + --display: 'Geist', 'Segoe UI', system-ui, sans-serif; + --sans: 'Geist', 'Segoe UI', system-ui, sans-serif; + --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; /* ── Typography: weights ─────────────────────────────────────── */ --fw-medium: 500; @@ -88,35 +107,35 @@ --fw-extrabold: 800; /* ── Typography: display / heading scale (clamped, fluid) ─────── */ - --fs-hero: clamp(52px, 9vw, 92px); + --fs-hero: clamp(44px, 6vw + 10px, 88px); --fs-h1: clamp(34px, 6vw, 56px); - --fs-h2: clamp(28px, 4vw, 40px); - --fs-h3: 23px; - --fs-h4: 18px; + --fs-h2: clamp(26px, 3.4vw, 36px); + --fs-h3: 20px; + --fs-h4: 17px; /* ── Typography: body / UI scale ─────────────────────────────── */ - --fs-lead: clamp(20px, 2.6vw, 26px); + --fs-lead: clamp(18px, 2.2vw, 22px); --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; + --tracking-hero: -0.03em; + --tracking-tight: -0.02em; + --tracking-snug: -0.01em; + --tracking-eyebrow: 0.08em; + --lh-tight: 1.06; --lh-snug: 1.3; --lh-body: 1.6; /* ── Radii ───────────────────────────────────────────────────── */ - --radius-sm: 9px; - --radius: 12px; - --radius-lg: 16px; - --radius-xl: 20px; - --radius-2xl: 24px; + --radius-sm: 8px; + --radius: 10px; + --radius-lg: 14px; + --radius-xl: 18px; + --radius-2xl: 22px; --radius-pill: 999px; /* ── Spacing scale ───────────────────────────────────────────── */ @@ -127,51 +146,50 @@ --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 — depth from darkness; no glow halos ────────────── */ + --shadow-card: 0 14px 40px -18px rgba(0, 0, 0, 0.8); + --shadow-accent: 0 8px 28px -10px color-mix(in srgb, var(--accent) 40%, transparent); + --shadow-toast: 0 14px 36px -14px rgba(0, 0, 0, 0.85); /* ── Layout & motion ─────────────────────────────────────────── */ --container: 1180px; --speed-fast: 0.15s; --speed: 0.2s; - --ease: ease; + --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. + * Light scheme — neutral near-whites for daylight readers. Only the semantic + * tokens flip; the raw palettes never change. Brand FILLS stay vibrant + * (dark ink on top); accent-as-TEXT darkens for WCAG-AA on white. Per-package + * colors darken via color-mix at the point of use. */ :root:not(.dark), [data-theme="light"], .light { - --bg: #FBF7F9; - --bg-grad: #FCEDF4; + --bg: #FAF9FA; + --bg-grad: #F4F1F4; --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); + --card-soft: #F0EDF0; + + --border: rgba(22, 6, 14, 0.12); + --border-2: rgba(22, 6, 14, 0.2); + + --text: #1B1218; + --muted: #5D555B; + --faint: #766E74; + + --text-accent: color-mix(in srgb, var(--accent), #000 26%); /* AA on white */ + --text-green: #15683A; + --focus: color-mix(in srgb, var(--accent), #000 16%); + + --glass: color-mix(in srgb, #ffffff 78%, transparent); + --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-deep) 36%, transparent); + --code-bg: color-mix(in srgb, var(--text) 5%, #fff); + + --shadow-card: 0 14px 36px -22px rgba(22, 6, 14, 0.4); + --shadow-accent: 0 8px 24px -10px color-mix(in srgb, var(--accent) 35%, transparent); + --shadow-toast: 0 14px 32px -14px rgba(22, 6, 14, 0.25); }