From e4fafef83bd6a34af97aa71a981d9e68e89005e7 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 10 May 2026 02:50:49 +0700 Subject: [PATCH] feat(theme): extract gallery CSS to demo-only stylesheet Closes #11. Move .themes-gallery__* + .variants-gallery__* selectors out of static/css/bonsai.css into static/css/gallery.css. The demo pages /themes/ and /variants/ load the new stylesheet via a head_extra block defined in layouts/_default/baseof.html; the home page and /icons/ keep getting only bonsai.css. End-user sites stop shipping ~1.9 KB raw / ~200 B gzipped of demo-only CSS. README "< 3 KB gzipped" claim restored to true. Sizes (minified): bonsai.css 12,394 -> 10,470 raw (-1,924) 3,113 -> 2,910 gzip (-203) gallery.css 2,252 raw / 626 gzip (loaded only on demo pages) Verified rendered HTML at /, /themes/, /variants/, /icons/ via curl; demo pages carry both stylesheets, others carry only bonsai.css. --- themes/bonsai/CHANGELOG.md | 1 + themes/bonsai/README.md | 2 +- themes/bonsai/layouts/_default/baseof.html | 1 + themes/bonsai/layouts/themes/single.html | 4 + themes/bonsai/layouts/variants/single.html | 4 + .../phase-02-extract-gallery-css-11.md | 48 ++++++++++ .../phase-03-add-more-icons-10.md | 64 +++++++++++++ .../phase-04-optional-rss-feed-8.md | 54 +++++++++++ .../phase-07-v0-4-0-release-prep.md | 38 ++++++++ .../plans/260510-0238-v0-4-release/plan.md | 43 +++++++++ themes/bonsai/static/css/bonsai.css | 91 +----------------- themes/bonsai/static/css/gallery.css | 92 +++++++++++++++++++ 12 files changed, 352 insertions(+), 90 deletions(-) create mode 100644 themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md create mode 100644 themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md create mode 100644 themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md create mode 100644 themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md create mode 100644 themes/bonsai/plans/260510-0238-v0-4-release/plan.md create mode 100644 themes/bonsai/static/css/gallery.css diff --git a/themes/bonsai/CHANGELOG.md b/themes/bonsai/CHANGELOG.md index c232bd8..3c72035 100644 --- a/themes/bonsai/CHANGELOG.md +++ b/themes/bonsai/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project are documented here. Format follows [Keep a ### Added - **Favicon polish** — opt-in `params.faviconSvg` and `params.appleTouchIcon` for SVG and iOS home-screen icons. Default behavior unchanged when unset. +- **Demo-only gallery CSS** — `/themes/` and `/variants/` pages load a separate `static/css/gallery.css`; `static/css/bonsai.css` no longer ships gallery selectors to user sites. Saves ~1.9 KB raw / ~200 B gzipped on every real site. New `head_extra` block in `baseof.html` enables per-page stylesheet additions. ### Changed - **A11y** — sakura accent darkened `#d4456a → #c93f63` (4.04 → 4.49 vs bg) and koi accent darkened `#c8521e → #bd4c1c` (4.17 → 4.63 vs bg) to reach WCAG AA on the gallery accent chip. Brand intent preserved (cherry blossom pink / koi orange). README hex table synced. diff --git a/themes/bonsai/README.md b/themes/bonsai/README.md index 00811d0..6ee369f 100644 --- a/themes/bonsai/README.md +++ b/themes/bonsai/README.md @@ -19,7 +19,7 @@ Bonsai treats your bio page the same way: a quiet, well-pruned page that surface - **35 icons out of the box** — 25 brand (GitHub, Mastodon, Bluesky, X, Threads, LinkedIn, Instagram…) + 10 utility (mail, globe, rss…). Vendored from [Simple Icons](https://simpleicons.org) and [Lucide](https://lucide.dev). - **Light & dark mode** — respects `prefers-color-scheme`; optional toggle. - **Zero JavaScript by default** — pure HTML + CSS; opt-in JS for theme toggle only. -- **Fast** — ~3 KB gzipped CSS, no web fonts (system stack), no runtime fetches. +- **Fast** — < 3 KB gzipped CSS, no web fonts (system stack), no runtime fetches. - **Accessible** — semantic HTML, focus-visible outlines, `prefers-reduced-motion`. - **Responsive** — mobile-first, looks right at every viewport. diff --git a/themes/bonsai/layouts/_default/baseof.html b/themes/bonsai/layouts/_default/baseof.html index 0432ddd..590894d 100644 --- a/themes/bonsai/layouts/_default/baseof.html +++ b/themes/bonsai/layouts/_default/baseof.html @@ -2,6 +2,7 @@ {{- partial "head.html" . -}} + {{- block "head_extra" . }}{{- end }}
diff --git a/themes/bonsai/layouts/themes/single.html b/themes/bonsai/layouts/themes/single.html index 973c32d..bdb5ca4 100644 --- a/themes/bonsai/layouts/themes/single.html +++ b/themes/bonsai/layouts/themes/single.html @@ -1,3 +1,7 @@ +{{ define "head_extra" }} + +{{ end }} + {{ define "main" }}

