feat(design): Impasto — the soft, tactile, answer-first redesign - #295
Closed
JanWelker wants to merge 3 commits into
Closed
feat(design): Impasto — the soft, tactile, answer-first redesign#295JanWelker wants to merge 3 commits into
JanWelker wants to merge 3 commits into
Conversation
The app read as assembled rather than designed: six identical cards, a form-first column that pushed the schedule below the fold on a phone, two type sizes doing the work of a scale, and a palette that resolved on screen to cream plus one red. "Impasto" gives it a point of view. A bake board opens the page with what you are baking, when, and what to do next, so the inputs sit behind the answer instead of in front of it. The fermentation window - the one decision that changes everything - stops being a hairline range input in a small box and becomes a tinted slab with a groove cut into it, a 1.75 rem thumb and a large duration readout. The schedule becomes a feed of step cards, each node carrying a drawing of what that step is. Tomato now means action, basil means time, and dough is the substrate every surface is made of, so the palette says something rather than decorating. Depth comes from light: panels are lighter than the ground and cast a soft warm shadow. That does nothing on a dark ground, so every raised surface also carries a hairline that is transparent in light and warm in dark - both halves designed, neither derived from the other. Semantic surface and ink tokens replace hand-picked greys with dark: twins, which is how the recipe tables once sat at 2.71:1 without anyone seeing it. The math, the URL schema and the print sheet are untouched: no share link resolves to a different recipe.
Referenced from the pull request body. Both themes at 390 px and 1440 px, plus one full page and the nine-step biga + poolish schedule, which is the densest thing the feed has to render.
|
The neutral chip carries a step's duration in the schedule and a ranking in the 50 Top Pizza table, and it lands on three different surfaces: the lifted panel, the flat card the collections sit in, and the tomato wash under the running step. A fixed fill can only be tuned to one of them - tuned to the panel it matched the quiet card closely enough that the ranking pills read as plain text in the dark theme. An alpha wash of the ink colour is relative to whatever it sits on, so it cannot collide with any of the three. The regression test measures the composited difference in both themes, and polls rather than reading once: the running step's card transitions its own background, so switching theme and measuring in the same tick catches the colour it is on its way from rather than the one it lands on.
Owner
Author
|
Closing: design variant not chosen. #300 (Servizio × Bottega) merged as v7.0.0. Impasto was right that the fermentation window deserves to be a hero control rather than a hairline range input, and that idea is still unbuilt — worth revisiting on top of v7. Its focus-ring fix is the one to remember: Kept for reference rather than deleted. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The answer comes first, and the fermentation window becomes the control the app is really about.
The feedback was that the site looks like it was made by an engineer, and it did: six identical cards, a form-first column that pushed the schedule below the fold on a phone, two type sizes doing the work of a scale, and an "Italian-warm" palette that resolved on screen to cream plus one red. "Impasto" is the phone-app answer — calm, tactile, warm, organised around a single hero decision instead of a wall of fields.
The math, the URL schema, the
.icsexport, the TRMNL payload and the print sheet are untouched. Every existing share link resolves to exactly the same recipe.What a user sees differently
Above everything — a bake board. A new region opens the page on every width: the bake moment as a large figure, a "Now / Next" line naming the step the clock is standing in with the time still left on it, and three stat tiles (pizzas, total dough, window). It computes nothing of its own — it reads the schedule the form already produced. On a phone the answer is now wholly inside the first screen; before, you met eight input rows first.
The form. Quieter, and grouped: the two field sets are separated by a rule, every label goes through one class, and the flour/W pair reads as one step. Nothing was removed — expert view still exposes all twelve numbers in the same order, in the same two-up grid, one tap away as before.
The fermentation window. Was a 2 px hairline range input in a small white box. It is now a dough-tinted slab inside the form with a 20 px groove cut into it, the flour's tolerance zones painted inside the groove, a 28 px thumb sitting in the channel, and the duration set as the panel's headline figure rather than a caption. The bake-deadline flag and the ideal-stop marker keep their exact geometry — arrow on the thumb to the pixel, half-thumb inset discipline intact, just at the new radius.
The schedule. A day-grouped feed of step cards instead of a timeline of identical dots. Each rail node carries a drawing of what that step actually is — scales for the weigh, a hook in the bowl for the mix, a fridge for the cold leg, a sliced pizza for the bake — filled tomato when the baker has to do something, a hollow basil ring when time is just passing. The running step is the only card that lifts, and it now sits on a tomato wash rather than the same beige as the ingredient lists inside its neighbours.
The reference sections. My recipes, Community and 50 Top Pizza are flat and hairlined now instead of being the same lifted card as the schedule. Browsing other people's recipes is a detour; the page should say so.
Dark mode is a warm dusk rather than a cooler copy of the light theme: deep red-brown greys, the same soft-light logic, and a visible edge on every panel — see below for why that edge is load-bearing.
Design tokens and component classes
Semantic surfaces and ink are
@themeutilities mapped onto--kt-*custom properties that:rootandhtml.darkboth define. Since the theme class sits on<html>, which is:root, one declaration serves both themes — so components writebg-surface text-ink-softinstead of a hand-picked grey plus adark:twin. A hand-picked pair is exactly how the recipe tables once sat at 2.71:1 in dark without anyone noticing.--kt-ground,--kt-surface,--kt-raised,--kt-sunk,--kt-tint,--kt-now,--kt-hairline,--kt-edge--kt-ink,--kt-ink-soft,--kt-ink-faint,--kt-accent,--kt-leaf--kt-lift-1,--kt-lift-2,--kt-press--font-displayis now a rounded geometric stack (ui-rounded→ SF Pro Rounded → Nunito → …), not a serif;--text-figureand--text-statclamp against the viewport.card,.card-quiet,.window-card,.well,.popover-panel,.figure,.stat-value/.stat-label,.chip+.chip-{action,time,now},.field-label,.fieldset-legend,.section-title,.eyebrow,.disclosure-caret— alongside the existing.btn-*,.notice*,.pill*,.input,.link-*,.dialog-panel,.row-divider,.text-accentBakeBoard.svelte,StepGlyph.svelteThree things a future contributor must not tidy away
A soft shadow does nothing on a dark ground. Depth here comes from light — a panel is lighter than the ground and casts a soft warm shadow — but that trick only works in one theme, so every raised surface also carries
--kt-edge: transparent in light, a warm hairline in dark. Drop the dark half and the panels dissolve into the page, visible only to whoever looks at the theme nobody checks.e2e/bake-board.spec.tspins it in both themes.A chip is a translucent wash, never a fixed fill.
.chipcarries a step's duration in the schedule and a ranking in the 50 Top Pizza table, and it lands on three different surfaces: the lifted panel, the flat card the collections sit in, and the tomato wash under the running step. A fixed colour can only be tuned to one of them — tuned to the panel, it matched the quiet card closely enough that the ranking pills read as plain text in dark. An alpha wash is relative to whatever it sits on, so it cannot collide with any of the three.No component transition may name
outline-color.transition-colorsincludes it, so any control carrying that utility fades its own focus ring into existence — 150 ms of no visible focus after a Tab. I hit this while restyling.input;e2e/cascade.spec.tscaught it because it reads the outline immediately after focus, which is exactly when a transitioned ring is still the colour it was before. Every transition in the components layer now lists its properties, and that test carries a note explaining the second failure mode it guards.Accessibility
--kt-ink-faintstarted at 4.29:1 against--kt-sunk— the wells are the darkest ground it ever lands on, and axe caught it on the bake board's stat labels. Both ink tokens are now held above 4.5:1 against the sunk surface, not merely against the panel. The full axe sweep is green in both themes and both view modes, every control keeps its accessible name and focus ring, the heading outline is unchanged, and there is no tracked-out capital anywhere in the app any more.Tests
e2e/helpers.tsaddresses the app through class names, so the ones I moved are updated rather than worked around:windowCard()was "the rounded div inside the form"; the form has several rounded surfaces now, so it findsform .window-card— a real component class, which is closer to an API than a style.card()matches.card, .card-quiet, because the regions are deliberately no longer all the same box.thumbCentreX()hard-codes half the thumb as its radius; the thumb went 1.25 rem → 1.75 rem, so it went 10 → 14 and the inset utilities went2.5→3.5. All three have to agree, and CLAUDE.md now says so.cascade.spec.tsandheadings.spec.tspinnedui-serif; the display face changed, so they pinui-rounded. The assertions themselves — a utility must be able to beat the base heading rule, and heading level must not silently change typeface — are untouched.Seven new browser tests in
e2e/bake-board.spec.ts, baseline raised 90 → 97:Checks
npm run lintnpm run checknpm run test:coveragenpm run test:baselineE2E_PORT=4184 npm run test:e2eVerified by eye at 390 px and 1440 px in both themes, on a cold schedule, a room schedule, a biga + poolish schedule and the print route.
Screenshots
Phone, first screen — the whole point of the change
Phone, dark
Desktop, above the fold
Before:
After:
Desktop, dark
Before:
After:
The whole page
The densest thing the feed has to render — biga + poolish, cold
Notes for review
CURRENT_VERSIONstays 6). The three sibling redesign PRs bump to the same number and will conflict with each other on merge — that is expected; whichever lands second re-bumps.playwright.config.tsreadsE2E_PORTwith the old 4173 as its default, so four agents can run the suite on one machine. All four PRs make this identical one-line change.src/lib/dough/**(no behaviour change anywhere), the.icsbuilder, the TRMNL payload and its 2 KB budget, and/print/[[locale]], which owns its own inline styles and still has to fit one B&W page.