02 · The Line Sheet — "Everything in its row"
Thesis
The homepage is a rational, ruled spec sheet in the spirit of the brand's own wholesale line card. Nine numbered rows (01 to 09), each with a big Syne index numeral and a sentence-case heading set in the left margin at 1440 and above the content at 375. Hairline rules top and bottom of every row and between every item inside a row. Tabular content is real <table> markup (the bundle ladder, the cards-vs-tiles comparison). Photography is large, in hairline-ruled rectangles, no radius, no shadows, no cards, no badges. Warmth comes from the pictures and the copy voice; everything else is restrained. Reference points: Aesop's restraint, Baggu's colorway-as-identity clarity.
The page still reads as a game night unfolding: sit down (01), browse the table (02), dress for it (03), set the table (04), pick your deck (05), cards not tiles (06), table talk (07), join the table (08), index (09).
Type, colour, rules
- Display: Syne 700/800 (Google Fonts), sentence case, tight letter-spacing. Used for the H1 "Game night, itemized.", row numerals, h2/h3, product names, and the primary CTA labels.
- Workhorse: Inter 400/500/600 for body, tables, UI.
font-variant-numeric: tabular-numson the whole page so prices and indices align. - Exactly these two faces. One stylesheet link,
display=swap, loaded non-blocking (media="print" onload="this.media='all'"with a<noscript>fallback). - Page background
#FCFAF6(warm white). Body text navy#17264A(14.3:1). Secondary text#4A5470(7.2:1). Rules#D9D2C7. Complete Set column tint#F4F1EB. - Accent: one hex,
#C4187A, used only on CTAs (the filled buttons, the accent text links) and the:focus-visibleoutline. White on it is 5.59:1; on warm white it is 5.36:1; on the Complete Set tint 4.96:1. Nothing else on the page is pink except the sweater photographs, which are product colour, not UI. Button hover is a 120msfilter: brightness(.92), so no second hex exists anywhere in the CSS. - White-background product shots (
.ph, the wall image and tiles, the drawer tile) usemix-blend-mode: multiply, so the pure-white studio box disappears into the warm-white page. The hairline frame is kept.
Layout system
- 12-column grid at ≥1024px (
max-width: 1360px, 24px gutters, 40px page margins). Row head in columns 1–2 (sticky attop: 80pxso the numeral stays with its row while it scrolls; sticky positioning is not animation). Row body in columns 3–12, itself subdivided into a 10-column grid where a row needs it (hero 5/5, colorway wall 6/4, ladder 3/7, join 4/6). - 375px: everything collapses to one ruled column with 16px margins. Row head becomes an inline "01 The sweater" line.
- Announcement bar (two static messages,
role="region" aria-label="Announcements") plus sticky header measure 104px at 375px (limit 112). - Photos:
.phis a hairline-bordered<img>at native aspect (hero, wall image, edition trays, the wide hand-fan shot, the racks overhead)..frameis a 4:5 hairline frame withobject-fit: coverfor the category tiles and review photos; the 3:4 sources lose about 6% top and bottom, the 4:5 sources are untouched. Review frames are 75% wide at 375 (a pasted snapshot in the ledger) and full column width at 1440.
The one motion moment
Row 03 at ≥1024px. Clicking a colorway row crossfades one large sweater image over 300ms (two stacked <img> elements inside a multiply-blended container, opacity transition; the incoming image is decode()d before the fade so there is never a blank frame) and updates the colorway name, the readout, and the CTA's label and deep link. A sequence token drops stale fades on rapid clicks. prefers-reduced-motion: reduce sets the transition to none. At 375 nothing is intercepted and nothing animates: the tiles are plain links. Nothing else on the page animates: no scroll reveals, no IntersectionObserver, no per-section fades.
Row by row
01 Hero. Navy/White flat (mahj-knit-sweater-navy-white-flat, the crispest 1600px flat) with fetchpriority="high" and a matching <link rel="preload">. H1 "Game night, itemized." in Syne 800 (41px at 375, 76px at 1440). Mobile order: index line, H1, primary button (318–370px on a 375×812 screen), text link (378–422px), the flat (438–781px), spec line, supporting line. Desktop: H1, supporting line and CTAs in the left five columns, the flat with its spec caption in the right five. Primary CTA to /products/mahj-knit-sweater, text link to /collections/mahjong-cards.
02 Shop by category. Four 4:5 frames with sub-indices 02.1–02.4, name, arrow, one line. Sweaters uses the storefront on-model shot (the only clean human-wearing-it photo), Cards the Beach scatter, Bundles & gifts the two-box kraft shipper, Accessories the racks with bag.
03 Colorway wall. Seven <a> elements, each a real deep link /products/mahj-knit-sweater?variant=<id> with the ids from the brief, each containing the colorway's flat photograph. At 375: a ruled 2-up grid of the seven flats (164px tiles, -400 twins, about 76KB for all seven) with index, name and "From $95" beneath in the tabular style; a tap goes straight to the PDP with the colour preselected. At ≥1024: a large image (default Hot Pink/Blush, deliberately not the hero's navy) beside a ruled list of rows (48px thumbnail, index, name, price), selected row tinted with aria-current="true"; clicking a row is a variant select (console line, toast, crossfade, CTA relabelled "Shop Green/Blush" and relinked). The CTA reads "Shop the sweater" and points at the base PDP until a desktop selection is made. Order: Hot Pink/Blush, Pink/White, Navy/White, Light Blue/White, White/Blue, Green/Blush, Lavender/Purple (short sleeve). Tile images carry alt="" because the colorway name sits beside them inside the same link.
04 Complete your table. A real <table> with <caption>, scoped headers, tabular prices: Cards only $62.95, Complete Set $85 to $100 (highlighted column, "Most tables"), Table Set $165 with <s>$199.90</s> compare-at and a screen-reader "Compare at" prefix. What's in the box as rows with CSS-drawn check marks (plus sr-only "Included"/"Not included"). One CTA per tier in a list under the table: Complete Set is the only filled button ("Build the set", single line at 1440); Cards only and Table Set are ruled accent text links. Desktop aligns the three to the table's columns; mobile stacks them with the tier name in the label and Complete Set first. The CTAs live outside the <table> because a block-level tfoot inside a fixed-layout table gets wrapped in an anonymous first-column cell and cannot stack. Racks overhead photo beside the table with an honest caption.
05 Choose your edition. Open-tray set (consistent angle and background), Beach first, then Classic, Modern, Texas, Summit. Mobile: flex row with scroll-snap, items 76% wide so Classic peeks; no arrows. Desktop: all five in a row. Each: index, name (link), $62.95, positioning line from the brief. Spec line beneath.
06 Cards, not tiles. Wide hand-fan hero shot at 16:9, then a <table> comparison (weight, setup, fits in, price, readability) beside the three original benefits as h3s in a ruled list, then Learn to play as two ruled rows (Traditional 12 min, Quick Play 4 min) linking to the real blog posts. No icons.
07 Table talk. A ledger: six <article data-placeholder="review"> with index, name, product, inline-SVG stars (navy, role="img" labels; Elena's fifth star outlined), quote. Three photos in 4:5 frames interleaved in DOM order (boat, kitchen table, Italian bar), forming a photo spine down the middle column on desktop. Aggregate "4.9 from 212 reviews" carries data-placeholder="aggregate".
08 Join the table. Labelled email field, "Get first access" button, note, then a trust strip (ruled rows on mobile, ruled inline on desktop): Ships from Texas, Ships gift-ready, 30-day returns.
09 Index (footer). The arched wordmark (same -400 file as the header, so zero extra bytes), Shop (six categories plus Gift cards), Help, Company (Wholesale, Instagram), a second labelled newsletter field, payment methods as a text list, copyright. No raw URLs printed.
Interactions and accessibility
- Every Shopify-bound action logs
[shopify] …to the console and shows a two-secondrole="status" aria-live="polite"toast: desktop colorway/variant select, both newsletter forms. Links to collections and products are real hrefs; at 375 the wall tiles navigate like any other product link. - Mobile drawer:
role="dialog" aria-modal, opens with focus on the close button, Tab/Shift+Tab wrap inside, Escape closes and returns focus to the menu button, body scroll locked while open. "Shop the sweater" tile at the top, six items at 48px. - Skip link,
header/nav[aria-label]/main/footer, the announcement bar as a labelled region, exactly one<h1>, h2 per row, h3 children only.:focus-visible3px accent outline. Icon-only controls havearia-label. Cart count isaria-hiddenwith the count in the link's label. - Tap targets: wall tiles 164×221, icon buttons 44×44, all buttons and text links ≥44px tall, nav rows 48px.
- No hover-only behaviour anywhere.
Performance
- Lighthouse 12, mobile, simulated throttling, against a local static server, on the final file: performance 100, accessibility 100, best practices 100, SEO 100. LCP 1.2s (the hero image), FCP 1.0s, CLS 0.035, TBT 0.
- Images fetched at 375 (DPR 2) if the visitor scrolls the whole page: 1.13MB across 22 files; 492KB by the time the first viewport and its near-viewport lazy images have loaded. Above the fold proper: two images (wordmark
-40012KB, hero-80093KB). - Every
srcsetlists the-400,-800and full twins with their true pixel widths (portrait files are 300/600/1200 or 320/640/1280 wide), so the browser never upscales and picks-400for anything rendered under about 200 CSS px at DPR 2 (header and footer wordmark, wall tiles and thumbnails, drawer tile). - HTML: about 60KB, inline CSS and JS, no dependencies beyond the one Google Fonts stylesheet.
- Remaining Lighthouse notes are server-side (no gzip, no cache headers on the python test server).
Assumptions
- "Cards only" in the ladder links to the Beach Edition PDP (the brief says "the edition PDP"; Beach is the lead edition). The edition row below offers the other four.
- Complete Set link goes to
/collections/bundles-gifts; Table Set to/products/table-setmarkeddata-placeholder="sku". - Cart count is a static 0. Search links to
/search. - Free shipping is shown as included for Complete Set and Table Set (both clear $85) and not for Cards only ($62.95).
- Copy claims stay inside the brief: cotton knit, MAHJ intarsia, S to XXL, October 1, 162 cards, 350gsm, 12 oz, guide read times. "Sizes run true; between sizes, go up" is taken from the redesign brief's fit guidance.
- The desktop wall CTA label is decided once at load from the
(min-width: 1024px)media query; resizing across that breakpoint keeps whatever label it has.
Photography gaps and how they were handled
- No sweater worn at a mahjong table exists. The hero is the navy flat as a spec-sheet object; the only human-in-sweater shots (storefront, trade-show booth) are used for the Sweaters category tile, not faked into a table scene.
- No Summit lifestyle shot: Summit appears only in the edition row (open tray, consistent with the other four).
- No exploded Table Set or all-components shot: the ladder is a table of contents rather than a picture, and the racks overhead photo carries a caption saying the set is shown by its parts.
- No knit texture close-up: the wall uses the flats themselves at tile and thumbnail size.
- No customer photos: the review photos are the brand's own travel-fan and kitchen-table shots, described honestly in
alttext and never captioned as customer submissions. - The Needlepoint Hat does not appear.
Revision (after the critique)
Applied, in index.html:
- Row 03 at 375 is a ruled 2-up grid of the seven sweater flats, each
<a>a one-tap deep link to its real variant id, name and "From $95" beneath in tabular style. The large image plus ruled list (now with 48px flat thumbnails instead of CSS colour chips) and the crossfade remain for ≥1024px. The click intercept runs only when(min-width: 1024px)matches; at 375 a tap navigates (verified: tapping tile 3 lands on/products/mahj-knit-sweater?variant=47163993751604). - Mobile hero order is H1 → primary button → text link → flat → spec → supporting line. The button now sits at 318–370px on 375×812 (was 786–835px); the flat ends at 781px.
- One accent hex.
--accent-deepis gone;#E13086appears nowhere; buttons, text links, the focus outline and the favicon all use#C4187A. Primary buttons dropped from 19px to 18px Syne since large-text contrast is no longer needed. - Row 04: Complete Set has the only filled button; Cards only and Table Set are ruled text links in the accent, on both breakpoints.
- "Build the set" no longer wraps at 1440 (
white-space: nowrap, 12px padding, 164px cell, 52px tall). - The wall's default image is Hot Pink/Blush; the hero stays Navy/White. The hero file is not shown again in the wall except as one 48px thumbnail in the list.
- H1 changed from "Dress for the table." to "Game night, itemized." (21 characters, sentence case, in the spec-sheet voice). Supporting line now opens "Item one: …".
- CSS colour chips removed entirely, so no 44px chips touch anywhere. The desktop list rows are 64px tall with a hairline between each.
-400twins added to all 28<img>srcsets, the JS-built srcset and the hero preload, with true widths. Wall tiles and thumbnails, the header and footer wordmark and the drawer tile all resolve to-400files. Review images'sizesnow match their 75% frame so the-800twin is chosen rather than the 1200px original.- Announcement bar is
<div role="region" aria-label="Announcements">with two<p>s (it was a<p>outside any landmark). mix-blend-mode: multiplyon the white-box product shots (hero, wall image container, wall tiles, edition trays, racks overhead, drawer tile); the hairline frames stay.
Also changed while re-verifying: row top padding 28→24px and row-head margin 20→16px at 375 (to land the button nearer 300px); review photo frames 75% wide at 375; footer wordmark 200px wide so it reuses the header's cached -400 file; a 20px gap between the wall grid and its note; the ladder CTAs' aria-labels removed after Lighthouse flagged label-content-name-mismatch (the visible text is the accessible name on both breakpoints).
Critic points not taken: none. The critic's alternative for point 6 ("or crop the hero tighter") was not needed once the wall default changed. The critic's aside that the hairline-framed flats read as "catalogue product-on-white boxes" is addressed by the multiply blend rather than by removing the frames, because the ruled frame is the thesis.
Re-verified after the pass: one <h1>, zero uppercase, zero carousel/autoplay, fetchpriority="high" exactly once, seven unique variant ids, six data-placeholder="review", all 66 unique image paths (plus the JS-built ones) exist on disk, no horizontal overflow at 375, drawer focus trap and Escape, desktop crossfade with toast and console line, no console errors on the page itself.