03 · One Thread — "The only color is the one you chose"
Thesis
A near-monochrome editorial page: ink (#2A2724) on warm white (#FBF8F3), drawn with hairlines (#D8D1C6) and large Fraunces headlines. The only chroma on the page is (1) the photography, (2) the navy CTA, and (3) a single 4px thread of yarn that starts at the sweater's collar in the hero and runs the full length of the page, weaving from gutter to gutter at section boundaries and ending in a small loop beside the email field ("join the table"). The thread is always the colorway the visitor last tapped. Default is Navy/White. Aesop's restraint, Baggu's colorway obsession.
The page reads as the game night spine from the concepts doc: Everything for the table, Dress for it, Set the table, Pick your deck, Cards, not tiles, Table talk, Join the table. Each section carries a small sentence-case label; there are no numerals and no eyebrow caps anywhere.
Fonts
- Display: Fraunces (Google Fonts, variable: ital 0/1, opsz 9..144, wght 400 only, SOFT 0..100). Headlines use weight 400,
font-variation-settings: "SOFT" 50, optical sizing automatic. Review quotes, tier tags and learn-card titles use the italic at SOFT 60 to 70. Sentence case throughout. - Workhorse: Instrument Sans 400 and 500 for body, UI, product details, prices in tables.
- Exactly these two. The stylesheet is loaded non-blocking (
<link rel="preload" as="style" onload="this.rel='stylesheet'">with a<noscript>stylesheet fallback),display=swap, preconnects to both Google Fonts hosts. Fallback stacks are Georgia and system-ui, so first paint is text on warm white, not blank.
Palette
- Background #FBF8F3, body ink #2A2724, secondary text #6B655D (5.4:1 on the background), hairlines #D8D1C6.
- Accent navy #17264A, used only on
.btn(primary buttons) and.link(primary text links: "Or start with a deck of cards", the Cards-only and Table Set tier links). Focus rings are also navy. No text, border or background outside those is navy. - Thread color per colorway (CSS custom property
--thread): Green/Blush #2F6B4F, Pink/White #E8A0BF, Navy/White #17264A, White/Blue #6BABFF, Hot Pink/Blush #E13086, Light Blue/White #7FB2F0 (brightened from #B9D4F5 for visibility on warm white), Lavender/Purple #9F86E3 (brightened from #C9B7F2). - The default thread is navy, the same hex as the CTA, because the hero now leads with Navy/White (see Revision). The thread is a decorative 4px line, not an interactive element; the critic recorded this overlap as a soft issue and the coordinator asked for the navy lead. Tapping any other tile removes the overlap.
The single motion moment
Tapping an unselected colorway tile (a) crossfades both large sweater figures (the wall's chest detail and, offscreen, the hero flat) to that colorway and (b) recolors the thread, the weave connectors, the hero thread and the end loop by transitioning --thread over 400ms. The image crossfade inserts the new <img> underneath and fades the old one out over the same 400ms, so the swap and the recolor happen together. Nothing else animates on scroll. There is no IntersectionObserver reveal choreography. Under prefers-reduced-motion: reduce, --thread-speed is 0ms and the JS removes the old image immediately: an instant swap.
Tile aria-current, the "Selected · tap to shop" line under it, the "Selected colorway" heading, the hero caption, the wall CTA label, and both sweater CTA hrefs update to the chosen variant. Button hover transitions are 120ms.
How the thread is built
- Every section after the hero has class
stitchedplus a lane (lane-l= 4% from the left,lane-r= 96%). A::beforepseudo-element draws the 4px vertical line at that lane,top: 0; bottom: 0. - Where the lane changes, the section has
weave-inand an inline<svg class="weave" viewBox="0 0 100 64" preserveAspectRatio="none">with a single cubic path from the previous lane to the new one;vector-effect: non-scaling-strokekeeps it 4px. The::beforeline then starts at 64px. Two path variants exist (right to left, left to right). This is a static offset change per boundary, not a per-section animation. - Lanes stay in the gutters at every width because
--gutterismax(24px, 4.5vw): the lane (4vw) is always inside the gutter, so the thread never crosses text. In the mobile edition row (full-bleed scroll) the row sits above the thread, so cards pass in front of it. - The hero thread is a separate SVG sized to the hero section in pixels. On load, font load, resize, and after every swap,
drawHero()measures the current sweater image and writes a path from the collar point (a per-colorway fraction of the image, e.g.[.57, .13]for the navy flat,[.6, .27]for the folded Light Blue shot) up in a small loop and over to the right lane, then straight down to the section bottom, where the categories section's line picks it up at the same 96%. - The thread ends in the email section (
stitched end): the line stops at 58% height and an::afterdraws a 16px ring in the thread color, a loose loop beside the input.
Section decisions
- Announcement: two static messages, wrapping to two lines on 375px, in a
role="region" aria-label="Announcements"landmark. Announcement plus sticky header measure about 100px at 375px. - Header: menu button (mobile), the roundel in its real pink and blue plus "Play Away Cards" set in Fraunces, six nav items (desktop), search and cart with count. The roundel uses
mix-blend-mode: multiplyonly, so its baked-in white background takes the warm white. The footer arch wordmark keeps the ink treatment: an inline SVG "ink" filter (desaturate plus gamma 2.6, which keeps white at white while darkening the mark) plus multiply. - Hero: H1 "Game night, anywhere." with one line beneath, then the button pair, then the Navy/White flat. On 375px the order is H1, line, buttons, image, so the primary button sits at about 323 to 371px, and the sweater's collar is still above a 667px fold. On desktop the copy and buttons sit left and the flat right. Primary CTA links to the sweater PDP with the current variant preselected; secondary text link to the cards collection. Caption names the current colorway and links to
#colorways. - The sweater flats and the edition open trays are shot on pure white.
mix-blend-mode: multiplyon those images makes the white take the page's warm white exactly, so there is no white square on the page. During the crossfade the two multiplied layers briefly darken where knit overlaps knit, which reads as a dissolve. - Category tiles: four 3:4 photos with hairline frames, title and one line beneath (never text over images, so the 4.5:1 rule is trivially met).
- Colorway wall: the H2 "The only color is the one you choose.", then a 5:4 chest-detail figure of the selected colorway (the same flat,
object-fit: coverat 1.6x around the collar and intarsia letters, so the wall does not repeat the hero's full flat), then all seven flats as 4:5 tiles (2-up at 375px, one row of seven at 1440px) with the colorway name under each, then the selected name, spec line and one CTA. Each tile is a real<a href="/products/mahj-knit-sweater?variant=<id>">. A tap on an unselected tile is intercepted (swap, console log, toast). A tap on the selected tile, markedaria-current="true"and labelled "Selected · tap to shop", is not intercepted and follows its deep link; modifier-clicks always follow the link. - Bundle ladder: a literal ladder of three rows (Cards only $62.95, Complete Set $85 to $100 "Most tables", Table Set $165 with $199.90 struck through "The gift"). All three prices are visible on one screen on desktop and stacked in order on mobile. Complete Set is the only tier with a 2px ink box and the only tier with a button; the other two use accent text links. Plain "In the box" lists on all three tiers. Add-on prices (racks $35, bag $8, First Lines $20) link to their PDPs. Table Set link carries
data-placeholder="sku". - Editions: Beach first, 72vw cards with scroll-snap and a visible peek on mobile, five across on desktop. Open-tray set for consistency. Deck facts in a hairline row beneath.
- Cards, not tiles: a real
<table>with the five comparison rows (Play Away column in Fraunces), the three original benefits as H3s beside it, then two Learn to play cards with read times. - Reviews: six quotes in Fraunces italic with a hairline above, name and product in small sans, star glyphs with aria-labels, each
data-placeholder="review"; three photos inline (boat travel fan, floral cloth fan, kitchen table). Aggregate "4.9 from 212 reviews" carriesdata-placeholder="aggregate". - Email: labelled field and button, trust strip beneath as text (not icons). Footer: Shop (six plus gift cards), Help, Company (Wholesale, Instagram), second labelled newsletter field, payment methods as a text list, no raw URLs.
Interactions and accessibility
- Every Shopify-shaped action (colorway/variant select, newsletter submit in both forms, search, cart) calls
shopify()whichconsole.logs a descriptive line and shows a 2-secondrole="status" aria-live="polite"toast. No navigation, except the deliberate follow-through on the already selected tile. - Mobile drawer:
role="dialog" aria-modal="true", opens with focus on the close button, Tab and Shift+Tab are trapped inside, Escape closes, focus returns to the menu button, body scroll locked, closes itself if the viewport grows past 900px. "Shop the sweater" tile at the top; six items at 48px. - Skip link,
header,navwith labels,main,footer, the announcement region, exactly one H1, H2 per section, H3 children, no skipped levels.:focus-visible3px navy outline. All icon buttons labelled. All inputs labelled (footer label is visually hidden). - Tap targets: wall tiles about 155 by 230px on mobile, nav items 44px, drawer items 48px, buttons 48px, inputs 48px.
Performance
- 25
<img>elements, all withsrcset, realsizes,width,height,alt. Every srcset includes the-400.webptwin; anything rendered under about 300px (header roundel, drawer tile, the four category tiles, the seven wall tiles, the five edition cards, the three review photos, the footer wordmark) uses the 400 twin as its only source. The hero flat, the wall detail figure and the ladder image carry 400w, 800w and the full file. - The hero image is the only
fetchpriority="high", is not lazy, and is preloaded in<head>withimagesrcset/imagesizesmatching the<img>exactly. The header roundel and the boat and floral review photos are also not lazy (see Revision); the other 21 images areloading="lazy" decoding="async". - Image bytes at 375px including lazy loads: about 540KB (the navy hero -800 at 93KB is shared with the wall detail; the ladder image is the only other -800; everything else is a -400 twin). The sum of every
srcattribute on the page is 532KB. - The other six colorway large images (-800, about 90 to 120KB each) are not preloaded on page load. They are warmed with
fetchPriority = 'low'the first time the visitor reaches for the tile grid (pointerenter, touchstart, or focusin). A tap on a not-yet-loaded colorway waits fordecode()and then crossfades, so there is never a broken frame. The tiles themselves are -400 twins (5 to 20KB each). - HTML is about 56KB with inline CSS and JS. No frameworks, no build. Fonts are non-blocking; the only render-blocking resource is the inline CSS.
- Srcset width descriptors are the files' true widths (1024w for Green/Blush and White/Blue, 1206w for Lavender, 1600w otherwise), so the browser's candidate selection is honest.
- Lighthouse was not run in this environment (no CLI available offline; the coordinator re-runs it). The previous run scored 73 with LCP 4.7s and FCP 4.0s, attributed to the render-blocking font stylesheet and LCP discovery; both are addressed above.
Assumptions
- "Sizes S to XXL", "From $95", $62.95 editions, bundle prices, the $199.90 compare-at, and the October 1 ship date are taken from the brief as given.
- The free-shipping threshold is $85, so the Complete Set clears it on its own; the copy does not state this explicitly to keep the ladder quiet.
- The Cards-only tier links to the Beach Edition PDP (the brief's "link to the edition PDP"), Beach being the discovery best seller.
- "Crak" is used once in the Easy to read microcopy as the spoken form; the product uses "Crack" on the cards. Left as voice.
- The email forms point at
/contact#newsletteras a harmless action; in production this becomes the Shopify Forms embed.
Photography honesty
- There is no photograph of the sweater worn at a mahjong table, so the hero is the sweater flat, large, as the object itself, with the on-model storefront shot used for the Sweaters category tile. No sweater-at-table image is faked or implied.
- There is no knit-texture close-up in the working set. The wall's detail figure is a 1.6x crop of the flat around the collar and letters; on the two 1024px files (Green/Blush, White/Blue) this is slightly soft at 560px on a retina display, which reads as knit rather than as a defect. It is the same photograph, cropped, and is described as such in its alt text.
- There is no exploded or all-components Table Set shot. The ladder uses
racks-with-cards-overhead(cards, two racks, a First Lines sheet) with a caption stating exactly what is in frame and that the bag and gift wrap are not pictured. - There is no Summit lifestyle shot; Summit appears only in the consistent open-tray row, which is what the row needed anyway.
- Light Blue/White has no flat, only a folded shot; its tile, the detail figure and the hero crossfade use the folded image and the hero thread's collar point is adjusted for it. Lavender/Purple is the short-sleeve style and is labelled as such on its tile.
lifestyle-hands-floral-tablecloth-overheadstill shows a thin baked-in frame after cropping, so the floral-table review photo isclassic-edition-fan-floral-clothinstead.racks-with-cards-angledandcomplete-set-gift-box-thank-youalso carry baked-in frames and were avoided.- No inline SVG photo placeholders were needed; every slot has an honest image.
Revision (after CRITIQUE.md)
What changed, in the order of the coordinator's list:
- The colorway wall's seven disc chips are gone. The seven flats are now 4:5 tiles (
.cw), 2-up at 375px and one row of seven at 1440px, each a real<a>to its variant deep link with the colorway name beneath. Tapping an unselected tile swaps the wall's detail figure and the hero flat and recolors the thread (still the only motion moment). Tapping the tile markedaria-currentfollows the link; the tile says "Selected · tap to shop" and the lede says "tap it again to shop it". The toast after a swap says the same. - Mobile fold: the button pair moved above the hero image at 375px (
.hero-actions{order:3},.hero-visual{order:4}) and the lede was cut to one sentence. The primary button now sits at about 323 to 371px on a 375px screen. - The two empty review frames in the critic's desktop capture could not be reproduced here (headless Chrome full-page captures at 1440 and 375 both rendered all three photos, before and after this pass), so the likely cause is a lazy-load race in a scrolling capture tool. As instructed,
loading="lazy"was removed from the boat and floral photos; at the 400 twin they cost 11KB and 51KB. The kitchen-table photo stays lazy. - The header roundel is back in its real pink and blue:
filter:url(#ink)was removed from.brand img, leaving onlymix-blend-mode: multiplyso its white background sits on the warm white. The footer wordmark keeps the ink filter. - The italic numerals 01 to 07 and the
.kicker brule were removed; the labels remain in sentence case. - The hero now leads with Navy/White (file
mahj-knit-sweater-navy-white-flat, 1600px), the default--threadis #17264A, and the drawer tile, preload, captions, CTA hrefs,aria-currenttile andcurall start on navy. The wall no longer repeats the hero flat at full width: its figure is a 5:4 chest-detail crop at 1.6x (see Photography honesty). Pink/White was considered as the alternative lead because navy makes the default thread the same hex as the CTA; navy was taken because the coordinator asked for it and because 02 already leads navy only by the critic's account of a different composition. The overlap is documented under Palette. - H1 changed from "Mahjong, made portable." (which belongs to 01) to "Game night, anywhere." The footer line changed to match. The wall keeps "The only color is the one you choose." as its H2.
- Performance: the Google Fonts stylesheet is non-blocking (preload-as-style with onload swap and a noscript fallback), Fraunces is requested at weight 400 only with the opsz and SOFT ranges the page actually uses, and Instrument Sans at 400 and 500 only (the 600 cart badge became 500). The hero preload was updated to the navy file with all three candidates and the same
imagesizesas the<img>. Every srcset gained the 400w twin and small renders use it exclusively, taking the 375px image total from about 1.17MB to about 540KB. - Accessibility:
.announcecarriesrole="region" aria-label="Announcements"so all content is inside a landmark. It stays outside<header>so the sticky header does not carry the announcement with it.
Critic points not taken, with the reason:
- Overall mobile page length. The wall tiles add about 900px on 375px, so the page is longer than the critic's 11167px measurement, not shorter. The tiles were the critic's own one change and the coordinator's first instruction, and the fold fix puts the primary button 400px higher; shortening elsewhere (smaller review photos, fewer editions visible) would have cut content the brief requires. Left as is and flagged here.
- "Do not show the hero file again in the wall." The wall figure still uses the same photograph as the hero for the selected colorway, but as a 1.6x chest-detail crop in a 5:4 box rather than the full flat at full width, because there is no second photograph of any colorway to use instead. The tiles beneath show the full garment.
Verified after the pass: headless Chrome captures at 375px (via a 375px iframe) and 1440px show the new hero order and fold, the navy lead, the real-colour roundel, the tile wall with the navy tile outlined, all three review photos, and a tap on the green tile swapping the detail figure, recoloring the thread and moving aria-current. Greps: <h1 1, uppercase 0, carousel|autoplay 0, fetchpriority="high" 1, needlepoint 0, seven distinct variant ids, six data-placeholder="review", one aggregate, one sku, 25 images all with srcset, sizes, width, height and alt, 26 unique image paths all present on disk, 55.6KB.
Post-revision patch by the lead (17:50)
The Google Fonts link used the preload-as-style swap, which Lighthouse still modelled as render-blocking (FCP 3.4s, performance 82). Switched to <link rel="stylesheet" media="print" onload="this.media='all'"> with the same <noscript> fallback. Lighthouse mobile performance 99, LCP 2.3s. No visual change.