Skip to content

The This Week hero for the project page, as PQ plates over live CSS - #624

Open
Georg-Klock wants to merge 14 commits into
mainfrom
feat/website-week-widget
Open

The This Week hero for the project page, as PQ plates over live CSS#624
Georg-Klock wants to merge 14 commits into
mainfrom
feat/website-week-widget

Conversation

@Georg-Klock

@Georg-Klock Georg-Klock commented Sep 11, 2026

Copy link
Copy Markdown
Owner

The "This Week" hero for the project page: the app's week grid, scaled up, as a Webflow embed whose open marks physically glow on a screen with headroom. Same technique as the word slider (docs/glow.md), applied to three shapes instead of one.

What is in it

  • Tools/make-week-plates.swift cuts the emitting tier — the open ring one to seven columns wide, the five weekday letters, the eight default habit names and their icons — as PQ AVIF plates with the halo baked in, for four tier/density sets (desktop 1x and 2x, tablet 2x, mobile 3x). 112 files, 199 KB. Icons are Phosphor outlines (MIT) drawn through a small SVG path parser, because NSImage's SVG drawing trapped in a grey mask context. play.rectangle has no Phosphor twin without a monitor stand, so it is drawn by hand in the same 256-unit box.
  • Tools/check-week-plates.swift reads every plate back with libavif — the decoder Chrome uses, not the one that wrote the file — and fails on an odd dimension, a missing PQ tag, an alpha plane, a missing file, or a decoded peak that is not above SDR white. With --preview it writes tone-mapped PNGs for looking at geometry.
  • Website/week-widget/: the embed template, its build (which inlines the manifests and icon outlines so the page never fetches a manifest), the plates, Inter 4.1, the icons, a preview page with test hooks, a tuning page for the ring's halo, the Webflow asset-map helper, and a README with the upload steps.

How the page works

Only the emitting tier is a plate. Lit #D9D9D9 and resting marks are CSS: the socket, the done disc with its three inner shadows, the ✕ well as an SVG filter. A state change shows or hides an <img> over its live twin, never a re-encode. Row placement is WeekSpans.dividedWithoutRestDay and assignColumns ported to JavaScript (docs/week-marks.md §4), so the six weekly rows draw the real pill layout for whatever weekday the visitor loads it on, and every open mark is clickable — invariant 7 means completing today never moves a future window. Today's letter emits while any row is open and drops to lit when the last one is done.

(dynamic-range: high) gates every fetch; a plate is shown only after it loads with a non-zero natural width, and an error or empty decode leaves the live twin in place. Tilt runs only under a fine pointer without reduced motion.

Plates paint beneath every other mark. A plate's halo padding is about a third of a cell, the gap is a third of a cell, so the first Chrome capture showed ring plates clipping the sockets beside them and the rows above and below. The card and the plate are the same #000000, so a socket over a plate's surround is indistinguishable from one over the card; only the halo goes behind the neighbour.

Where this follows the source over the brief

The brief described a small done dot and a resting-grey ✕. SlotMarkView draws a full lit disc with inner shadows and a recessed cross-shaped well, and the App Store frame confirms it, so that is what the page draws. The README lists the three differences.

On the site

It is live on georgklock.com/glow-up, under the headline. Webflow caps every custom-code block at 10,000 characters and the one-block embed is 44 KB, so build-embed.py --webflow also writes three pieces that fit — CSS for the page head, markup for an HTML Embed element, the comment-stripped script for the site footer (it returns at once on a page without the card) — plus one JSON asset with the manifests, icon outlines and name→URL map, fetched once at load. The 112 plates came back byte-identical from the CDN; Inter is a site custom font. The README records the placement and the update path.

Second cut, on review: no halo, grey card

The halo read as a drop shadow on every emitting mark and letter, and a black card looked nothing like the widget in the screenshots beside it. The plates are re-cut with no halo and a two-pixel edge (--halo-strength 0 --pad 0.08), and the card is #202020, the mode of the widget's material measured on the App Store frame. A PQ plate carries no alpha, so the generator gains --surround and the plates are cut with that grey as their surround; decoded through libavif it is 2.93 nits, exactly sRGB 32 at 203-nit white. Re-uploaded, re-mapped, re-published. tuning.html is history.

Third cut, on review: SF Symbols, masked plates, a slider, the glass

Every plate is now clipped to its own shape with a CSS mask from an alpha PNG the generator writes at the plate's exact size — a PQ file's opaque surround only matched the card on a screen with headroom, and framed every emitting mark in a darker rectangle on any other. Every headroom step from 1x to 8x is a plate, so the new slider below the card never cross-fades a plate with the live CSS twin (rasterised differently; the letters appeared to move). The icons are the app's SF Symbols rasterised by AppKit, the lit tier a mask of the same symbol; the ✕ is CrossShape rendered through ImageRenderer; the card's background is the widget's material lifted from the App Store frame and inpainted. 780 files on the CDN. SF Symbols on the web is outside Apple's licence for them and was asked for explicitly.

Fourth cut, on review: one slider for the card and the sentence

