CARD DEEP-DIVE · V2

Mandatory imagery, auto-tinted, differentiation without stripes

Three changes from v1: imagery is now mandatory on every card, tint is automatic at the component layer (zero author effort), and the writing-card left stripe is dropped in favour of lighter alternatives. Layout D (corner accent) is assumed throughout; the question on this page is no longer which layout, it's how to cohere mandatory imagery and how much register differentiation the cards actually need.

§1

Automatic tint — CSS-only, zero author effort

The container wraps the image in a slot with overflow: hidden and a ::after pseudo-element that sits over the image with mix-blend-mode: color. That blend mode keeps the image's luminance (shadows, highlights, shape) but replaces its hue with the overlay colour. The author drops in any screenshot — the component pulls it into the royal (or violet, for writing) palette automatically.

/* applied by the component, not the author */
.img-slot { position: relative; overflow: hidden; }
.img-slot img { filter: saturate(0.75); }
.img-slot::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--royal-8);
  mix-blend-mode: color;
  opacity: 0.55;
  pointer-events: none;
}
.card.writing .img-slot::after { background: var(--violet-8); }
Proof below: the screenshot mock inside the first card uses a deliberately off-palette "photo" (magenta → yellow → cyan gradient) to demonstrate that tint automation works on arbitrary input. You should see it rendered as royal-toned — no pink, no yellow. The writing card uses the same off-palette photo tinted violet.
CASE STUDY · 2026

Planner App

Migrating a GTD planner from Python/Railway to TypeScript/Cloudflare with a single choke point for every paid send.

  • typescript
  • cloudflare
  • twilio
WRITING · 2026

The four-layer spend guard

Killswitch, budget, idempotency, execute — why every paid operation needs all four and what fails when you skip one.

  • architecture
  • reliability
§2

Visual type library — six options, one frame

The frame is constant: 64×64 rounded rectangle, 1px royal-7 border (violet-tinted on writing cards), pinned to the top-right, inset by the card padding. Whatever content fills it, the container never changes. That's the single strongest consistency lever — author-varied imagery, system-fixed frame.

Type 1 — Screenshot (auto-tinted, authors drop in raw)
CASE STUDY · 2026

The Weekly

Coles recipe scraper that consolidates ingredients across a week's meals into one trolley.

  • react
  • vite
  • netlify
Type 2 — Diagram (palette-locked, SVG or mermaid)
CASE STUDY · 2026

Spend guard architecture

Four layers between a button press and a Twilio API call — each catching a different failure mode.

  • architecture
  • typescript
WRITING · 2026

Drawing the lines right

When hand-drawn flowcharts beat polished ones, and the tools that get you there fastest.

  • diagrams
  • mermaid
Type 3 — Numeral (Fraunces display + gradient fill)
01
CASE STUDY · 2026

Portfolio v1

Astro 6, React islands, Tailwind v4 — content-first, design-second, shipping-third.

  • astro
  • tailwind
03
WRITING · 2026

Three weeks of style guide

What I learned re-reading Wes Kao and John Cutler before writing any of my own posts.

  • writing
  • research
Type 4 — Gradient mark (abstract, CSS-only, always palette-correct)
CASE STUDY · 2026

Design system retrofit

Tokens-first architecture applied across three existing projects without changing what they look like.

  • tokens
  • refactor
WRITING · 2026

Royal Tonal in twelve steps

How to anchor a dark-mode palette on one hex and derive everything else deterministically.

  • design
  • colour
Type 5 — Icon (lucide-react, line-only, palette stroke)
CASE STUDY · 2026

MOB analysis

Quarter-by-quarter breakdown of a two-year B2B sales cycle, built from raw Salesforce exports.

  • analysis
  • sql
WRITING · 2026

Writing as research

The posts that forced me to understand what I was actually claiming about the systems I was building.

  • craft
  • meta
Type 6 — Monogram (typographic lockup, initials or short code)
PLN
CASE STUDY · 2026

Planner App

Short-code lockup when no diagram or screenshot fits the brief — lightest-weight option.

  • typography
R/W
WRITING · 2026

Read-write reciprocity

Every piece of writing that's shipped owes a debt to a piece of reading that didn't.

  • notes
Consistency mechanics recap: (1) identical frame across all types, (2) identical border + radius + inset, (3) auto-tint forces palette adherence on screenshots, (4) diagrams and icons use palette tokens as strokes, (5) numerals and gradient marks are palette by construction, (6) all six types share the same hover behaviour. Six visual languages, one system.
§3

Writing differentiation — how hard to push the register split

v1 used a left-edge violet stripe to mark writing cards. That's dropped. Four lighter alternatives below, plus a baseline that does nothing structural and lets kicker-colour + tag-colour carry the whole job. Each variant shows a work card next to a writing card so you can read the register difference directly.

