]> danix's work - danix.xyz-2.git/commitdiff
chore: add back-to-top button planning and design docs, update local settings
authorDanilo M. <redacted>
Tue, 21 Apr 2026 08:00:30 +0000 (10:00 +0200)
committerDanilo M. <redacted>
Tue, 21 Apr 2026 08:00:30 +0000 (10:00 +0200)
Co-Authored-By: Claude Haiku 4.5 <redacted>
.claude/settings.local.json
docs/superpowers/plans/2026-04-20-back-to-top-button.md [new file with mode: 0644]
docs/superpowers/specs/2026-04-20-back-to-top-button-design.md [new file with mode: 0644]

index ab7c2d927cda1fda1bcbba7fc6ba32e819c9b17b..7eaaefd173c0a0ab4bfc67904fc5d5eee33e8b83 100644 (file)
       "Bash(hugo config *)",
       "Bash(hugo --debug)",
       "Bash(hugo)",
-      "Bash(hugo -D)"
+      "Bash(hugo -D)",
+      "Bash(curl -s http://localhost:1313/)",
+      "Bash(curl -s http://localhost:1313/articles/haiku-2/)",
+      "Bash(curl -s http://localhost:1313/articles/)",
+      "Bash(curl -s http://localhost:1313/css/main.min.css)",
+      "Bash(hugo version *)"
     ]
   }
 }