The page carried two demonstrations of the same trick with two controls — the twelve-step word slider in the centered section and the hero with its own eight-step slider in a section of its own. They are one thing now. The sentence's twelve existing files sit above the card as stacked layers, sized like the site's "Larger Text" style (32px of type is a 598px stage; 486 / 318px on the tablet and phone breakpoints), and one range from 1x to 12x drives the plates and the layers together, sweeping up and back on a loop — 5.2 s a pass, eased at both ends — until a visitor drags it, resuming from where they left it after four seconds. It stops while the tab is hidden, holds at 12x under reduced motion, and never starts on a screen without headroom, where the slider is hidden and only the 1x sentence file is fetched. The plates were re-cut for the four steps above 8x (336 new files, verified with libavif like the rest; 1,095 on the CDN now) and every step is fetched up front once the plate path opens, so the first pass never meets a plate mid-load. On the page the hero embed took the old embed's place ahead of the "if the text above doesn't glow" note, the empty section and paragraph went, and the Dynamic Island video joined the two-frame row beside the open-close video and the widgets shot; its own section went too.

On review the card was pinned to one Thursday rather than following the visitor's weekday: Thursday's letter is the only one emitting, five habits are open, and Workout, VO2 Max and Tutorial are already logged today, so their disc, name and icon sit at the lit tier the app gives a completion — clicking a done mark undoes it, clicking an open ring completes it and drops the row to lit, as before. The midnight re-seed went with the pin. The gaps between sentence, card, slider and the note below halved.

Two more page changes on review, both Webflow-only apart from one file. The first screenshot row is now widget gallery video, home-screen image, open-close video; the second row and the Island video are gone, and a grey section with the four App Store listing screenshots follows the pitch instead. Above the "Agentically built" item sits Website/agent-flow/embed.html: the loop the app was built in as cards, emoji, the GitHub mark and arrows — designer, interviewing Sonnet, GitHub Issues, building agents, tests and CI, the tethered iPhone, and back.

A plate is the emitting tier and nothing else. Every weekday letter, name and icon showed its plate on a screen with headroom whatever its tier, so the whole card glowed and a click on today's mark changed nothing visible — the tier classes were right and the plates ignored them. applyGain now shows a plate only on a host carrying is-emitting, and updateTiers ends by re-applying the plates, so completing today takes the row's name and icon out of the glow at once and undo puts them back; only Thursday's letter glows. Checked on the local preview by clicking Workout's done mark and back: its name, icon and ring plates appear on undo and vanish on redo, and the letters' plate count stays at one. The seed has Workout, VO2 Max and Tutorial complete for the week and Sunset done Monday and Tuesday.

The tiers read without headroom. There the emitting tier is plain white and the app's lit #D9D9D9 sits too close to it, so lit names and icons drop to #B4B4B4 on a screen without headroom (--lit-sdr); with headroom the plate carries the difference and lit stays the app's grey. The six weekday letters that are not today are #A0A0A0 on every screen (--off-day). Computed colours checked on both preview paths: white for the five open rows and Thursday's letter, the step-down grey for the three done rows, the off-day grey for the other six letters.

Verified