Variant A — Baseline: nothing structural. Only kicker colour + tag colour differ. Minimal.
01
CASE STUDY · 2026

Planner App

GTD planner migrating from Python/Railway to TypeScript/Cloudflare.

  • typescript
  • cloudflare
02
WRITING · 2026

The four-layer spend guard

Killswitch, budget, idempotency, execute — and what fails when you skip one.

  • architecture
  • reliability
Variant B — Top rule. A 2px violet line across the top of writing cards.
01
CASE STUDY · 2026

Planner App

GTD planner migrating from Python/Railway to TypeScript/Cloudflare.

  • typescript
  • cloudflare
02
WRITING · 2026

The four-layer spend guard

Killswitch, budget, idempotency, execute — and what fails when you skip one.

  • architecture
  • reliability
Variant C — Kicker dot. A small violet disc before the kicker text on writing cards.
01
CASE STUDY · 2026

Planner App

GTD planner migrating from Python/Railway to TypeScript/Cloudflare.

  • typescript
  • cloudflare
02
WRITING · 2026

The four-layer spend guard

Killswitch, budget, idempotency, execute — and what fails when you skip one.

  • architecture
  • reliability
Variant D — Kicker icon. A small line-icon (pencil) before the kicker on writing cards.
01
CASE STUDY · 2026

Planner App

GTD planner migrating from Python/Railway to TypeScript/Cloudflare.

  • typescript
  • cloudflare
02
WRITING · 2026

The four-layer spend guard

Killswitch, budget, idempotency, execute — and what fails when you skip one.

  • architecture
  • reliability
Variant E — Corner accent tinted violet. The corner image frame itself does the differentiation — writing cards tint the same frame in violet tones. No other change.
CASE STUDY · 2026

Planner App

GTD planner migrating from Python/Railway to TypeScript/Cloudflare.

  • typescript
  • cloudflare
WRITING · 2026

The four-layer spend guard

Killswitch, budget, idempotency, execute — and what fails when you skip one.

  • architecture
  • reliability
Variant Differentiation signal Noise added Works when mixed in feed Author cost
A · baseline kicker colour + tag colour only none yes zero
B · top rule horizontal line + colours one new element yes zero
C · kicker dot dot glyph + colours one glyph yes zero
D · kicker icon line icon + colours changes kicker rhythm ok icon choice per card type
E · tinted frame corner accent hue shift none yes zero
RECOMMENDATION

Variant A (baseline) — let kicker/tag colour + corner tint carry it

You said writing and case-study cards don't need to be super distinctive — tags, kicker pills, and colours already do the work. Variant A agrees: nothing structural, just the two existing colour signals. Because imagery is now mandatory and the corner frame auto-tints by register (royal for work, violet for writing), Variant E is effectively free on top of A — the tint is already happening, and it quietly doubles the signal without adding any new element. So the real answer is A + E together, which is what the §2 mocks above are already rendering.

Variants B / C / D are fallbacks if, after seeing real content in the /writing index, the register split doesn't read clearly enough. My prediction: it will. The violet kicker + violet tag + violet-tinted corner accent is already three reinforcing signals on a small surface.

§4

Featured variant

One card per index surface can be marked featured: gradient border (via padding + background wrapper), a FEATURED pin at top-left, same inner card otherwise. The featured state amplifies, it doesn't replace.

§5

Loading state

Shimmer skeletons preserve the card's geometry so the layout doesn't jump when content hydrates. The corner accent is a plain tinted square during load.

§6

Whole-card click target

Every card above is already using the pattern. Visible anchor stays on the heading (so focus ring + screen reader semantics land on the title), and a pseudo-element extends the hit area over the whole card without touching the visual design. Text selection still works because the overlay is transparent and has no background.

/* semantic anchor stays on the title */
<h3><a class="card-link" href="...">Title</a></h3>

/* card container is the click target */
.card { position: relative; cursor: pointer; }
.card .card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* anything interactive inside the card sits above the overlay */
.card .tags { position: relative; z-index: 2; }
Accessibility: focus ring still appears on the title (keyboard users see the correct affordance). Screen readers still announce a single link with the title as the accessible name. Mouse users get the expanded hit target. If tags become real links later, the z-index: 2 rule keeps them clickable independently.
§7

Decision summary

DECISIONS IN THIS DOC

Six locked, two open for your call

Locked: mandatory imagery (every card has a corner accent); automatic tint via mix-blend-mode: color (no author effort); six valid visual types (screenshot, diagram, numeral, gradient-mark, icon, monogram); one frame across all types; featured variant as amplification not replacement; whole-card click via ::after overlay.

Open: (1) writing-card differentiation — my pick is A + E (baseline + tinted corner), but you may want B/C/D as a backup if real content doesn't read clearly. (2) Whether to adopt this as the locked card component and move to chunk 4b (content hierarchy), or iterate once more on a v3 with refinements.