]> danix's work - danix.xyz-2.git/commitdiff
feat: add // prefix to H2–H6 headings in prose
authorDanilo M. <redacted>
Sat, 18 Apr 2026 14:25:02 +0000 (16:25 +0200)
committerDanilo M. <redacted>
Sat, 18 Apr 2026 14:25:02 +0000 (16:25 +0200)
Render all heading levels 2–6 with a terminal-style `// ` prefix in JetBrains Mono.
H1 (page/article titles) is excluded. The prefix is aria-hidden for A11y compliance
(WCAG 2.1 AA — heading text contrast 14.72:1, prefix is decorative and exempt from
contrast requirements).

Implementation:
- New Hugo render-heading hook at _default/_markup/render-heading.html
- Added .heading-prefix CSS rule with accent purple color, 0.8em font size, 0.7 opacity
- CSS rebuil with npm run build

Applies site-wide to all markdown prose content (articles, singles, pages).

Co-Authored-By: Claude Haiku 4.5 <redacted>
docs/superpowers/specs/2026-04-18-prose-heading-prefix-design.md [new file with mode: 0644]
themes/danix-xyz-hacker/assets/css/main.css
themes/danix-xyz-hacker/assets/css/main.min.css
themes/danix-xyz-hacker/layouts/_default/_markup/render-heading.html [new file with mode: 0644]

diff --git a/docs/superpowers/specs/2026-04-18-prose-heading-prefix-design.md b/docs/superpowers/specs/2026-04-18-prose-heading-prefix-design.md
new file mode 100644 (file)
index 0000000..ebc0ee8
--- /dev/null
@@ -0,0 +1,56 @@
+# Design: Prose Heading `//` Prefix
+
+**Date:** 2026-04-18
+**Status:** Approved
+
+## Context
+
+danix.xyz uses a hacker/terminal aesthetic throughout. Article and single page body headings (H2–H6) currently render in Oxanium font with no visual marker. The goal is to prefix all prose headings with `// ` in JetBrains Mono to reinforce the terminal/comment idiom — matching the hacker identity of the site.
+
+## Requirements
+
+- All headings H2–H6 inside `.prose` content (site-wide — all singles and pages)
+- H1 is excluded (used for page/article titles, already styled distinctly)
+- Prefix `//` must be `aria-hidden="true"` — screen readers announce only the heading text
+- Heading DOM hierarchy (H2→H6) must be preserved exactly
+- WCAG 2.1 AA compliant: heading text contrast ≥ 4.5:1 (measured at 14.72:1 on dark theme)
+- The prefix is decorative — exempt from contrast requirements per WCAG 1.4.3
+- Must work in both dark and light themes
+
+## Approach: Hugo render-heading hook
+
+Create `layouts/_default/_markup/render-heading.html` — Hugo's Markdown render hook for headings. For H2–H6, wrap the heading in the correct `<hN>` tag and inject `<span aria-hidden="true" class="heading-prefix">//</span>` before the text. H1 passes through unmodified.
+
+Style the `.heading-prefix` span in `main.css`:
+- Font: `JetBrains Mono` (Tailwind `font-mono`)
+- Color: `var(--accent)` (purple)
+- Weight: `400` (lighter than heading bold — creates comment-marker feel)
+- Size: `0.8em` relative to heading (scales automatically across H2–H6)
+- Opacity: `0.7` (decorative dimming — exempt from contrast check)
+- `margin-right: 0.35em`
+
+No changes needed to any layout template. No JS. CSS is the only addition beyond the new hook file.
+
+## Files
+
+| File | Action |
+|------|--------|
+| `themes/danix-xyz-hacker/layouts/_default/_markup/render-heading.html` | Create (new) |
+| `themes/danix-xyz-hacker/assets/css/main.css` | Add `.heading-prefix` rule |
+
+## A11y Checklist
+
+- [x] `aria-hidden="true"` on prefix span — screen reader reads "Section Title, heading level 2" not "slash slash Section Title"
+- [x] Heading text contrast 14.72:1 dark / verified light theme passes
+- [x] DOM heading order unchanged
+- [x] `prefers-reduced-motion` not relevant (no animation)
+- [x] Decorative prefix contrast exempted under WCAG 1.4.3
+
+## Verification
+
+1. `hugo server` — navigate to any article or single page
+2. Headings H2–H6 show `// Title` in JetBrains Mono with purple dimmed prefix
+3. H1 (page title) unchanged
+4. Toggle dark/light theme — prefix visible and not garish in both
+5. Run browser accessibility inspector — prefix span reports `aria-hidden: true`
+6. `npm run build` — CSS compiles cleanly
index a1d1accc9fb544c1304f8ebd4a3c48091fd08a72..6f00eb5a746ea835102d7cef0a7dd59161813f6c 100644 (file)
@@ -103,6 +103,16 @@ html.theme-light {
     @apply text-xl md:text-2xl;
   }
 
+  .heading-prefix {
+    font-family: 'JetBrains Mono', monospace;
+    font-weight: 400;
+    font-size: 0.8em;
+    color: var(--accent);
+    opacity: 0.7;
+    margin-right: 0.35em;
+    user-select: none;
+  }
+
   a {
     @apply text-accent hover:opacity-80 transition-opacity;
   }
index 9c5c690ea25346c076a5908b05c85bac0d86fdd1..87ce73b41ea6506dabd5652abf96dcc58f09b925 100644 (file)
@@ -616,6 +616,18 @@ h3 {
   }
 }
 
+.heading-prefix {
+  font-family: 'JetBrains Mono', monospace;
+  font-weight: 400;
+  font-size: 0.8em;
+  color: var(--accent);
+  opacity: 0.7;
+  margin-right: 0.35em;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+          user-select: none;
+}
+
 a {
   color: var(--accent);
   transition-property: opacity;
diff --git a/themes/danix-xyz-hacker/layouts/_default/_markup/render-heading.html b/themes/danix-xyz-hacker/layouts/_default/_markup/render-heading.html
new file mode 100644 (file)
index 0000000..d0d5e05
--- /dev/null
@@ -0,0 +1,5 @@
+{{- if eq .Level 1 -}}
+<h1 id="{{ .Anchor }}">{{ .Text | safeHTML }}</h1>
+{{- else -}}
+<h{{ .Level }} id="{{ .Anchor }}"><span aria-hidden="true" class="heading-prefix">//</span> {{ .Text | safeHTML }}</h{{ .Level }}>
+{{- end -}}