From e310e4991fdac695ebf680e0ab2ed4b0fd47ddd5 Mon Sep 17 00:00:00 2001 From: Rodrigo Dlugokenski Date: Sat, 17 Oct 2026 12:50:48 -0300 Subject: [PATCH] Type the home headline round and round, with a pause control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hero now types each line of the home page's `hero` front matter, holds it, backspaces it and moves on, with a 35-70 ms jitter a key. The tagline is the h1's aria-label and what reduced motion and no-JS get; a return visit in the same tab picks up at it. Every line sits invisibly in the h1's grid cell, so the typing never moves the page. A `‖ pause` toggle in the headline's bottom margin stops it (WCAG 2.2.2); it also holds while the mouse is over the line, the toggle has keyboard focus, the tab is hidden or the hero is scrolled away. Co-Authored-By: Claude Opus 5.5 --- site/assets/css/custom.css | 55 ++++++++++++- site/content/_index.md | 12 ++- site/layouts/_shortcodes/hero-typed.html | 99 ++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 site/layouts/_shortcodes/hero-typed.html diff --git a/site/assets/css/custom.css b/site/assets/css/custom.css index b5e8130..10f3c3b 100644 --- a/site/assets/css/custom.css +++ b/site/assets/css/custom.css @@ -587,12 +587,24 @@ nav[aria-label="breadcrumb"], -webkit-text-fill-color: currentColor; letter-spacing: -0.02em; } -.plugii-hero h1::before { +/* Every line of the typed headline (layouts/_shortcodes/hero-typed.html) in + one grid cell: the invisible ones hold the tallest line's height. Each gets + the prompt and the cursor so they wrap the same way. */ +.plugii-typed { + display: grid; +} +.plugii-typed > span { + grid-area: 1 / 1; +} +.plugii-typed > .sizer { + visibility: hidden; +} +.plugii-typed > span::before { content: "> "; color: var(--p-accent); } -/* The block cursor. */ -.plugii-hero h1::after { +/* The block cursor: solid while it types or erases, blinking otherwise. */ +.plugii-typed > span::after { content: ""; display: inline-block; width: 0.55em; @@ -602,16 +614,51 @@ nav[aria-label="breadcrumb"], background: var(--p-accent); animation: plugii-blink 1.1s steps(1, end) infinite; } +.plugii-typed.busy > span::after { + animation: none; +} @keyframes plugii-blink { 50% { opacity: 0; } } @media (prefers-reduced-motion: reduce) { - .plugii-hero h1::after { + .plugii-typed > span::after { animation: none; } } +/* The pause toggle hangs in the headline's bottom margin, out of the flow, + so it takes no room and never shifts anything. 24px: the minimum target. */ +.plugii-typed-box { + position: relative; +} +.plugii-typed-pause { + position: absolute; + top: 100%; + left: 0; + height: 24px; + padding: 0 6px; + font-family: var(--p-mono); + font-size: 0.75rem; + line-height: 1; + color: var(--p-faint); + background: none; + border: 0; + cursor: pointer; +} +.plugii-typed-pause span { + display: inline-block; + width: 1ch; + color: var(--p-accent); +} +.plugii-typed-pause:hover, +.plugii-typed-pause:focus-visible { + color: var(--p-accent); +} +.plugii-typed-pause:focus-visible { + outline: 2px solid var(--p-accent); + outline-offset: 1px; +} .plugii-hero p { color: var(--p-muted) !important; } diff --git a/site/content/_index.md b/site/content/_index.md index c7a992c..e991d41 100644 --- a/site/content/_index.md +++ b/site/content/_index.md @@ -2,6 +2,14 @@ title: plugii description: A live USB port monitor for Linux. Which socket, what link speed, USB-C / PD power, a health verdict, and a vim-key TUI. layout: hextra-home +# The headline, typed round and round (layouts/_shortcodes/hero-typed.html): +# each line held for `hold` ms. `tagline` is what screen readers, reduced +# motion and no-JS get, and where a return visit picks up. +hero: + - { text: "plugii: a live USB port monitor for Linux", hold: 4500 } + - { text: "Know what every USB port is really doing", hold: 5000, tagline: true } + - { text: "10G capable, linked at 5G. It's the cable.", hold: 2500 } + - { text: "USB-C: the C is for Could be anything.", hold: 2500 } --- {{< hextra/hero-badge link="https://github.com/rdlu/plugii" >}} @@ -12,9 +20,7 @@ layout: hextra-home
-{{< hextra/hero-headline >}} - Know what every USB port 
is really doing -{{< /hextra/hero-headline >}} +{{< hero-typed >}}
diff --git a/site/layouts/_shortcodes/hero-typed.html b/site/layouts/_shortcodes/hero-typed.html new file mode 100644 index 0000000..c90ecd4 --- /dev/null +++ b/site/layouts/_shortcodes/hero-typed.html @@ -0,0 +1,99 @@ +{{- /* + The home page's headline, typed like a prompt, round and round: each line + of the page's `hero` front matter typed, held for its `hold` ms, backspaced, + then the next. The line marked `tagline: true` is the h1's aria-label (screen + readers never hear the typing), what shows without JavaScript or with + reduced motion, and where the rotation picks up on a return visit in the + same tab: the first line plays once per session. + + Every line sits invisibly in the h1's grid cell, so the box is the tallest + one's height at any width and the typing never moves the page. + + It stops while the mouse is over the line, the pause toggle has keyboard + focus, the tab is hidden or the hero is scrolled away, and for good with the + toggle (WCAG 2.2.2). A pause keeps its place in the line. +*/ -}} +{{- $lines := .Page.Params.hero -}} +{{- $tagline := index $lines 0 -}} +{{- range $lines }}{{ if .tagline }}{{ $tagline = . }}{{ end }}{{ end -}} +
+

+{{- range $lines }}{{ end -}} + +

+ +
+