ModsCodeMERGEBLE LLC Search Claude Code Mods

ModsCode / Gallery / Designs / Notification stack

Notification stack

The latest notifications as a receding stack of cards, newest on top; a left stripe and a label name the kind, and older cards inset and fade.

Notification stackNotification stack
Work and agents · 380×196 · sample data
hooks/designs/notify-stack.mjs
// notify-stack: the four latest Claude Code notifications, newest on top, as kanban's cards (1px outline
// at 18% ink, no fill, a 2px stripe inside the left edge that stops 6px short of the corners). Each older
// card is inset 6px more on both sides and drawn at 100 / 82 / 65 / 50% opacity as one group (no lower: at 25% the muted line
// of the oldest card vanished at thumbnail size), so the
// stripe, outline and words fade together and the list recedes like a stack. The stripe and the small
// label on the right name the kind: needs you (Claude orange, the one accent: a permission prompt waiting on the
// person), failed (red), done (green) and info (ink). Title 10px ink, one line and the age 9px muted.
// Sample data only.
const W = 380
const H = 196
const STAGE = '#FAF9F5'
const INK = '#141413'
const MUTED = '#73726C'
const UP = '#2C7A39'
const DOWN = '#AB2B3F'
const CLAUDE = '#D77757'
const FONT = "'Inter Tight','Segoe UI',system-ui,sans-serif"
const MONO = "ui-monospace,'Cascadia Mono',Consolas,monospace"
const f = n => n.toFixed(1)

const CARD = { x0: 12, y0: 12, h: 36, gap: 6, inset: 6 }
const FADE = [1, 0.82, 0.65, 0.5]
const KIND = {
  needs: { label: 'NEEDS YOU', stripe: CLAUDE, opacity: 1 },
  failed: { label: 'FAILED', stripe: DOWN, opacity: 1 },
  done: { label: 'DONE', stripe: UP, opacity: 1 },
  info: { label: 'INFO', stripe: INK, opacity: 0.65 },
}

// Newest first. A body in mono is a command or a path; the rest is prose.
const NOTES = [
  { kind: 'needs', title: 'Approve a command', body: 'git push origin main', mono: true, age: 'NOW' },
  { kind: 'failed', title: 'Build failed', body: 'tsc found 3 errors in src/api/retry.ts', age: '2m' },
  { kind: 'done', title: 'Refactor finished', body: '12 files changed, 214 tests passed, $0.42', age: '9m' },
  { kind: 'info', title: 'Context compacted', body: '71% to 18%, the summary stays in the session', age: '24m' },
]

const esc = s => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
const text = (x, y, s, { size = 9, fill = MUTED, anchor = 'start', font = FONT } = {}) =>
  `<text x="${f(x)}" y="${f(y)}"${anchor === 'start' ? '' : ` text-anchor="${anchor}"`} font-size="${size}" font-family="${font}" fill="${fill}">${esc(s)}</text>`

function card(n, i) {
  const k = KIND[n.kind]
  const x = CARD.x0 + i * CARD.inset
  const w = W - 2 * x
  const y = CARD.y0 + i * (CARD.h + CARD.gap)
  return [
    `<g opacity="${FADE[i]}">`,
    `<rect x="${f(x + 0.5)}" y="${f(y + 0.5)}" width="${f(w - 1)}" height="${CARD.h - 1}" rx="3" fill="none" stroke="${INK}" stroke-opacity="0.18"/>`,
    `<rect x="${f(x + 1)}" y="${f(y + 6)}" width="2" height="${CARD.h - 12}" fill="${k.stripe}"${k.opacity < 1 ? ` fill-opacity="${k.opacity}"` : ''}/>`,
    text(x + 12, y + 15, n.title, { size: 10, fill: INK }),
    text(x + w - 10, y + 15, k.label, { fill: n.kind === 'info' ? MUTED : k.stripe, anchor: 'end' }),
    text(x + 12, y + 28, n.body, { font: n.mono ? MONO : FONT }),
    text(x + w - 10, y + 28, n.age, { anchor: 'end' }),
    '</g>',
  ].join('')
}

function draw() {
  const parts = [`<rect width="${W}" height="${H}" rx="4" fill="${STAGE}"/>`]
  NOTES.forEach((n, i) => parts.push(card(n, i)))
  parts.push(text(12, H - 8, 'SAMPLE NOTIFICATIONS'))
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}">${parts.join('')}</svg>`
}

let source // drawn on first use and kept: the pane loads every design, so none pays at load time
export default {
  id: 'notify-stack',
  title: 'Notification stack',
  category: 'work',
  description: 'The latest notifications as a receding stack of cards, newest on top; a left stripe and a label name the kind, and older cards inset and fade.',
  element: ui => ui.Svg({
    source: (source ??= draw()),
    alt: `A stack of ${NOTES.length} notifications, newest on top: ${NOTES.map(n => n.title.toLowerCase()).join(', ')}, older ones fading, sample data`,
    width: W,
    height: H,
  }),
}

To use it in a mod: copy the file to your mod's hooks/designs/ and return its element(ui) from a pane's ui.render. How a pane is built.

Code: PolyForm Noncommercial License 1.0.0: noncommercial use only. Required Notice: Copyright 2026 Haruki Nakada (https://modscode.com).