Planner App→
Migrating a GTD planner from Python/Railway to TypeScript/Cloudflare with a single choke point for every paid send.
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.
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); }
Migrating a GTD planner from Python/Railway to TypeScript/Cloudflare with a single choke point for every paid send.
Killswitch, budget, idempotency, execute — why every paid operation needs all four and what fails when you skip one.
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.
Coles recipe scraper that consolidates ingredients across a week's meals into one trolley.
Why every portfolio screenshot should be captured at render time, never baked in.
Four layers between a button press and a Twilio API call — each catching a different failure mode.
When hand-drawn flowcharts beat polished ones, and the tools that get you there fastest.
Astro 6, React islands, Tailwind v4 — content-first, design-second, shipping-third.
What I learned re-reading Wes Kao and John Cutler before writing any of my own posts.
Tokens-first architecture applied across three existing projects without changing what they look like.
How to anchor a dark-mode palette on one hex and derive everything else deterministically.
Quarter-by-quarter breakdown of a two-year B2B sales cycle, built from raw Salesforce exports.
The posts that forced me to understand what I was actually claiming about the systems I was building.
Short-code lockup when no diagram or screenshot fits the brief — lightest-weight option.
Every piece of writing that's shipped owes a debt to a piece of reading that didn't.
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.
GTD planner migrating from Python/Railway to TypeScript/Cloudflare.
Killswitch, budget, idempotency, execute — and what fails when you skip one.
GTD planner migrating from Python/Railway to TypeScript/Cloudflare.
Killswitch, budget, idempotency, execute — and what fails when you skip one.
GTD planner migrating from Python/Railway to TypeScript/Cloudflare.
Killswitch, budget, idempotency, execute — and what fails when you skip one.
GTD planner migrating from Python/Railway to TypeScript/Cloudflare.
Killswitch, budget, idempotency, execute — and what fails when you skip one.
GTD planner migrating from Python/Railway to TypeScript/Cloudflare.
Killswitch, budget, idempotency, execute — and what fails when you skip one.
| 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 |
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.
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.
A rebuild from Python/Railway to TypeScript/Cloudflare that turned convention-based spend discipline into a structural guarantee. Killswitch, budget, idempotency, execute — every paid operation passes through all four.
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.
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; }
z-index: 2 rule keeps them clickable independently.
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.