# 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)