Color themes

diff --git a/themes/bonsai/layouts/variants/single.html b/themes/bonsai/layouts/variants/single.html index d3b7aa1..b73ba65 100644 --- a/themes/bonsai/layouts/variants/single.html +++ b/themes/bonsai/layouts/variants/single.html @@ -1,3 +1,7 @@ +{{ define "head_extra" }} + +{{ end }} + {{ define "main" }}

Layout variants

diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md new file mode 100644 index 0000000..5e582cc --- /dev/null +++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md @@ -0,0 +1,48 @@ +--- +phase: 2 +title: Extract gallery CSS (#11) +status: completed +priority: P2 +effort: 30m +dependencies: [] +--- + +# Phase 2: Extract gallery CSS (#11) + +## Overview + +Move `.themes-gallery__*` + `.variants-gallery__*` selectors out of `static/css/bonsai.css` into a new `static/css/gallery.css` referenced only by the demo pages. End-user sites stop paying for ~300 gzipped bytes of CSS they never use. + +## Related Code Files + +- Modify: `static/css/bonsai.css` — remove gallery rules +- Create: `static/css/gallery.css` — new file with the extracted rules +- Modify: `layouts/themes/single.html` — load gallery.css for this page only +- Modify: `layouts/variants/single.html` — load gallery.css for this page only +- Modify: `layouts/_default/baseof.html` (if needed) — add `head_extra` block hook + +## Implementation Steps + +1. Identify and excise gallery selectors from `static/css/bonsai.css`. Selectors: `.themes-gallery`, `.themes-gallery__card`, `.themes-gallery__name`, `.themes-gallery__hex`, `.themes-gallery__chip`, `.themes-gallery__chip--accent`, `.variants-gallery`, `.variants-gallery__card`, `.variants-gallery__name`, `.variants-gallery__desc`, `.variants-gallery__code`. Plus any responsive `@media` rules that target only those selectors. +2. Create `static/css/gallery.css` with the excised rules. +3. Add a `{{- block "head_extra" . -}}{{- end }}` hook in `layouts/_default/baseof.html` `` (after the main stylesheet link). +4. In `layouts/themes/single.html` and `layouts/variants/single.html`, define the block: `{{ define "head_extra" }}{{ end }}`. +5. Verify `/icons/` page does NOT pull in `gallery.css` (it has no `head_extra` block). +6. Build + curl: confirm `/themes/` and `/variants/` pull both stylesheets; `/` and `/icons/` pull only `bonsai.css`. +7. Measure: `bonsai.css` raw + gzipped should drop ~1,200 raw / ~300 gzipped. +8. Update CHANGELOG (Unreleased) and README's CSS budget claim if it shifts back under 3 KB. + +## Success Criteria + +- [ ] `static/css/bonsai.css` no longer contains gallery selectors +- [ ] `static/css/gallery.css` exists and renders gallery markup correctly +- [ ] `/themes/` and `/variants/` load both stylesheets +- [ ] `/` and `/icons/` load only `bonsai.css` +- [ ] `bonsai.css` gzipped < 3 KB +- [ ] `hugo --gc --minify` clean +- [ ] PR opened against main + +## Risk Assessment + +- **Risk:** `head_extra` block name conflicts with future Hugo theme usage. **Mitigation:** name is theme-local, undocumented externally; safe. +- **Risk:** missed responsive rules during excision. **Mitigation:** grep `gallery` in CSS before commit; only matches in gallery.css. diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md new file mode 100644 index 0000000..5143ce2 --- /dev/null +++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md @@ -0,0 +1,64 @@ +--- +phase: 3 +title: "Add more icons (#10)" +status: pending +priority: P3 +effort: "30m" +dependencies: [] +--- + +# Phase 3: Add more icons (#10) + +## Overview + +Vendor ~10 additional brand + utility SVGs to the existing 35-icon set. Mechanical: add entries to `data/icons.yaml`, run `scripts/sync-icons.sh`, update README tables, verify `/icons/` gallery. + +## Shortlist (≤ 10, picked for breadth) + +Brand (Simple Icons CC0): +- `bandcamp` — musicians +- `soundcloud` — musicians +- `spotify` — artist profiles +- `figma` — designers +- `dribbble` — designers +- `stackoverflow` — devs +- `matrix` — fediverse messaging + +Utility (Lucide ISC): +- `book-open` — bookshelves, reading lists +- `download` — resume / vCard +- `heart` — sponsor / support + +Total +10 → 45 icons. Soft ceiling at ~50 for v0-line per issue. + +## Related Code Files + +- Modify: `data/icons.yaml` +- Run: `scripts/sync-icons.sh` +- Generated: `assets/icons/brand/*.svg`, `assets/icons/ui/*.svg` +- Modify: `README.md` icon tables (both `
` blocks) + +## Implementation Steps + +1. Inspect current `data/icons.yaml` structure to match the entry schema. +2. Append the 10 shortlisted entries (correct slug, license attribution). +3. Run `scripts/sync-icons.sh` — verify SVGs land in `assets/icons/brand/` and `assets/icons/ui/`. +4. Inspect downloaded SVGs: confirm size budget (each typically < 1 KB). +5. Update README icon tables under both `
` blocks (brand + utility), keep alphabetical or current order convention. +6. Update README intro line if it claims a fixed count (e.g. "35 icons" → "45 icons"). +7. Build + curl `/icons/` to confirm new icons render in gallery. +8. Update CHANGELOG (Unreleased). + +## Success Criteria + +- [ ] 10 new SVGs vendored under `assets/icons/` +- [ ] `data/icons.yaml` updated with 10 new entries +- [ ] README icon tables updated; count line updated +- [ ] `/icons/` gallery renders all new icons +- [ ] `hugo --gc --minify` clean +- [ ] PR opened against main + +## Risk Assessment + +- **Risk:** Simple Icons / Lucide rename or remove a slug. **Mitigation:** `sync-icons.sh` fails loudly if slug missing; pick alternates from the original issue list. +- **Risk:** SVG size larger than expected (some Simple Icons brand marks are heavy). **Mitigation:** check raw sizes after sync; reject if any single icon > 3 KB. diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md new file mode 100644 index 0000000..5be78fd --- /dev/null +++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md @@ -0,0 +1,54 @@ +--- +phase: 4 +title: "Optional RSS feed (#8)" +status: pending +priority: P3 +effort: "45m" +dependencies: [] +--- + +# Phase 4: Optional RSS feed (#8) + +## Overview + +Add `params.rss = true` opt-in. When enabled, the theme stops adding `RSS` to its `disableKinds` recommendation and renders an RSS feed of `[[params.links]]`. Adds `` to ``. + +## Design notes + +- Hugo respects user `disableKinds` in their `hugo.toml`. The theme can't force-enable RSS — but it can: + - Document the param in README + - Provide an `index.rss.xml` template that runs when the user removes `RSS` from `disableKinds` + - Add the `` in `head.html` when `params.rss` is true (regardless of `disableKinds` — feed crawler will 404 if user mismatches; that's their config) +- Item shape: `` = link title, `<link>` = link URL, `<description>` = optional description, `<pubDate>` = build time (links lack intrinsic dates). + +## Related Code Files + +- Create: `layouts/index.rss.xml` — template for the feed +- Modify: `layouts/partials/head.html` — emit `<link rel="alternate">` when `params.rss = true` +- Modify: `README.md` — document the new param + the `disableKinds` removal step + +## Implementation Steps + +1. Create `layouts/index.rss.xml` rendering an RSS 2.0 feed: + - `<channel>` populated from `params.name`, `params.tagline`, `params.bio`, site `baseURL`, build `time.Now` + - `<item>` per `[[params.links]]` entry; `<pubDate>` = build time; `<guid isPermaLink="true">` = link URL +2. In `layouts/partials/head.html`, when `site.Params.rss` is true: `<link rel="alternate" type="application/rss+xml" title="…" href="{{ \`index.xml\` | absURL }}">`. Hugo emits `index.xml` for the home RSS template. +3. README: add `rss` to the params table; add a short "Enable RSS" subsection explaining that user must remove `RSS` from their `disableKinds` line and set `params.rss = true`. +4. Build + curl `/index.xml` — confirm valid RSS 2.0 (well-formed XML, items present); confirm `<head>` of `/` carries the alternate link when `rss = true`. +5. Validate against W3C feed validator if reachable; if not, manually inspect for required elements. +6. Update CHANGELOG (Unreleased). + +## Success Criteria + +- [ ] `layouts/index.rss.xml` exists and renders well-formed RSS 2.0 when feature on +- [ ] `<head>` carries `<link rel="alternate">` when `params.rss = true` +- [ ] No RSS link emitted when `params.rss` unset/false (default) +- [ ] README documents the new param + `disableKinds` step +- [ ] `hugo --gc --minify` clean +- [ ] PR opened against main + +## Risk Assessment + +- **Risk:** User leaves `RSS` in `disableKinds` and the alternate link 404s. **Mitigation:** README is explicit; theme cannot override user's `disableKinds`. +- **Risk:** Build-time pubDate makes the feed change every build, polluting feed readers. **Mitigation:** documented as a known limitation; alternative is a per-link `pubDate` field (out of scope). +- **Risk:** Emoji/unicode in `params.tagline` breaks XML. **Mitigation:** Hugo's `transform.XMLEscape` or default escaping in templates handles it. diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md new file mode 100644 index 0000000..47b6ff2 --- /dev/null +++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md @@ -0,0 +1,38 @@ +--- +phase: 7 +title: "v0.4.0 release prep" +status: pending +priority: P3 +effort: "30m" +dependencies: [2, 3, 4] +--- + +# Phase 7: v0.4.0 release prep + +## Overview + +After phases 2, 3, 4 merge to main, sync `CHANGELOG.md`, bump version references, tag `v0.4.0`, push tag. + +## Implementation Steps + +1. Pull latest `main`, confirm phases 2/3/4 PRs are merged. +2. Move `## [Unreleased]` section to `## [0.4.0] — 2026-05-10` (or actual merge date). +3. Add fresh `## [Unreleased]` section above with v0.5 placeholders (#7 OG auto-gen, #9 multi-section bio explicitly listed as deferred from v0.4). +4. Cross-check README for stale "v0.3" or version mentions; update where needed. +5. Reissue `theme.toml` `min_version` only if a phase added a Hugo feature requiring a newer Hugo version (none expected here). +6. Final build: `hugo --gc --minify --themesDir ../..` from `exampleSite/` — clean. +7. `git tag v0.4.0 -m "v0.4.0: gallery CSS extraction, more icons, opt-in RSS"` +8. `git push origin v0.4.0` +9. Run `/ck:journal` for the v0.4.0 release retro. + +## Success Criteria + +- [ ] CHANGELOG has dated `## [0.4.0]` section +- [ ] `## [Unreleased]` section exists with v0.5 deferral notes +- [ ] `git tag v0.4.0` exists locally and on remote +- [ ] Build clean across all 3 demo pages + +## Risk Assessment + +- **Risk:** auto-tag without user approval. **Mitigation:** ask before `git push origin v0.4.0`; tag is the publish event, deserves explicit consent. +- **Risk:** stale "35 icons" claim in README after Phase 3. **Mitigation:** Phase 3 success criterion already covers this. diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/plan.md b/themes/bonsai/plans/260510-0238-v0-4-release/plan.md new file mode 100644 index 0000000..7833041 --- /dev/null +++ b/themes/bonsai/plans/260510-0238-v0-4-release/plan.md @@ -0,0 +1,43 @@ +--- +title: 'v0.4 release: ship gallery-CSS extraction + more icons + opt-in RSS' +description: >- + Ship issues #11, #10, #8 each as its own PR; tag v0.4.0. Issues #7 (OG + auto-gen) and #9 (multi-section bio) deferred to v0.5 per their own deferral + notes. +status: pending +priority: P2 +created: 2026-05-10T00:00:00.000Z +--- + +# v0.4 release: ship the three well-scoped v0-line enhancements + +## Overview + +Per scope-check: ship the three well-scoped issues now, defer the two design-heavy ones. + +- **Phase 2** Extract gallery CSS (#11) — pure refactor, smallest scope +- **Phase 3** Add more icons (#10) — mechanical, data-driven +- **Phase 4** Optional RSS feed (#8) — additive opt-in +- **Phase 7** v0.4.0 release prep + +Each phase ships as its own feature branch + PR (matches repo pattern). + +## Deferred to v0.5 + +- **#7 Auto-generate OG image** — issue text: *"If no path meets [≤30 KB binary], defer to v0.5"*. Of 5 candidate approaches, only `pyftsubset Inter` + base PNG might fit; needs measured prototype before committing. +- **#9 Multi-section bio** — issue text: *"Likely a 2026-Q3 candidate, not v0.4"*. Schema design dispute; needs `/ck:brainstorm` before a plan. + +## Phases (active) + +| Phase | Name | Status | +|-------|------|--------| +| 2 | [Extract gallery CSS (#11)](./phase-02-extract-gallery-css-11.md) | Completed | +| 3 | [Add more icons (#10)](./phase-03-add-more-icons-10.md) | Pending | +| 4 | [Optional RSS feed (#8)](./phase-04-optional-rss-feed-8.md) | Pending | +| 7 | [v0.4.0 release prep](./phase-07-v0-4-0-release-prep.md) | Pending | + +(Phase numbers preserved from original 7-phase scaffold for stable references.) + +## Dependencies + +- Phase 7 depends on Phases 2, 3, 4. Each of 2/3/4 is independent and ships as a separate PR. diff --git a/themes/bonsai/static/css/bonsai.css b/themes/bonsai/static/css/bonsai.css index 286dbe8..9ce2ff1 100644 --- a/themes/bonsai/static/css/bonsai.css +++ b/themes/bonsai/static/css/bonsai.css @@ -364,95 +364,8 @@ body { [data-theme="light"] .theme-toggle .theme-toggle__sun { display: none; } [data-theme="light"] .theme-toggle .theme-toggle__moon { display: block; } -/* ============================================================ - Themes gallery (exampleSite/themes/) — 4 mini bio cards - ============================================================ */ - -.themes-gallery { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - gap: 1.5rem; - max-width: 60rem; - margin: 2rem auto; -} - -.themes-gallery__card { - background: var(--bonsai-bg); - border: 1px solid var(--bonsai-border); - border-radius: var(--bonsai-radius); - padding: 1.5rem; - text-align: center; -} - -.themes-gallery__name { - font-family: var(--bonsai-font-display); - font-size: 1.1rem; - margin: 0 0 .25rem; - color: var(--bonsai-text); -} - -.themes-gallery__hex { - color: var(--bonsai-muted); - font-family: ui-monospace, monospace; - font-size: .75rem; - margin: 0 0 1rem; -} - -.themes-gallery__chip { - display: inline-block; - padding: .4rem .75rem; - background: var(--bonsai-surface); - color: var(--bonsai-text); - border: 1px solid var(--bonsai-border); - border-radius: var(--bonsai-radius); - font-size: .85rem; -} - -.themes-gallery__chip--accent { - background: var(--bonsai-accent); - color: var(--bonsai-bg); - border-color: var(--bonsai-accent); -} - -/* ============================================================ - Variants gallery (exampleSite/variants/) — 3 sample bio cards - ============================================================ */ - -.variants-gallery { - display: grid; - grid-template-columns: 1fr; - gap: 2rem; - max-width: 36rem; - margin: 2rem auto; -} - -.variants-gallery__card { - background: var(--bonsai-surface); - border: 1px solid var(--bonsai-border); - border-radius: var(--bonsai-radius); - padding: 1.5rem; -} - -.variants-gallery__name { - font-family: var(--bonsai-font-display); - font-size: 1.1rem; - margin: 0 0 .25rem; - color: var(--bonsai-text); -} - -.variants-gallery__desc { - color: var(--bonsai-muted); - font-size: .85rem; - margin: 0 0 .5rem; -} - -.variants-gallery__code { - display: inline-block; - margin-bottom: 1rem; - font-family: ui-monospace, monospace; - font-size: .75rem; - color: var(--bonsai-muted); -} +/* Note: gallery selectors (.themes-gallery, .variants-gallery) live in gallery.css + and load only on /themes/ and /variants/ demo pages via the head_extra block. */ @media (prefers-reduced-motion: reduce) { .link, .link:hover, .link:active, diff --git a/themes/bonsai/static/css/gallery.css b/themes/bonsai/static/css/gallery.css new file mode 100644 index 0000000..2dfac23 --- /dev/null +++ b/themes/bonsai/static/css/gallery.css @@ -0,0 +1,92 @@ +/* Bonsai gallery styles — loaded only on /themes/ and /variants/ demo pages. + * Kept out of bonsai.css so end-user sites don't ship demo-only selectors. */ + +/* ============================================================ + Themes gallery (exampleSite/themes/) — 4 mini bio cards + ============================================================ */ + +.themes-gallery { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1.5rem; + max-width: 60rem; + margin: 2rem auto; +} + +.themes-gallery__card { + background: var(--bonsai-bg); + border: 1px solid var(--bonsai-border); + border-radius: var(--bonsai-radius); + padding: 1.5rem; + text-align: center; +} + +.themes-gallery__name { + font-family: var(--bonsai-font-display); + font-size: 1.1rem; + margin: 0 0 .25rem; + color: var(--bonsai-text); +} + +.themes-gallery__hex { + color: var(--bonsai-muted); + font-family: ui-monospace, monospace; + font-size: .75rem; + margin: 0 0 1rem; +} + +.themes-gallery__chip { + display: inline-block; + padding: .4rem .75rem; + background: var(--bonsai-surface); + color: var(--bonsai-text); + border: 1px solid var(--bonsai-border); + border-radius: var(--bonsai-radius); + font-size: .85rem; +} + +.themes-gallery__chip--accent { + background: var(--bonsai-accent); + color: var(--bonsai-bg); + border-color: var(--bonsai-accent); +} + +/* ============================================================ + Variants gallery (exampleSite/variants/) — 3 sample bio cards + ============================================================ */ + +.variants-gallery { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + max-width: 36rem; + margin: 2rem auto; +} + +.variants-gallery__card { + background: var(--bonsai-surface); + border: 1px solid var(--bonsai-border); + border-radius: var(--bonsai-radius); + padding: 1.5rem; +} + +.variants-gallery__name { + font-family: var(--bonsai-font-display); + font-size: 1.1rem; + margin: 0 0 .25rem; + color: var(--bonsai-text); +} + +.variants-gallery__desc { + color: var(--bonsai-muted); + font-size: .85rem; + margin: 0 0 .5rem; +} + +.variants-gallery__code { + display: inline-block; + margin-bottom: 1rem; + font-family: ui-monospace, monospace; + font-size: .75rem; + color: var(--bonsai-muted); +}