diff --git a/docs/superpowers/plans/2026-04-20-back-to-top-button.md b/docs/superpowers/plans/2026-04-20-back-to-top-button.md
new file mode 100644 (file)
index 0000000..e61aad2
--- /dev/null
@@ -0,0 +1,220 @@
+# Back-to-Top Button Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Add a fixed bottom-right back-to-top button that appears after 33% scroll depth on article/single pages, with slide-up animation, full accessibility, and hacker-theme styling.
+
+**Architecture:** A new `back-to-top.html` partial uses inline Alpine.js `x-data` with `@scroll.window` to track scroll depth; the button is conditionally included in `baseof.html` on `.Kind "page"` pages (same condition as reading progress bar). CSS lives in a new `.back-to-top` component class in `main.css`.
+
+**Tech Stack:** Hugo partials, Alpine.js 3.x (inline x-data), Tailwind CSS (@apply), Feather Icons (chevron-up inlined SVG)
+
+---
+
+## File Map
+
+| Action | File | Responsibility |
+|---|---|---|
+| Create | `themes/danix-xyz-hacker/layouts/partials/back-to-top.html` | Button markup + Alpine logic |
+| Modify | `themes/danix-xyz-hacker/assets/css/main.css` | `.back-to-top` component class |
+| Modify | `themes/danix-xyz-hacker/layouts/_default/baseof.html` | Include partial on `.Kind "page"` |
+| Modify | `themes/danix-xyz-hacker/i18n/en.yaml` | `back_to_top` key |
+| Modify | `themes/danix-xyz-hacker/i18n/it.yaml` | `back_to_top` key (Italian) |
+
+---
+
+### Task 1: Add `.back-to-top` CSS component class
+
+**Files:**
+- Modify: `themes/danix-xyz-hacker/assets/css/main.css` (append inside `@layer components`, after `.toast-container`)
+
+- [ ] **Step 1: Open `main.css` and locate the end of `@layer components`**
+
+Find the closing `}` of `@layer components`. It ends around line 1165 (after `.tooltip-text`). Add the new class block just before that closing brace.
+
+- [ ] **Step 2: Add the `.back-to-top` class**
+
+```css
+  /* Back to top button */
+  .back-to-top {
+    @apply fixed bottom-6 right-6 z-40 w-11 h-11 rounded-full flex items-center justify-content-center;
+    background: var(--accent);
+    box-shadow: 0 0 12px rgba(168, 85, 247, 0.4);
+    transition: background 200ms ease, box-shadow 200ms ease;
+    color: #fff;
+  }
+  .back-to-top:hover {
+    background: #9333ea;
+    box-shadow: 0 0 20px var(--accent);
+  }
+  .back-to-top:focus-visible {
+    outline: 2px solid var(--accent);
+    outline-offset: 2px;
+  }
+```
+
+- [ ] **Step 3: Rebuild CSS**
+
+```bash
+cd /home/danix/Programming/GIT/danix.xyz-hacker-theme && npm run build
+```
+
+Expected: exits 0, `themes/danix-xyz-hacker/static/css/main.min.css` updated.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add themes/danix-xyz-hacker/assets/css/main.css themes/danix-xyz-hacker/static/css/main.min.css
+git commit -m "style: add .back-to-top component class"
+```
+
+---
+
+### Task 2: Create `back-to-top.html` partial
+
+**Files:**
+- Create: `themes/danix-xyz-hacker/layouts/partials/back-to-top.html`
+
+- [ ] **Step 1: Create the partial file**
+
+```html
+<div
+  x-data="{ visible: false }"
+  @scroll.window="visible = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) >= 0.33"
+>
+  <button
+    x-show="visible"
+    x-cloak
+    x-transition:enter="transition ease-out duration-300"
+    x-transition:enter-start="opacity-0 translate-y-4"
+    x-transition:enter-end="opacity-100 translate-y-0"
+    x-transition:leave="transition ease-in duration-200"
+    x-transition:leave-start="opacity-100 translate-y-0"
+    x-transition:leave-end="opacity-0 translate-y-4"
+    class="back-to-top"
+    aria-label="{{ i18n "back_to_top" | default "Back to top" }}"
+    type="button"
+    @click="window.scrollTo({ top: 0, behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' })"
+  >
+    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
+      <polyline points="18 15 12 9 6 15"></polyline>
+    </svg>
+  </button>
+</div>
+```
+
+Note: SVG is Feather `chevron-up` inlined to avoid race condition with `feather.replace()` running before Alpine shows the button. `aria-hidden="true"` on the SVG because the button has `aria-label`.
+
+- [ ] **Step 2: Add i18n keys**
+
+In `themes/danix-xyz-hacker/i18n/en.yaml`, add:
+```yaml
+back_to_top: "Back to top"
+```
+
+In `themes/danix-xyz-hacker/i18n/it.yaml`, add:
+```yaml
+back_to_top: "Torna in cima"
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add themes/danix-xyz-hacker/layouts/partials/back-to-top.html themes/danix-xyz-hacker/i18n/en.yaml themes/danix-xyz-hacker/i18n/it.yaml
+git commit -m "feat: add back-to-top partial with Alpine.js and i18n"
+```
+
+---
+
+### Task 3: Include partial in `baseof.html`
+
+**Files:**
+- Modify: `themes/danix-xyz-hacker/layouts/_default/baseof.html`
+
+- [ ] **Step 1: Locate the `.Kind "page"` block for reading-progress (lines ~28–34)**
+
+```html
+{{ if eq .Kind "page" }}
+<div
+  id="reading-progress"
+  class="fixed top-0 left-0 h-1 transition-all duration-100"
+  style="width: 0%; background: linear-gradient(to right, var(--accent), var(--accent2)); z-index: 9999;"
+></div>
+{{ end }}
+```
+
+- [ ] **Step 2: Add the partial include inside the same condition block**
+
+```html
+{{ if eq .Kind "page" }}
+<div
+  id="reading-progress"
+  class="fixed top-0 left-0 h-1 transition-all duration-100"
+  style="width: 0%; background: linear-gradient(to right, var(--accent), var(--accent2)); z-index: 9999;"
+></div>
+{{ partial "back-to-top.html" . }}
+{{ end }}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add themes/danix-xyz-hacker/layouts/_default/baseof.html
+git commit -m "feat: include back-to-top button on single pages"
+```
+
+---
+
+### Task 4: Verify end-to-end
+
+- [ ] **Step 1: Start Hugo dev server**
+
+```bash
+cd /home/danix/Programming/GIT/danix.xyz-hacker-theme && hugo server -D
+```
+
+- [ ] **Step 2: Open an article page and scroll past 33%**
+
+Navigate to any article. Confirm the purple circle button slides up from bottom-right.
+
+- [ ] **Step 3: Scroll back above 33%**
+
+Confirm button fades/slides out.
+
+- [ ] **Step 4: Click button**
+
+Confirm smooth scroll to top.
+
+- [ ] **Step 5: Keyboard accessibility**
+
+Tab to the button when visible. Confirm focus ring (purple outline). Press Enter — confirm scroll to top.
+
+- [ ] **Step 6: Reduced motion**
+
+DevTools → Rendering → "Emulate prefers-reduced-motion: reduce". Scroll past 33% — button appears instantly (no animation). Click — page jumps instantly (no smooth scroll).
+
+- [ ] **Step 7: z-index layering**
+
+Confirm button sits below fixed header, search modal, and toast notifications.
+
+- [ ] **Step 8: Non-article pages**
+
+Navigate to home or list page. Confirm no button appears.
+
+- [ ] **Step 9: Final CSS rebuild and commit**
+
+```bash
+npm run build
+git add themes/danix-xyz-hacker/static/css/main.min.css
+git commit -m "build: rebuild CSS for back-to-top button"
+```
+
+- [ ] **Step 10: Update TODO.md**
+
+```markdown
+- [✅] add back-to-top button
+```
+
+```bash
+git add TODO.md
+git commit -m "chore: mark back-to-top as complete in TODO"
+```
diff --git a/docs/superpowers/specs/2026-04-20-back-to-top-button-design.md b/docs/superpowers/specs/2026-04-20-back-to-top-button-design.md
new file mode 100644 (file)
index 0000000..bc5959f
--- /dev/null
@@ -0,0 +1,133 @@
+# Back-to-Top Button — Design Spec
+
+**Date:** 2026-04-20
+**Status:** Approved
+
+---
+
+## Context
+
+The TODO list includes "add back-to-top button". Article pages can be long and users currently have no quick way to return to the top. The button should integrate cleanly with the existing hacker-aesthetic theme, follow Alpine.js patterns already in use, and meet WCAG 2.1 AA accessibility standards.
+
+---
+
+## Design Decisions
+
+| Decision | Choice | Rationale |
+|---|---|---|
+| Position | Fixed bottom-right | Conventional, expected UX |
+| Shape | Circle solid | Consistent with theme energy; always-visible purple matches accent |
+| Trigger | 33% scroll depth | Balanced — not too early, not too late |
+| Animation | Slide up + fade (300ms) | Reuses existing `slideUp` keyframe |
+| Pages | `.Kind "page"` only | Articles and singles — same condition as reading progress bar |
+| Implementation | Inline Alpine.js in a partial | Consistent with `social-share.html`, `header.html` patterns |
+
+---
+
+## Behaviour
+
+- **Appears** when `window.scrollY / (document.documentElement.scrollHeight - window.innerHeight) >= 0.33`
+- **Disappears** when scroll drops back below 33%
+- **On click**: smooth-scrolls to top (`window.scrollTo({ top: 0, behavior: 'smooth' })`)
+- **Reduced motion**: CSS global `prefers-reduced-motion` rule already suppresses all animations; the button still appears/disappears (via Alpine `x-show`) but without the slide animation. The `smooth` scroll behavior should also be skipped — handled in the click handler by checking `window.matchMedia('(prefers-reduced-motion: reduce)').matches`.
+- **z-index**: `z-40` — sits below toasts (`z-50`), modals (`z-50`), and reading progress bar (`z-9999`). Toasts stack above it naturally.
+
+---
+
+## Visual Spec
+
+```
+Position:  fixed bottom-6 right-6  (24px from edges)
+Size:      44×44px circle
+Default:   bg: var(--accent) #a855f7, glow: box-shadow 0 0 12px rgba(168,85,247,0.4)
+Hover:     bg: #9333ea (one shade darker), glow: box-shadow 0 0 20px var(--accent)
+Icon:      Feather Icons `chevron-up` (already loaded globally via CDN)
+Entrance:  slideUp keyframe, 300ms ease-out
+Exit:      opacity fade (Alpine x-show transition), 200ms
+```
+
+---
+
+## Accessibility
+
+- `aria-label="Back to top"` on the button element
+- `role="button"` implicit (native `<button>`)
+- Keyboard accessible: Tab-focusable, Enter/Space triggers scroll
+- Focus ring: `focus-visible` outline using `var(--accent)` (already defined globally in CSS)
+- Button is removed from tab order when hidden via Alpine `x-show` (Alpine removes from DOM flow when not visible)
+- WCAG 2.1 AA contrast: white `↑` icon on `#a855f7` purple background — passes AA for UI components
+
+---
+
+## Implementation
+
+### New files
+- `themes/danix-xyz-hacker/layouts/partials/back-to-top.html` — the button partial
+
+### Modified files
+- `themes/danix-xyz-hacker/layouts/_default/baseof.html` — include partial + JS, conditionally on `.Kind "page"`
+- `themes/danix-xyz-hacker/assets/css/main.css` — add `.back-to-top` component class
+
+### No new JS file needed
+Logic is simple enough for inline Alpine `x-data` — consistent with `social-share.html`. No separate `.js` asset required.
+
+---
+
+## Component Structure
+
+**`back-to-top.html` partial:**
+```html
+<div
+  x-data="{ visible: false }"
+  @scroll.window="visible = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) >= 0.33"
+>
+  <button
+    x-show="visible"
+    x-transition:enter="..."
+    x-transition:enter-start="..."
+    x-transition:enter-end="..."
+    x-transition:leave="..."
+    x-transition:leave-start="..."
+    x-transition:leave-end="..."
+    class="back-to-top"
+    aria-label="Back to top"
+    @click="window.scrollTo({ top: 0, behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' })"
+  >
+    <!-- feather chevron-up icon -->
+  </button>
+</div>
+```
+
+**`.back-to-top` CSS class** (added to `main.css` `@layer components`):
+```css
+.back-to-top {
+  @apply fixed bottom-6 right-6 z-40 w-11 h-11 rounded-full flex items-center justify-center;
+  background: var(--accent);
+  box-shadow: 0 0 12px rgba(168, 85, 247, 0.4);
+  transition: background 200ms ease, box-shadow 200ms ease;
+  color: #fff;
+}
+.back-to-top:hover {
+  background: #9333ea;
+  box-shadow: 0 0 20px var(--accent);
+}
+.back-to-top:focus-visible {
+  outline: 2px solid var(--accent);
+  outline-offset: 2px;
+}
+```
+
+---
+
+## Verification
+
+1. Start Hugo dev server (`hugo server`)
+2. Open any article page
+3. Scroll past 33% — button slides up from bottom-right
+4. Scroll back up — button disappears
+5. Click button — smooth scroll to top
+6. Tab to button — focus ring visible
+7. Press Enter while focused — smooth scroll to top
+8. Test with `prefers-reduced-motion: reduce` in browser devtools — button appears without animation, click scrolls instantly
+9. Confirm toasts (if triggered) render above the button
+10. Check on mobile (narrow viewport) — button sits at bottom-right without overlapping content