Skip to content

Commit 346de95

Browse files
authored
Merge pull request #6 from btravstack/feat/design-system-tokens
Implement the btravstack Design System token model
2 parents dc1f852 + f8b28a2 commit 346de95

4 files changed

Lines changed: 201 additions & 138 deletions

File tree

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

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ onMounted(() => {
9595
<a class="navlink nav-hide" href="#philosophy">Philosophy</a>
9696
<a class="navlink nav-hide" href="#projects" style="margin-right:6px">Projects</a>
9797
<ClientOnly>
98-
<button type="button" class="btv-toggle" :title="isDark ? 'Switch to light' : 'Switch to dark'" :aria-label="isDark ? 'Switch to light theme' : 'Switch to dark theme'" @click="toggleAppearance">
98+
<button type="button" class="btv-toggle" :title="isDark ? 'Switch to light' : 'Switch to dark'" :aria-label="isDark ? 'Switch to light theme' : 'Switch to dark theme'" :aria-pressed="isDark" @click="toggleAppearance">
9999
<svg v-if="isDark" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.5v2M12 19.5v2M4.8 4.8l1.4 1.4M17.8 17.8l1.4 1.4M2.5 12h2M19.5 12h2M4.8 19.2 6.2 17.8M17.8 6.2 19.2 4.8"/></svg>
100100
<svg v-else width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 14.5A8 8 0 1 1 9.5 4 6.4 6.4 0 0 0 20 14.5Z"/></svg>
101101
</button>
@@ -232,7 +232,6 @@ onMounted(() => {
232232

233233
<style scoped>
234234
.btv {
235-
--btv-glass: rgba(21, 16, 28, 0.66);
236235
--btv-hover: rgba(255, 255, 255, 0.05);
237236
--btv-pill: rgba(255, 255, 255, 0.04);
238237
--btv-footer: rgba(0, 0, 0, 0.2);
@@ -242,28 +241,17 @@ onMounted(() => {
242241
font-family: var(--sans);
243242
}
244243
245-
/* Light mode (system preference or the header toggle) — scoped to the landing */
244+
/* Light mode (system / toggle): surfaces, text and accent-as-text come from the
245+
shared tokens (tokens.css :root:not(.dark)); only these landing-local
246+
overlays — header/nav washes, pills, footer tint — flip here. */
246247
:global(html:not(.dark)) .btv {
247-
--bg: #fbf7fb;
248-
--bg-grad: #f4e7f0;
249-
--card: #ffffff;
250-
--card-soft: #f6eff4;
251-
--border: rgba(26, 12, 20, 0.1);
252-
--border-2: rgba(26, 12, 20, 0.16);
253-
--text: #241a22;
254-
--muted: #6b5e68;
255-
--faint: #9c8f99;
256-
--accent: #c13c79;
257-
--shadow-card: 0 20px 45px -28px rgba(142, 26, 82, 0.28);
258-
--shadow-toast: 0 18px 40px -18px rgba(142, 26, 82, 0.2);
259-
--btv-glass: rgba(251, 247, 251, 0.72);
260248
--btv-hover: rgba(26, 12, 20, 0.05);
261249
--btv-pill: rgba(26, 12, 20, 0.035);
262250
--btv-footer: rgba(26, 12, 20, 0.025);
263251
}
264252
:global(html:not(.dark)) .btv-glow { opacity: 0.55; }
265253
266-
.btv-pink { color: var(--accent); }
254+
.btv-pink { color: var(--text-accent); }
267255
.btv-toggle {
268256
display: inline-flex; align-items: center; justify-content: center;
269257
width: 38px; height: 38px;
@@ -277,7 +265,7 @@ onMounted(() => {
277265
.btv-head {
278266
position: sticky; top: 0; z-index: 50;
279267
backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
280-
background: var(--btv-glass);
268+
background: var(--glass);
281269
border-bottom: 1px solid var(--border);
282270
}
283271
.btv-head-inner { max-width: 1180px; margin: 0 auto; padding: 0 28px; height: 68px; display: flex; align-items: center; justify-content: space-between; gap: 24px; }
@@ -307,7 +295,7 @@ onMounted(() => {
307295
/* Sections */
308296
.btv-section { max-width: 1180px; margin: 0 auto; padding: 64px 28px 40px; }
309297
.btv-narrow { padding: 34px 28px 8px; }
310-
.btv-eyebrow { margin: 0; font-family: var(--mono); font-size: 13px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--accent); }
298+
.btv-eyebrow { margin: 0; font-family: var(--mono); font-size: 13px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--text-accent); }
311299
.btv-h2 { margin: 14px 0 0; font-weight: 800; letter-spacing: -1px; line-height: 1.08; font-size: clamp(28px, 4vw, 40px); }
312300
.btv-h2-wide { max-width: 680px; }
313301
.btv-section-lead { margin: 16px 0 0; max-width: 640px; font-size: 16.5px; line-height: 1.65; color: var(--muted); }
@@ -316,7 +304,7 @@ onMounted(() => {
316304
.btv-prin { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin-top: 34px; }
317305
.btv-prin-card { background: var(--card); border: 1px solid var(--border); border-radius: 16px; padding: 22px; transition: transform .2s ease, border-color .2s ease; }
318306
.btv-prin-card:hover { transform: translateY(-3px); border-color: var(--border-2); }
319-
.btv-prin-num { width: 38px; height: 38px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: rgba(224,88,154,0.12); border: 1px solid rgba(224,88,154,0.25); color: var(--accent); font-family: var(--mono); font-weight: 600; font-size: 15px; }
307+
.btv-prin-num { width: 38px; height: 38px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: var(--accent-wash); border: 1px solid var(--accent-line); color: var(--text-accent); font-family: var(--mono); font-weight: 600; font-size: 15px; }
320308
.btv-prin-title { margin: 16px 0 0; font-weight: 700; font-size: 18px; letter-spacing: -0.3px; color: var(--text); }
321309
.btv-prin-body { margin: 9px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
322310
@@ -328,10 +316,10 @@ onMounted(() => {
328316
.btv-pcard-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
329317
.btv-pcard-id { display: flex; align-items: center; gap: 11px; min-width: 0; }
330318
.btv-logo { display: block; flex: none; }
331-
.btv-tag { display: inline-flex; align-items: center; font-family: var(--mono); font-size: 11.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--accent); background: rgba(224,88,154,0.1); border: 1px solid rgba(224,88,154,0.22); padding: 5px 10px; border-radius: 999px; }
319+
.btv-tag { display: inline-flex; align-items: center; font-family: var(--mono); font-size: 11.5px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-accent); background: var(--accent-wash); border: 1px solid var(--accent-line); padding: 5px 10px; border-radius: 999px; }
332320
.btv-stars { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 600; color: var(--faint); }
333321
.btv-pname { margin: 18px 0 0; font-weight: 800; font-size: 23px; letter-spacing: -0.6px; color: var(--text); }
334-
.btv-pkg { display: inline-block; margin: 7px 0 0; font-family: var(--mono); font-size: 13px; color: var(--accent); background: none; padding: 0; }
322+
.btv-pkg { display: inline-block; margin: 7px 0 0; font-family: var(--mono); font-size: 13px; color: var(--text-accent); background: none; padding: 0; }
335323
.btv-blurb { margin: 14px 0 0; min-height: 92px; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
336324
.btv-points { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
337325
.btv-points li { display: flex; align-items: flex-start; gap: 9px; font-size: 14px; line-height: 1.4; color: var(--text); }
@@ -345,7 +333,7 @@ onMounted(() => {
345333
.btv-foot-a { display: inline-flex; align-items: center; gap: 7px; text-decoration: none; color: var(--text); font-weight: 700; font-size: 14px; }
346334
.btv-foot-muted { color: var(--muted); }
347335
.repolink { transition: color .15s; }
348-
.repolink:hover { color: var(--accent); }
336+
.repolink:hover { color: var(--text-accent); }
349337
350338
/* CTA banner */
351339
.btv-cta-wrap { padding: 30px 28px 80px; }
@@ -362,7 +350,7 @@ onMounted(() => {
362350
.btv-foot-col { display: flex; flex-direction: column; gap: 11px; }
363351
.btv-foot-h { margin: 0 0 4px; font-size: 12px; letter-spacing: 1px; text-transform: uppercase; color: var(--faint); font-weight: 700; }
364352
.foot-link { color: var(--muted); text-decoration: none; font-size: 14px; transition: color .15s; }
365-
.foot-link:hover { color: var(--accent); }
353+
.foot-link:hover { color: var(--text-accent); }
366354
.btv-foot-bottom { max-width: 1180px; margin: 0 auto; padding: 18px 28px 34px; border-top: 1px solid var(--border); display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; font-size: 13px; color: var(--faint); }
367355
.btv-foot-muted2 { color: var(--muted); }
368356
.btv-foot-mono { font-family: var(--mono); font-size: 12px; color: var(--faint); }

packages/theme/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@btravstack/theme",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "Shared VitePress theme and design tokens for btravstack sites",
55
"license": "MIT",
66
"author": "Benoit TRAVERS",
@@ -11,7 +11,9 @@
1111
"url": "git+https://github.com/btravstack/btravstack.github.io.git",
1212
"directory": "packages/theme"
1313
},
14-
"files": ["dist"],
14+
"files": [
15+
"dist"
16+
],
1517
"exports": {
1618
".": {
1719
"types": "./dist/index.d.mts",
@@ -20,7 +22,9 @@
2022
"./style.css": "./dist/style.css",
2123
"./tokens.css": "./dist/tokens.css"
2224
},
23-
"sideEffects": ["**/*.css"],
25+
"sideEffects": [
26+
"**/*.css"
27+
],
2428
"publishConfig": {
2529
"access": "public"
2630
},

packages/theme/src/style.css

Lines changed: 51 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,76 @@
11
/*
22
* @btravstack/theme — VitePress style entry.
33
*
4-
* Maps the btravstack design tokens onto VitePress's own `--vp-*` theme
5-
* variables so every btravstack site (this website + the docs sites) shares
6-
* one look. Pulled in automatically by the theme's index.ts; consumers can
7-
* also import it directly as `@btravstack/theme/style.css`.
4+
* Maps the btravstack design tokens onto VitePress's own `--vp-*` variables so
5+
* every btravstack site (the website + the docs sites) shares one look.
6+
* Because the semantic tokens (--bg, --text, --text-accent, …) flip per scheme
7+
* in tokens.css, this mapping is written once and adapts automatically:
8+
* - dark → the btravstack plum surfaces, pink links
9+
* - light → clean warm-white surfaces, brand darkened to deep beetroot
10+
* (--text-accent) so links/headings pass WCAG-AA on white
811
*
9-
* Theming covers BOTH color schemes, so it's safe to let VitePress follow
10-
* the visitor's OS preference (its default, `appearance: true`):
11-
* - dark → adopts the btravstack surface palette
12-
* - light → keeps VitePress's light surfaces, with the brand darkened to
13-
* the deep beetroot so links/headings pass WCAG-AA on white.
12+
* The `:root:root` selector raises specificity to (0,2,0) so the mapping wins
13+
* over VitePress's own `:root` / `.dark` defaults regardless of load order.
1414
*/
1515
@import "./fonts.css";
1616
@import "./tokens.css";
1717

18-
/* ── Brand defaults + typography (both schemes) ────────────────── */
19-
/* `:root:root` / `:root.dark` raise specificity to (0,2,0) so these win
20-
over VitePress's own `:root` / `.dark` defaults regardless of the order
21-
the remote @import lands in the cascade. */
2218
:root:root {
23-
--vp-c-brand-1: var(--accent);
24-
--vp-c-brand-2: var(--bt-magenta);
25-
--vp-c-brand-3: var(--accent-deep);
26-
--vp-c-brand-soft: rgba(var(--accent-rgb), 0.14);
27-
--vp-c-brand: var(--accent); /* legacy alias */
19+
/* Surfaces / text / dividers — scheme-aware via the semantic tokens */
20+
--vp-c-bg: var(--bg);
21+
--vp-c-bg-alt: var(--card);
22+
--vp-c-bg-soft: var(--card-soft);
23+
--vp-c-bg-elv: var(--card);
24+
25+
--vp-c-text-1: var(--text);
26+
--vp-c-text-2: var(--muted);
27+
--vp-c-text-3: var(--faint);
28+
29+
--vp-c-divider: var(--border);
30+
--vp-c-border: var(--border-2);
31+
--vp-c-gutter: var(--border);
32+
33+
/* Brand — links/active use accent-as-TEXT (darkens on light for AA) */
34+
--vp-c-brand-1: var(--text-accent);
35+
--vp-c-brand-2: var(--bt-pink-soft);
36+
--vp-c-brand-3: var(--accent-deep);
37+
--vp-c-brand-soft: rgba(var(--accent-rgb), 0.16);
38+
--vp-c-brand: var(--text-accent); /* legacy alias */
39+
40+
/* Inline code — accent-as-text so it stays readable on the (scheme-aware)
41+
code inset: pink on dark, deep beetroot on the light code wash */
42+
--vp-code-color: var(--text-accent);
43+
--vp-code-bg: var(--code-bg);
2844

2945
--vp-font-family-base: var(--sans);
3046
--vp-font-family-mono: var(--mono);
3147

32-
/* Solid brand buttons — dark ink on the pink fill, readable in both schemes */
33-
--vp-button-brand-bg: var(--accent);
34-
--vp-button-brand-text: var(--accent-contrast);
35-
--vp-button-brand-border: transparent;
36-
--vp-button-brand-hover-bg: var(--bt-magenta);
37-
--vp-button-brand-hover-text: var(--accent-contrast);
48+
/* Solid brand buttons — dark ink on the pink FILL, readable in both schemes */
49+
--vp-button-brand-bg: var(--accent);
50+
--vp-button-brand-text: var(--accent-contrast);
51+
--vp-button-brand-border: transparent;
52+
--vp-button-brand-hover-bg: var(--bt-magenta);
53+
--vp-button-brand-hover-text: var(--accent-contrast);
3854
--vp-button-brand-hover-border: transparent;
39-
--vp-button-brand-active-bg: var(--accent-deep);
40-
--vp-button-brand-active-text: var(--bt-cream);
55+
--vp-button-brand-active-bg: var(--accent-deep);
56+
--vp-button-brand-active-text: var(--bt-cream);
4157
--vp-button-brand-active-border: transparent;
4258

59+
/* Custom-block (admonition) tints */
60+
--vp-custom-block-tip-bg: rgba(var(--green-rgb), 0.08);
61+
--vp-custom-block-tip-border: rgba(var(--green-rgb), 0.30);
62+
--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-border: var(--accent-line);
65+
--vp-custom-block-warning-text: var(--text-accent);
66+
4367
/* Home hero: gradient name + beetroot glow behind the logo */
4468
--vp-home-hero-name-color: transparent;
45-
--vp-home-hero-name-background: linear-gradient(120deg, var(--accent), var(--bt-pink-soft));
69+
--vp-home-hero-name-background: linear-gradient(120deg, var(--text-accent), var(--bt-pink-soft));
4670
--vp-home-hero-image-background-image: radial-gradient(closest-side, rgba(var(--accent-rgb), 0.30), transparent 72%);
4771
--vp-home-hero-image-filter: blur(8px);
4872
}
4973

50-
/* ── Light scheme: darken the brand so it passes WCAG-AA on white ── */
51-
:root:not(.dark) {
52-
--vp-c-brand-1: var(--accent-deep); /* link / active text — ~7:1 on white */
53-
--vp-c-brand-2: var(--bt-deep-2); /* hover, a touch darker */
54-
--vp-c-brand-3: var(--accent-deep);
55-
--vp-c-brand-soft: rgba(var(--accent-rgb), 0.10);
56-
/* deeper gradient so the hero name reads on a light background */
57-
--vp-home-hero-name-background: linear-gradient(120deg, var(--accent-deep), var(--bt-magenta));
58-
}
59-
60-
/* ── Dark scheme: btravstack surfaces (matches the landing page) ── */
61-
:root.dark {
62-
--vp-c-bg: var(--bg);
63-
--vp-c-bg-alt: var(--card);
64-
--vp-c-bg-soft: var(--card-soft);
65-
--vp-c-bg-elv: var(--card);
66-
67-
--vp-c-text-1: var(--text);
68-
--vp-c-text-2: var(--muted);
69-
--vp-c-text-3: var(--faint);
70-
71-
--vp-c-divider: var(--border);
72-
--vp-c-border: var(--border-2);
73-
--vp-c-gutter: var(--border);
74-
75-
--vp-c-brand-1: var(--accent); /* pink reads great on dark */
76-
--vp-c-brand-2: var(--bt-pink-soft);
77-
--vp-c-brand-soft: rgba(var(--accent-rgb), 0.16);
78-
79-
/* Inline + block code */
80-
--vp-code-color: var(--bt-pink-soft);
81-
--vp-code-bg: rgba(0, 0, 0, 0.35);
82-
}
83-
8474
/* ── Playful flourishes — shared across every btravstack site ───── */
8575

8676
/* Home hero: a soft beetroot glow + a gently floating logo */

0 commit comments

Comments
 (0)