Check Result
libavif 1.4.2 All 112 plates: even dimensions, 10-bit, 4:2:0, primaries 9, transfer 16, no alpha
Decoded peak 424–440 nits, 2.09–2.17× SDR white (the PQ round trip's ~8% over the requested 2×, as measured for the slider)
Chrome 152 on the XDR HDR true, 31 plates shown; a real click on today's ring → done disc, name and icon drop to lit, the F letter drops after the last open mark; undo restores
Emit → lit swap Plate and live twin at 4×: same position and size for name, icon, letter and ring
SDR fallback preview.html?sdr: nothing fetched, emitting renders white, lit grey
Decode failure ?corrupt (truncated AVIF) and ?missing (404): affected plates flagged, live twins shown, the other 27 unaffected
Tiers 676 / 507 / 296px cards, no horizontal scroll at 375px; tilt off under touch emulation
Network Interaction makes no request

Not verified, and still yours: the glow judged by eye on an HDR screen — a capture cannot show it — and Safari. The one thing the second cut could not be checked for in a browser is a seam between a plate's grey surround and the card; the numbers say there is none, but Chrome had moved to the SDR display by then, so a look at the live page on the MacBook screen settles it.

The app is untouched; Tools/test.sh:

L1 785/785

🤖 Generated with Claude Code

Tools/make-week-plates.swift cuts the emitting tier of the app's week grid
for a Webflow embed — the open ring one to seven columns wide, the weekday
letters, the eight default habit names and their icons — as PQ AVIF plates
with the halo baked in, per responsive tier and density. It is the word
slider's pipeline with three shapes instead of one; the SVG icons go through
a small path parser because NSImage's SVG drawing trapped in a grey mask
context.

Tools/check-week-plates.swift reads every plate back with libavif, the
decoder Chrome uses, and fails on an odd dimension, a missing PQ tag, an
alpha plane, or a decoded peak that is not above SDR white. All 112 pass at
2.09–2.17x SDR white.

Website/week-widget/ holds the embed and its build: the lit and resting
tiers are CSS, a state change shows or hides a plate over its live twin, the
row placement is WeekSpans ported to JavaScript, and the page fetches
nothing on a screen without headroom and falls back per plate on a decode
failure. Plates paint beneath every other mark, because a plate's halo
padding is wider than half a gap and the first Chrome capture showed ring
plates clipping the sockets beside them.

The ring's halo ships at the word slider's numbers, unjudged; tuning.html
shows six cuts for the decision.
Webflow caps every custom-code block at 10,000 characters and the one-block
embed is 44 KB, so build-embed.py --webflow now also writes the three pieces
that fit — the CSS for the page head, the markup for an HTML Embed element,
the comment-stripped script for the site footer — and one JSON asset holding
the manifests, icon outlines and name-to-URL map, which the script fetches
once at load. The script returns at once on a page without the card.

Uploaded: 112 plates (byte-identical on the CDN), the manifest, and Inter as
a custom font. Placed in a new centred section under the headline and
published. Verified live in Chrome on the XDR: HDR true, 31 plates ready,
click and undo work, no console errors.
The halo read as a drop shadow on every emitting mark and letter, and a black
card looked nothing like the widget in the screenshots beside it. The plates
are re-cut with no halo and a two-pixel edge, and the card is #202020, the
mode of the widget's material measured on the App Store frame. A PQ plate
carries no alpha, so make-week-plates.swift gains --surround and --pad and the
plates are cut with that grey as their surround; decoded, it is exactly
sRGB 32 at 203-nit white. Re-uploaded, re-mapped, re-published.
…he glass

Every plate is now clipped to its own shape with a CSS mask, from an alpha
PNG the generator writes at the plate's exact size. A PQ file's opaque
surround only matched the card on a screen with headroom; on any other it
tone-mapped darker and framed every emitting mark in a rectangle. The mask
ends that on every display and lets the card carry a texture.

Every headroom step from 1x to 8x is a plate, so the slider never
cross-fades a plate with the live CSS twin — the two are rasterised
differently and the letters appeared to move between 1x and 2x.

The icons are the app's SF Symbols, rasterised by AppKit; the lit tier is a
mask of the same symbol. The ✕ is the app's own CrossShape rendered through
ImageRenderer, because an SVG-filter rebuild came out flat. The card's
background is the widget's material lifted from the App Store frame and
inpainted where the marks stood. 780 files on the CDN; published.
The word slider and the hero were two demonstrations of one trick with two
controls. The sentence's twelve existing files now sit under the card as
stacked layers, sized like the site's Larger Text style, and one range from
1x to 12x drives the plates and the layers together, sweeping up and back on
a loop until a visitor drags it. The plates were re-cut for the four steps
above 8x (336 new files, verified with libavif), every step is fetched up
front once the plate path opens, and the page tree lost the old embed and its
empty section; the Dynamic Island video joined the two-frame row.
The column gap halves to 14px per unit of scale, the sentence loses its
extra top margin, and the padding below the slider drops to 12px per unit
so the HDR note follows sooner.
The card no longer follows the visitor's weekday: it always shows Thursday,
so the reviewed picture is the one everyone sees — Thursday's letter the
only one emitting, five habits open, and Workout, VO2 Max and Tutorial
already logged today (doneToday in the seed), whose disc, name and icon sit
at the lit tier the app gives a completion. The midnight re-seed goes with
it. The sentence moves above the card, the slider stays below.
One HTML Embed above the Agentically built item: the loop the app was built
in as cards with emoji, the GitHub mark and arrows, from Georg through the
interviewing Sonnet, GitHub Issues, the building agents, tests and CI, to
the tethered iPhone and back. Column with down arrows on phones.
… week

Every letter, name and icon showed its plate on a screen with headroom
whatever its tier, so the whole card glowed and a click on today's mark
changed nothing visible — the tier classes were right and the plates
ignored them. applyGain now shows a plate only on a host carrying
is-emitting, and updateTiers ends by re-applying the plates, so completing
today takes the row out of the glow at once and undo puts it back; only
Thursday's letter glows. The seed has Workout, VO2 Max and Tutorial
complete for the week and Sunset done Monday and Tuesday.
…tep down

On a screen without headroom the emitting tier is plain white and the
app's lit grey sits too close to it, so lit names and icons drop to
#B4B4B4 there; with headroom the plate carries the difference and lit
stays the app's grey. The six weekday letters that are not today are
#A0A0A0 on every screen.
…he slider

The sentence above the card is gone. The note — three lines, rendered by
make-glow-word.swift as one AVIF per line and headroom step in the site's
body face — sits under the slider as stacked layers like the plates, at the
body's 17px, and the plain-text paragraph came off the page. SDR shows the
1x file alone.
The strip of six cards drew the process as a line with a loop written
underneath it. There are really two cycles at different speeds sharing one
spine: a machine loop that closes in minutes without a person, and a human
loop that turns twice a day. The agents are a cluster rather than a box,
since Fable plans and Codex takes the heavy implementation, and that
hand-off is conditional. The two phones are now distinct: the old tethered
debug phone sits in the machine loop, my TestFlight phone in the human one.
The one question no gate can answer is the glow itself, so it crosses the
middle as the only dashed edge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants