mirror of
https://github.com/tiennm99/iammiti99.git
synced 2026-10-11 03:13:37 +00:00
feat: v0.2 polish & SEO
Bundle of four small additions for the v0.2 release. All strictly additive — no breaking changes; v0.1 sites upgrade without edits. Phase 1 — Color theme presets - Refactor static/css/bonsai.css: palette vars now scoped per [data-bonsai-theme]; layout vars (radius/gap/pad/fonts) stay in :root - Four palettes: bonsai (default, vermilion+washi), sakura (cherry blossom), sumi (monochrome ink), koi (orange+cream). Light + dark variants each, both prefers-color-scheme and manual data-theme override - New params.colorTheme (default 'bonsai'); applied via baseof - Themes gallery at exampleSite /themes/ shows all 4 side-by-side Phase 2 — schema.org Person markup - New layouts/partials/schema-person.html emits JSON-LD ProfilePage containing a Person (built from name/bio/avatar/links + optional jobTitle/location/email) - mailto: and tel: links excluded from sameAs - Suppress with params.schema = false - Wired into head.html Phase 3 — Theme toggle UI button - New layouts/partials/theme-toggle-button.html (sun/moon SVGs) - Rendered in footer when params.themeToggle = true - aria-pressed reflects current state; updated theme-toggle.js to sync on click and on first paint - Sun shown in dark mode, moon in light (button shows target state) - exampleSite enables the toggle by default for demo Phase 4 — Avatar initials fallback - New layouts/partials/avatar.html: img if params.avatar set, inline SVG circle with initials otherwise - Initials auto-derived from params.name (first letter of up to 2 words, uppercased); override with params.avatarInitials - Background overrideable with params.avatarBg; defaults to accent - bio-card.html simplified to delegate to the partial
This commit is contained in:
1 parent
ce08594810
commit
7468f3afba
12 files changed
+360
-30
No files matched your search
@@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Color themes"
|
||||
type: themes
|
||||
---
|
||||
@@ -11,7 +11,7 @@ disableKinds = ["taxonomy", "term", "RSS", "sitemap", "404"]
|
||||
tagline = "Software engineer · tending small things with care"
|
||||
bio = "Building tools, writing code, growing a quiet corner of the internet."
|
||||
avatar = "/images/avatar.svg"
|
||||
themeToggle = false
|
||||
themeToggle = true
|
||||
footerText = "© 2026 · made with bonsai"
|
||||
|
||||
[[params.links]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.LanguageCode | default `en` }}" data-theme="auto">
|
||||
<html lang="{{ site.LanguageCode | default `en` }}" data-theme="auto" data-bonsai-theme="{{ site.Params.colorTheme | default `bonsai` }}">
|
||||
<head>
|
||||
{{- partial "head.html" . -}}
|
||||
</head>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{{- /* Render bio avatar.
|
||||
If params.avatar is set, render <img>.
|
||||
Otherwise, render an inline SVG circle with initials in accent color.
|
||||
Initials override: params.avatarInitials.
|
||||
Background override: params.avatarBg (defaults to --bonsai-accent). */ -}}
|
||||
{{- $name := site.Params.name | default site.Title -}}
|
||||
{{- $avatar := site.Params.avatar -}}
|
||||
|
||||
{{- if $avatar -}}
|
||||
<img class="bio__avatar" src="{{ $avatar | strings.TrimPrefix "/" | relURL }}" alt="{{ $name }}" loading="eager" />
|
||||
{{- else -}}
|
||||
{{- $initials := site.Params.avatarInitials -}}
|
||||
{{- if not $initials -}}
|
||||
{{- $words := split (strings.TrimSpace $name) " " -}}
|
||||
{{- $first := "" -}}
|
||||
{{- $second := "" -}}
|
||||
{{- with index $words 0 -}}{{- $first = substr . 0 1 -}}{{- end -}}
|
||||
{{- if gt (len $words) 1 -}}{{- $second = substr (index $words 1) 0 1 -}}{{- end -}}
|
||||
{{- $initials = upper (printf "%s%s" $first $second) -}}
|
||||
{{- if not $initials -}}{{- $initials = "?" -}}{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $bg := site.Params.avatarBg | default "var(--bonsai-accent)" -}}
|
||||
<svg class="bio__avatar bio__avatar--initials" viewBox="0 0 112 112" width="112" height="112" role="img" aria-label="{{ $name }}">
|
||||
<circle cx="56" cy="56" r="56" fill="{{ $bg }}"/>
|
||||
<text x="56" y="74" text-anchor="middle"
|
||||
font-family="ui-serif, Georgia, serif"
|
||||
font-size="56" font-weight="600"
|
||||
fill="var(--bonsai-bg)">{{ $initials }}</text>
|
||||
</svg>
|
||||
{{- end -}}
|
||||
@@ -1,13 +1,10 @@
|
||||
{{- $name := site.Params.name | default site.Title -}}
|
||||
{{- $tagline := site.Params.tagline -}}
|
||||
{{- $bio := site.Params.bio -}}
|
||||
{{- $avatar := site.Params.avatar -}}
|
||||
{{- $links := site.Params.links -}}
|
||||
|
||||
<article class="bio">
|
||||
{{- if $avatar }}
|
||||
<img class="bio__avatar" src="{{ $avatar | strings.TrimPrefix "/" | relURL }}" alt="{{ $name }}" loading="eager" />
|
||||
{{- end }}
|
||||
{{ partial "avatar.html" . }}
|
||||
|
||||
<h1 class="bio__name">{{ $name }}</h1>
|
||||
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
{{- else -}}
|
||||
<p>© {{ now.Year }} {{ site.Params.name | default site.Title }}</p>
|
||||
{{- end -}}
|
||||
{{- if site.Params.themeToggle -}}
|
||||
{{ partial "theme-toggle-button.html" . }}
|
||||
{{- end -}}
|
||||
</footer>
|
||||
{{- end -}}
|
||||
@@ -20,6 +20,8 @@
|
||||
{{- end }}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
|
||||
{{ partial "schema-person.html" . }}
|
||||
|
||||
<link rel="icon" href="{{ (site.Params.favicon | default `favicon.ico`) | strings.TrimPrefix "/" | relURL }}" />
|
||||
<link rel="stylesheet" href="{{ `css/bonsai.css` | relURL }}" />
|
||||
{{- if site.Params.themeToggle }}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{{- /* Emit JSON-LD ProfilePage describing the bio as a Person.
|
||||
Suppressed when site.Params.schema = false. */ -}}
|
||||
{{- if ne (site.Params.schema | default true) false -}}
|
||||
{{- $name := site.Params.name | default site.Title -}}
|
||||
{{- $sameAs := slice -}}
|
||||
{{- range site.Params.links -}}
|
||||
{{- with .url -}}
|
||||
{{- if not (or (strings.HasPrefix . "mailto:") (strings.HasPrefix . "tel:")) -}}
|
||||
{{- $sameAs = $sameAs | append . -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $person := dict
|
||||
"@type" "Person"
|
||||
"name" $name
|
||||
"url" (site.BaseURL | absURL)
|
||||
-}}
|
||||
{{- with site.Params.bio -}}{{- $person = merge $person (dict "description" .) -}}{{- end -}}
|
||||
{{- with site.Params.avatar -}}
|
||||
{{- $person = merge $person (dict "image" (. | strings.TrimPrefix "/" | absURL)) -}}
|
||||
{{- end -}}
|
||||
{{- with site.Params.jobTitle -}}{{- $person = merge $person (dict "jobTitle" .) -}}{{- end -}}
|
||||
{{- with site.Params.location -}}{{- $person = merge $person (dict "address" .) -}}{{- end -}}
|
||||
{{- with site.Params.email -}}{{- $person = merge $person (dict "email" .) -}}{{- end -}}
|
||||
{{- if $sameAs -}}{{- $person = merge $person (dict "sameAs" $sameAs) -}}{{- end -}}
|
||||
{{- $payload := dict "@context" "https://schema.org" "@type" "ProfilePage" "mainEntity" $person -}}
|
||||
<script type="application/ld+json">{{ $payload | jsonify | safeJS }}</script>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,24 @@
|
||||
{{- /* Sun/moon theme toggle. Only loaded/rendered when site.Params.themeToggle = true.
|
||||
Sun shown in dark mode (target = light). Moon shown in light mode (target = dark). */ -}}
|
||||
<button type="button" class="theme-toggle"
|
||||
data-bonsai-theme-toggle
|
||||
aria-label="Toggle light and dark theme"
|
||||
aria-pressed="false"
|
||||
title="Toggle theme">
|
||||
<svg class="theme-toggle__sun" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="4" fill="currentColor"/>
|
||||
<g stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<line x1="12" y1="2" x2="12" y2="5"/>
|
||||
<line x1="12" y1="19" x2="12" y2="22"/>
|
||||
<line x1="2" y1="12" x2="5" y2="12"/>
|
||||
<line x1="19" y1="12" x2="22" y2="12"/>
|
||||
<line x1="4.9" y1="4.9" x2="7" y2="7"/>
|
||||
<line x1="17" y1="17" x2="19.1" y2="19.1"/>
|
||||
<line x1="4.9" y1="19.1" x2="7" y2="17"/>
|
||||
<line x1="17" y1="7" x2="19.1" y2="4.9"/>
|
||||
</g>
|
||||
</svg>
|
||||
<svg class="theme-toggle__moon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
||||
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" fill="currentColor"/>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -0,0 +1,26 @@
|
||||
{{ define "main" }}
|
||||
<article style="text-align:center;">
|
||||
<h1 style="font-family:var(--bonsai-font-display);">Color themes</h1>
|
||||
<p style="color:var(--bonsai-muted);max-width:32rem;margin:0 auto 2rem;">
|
||||
Set <code>colorTheme = "bonsai"</code> in your <code>hugo.toml</code> <code>[params]</code> block. Each palette has light + dark variants.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
{{- $palettes := slice
|
||||
(dict "name" "bonsai" "label" "Bonsai (washi + vermilion)" "accent" "#8b3a2b")
|
||||
(dict "name" "sakura" "label" "Sakura (cherry blossom)" "accent" "#d4456a")
|
||||
(dict "name" "sumi" "label" "Sumi (monochrome ink)" "accent" "#1a1a1a")
|
||||
(dict "name" "koi" "label" "Koi (orange + cream)" "accent" "#c8521e")
|
||||
-}}
|
||||
|
||||
<div class="themes-gallery">
|
||||
{{- range $palettes }}
|
||||
<div data-bonsai-theme="{{ .name }}" class="themes-gallery__card">
|
||||
<h3 class="themes-gallery__name">{{ .label }}</h3>
|
||||
<p class="themes-gallery__hex">accent {{ .accent }}</p>
|
||||
<span class="themes-gallery__chip">link button</span>
|
||||
<span class="themes-gallery__chip themes-gallery__chip--accent">accent</span>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,15 +1,9 @@
|
||||
/* Bonsai — minimalist link-in-bio Hugo theme
|
||||
* Aesthetic: Japanese zen — generous whitespace, calm palette, restrained type. */
|
||||
* Aesthetic: Japanese zen — generous whitespace, calm palette, restrained type.
|
||||
* Palettes: bonsai (default), sakura, sumi, koi. Selected via [data-bonsai-theme]. */
|
||||
|
||||
:root {
|
||||
--bonsai-bg: #f4efe6; /* washi paper */
|
||||
--bonsai-surface: #ffffff;
|
||||
--bonsai-text: #2b2b2b; /* sumi ink */
|
||||
--bonsai-muted: #6b6b6b;
|
||||
--bonsai-accent: #8b3a2b; /* shu-iro / vermilion seal */
|
||||
--bonsai-border: #d9d0bf;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 8px 24px rgba(0, 0, 0, .04);
|
||||
|
||||
--bonsai-radius: 14px;
|
||||
--bonsai-gap: clamp(.75rem, 1.6vw, 1rem);
|
||||
--bonsai-pad: clamp(1.25rem, 4vw, 2rem);
|
||||
@@ -19,8 +13,29 @@
|
||||
--bonsai-font-display: ui-serif, Georgia, "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif JP", "Times New Roman", serif;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Palettes
|
||||
Each palette defines six color vars in light + dark variants.
|
||||
Toggle order:
|
||||
1. base block sets light values
|
||||
2. @media (prefers-color-scheme:dark) overrides to dark
|
||||
3. [data-theme="dark"] forces dark (manual toggle)
|
||||
4. [data-theme="light"] forces light (manual toggle)
|
||||
============================================================ */
|
||||
|
||||
/* --- Bonsai (default: washi + vermilion) --- */
|
||||
:root,
|
||||
[data-bonsai-theme="bonsai"] {
|
||||
--bonsai-bg: #f4efe6;
|
||||
--bonsai-surface: #ffffff;
|
||||
--bonsai-text: #2b2b2b;
|
||||
--bonsai-muted: #6b6b6b;
|
||||
--bonsai-accent: #8b3a2b;
|
||||
--bonsai-border: #d9d0bf;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
:root:not([data-theme="light"]):not([data-bonsai-theme]),
|
||||
[data-bonsai-theme="bonsai"]:not([data-theme="light"]) {
|
||||
--bonsai-bg: #1a1817;
|
||||
--bonsai-surface: #232020;
|
||||
--bonsai-text: #ece6d9;
|
||||
@@ -30,24 +45,111 @@
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--bonsai-bg: #f4efe6;
|
||||
--bonsai-surface: #ffffff;
|
||||
--bonsai-text: #2b2b2b;
|
||||
--bonsai-muted: #6b6b6b;
|
||||
--bonsai-accent: #8b3a2b;
|
||||
--bonsai-border: #d9d0bf;
|
||||
}
|
||||
[data-theme="dark"] {
|
||||
[data-theme="dark"]:not([data-bonsai-theme]),
|
||||
[data-bonsai-theme="bonsai"][data-theme="dark"] {
|
||||
--bonsai-bg: #1a1817;
|
||||
--bonsai-surface: #232020;
|
||||
--bonsai-text: #ece6d9;
|
||||
--bonsai-muted: #a09a8d;
|
||||
--bonsai-accent: #d6856e;
|
||||
--bonsai-border: #3a3633;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
/* --- Sakura (cherry blossom) --- */
|
||||
[data-bonsai-theme="sakura"] {
|
||||
--bonsai-bg: #fff5f5;
|
||||
--bonsai-surface: #ffffff;
|
||||
--bonsai-text: #3a2228;
|
||||
--bonsai-muted: #876773;
|
||||
--bonsai-accent: #d4456a;
|
||||
--bonsai-border: #f0d4dc;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
[data-bonsai-theme="sakura"]:not([data-theme="light"]) {
|
||||
--bonsai-bg: #1f1418;
|
||||
--bonsai-surface: #2a1d22;
|
||||
--bonsai-text: #f7e3e9;
|
||||
--bonsai-muted: #b89aa3;
|
||||
--bonsai-accent: #ec7596;
|
||||
--bonsai-border: #3d2932;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
}
|
||||
[data-bonsai-theme="sakura"][data-theme="dark"] {
|
||||
--bonsai-bg: #1f1418;
|
||||
--bonsai-surface: #2a1d22;
|
||||
--bonsai-text: #f7e3e9;
|
||||
--bonsai-muted: #b89aa3;
|
||||
--bonsai-accent: #ec7596;
|
||||
--bonsai-border: #3d2932;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
/* --- Sumi (monochrome ink) --- */
|
||||
[data-bonsai-theme="sumi"] {
|
||||
--bonsai-bg: #fafafa;
|
||||
--bonsai-surface: #ffffff;
|
||||
--bonsai-text: #111111;
|
||||
--bonsai-muted: #666666;
|
||||
--bonsai-accent: #1a1a1a;
|
||||
--bonsai-border: #e0e0e0;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
[data-bonsai-theme="sumi"]:not([data-theme="light"]) {
|
||||
--bonsai-bg: #0d0d0d;
|
||||
--bonsai-surface: #1a1a1a;
|
||||
--bonsai-text: #f5f5f5;
|
||||
--bonsai-muted: #a0a0a0;
|
||||
--bonsai-accent: #dddddd;
|
||||
--bonsai-border: #2a2a2a;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
}
|
||||
[data-bonsai-theme="sumi"][data-theme="dark"] {
|
||||
--bonsai-bg: #0d0d0d;
|
||||
--bonsai-surface: #1a1a1a;
|
||||
--bonsai-text: #f5f5f5;
|
||||
--bonsai-muted: #a0a0a0;
|
||||
--bonsai-accent: #dddddd;
|
||||
--bonsai-border: #2a2a2a;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
/* --- Koi (orange + cream) --- */
|
||||
[data-bonsai-theme="koi"] {
|
||||
--bonsai-bg: #fef6e4;
|
||||
--bonsai-surface: #ffffff;
|
||||
--bonsai-text: #3a2620;
|
||||
--bonsai-muted: #8a6f5c;
|
||||
--bonsai-accent: #c8521e;
|
||||
--bonsai-border: #f2dfb8;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
[data-bonsai-theme="koi"]:not([data-theme="light"]) {
|
||||
--bonsai-bg: #1c1410;
|
||||
--bonsai-surface: #2a1f18;
|
||||
--bonsai-text: #f7e2c4;
|
||||
--bonsai-muted: #c9a988;
|
||||
--bonsai-accent: #ff8b5c;
|
||||
--bonsai-border: #3d2c20;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
}
|
||||
[data-bonsai-theme="koi"][data-theme="dark"] {
|
||||
--bonsai-bg: #1c1410;
|
||||
--bonsai-surface: #2a1f18;
|
||||
--bonsai-text: #f7e2c4;
|
||||
--bonsai-muted: #c9a988;
|
||||
--bonsai-accent: #ff8b5c;
|
||||
--bonsai-border: #3d2c20;
|
||||
--bonsai-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Layout & components
|
||||
============================================================ */
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
html { -webkit-text-size-adjust: 100%; }
|
||||
@@ -85,6 +187,12 @@ body {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.bio__avatar--initials {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.bio__name {
|
||||
font-family: var(--bonsai-font-display);
|
||||
font-weight: 600;
|
||||
@@ -168,6 +276,105 @@ body {
|
||||
padding: 1.5rem var(--bonsai-pad) 2rem;
|
||||
color: var(--bonsai-muted);
|
||||
font-size: .8rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .75rem;
|
||||
}
|
||||
|
||||
.bonsai-footer p { margin: 0; }
|
||||
|
||||
/* Theme toggle button */
|
||||
.theme-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
background: var(--bonsai-surface);
|
||||
color: var(--bonsai-accent);
|
||||
border: 1px solid var(--bonsai-border);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: transform .15s ease, border-color .15s ease;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--bonsai-accent);
|
||||
}
|
||||
|
||||
.theme-toggle:focus-visible {
|
||||
outline: 2px solid var(--bonsai-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.theme-toggle svg { display: block; }
|
||||
|
||||
/* Show moon in light, sun in dark.
|
||||
The button shows the icon for the mode you'll switch TO. */
|
||||
.theme-toggle__sun { display: none; }
|
||||
.theme-toggle__moon { display: block; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not([data-theme="light"]) .theme-toggle__sun { display: block; }
|
||||
html:not([data-theme="light"]) .theme-toggle__moon { display: none; }
|
||||
}
|
||||
|
||||
[data-theme="dark"] .theme-toggle__sun { display: block; }
|
||||
[data-theme="dark"] .theme-toggle__moon { display: none; }
|
||||
[data-theme="light"] .theme-toggle__sun { display: none; }
|
||||
[data-theme="light"] .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);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Optional: enable via params.themeToggle = true in hugo.toml
|
||||
// Persists user preference and overrides system theme.
|
||||
// Loaded only when params.themeToggle = true.
|
||||
// Persists user preference and overrides system color-scheme.
|
||||
(function () {
|
||||
const KEY = 'bonsai-theme';
|
||||
const root = document.documentElement;
|
||||
@@ -9,9 +9,18 @@
|
||||
const btn = document.querySelector('[data-bonsai-theme-toggle]');
|
||||
if (!btn) return;
|
||||
|
||||
function syncPressed() {
|
||||
const isDark = root.dataset.theme === 'dark'
|
||||
|| (root.dataset.theme === 'auto' && matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
btn.setAttribute('aria-pressed', String(isDark));
|
||||
}
|
||||
|
||||
syncPressed();
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
const current = root.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
root.dataset.theme = current;
|
||||
localStorage.setItem(KEY, current);
|
||||
const next = root.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
root.dataset.theme = next;
|
||||
localStorage.setItem(KEY, next);
|
||||
syncPressed();
|
||||
});
|
||||
})();
|
||||
Reference in new issue
Block a user