summaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-04-29-callout-shortcode-design.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/superpowers/specs/2026-04-29-callout-shortcode-design.md')
-rw-r--r--docs/superpowers/specs/2026-04-29-callout-shortcode-design.md225
1 files changed, 225 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-04-29-callout-shortcode-design.md b/docs/superpowers/specs/2026-04-29-callout-shortcode-design.md
new file mode 100644
index 0000000..e9c6a3d
--- /dev/null
+++ b/docs/superpowers/specs/2026-04-29-callout-shortcode-design.md
@@ -0,0 +1,225 @@
+# Callout Shortcode — Design Spec
+
+**Date:** 2026-04-29
+**Status:** Approved
+
+---
+
+## Context
+
+Articles on danix.xyz need a way to call out notes, tips, warnings, and other semantic asides inline. No callout shortcode exists yet. SHORTCODES.md lists it as a planned future shortcode. This spec defines the design, CSS architecture, A11y requirements, and shortcode API.
+
+---
+
+## Approach
+
+**Single shortcode + CSS-only variants** (Approach C from brainstorm).
+
+One `callout.html` template handles all 6 types. Type-specific styling lives entirely in `main.css` via CSS custom properties and Tailwind utility classes. No inline style logic in the template. CSS rebuild required after adding new types.
+
+Chosen over:
+- Single shortcode with inline Hugo logic (more template complexity)
+- Separate shortcode per type (6 files, duplicated logic)
+
+---
+
+## Shortcode API
+
+```
+{{< callout type="note" >}}
+Content here. Supports **markdown**.
+{{< /callout >}}
+
+{{< callout type="danger" title="Custom heading" >}}
+Overrides the default i18n title.
+{{< /callout >}}
+```
+
+### Parameters
+
+| Parameter | Required | Default | Description |
+|-----------|----------|---------|-------------|
+| `type` | Yes | — | One of: `note`, `tip`, `info`, `warning`, `danger`, `success` |
+| `title` | No | i18n key for type | Overrides the default translated title |
+
+---
+
+## Visual Design
+
+**Layout:** Left border accent (4px solid, type color) + fading bottom border (gradient left→transparent, 50% width, 1px, type color). Subtle background tint. Rounded on right side only (`border-radius: 0 6px 6px 0`).
+
+**Title row:** Feather icon (14×14, `aria-hidden="true"`) + uppercase label in `font-family: var(--font-mono)` (JetBrains Mono), 11px, 0.08em letter-spacing, type color.
+
+**Body text:** `var(--text)`, 14px, 1.6 line-height. Supports markdown via `.Inner | markdownify`.
+
+**Spacing:** `my-6` vertical margin, `py-3 px-4` inner padding.
+
+**Background opacity:** dark 0.08, light 0.06 — consistent with existing toast/badge pattern.
+
+---
+
+## Color System
+
+All colors via CSS custom properties. No hardcoded hex except the approved Danger exception.
+
+### CSS Variables (added to `main.css`)
+
+```css
+/* Dark mode (:root) */
+--callout-note: var(--accent); /* purple #a855f7 */
+--callout-tip: var(--accent2); /* green #00ff88 */
+--callout-info: var(--type-link); /* cyan #38bdf8 */
+--callout-warning: var(--type-life); /* amber #f59e0b */
+--callout-danger: #ef4444; /* red — approved exception, universally semantic */
+--callout-success: var(--accent2); /* green #00ff88 */
+
+/* Light mode (html.theme-light) */
+--callout-note: var(--accent); /* purple #7c3aed */
+--callout-tip: var(--accent2); /* green #008f5a */
+--callout-info: var(--type-link); /* cyan #0284c7 */
+--callout-warning: var(--type-life); /* amber #d97706 */
+--callout-danger: #ef4444; /* red — same in both themes */
+--callout-success: var(--accent2); /* green #008f5a */
+```
+
+### Tailwind Classes (added to `main.css` via `@apply`)
+
+```css
+.callout { @apply relative my-6 py-3 px-4 rounded-r-md; }
+
+/* Each type sets --callout-color so ::after gradient picks it up automatically */
+.callout-note { --callout-color: var(--callout-note); border-left: 4px solid var(--callout-note); background: rgba(168,85,247,0.08); }
+.callout-tip { --callout-color: var(--callout-tip); border-left: 4px solid var(--callout-tip); background: rgba(0,255,136,0.08); }
+.callout-info { --callout-color: var(--callout-info); border-left: 4px solid var(--callout-info); background: rgba(56,189,248,0.08); }
+.callout-warning { --callout-color: var(--callout-warning); border-left: 4px solid var(--callout-warning); background: rgba(245,158,11,0.08); }
+.callout-danger { --callout-color: var(--callout-danger); border-left: 4px solid var(--callout-danger); background: rgba(239,68,68,0.08); }
+.callout-success { --callout-color: var(--callout-success); border-left: 4px solid var(--callout-success); background: rgba(0,255,136,0.08); }
+```
+
+The fading bottom border is a `::after` pseudo-element:
+```css
+.callout::after {
+ content: '';
+ position: absolute;
+ bottom: 0; left: 0;
+ width: 50%; height: 1px;
+ background: linear-gradient(to right, var(--callout-color), transparent);
+}
+```
+
+**Light mode background opacity** — reduce to 0.06 per theming standard (light needs less emphasis).
+rgba values use THEMING-STANDARD.md corrected light vars: `--accent #7c3aed`, `--accent2 #008f5a`, `--type-link #0284c7`, `--type-life #d97706`.
+
+**Prerequisite:** `html.theme-light` in `main.css` must first be corrected to match THEMING-STANDARD.md (see implementation plan Task 0).
+
+```css
+html.theme-light .callout-note { background: rgba(124,58,237,0.06); } /* #7c3aed */
+html.theme-light .callout-tip { background: rgba(0,143,90,0.06); } /* #008f5a */
+html.theme-light .callout-info { background: rgba(2,132,199,0.06); } /* #0284c7 */
+html.theme-light .callout-warning { background: rgba(217,119,6,0.06); } /* #d97706 */
+html.theme-light .callout-danger { background: rgba(239,68,68,0.06); } /* #ef4444 */
+html.theme-light .callout-success { background: rgba(0,143,90,0.06); } /* #008f5a */
+```
+
+---
+
+## Feather Icons per Type
+
+| Type | Feather icon name | Rationale |
+|------|-------------------|-----------|
+| note | `edit-2` | Writing/annotation |
+| tip | `zap` | Quick insight |
+| info | `info` | Standard info |
+| warning | `alert-triangle` | Universal warning symbol |
+| danger | `x-circle` | Stop/error |
+| success | `check-circle` | Confirmed/done |
+
+Icons rendered inline as `<i data-feather="icon-name" class="w-3.5 h-3.5" aria-hidden="true"></i>`. Feather is already loaded site-wide via `baseof.html`.
+
+---
+
+## i18n Keys
+
+Added to all 4 i18n files: `themes/danix-xyz-hacker/i18n/en.yaml`, `it.yaml` and content repo `i18n/en.yaml`, `it.yaml`.
+
+```yaml
+# en.yaml
+callout_note: "Note"
+callout_tip: "Tip"
+callout_info: "Info"
+callout_warning: "Warning"
+callout_danger: "Danger"
+callout_success: "Success"
+
+# it.yaml
+callout_note: "Nota"
+callout_tip: "Suggerimento"
+callout_info: "Informazione"
+callout_warning: "Attenzione"
+callout_danger: "Pericolo"
+callout_success: "Successo"
+```
+
+---
+
+## Accessibility
+
+- `role="note"` on all types — semantic, non-intrusive to screen readers
+- `role="alert"` on `danger` type only — interrupts screen readers immediately (appropriate for critical warnings)
+- `aria-hidden="true"` on all Feather icons — decorative
+- Title text always present (i18n fallback) — color never used alone to convey meaning (WCAG 1.4.1)
+- Body text uses `var(--text)` — verified ≥4.5:1 contrast ratio against `var(--bg)` in both themes per THEMING-STANDARD.md
+- No animations — `prefers-reduced-motion` not required, but `::after` pseudo is static CSS, no transitions added
+
+---
+
+## Files to Create / Modify
+
+| File | Action | Notes |
+|------|--------|-------|
+| `themes/danix-xyz-hacker/layouts/shortcodes/callout.html` | **Create** | Shortcode template |
+| `themes/danix-xyz-hacker/assets/css/main.css` | **Modify** | Add `--callout-*` vars + `.callout-*` classes |
+| `themes/danix-xyz-hacker/i18n/en.yaml` | **Modify** | Add 6 callout title keys |
+| `themes/danix-xyz-hacker/i18n/it.yaml` | **Modify** | Add 6 callout title keys (Italian) |
+| `i18n/en.yaml` | **Modify** | Mirror keys in content repo |
+| `i18n/it.yaml` | **Modify** | Mirror keys in content repo |
+| `SHORTCODES.md` | **Modify** | Document callout shortcode |
+
+CSS rebuild required (`npm run build`) after CSS changes.
+
+---
+
+## Shortcode Template Sketch
+
+```html
+{{- $type := .Get "type" | default "note" -}}
+{{- $title := .Get "title" | default (i18n (printf "callout_%s" $type)) -}}
+{{- $role := cond (eq $type "danger") "alert" "note" -}}
+
+<div class="callout callout-{{ $type }}" role="{{ $role }}">
+ <div class="callout-title">
+ <i data-feather="{{ /* icon per type */ }}" class="w-3.5 h-3.5" aria-hidden="true"></i>
+ <span>{{ $title }}</span>
+ </div>
+ <div class="callout-body">
+ {{ .Inner | markdownify }}
+ </div>
+</div>
+```
+
+Icon selection via Hugo `dict` lookup — no if/else chain:
+```
+{{- $icons := dict "note" "edit-2" "tip" "zap" "info" "info" "warning" "alert-triangle" "danger" "x-circle" "success" "check-circle" -}}
+{{- $icon := index $icons $type | default "info" -}}
+```
+
+---
+
+## Verification
+
+1. Run `hugo server` — render an article with all 6 callout types
+2. Toggle dark/light theme — verify colors switch correctly via CSS vars
+3. Override title with `title="Custom"` — verify it replaces i18n default
+4. Screen reader test: `danger` type must announce immediately (`role="alert"`); others must not interrupt
+5. Run `npm run build` — verify CSS compiles without errors and Tailwind includes all `.callout-*` classes
+6. Check light theme backgrounds use lower opacity (0.06) vs dark (0.08)