Skip to content

Commit 6d8c1c5

Browse files
authored
Merge pull request #20 from btravstack/design/refined-theme
Accent-driven theme (1.4.0) + demesne on the landing
2 parents f8e9f48 + 6eceed4 commit 6d8c1c5

6 files changed

Lines changed: 73 additions & 36 deletions

File tree

.changeset/accent-driven-theme.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@btravstack/theme": minor
3+
---
4+
5+
Make the accent a single configurable knob. Every accent-derived value (deep, hover, soft, washes, hero glow, feature-icon chip, brand shades, light-mode AA text) now derives from `--accent` via `color-mix`, so a consumer site rebrands the whole theme with just:
6+
7+
```css
8+
:root { --accent: #FF6600; }
9+
```
10+
11+
The beetroot pink remains the default, so the btravstack org site is unchanged. `--text-accent` and `--accent-contrast` stay overridable for fine-tuning light-mode link contrast or ink-on-fill.

apps/website/.vitepress/theme/Landing.vue

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ const projects: Project[] = [
5454
install: "pnpm add unthrown",
5555
repo: "https://github.com/btravstack/unthrown", docs: "https://btravstack.github.io/unthrown/",
5656
},
57+
{
58+
tag: "Wiring", name: "demesne", pkg: "demesne",
59+
logo: "/logos/demesne", repoFull: "btravstack/demesne", stars0: 0,
60+
blurb: "Type-safe dependency injection. A container holds your services' domain and provides it — with requirements and construction errors tracked in the type system.",
61+
points: ["Missing wiring is a compile error", "Construction errors in the result type", "No decorators, no reflect-metadata"],
62+
install: "pnpm add demesne",
63+
repo: "https://github.com/btravstack/demesne", docs: "https://btravstack.github.io/demesne/",
64+
},
5765
];
5866
5967
const stars = reactive<Record<string, number>>(
@@ -88,7 +96,7 @@ onMounted(() => {
8896
<header class="btv-head">
8997
<div class="btv-head-inner">
9098
<a href="#top" class="btv-brand">
91-
<svg viewBox="0 0 100 124" width="26" height="32" fill="none"><g transform="translate(50,30)"><g transform="rotate(-33)"><path d="M0,3 C-6,-7 -6,-18 0,-27 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><g transform="rotate(33)"><path d="M0,3 C-6,-7 -6,-18 0,-27 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><path d="M0,5 C-6.5,-7 -6.5,-19 0,-30 C6.5,-19 6.5,-7 0,5 Z" fill="#3DAE62"/></g><path d="M50,30 C29,28 17,42 17,57 C17,77 38,100 50,107 Z" fill="#CE3D80"/><path d="M50,30 C71,28 83,42 83,57 C83,77 62,100 50,107 Z" fill="#8E1A52"/><path d="M30,46 C26,53 27,65 33,74" stroke="#EE9CC4" stroke-width="3.2" stroke-linecap="round" opacity="0.5"/><path d="M50,107 C49,114 47,118 44,122" stroke="#7A1646" stroke-width="2.6" stroke-linecap="round"/><path d="M50,107 C51,114 53,117 56,120" stroke="#7A1646" stroke-width="2.6" stroke-linecap="round"/></svg>
99+
<svg viewBox="0 0 100 124" width="26" height="32" fill="none" aria-hidden="true" focusable="false"><g transform="translate(50,34)"><g transform="rotate(-35)"><path d="M0,3 C-6,-7 -6,-18 0,-28 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><g transform="rotate(35)"><path d="M0,3 C-6,-7 -6,-18 0,-28 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><path d="M0,4 C-7,-8 -7,-20 0,-32 C7,-20 7,-8 0,4 Z" fill="#3DAE62"/></g><path d="M50,32 C29,30 17,44 17,60 C17,79 37,99 50,107 Z" fill="#CE3D80"/><path d="M50,32 C71,30 83,44 83,60 C83,79 63,99 50,107 Z" fill="#8E1A52"/><path d="M31,48 C27,55 27.5,65 32,73" stroke="#EE9CC4" stroke-width="4" stroke-linecap="round" fill="none" opacity="0.55"/><path d="M46.5,104 C46.5,112 44,118 39,124 C47,120 52.5,112 53.5,104 Z" fill="#8E1A52"/></svg>
92100
<span class="btv-word"><span class="btv-pink">btrav</span><span>stack</span></span>
93101
</a>
94102
<nav class="btv-nav">
@@ -112,7 +120,7 @@ onMounted(() => {
112120
<div class="btv-glow"></div>
113121
<div class="btv-hero-inner">
114122
<div class="btv-float">
115-
<svg viewBox="0 0 100 124" width="116" height="144" fill="none"><g transform="translate(50,30)"><g transform="rotate(-33)"><path d="M0,3 C-6,-7 -6,-18 0,-27 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><g transform="rotate(33)"><path d="M0,3 C-6,-7 -6,-18 0,-27 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><path d="M0,5 C-6.5,-7 -6.5,-19 0,-30 C6.5,-19 6.5,-7 0,5 Z" fill="#3DAE62"/></g><path d="M50,30 C29,28 17,42 17,57 C17,77 38,100 50,107 Z" fill="#CE3D80"/><path d="M50,30 C71,28 83,42 83,57 C83,77 62,100 50,107 Z" fill="#8E1A52"/><path d="M30,46 C26,53 27,65 33,74" stroke="#EE9CC4" stroke-width="3.2" stroke-linecap="round" opacity="0.5"/><path d="M50,107 C49,114 47,118 44,122" stroke="#7A1646" stroke-width="2.6" stroke-linecap="round"/><path d="M50,107 C51,114 53,117 56,120" stroke="#7A1646" stroke-width="2.6" stroke-linecap="round"/></svg>
123+
<svg viewBox="0 0 100 124" width="116" height="144" fill="none" aria-hidden="true" focusable="false"><g transform="translate(50,34)"><g transform="rotate(-35)"><path d="M0,3 C-6,-7 -6,-18 0,-28 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><g transform="rotate(35)"><path d="M0,3 C-6,-7 -6,-18 0,-28 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><path d="M0,4 C-7,-8 -7,-20 0,-32 C7,-20 7,-8 0,4 Z" fill="#3DAE62"/></g><path d="M50,32 C29,30 17,44 17,60 C17,79 37,99 50,107 Z" fill="#CE3D80"/><path d="M50,32 C71,30 83,44 83,60 C83,79 63,99 50,107 Z" fill="#8E1A52"/><path d="M31,48 C27,55 27.5,65 32,73" stroke="#EE9CC4" stroke-width="4" stroke-linecap="round" fill="none" opacity="0.55"/><path d="M46.5,104 C46.5,112 44,118 39,124 C47,120 52.5,112 53.5,104 Z" fill="#8E1A52"/></svg>
116124
</div>
117125
<h1 class="btv-title"><span class="btv-pink">btrav</span><span>stack</span></h1>
118126
<p class="btv-lead">An expressive, robust TypeScript backend.</p>
@@ -145,7 +153,7 @@ onMounted(() => {
145153

146154
<section id="projects" class="btv-section">
147155
<p class="btv-eyebrow">The packages</p>
148-
<h2 class="btv-h2" style="margin-bottom:30px">Three libraries, one stack.</h2>
156+
<h2 class="btv-h2" style="margin-bottom:30px">Four libraries, one stack.</h2>
149157
<div class="grid-3 btv-projects">
150158
<div v-for="p in projects" :key="p.name" class="pcard btv-pcard">
151159
<div class="btv-pcard-top">
@@ -196,7 +204,7 @@ onMounted(() => {
196204
<div class="foot-grid btv-foot-grid">
197205
<div>
198206
<div class="btv-foot-brand">
199-
<svg viewBox="0 0 100 124" width="24" height="30" fill="none"><g transform="translate(50,30)"><g transform="rotate(-33)"><path d="M0,3 C-6,-7 -6,-18 0,-27 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><g transform="rotate(33)"><path d="M0,3 C-6,-7 -6,-18 0,-27 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><path d="M0,5 C-6.5,-7 -6.5,-19 0,-30 C6.5,-19 6.5,-7 0,5 Z" fill="#3DAE62"/></g><path d="M50,30 C29,28 17,42 17,57 C17,77 38,100 50,107 Z" fill="#CE3D80"/><path d="M50,30 C71,28 83,42 83,57 C83,77 62,100 50,107 Z" fill="#8E1A52"/><path d="M50,107 C49,114 47,118 44,122" stroke="#7A1646" stroke-width="2.6" stroke-linecap="round"/><path d="M50,107 C51,114 53,117 56,120" stroke="#7A1646" stroke-width="2.6" stroke-linecap="round"/></svg>
207+
<svg viewBox="0 0 100 124" width="24" height="30" fill="none" aria-hidden="true" focusable="false"><g transform="translate(50,34)"><g transform="rotate(-35)"><path d="M0,3 C-6,-7 -6,-18 0,-28 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><g transform="rotate(35)"><path d="M0,3 C-6,-7 -6,-18 0,-28 C6,-18 6,-7 0,3 Z" fill="#2C8B4E"/></g><path d="M0,4 C-7,-8 -7,-20 0,-32 C7,-20 7,-8 0,4 Z" fill="#3DAE62"/></g><path d="M50,32 C29,30 17,44 17,60 C17,79 37,99 50,107 Z" fill="#CE3D80"/><path d="M50,32 C71,30 83,44 83,60 C83,79 63,99 50,107 Z" fill="#8E1A52"/><path d="M31,48 C27,55 27.5,65 32,73" stroke="#EE9CC4" stroke-width="4" stroke-linecap="round" fill="none" opacity="0.55"/><path d="M46.5,104 C46.5,112 44,118 39,124 C47,120 52.5,112 53.5,104 Z" fill="#8E1A52"/></svg>
200208
<span class="btv-word btv-foot-word"><span class="btv-pink">btrav</span><span>stack</span></span>
201209
</div>
202210
<p class="btv-foot-tag">Type-safe building blocks for the TypeScript backend. Open source, MIT.</p>
@@ -206,12 +214,14 @@ onMounted(() => {
206214
<a class="foot-link" href="https://github.com/btravstack/amqp-contract" target="_blank" rel="noopener">amqp-contract</a>
207215
<a class="foot-link" href="https://github.com/btravstack/temporal-contract" target="_blank" rel="noopener">temporal-contract</a>
208216
<a class="foot-link" href="https://github.com/btravstack/unthrown" target="_blank" rel="noopener">unthrown</a>
217+
<a class="foot-link" href="https://github.com/btravstack/demesne" target="_blank" rel="noopener">demesne</a>
209218
</div>
210219
<div class="btv-foot-col">
211220
<p class="btv-foot-h">Docs</p>
212221
<a class="foot-link" href="https://btravstack.github.io/amqp-contract/" target="_blank" rel="noopener">amqp-contract</a>
213222
<a class="foot-link" href="https://btravstack.github.io/temporal-contract/" target="_blank" rel="noopener">temporal-contract</a>
214223
<a class="foot-link" href="https://btravstack.github.io/unthrown/" target="_blank" rel="noopener">unthrown</a>
224+
<a class="foot-link" href="https://btravstack.github.io/demesne/" target="_blank" rel="noopener">demesne</a>
215225
</div>
216226
<div class="btv-foot-col">
217227
<p class="btv-foot-h">Org</p>
@@ -328,8 +338,10 @@ onMounted(() => {
328338
.btv-prin-item:first-child { border-top: none; padding-top: 30px; }
329339
}
330340
331-
/* Project cards */
332-
.btv-projects { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
341+
/* Project cards — four across on wide screens ("four libraries, one stack"),
342+
two up on tablet, one on mobile (the .grid-3 rule below handles ≤880). */
343+
.btv-projects { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; }
344+
@media (max-width: 1080px) { .btv-projects { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
333345
.btv-pcard { display: flex; flex-direction: column; background: var(--card); border: 1px solid var(--border); border-radius: 20px; padding: 26px; box-shadow: var(--card-rest-shadow); }
334346
.pcard { transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease; }
335347
.pcard:hover { transform: translateY(-4px); border-color: var(--border-2); box-shadow: var(--shadow-card); }
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

packages/theme/src/style.css

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@
3030
--vp-c-border: var(--border-2);
3131
--vp-c-gutter: var(--border);
3232

33-
/* Brand — links/active use accent-as-TEXT (darkens on light for AA) */
33+
/* Brand — links/active use accent-as-TEXT (darkens on light for AA).
34+
All shades derive from --accent, so a site override rebrands everything. */
3435
--vp-c-brand-1: var(--text-accent);
35-
--vp-c-brand-2: var(--bt-pink-soft);
36+
--vp-c-brand-2: var(--accent-soft);
3637
--vp-c-brand-3: var(--accent-deep);
37-
--vp-c-brand-soft: rgba(var(--accent-rgb), 0.16);
38+
--vp-c-brand-soft: color-mix(in srgb, var(--accent) 16%, transparent);
3839
--vp-c-brand: var(--text-accent); /* legacy alias */
3940

4041
/* Inline code — accent-as-text so it stays readable on the (scheme-aware)
@@ -49,7 +50,7 @@
4950
--vp-button-brand-bg: var(--accent);
5051
--vp-button-brand-text: var(--accent-contrast);
5152
--vp-button-brand-border: transparent;
52-
--vp-button-brand-hover-bg: var(--bt-magenta);
53+
--vp-button-brand-hover-bg: var(--accent-hover);
5354
--vp-button-brand-hover-text: var(--accent-contrast);
5455
--vp-button-brand-hover-border: transparent;
5556
--vp-button-brand-active-bg: var(--accent-deep);
@@ -60,14 +61,14 @@
6061
--vp-custom-block-tip-bg: rgba(var(--green-rgb), 0.08);
6162
--vp-custom-block-tip-border: rgba(var(--green-rgb), 0.30);
6263
--vp-custom-block-tip-text: var(--text-green);
63-
--vp-custom-block-warning-bg: rgba(var(--accent-rgb), 0.08);
64+
--vp-custom-block-warning-bg: color-mix(in srgb, var(--accent) 8%, transparent);
6465
--vp-custom-block-warning-border: var(--accent-line);
6566
--vp-custom-block-warning-text: var(--text-accent);
6667

67-
/* Home hero: gradient name + beetroot glow behind the logo */
68+
/* Home hero: gradient name + accent glow behind the logo */
6869
--vp-home-hero-name-color: transparent;
69-
--vp-home-hero-name-background: linear-gradient(120deg, var(--text-accent), var(--bt-pink-soft));
70-
--vp-home-hero-image-background-image: radial-gradient(closest-side, rgba(var(--accent-rgb), 0.30), transparent 72%);
70+
--vp-home-hero-name-background: linear-gradient(120deg, var(--text-accent), var(--accent-soft));
71+
--vp-home-hero-image-background-image: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 30%, transparent), transparent 72%);
7172
--vp-home-hero-image-filter: blur(8px);
7273
}
7374

@@ -81,7 +82,7 @@
8182
image on the right — a hard-centered glow there just floats, disconnected. */
8283
.VPHome { overflow-x: clip; }
8384
.VPHero .image-container { animation: btv-floaty 6s ease-in-out infinite; }
84-
.VPHero .image-src { filter: drop-shadow(0 18px 40px rgba(142, 26, 82, 0.4)); }
85+
.VPHero .image-src { filter: drop-shadow(0 18px 40px color-mix(in srgb, var(--accent-deep) 45%, transparent)); }
8586
@keyframes btv-floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
8687

8788
/* Navbar logo: VitePress's 24px mark reads as a speck next to the wordmark.
@@ -116,7 +117,7 @@
116117
.VPFeature .icon {
117118
border-radius: var(--radius) !important;
118119
background: var(--vp-c-brand-soft) !important;
119-
border: 1px solid rgba(var(--accent-rgb), 0.22);
120+
border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
120121
}
121122
.VPFeature .title { letter-spacing: -0.3px; }
122123

packages/theme/src/tokens.css

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,38 @@
4343
--muted: #AEA1BC;
4444
--faint: #7E7290;
4545

46-
/* ── Accent (brand FILL) ─────────────────────────────────────── */
47-
--accent: #E0589A;
48-
--accent-deep: #8E1A52;
46+
/* ── Accent — the single brand knob (v1.4.0) ─────────────────────
47+
Set ONE value, `--accent`, and the whole theme rebrands: the deep,
48+
hover, soft and wash shades below all derive from it via color-mix,
49+
so a consumer site needs only `:root { --accent: #FF6600 }`. The
50+
beetroot pink here is the default (the btravstack org brand); the
51+
docs sites override it with their own accent.
52+
(Optional finer knobs: override --text-accent for a hand-tuned
53+
light-mode AA link color, or --accent-contrast for ink on the fill.) */
54+
--accent: #E0589A; /* ← the knob */
4955
--accent-contrast: #1a0c14; /* readable text on an accent fill */
50-
--accent-rgb: 224, 88, 154;
56+
--accent-deep: color-mix(in srgb, var(--accent), #000 42%); /* deep root — active/press */
57+
--accent-hover: color-mix(in srgb, var(--accent), #000 18%); /* button hover fill */
58+
--accent-soft: color-mix(in srgb, var(--accent) 62%, #fff); /* highlight vein / brand-2 */
59+
--accent-rgb: 224, 88, 154; /* legacy: prefer color-mix over rgba(var(--accent-rgb),…) */
5160
--green: #46B86C;
5261
--green-rgb: 70, 184, 108;
5362

54-
/* ── Accent-as-TEXT (split from fill; light darkens these for AA) ─ */
55-
--text-accent: #E0589A;
63+
/* ── Accent-as-TEXT (split from fill; light darkens it for AA) ─── */
64+
--text-accent: var(--accent); /* on dark plum the accent reads fine as text */
5665
--text-green: #46B86C;
5766

58-
/* Display accent — the brand pink as the LARGE display wordmark color in
67+
/* Display accent — the accent as the LARGE display wordmark color in
5968
both schemes (large text only needs 3:1, so it can stay fully vibrant). */
60-
--display-accent: #E0589A;
69+
--display-accent: var(--accent);
6170

6271
/* ── Glass (frosted sticky headers) ──────────────────────────── */
6372
--glass: rgba(21, 16, 28, 0.66);
6473

6574
/* ── Accent washes / lines / code inset ──────────────────────── */
66-
--accent-wash: rgba(224, 88, 154, 0.10);
67-
--accent-wash-2: rgba(224, 88, 154, 0.16);
68-
--accent-line: rgba(224, 88, 154, 0.25);
75+
--accent-wash: color-mix(in srgb, var(--accent) 10%, transparent);
76+
--accent-wash-2: color-mix(in srgb, var(--accent) 16%, transparent);
77+
--accent-line: color-mix(in srgb, var(--accent) 25%, transparent);
6978
--code-bg: rgba(0, 0, 0, 0.35); /* inline code inset */
7079

7180
/* ── Typography: families ────────────────────────────────────── */
@@ -120,7 +129,7 @@
120129

121130
/* ── Shadows ─────────────────────────────────────────────────── */
122131
--shadow-card: 0 24px 60px -28px rgba(0, 0, 0, 0.8);
123-
--shadow-accent: 0 12px 30px -10px rgba(224, 88, 154, 0.6);
132+
--shadow-accent: 0 12px 30px -10px color-mix(in srgb, var(--accent) 60%, transparent);
124133
--shadow-toast: 0 18px 40px -16px rgba(0, 0, 0, 0.8);
125134

126135
/* ── Layout & motion ─────────────────────────────────────────── */
@@ -150,17 +159,19 @@
150159
--muted: #5E5159; /* ~7.1:1 on white */
151160
--faint: #766678; /* ~5.0:1 on white (was #978996, 3.1:1 — failed AA) */
152161

153-
--accent: #E0589A; /* fill unchanged (dark ink on top) */
154-
--text-accent: #D6246F; /* small labels / links — vivid raspberry, ~4.6:1 AA on white */
162+
/* --accent (the FILL) is NOT overridden here — it stays the site's vibrant
163+
accent in both schemes (dark ink sits on top). Only accent-as-TEXT darkens
164+
for WCAG-AA on white. */
165+
--text-accent: color-mix(in srgb, var(--accent), #000 22%); /* darkened accent, ~AA on white */
155166
--text-green: #15683A; /* ~5.2:1 on white — affirmative text */
156167

157-
--accent-wash: rgba(224, 88, 154, 0.12);
158-
--accent-wash-2: rgba(224, 88, 154, 0.16);
159-
--accent-line: rgba(142, 26, 82, 0.22);
168+
--accent-wash: color-mix(in srgb, var(--accent) 12%, transparent);
169+
--accent-wash-2: color-mix(in srgb, var(--accent) 16%, transparent);
170+
--accent-line: color-mix(in srgb, var(--accent-deep) 40%, transparent);
160171
--glass: rgba(255, 255, 255, 0.72);
161-
--code-bg: #F4EAF0;
172+
--code-bg: color-mix(in srgb, var(--accent) 8%, #fff);
162173

163-
--shadow-card: 0 18px 44px -26px rgba(142, 26, 82, 0.30);
164-
--shadow-accent: 0 12px 26px -10px rgba(224, 88, 154, 0.45);
174+
--shadow-card: 0 18px 44px -26px color-mix(in srgb, var(--accent-deep) 55%, transparent);
175+
--shadow-accent: 0 12px 26px -10px color-mix(in srgb, var(--accent) 45%, transparent);
165176
--shadow-toast: 0 18px 40px -16px rgba(40, 16, 30, 0.18);
166177
}

0 commit comments

Comments
 (0)