Concept 01 · Postcards — "Mahjong, made portable"
Thesis
The homepage is a travelogue on a warm-white wall. Every section is a postcard from a place the cards have already been (the brand's own hand-fan series: beach, boat, Italy, Philadelphia, the kitchen table). The sweater is the first thing you pack, so it is the first postcard. The editions are destinations. Product details live on the backs of postcards (address side: vertical divider, dashed stamp box, ruled lines) in plain scannable type; photos live on the fronts. The page scrolls like the game night in the concepts doc: sit down, dress for it, set the table, pick your deck, cards not tiles, table talk, join the table. Each section carries a short sentence-case kicker naming that beat.
Type, colour, layout language
- Display: Fredoka. h1 and h2 at weight 500 with
letter-spacing:-.02em; h3, captions, prices and postmark text at 600. Workhorse: Nunito Sans 400/600/700 for body, UI and product details. Exactly these two. - Page: warm white
#FBF8F3; postcard paper#FFFDF9; 1px edge#D9D1C5; ruled lines#C9D3E6. No drop shadows anywhere. - Text: navy
#17264A; secondary#4A5470(7:1 on the page). - Accent: sky blue
#6BABFF, used only on CTAs: button fills (.btn, hover#8DBFFF, navy text, 6.3:1), the thick underline on primary text links (.tlink), the skip link, and the:focus-visiblering. Nowhere else. Sky-blue text on warm white is only 2.2:1, so the accent is never a text colour. - Postcards: flat paper rectangles, 1px edge, 10px paper margin, photo with its own hairline printed border. Every postcard on the page sits straight; only the two hero postcards are tilted (−1.5° and 2.6°) and only the hero's beach postcard carries a strip of translucent blush tape. No pins. The sweater flats (hero and wall) use
mix-blend-mode:multiplyand the photo box is paper-coloured, so the white product boxes disappear and the garment sits on the paper inside its hairline frame. - Stamps: the real
logo-roundel-primary(400px twin) inside a dashed perforation box, on the hero's paper band, the Complete Set tier and the email form. Postmarks: inline SVG, circular text on a shared<path>via<textPath>, wavy cancellation lines, sentence case; one<defs>block reused with<use>. - Text over images: none. Captions sit on paper below the photo, so 4.5:1 holds without scrims.
The single motion moment
On load the hero postcard stamps itself: the roundel lands (scale(1.8) rotate(-18deg) → scale(1) rotate(-6deg), 500ms, slight overshoot) and the postmark inks in (opacity, 300ms, from 500ms). 800ms total, CSS keyframes, once. Under prefers-reduced-motion: reduce both animations are removed. Nothing else animates on scroll; hover transitions are 120ms; the ladder's rung buttons scroll the snap row (user-initiated, auto under reduced motion).
Section decisions
- Announcement bar: navy, two static messages,
role="region" aria-label="Announcements". Stacked at 375px (bar + header ≈ 104px), one line on desktop. - Header: real roundel (400px twin) + "Play Away" in Fredoka. Six nav items ≥1000px; search and cart (count 0); menu button on mobile. Sticky.
- Drawer: full screen,
role="dialog", "Shop the sweater" postcard tile (lazy, loads only when opened), six items at 56px, close button, focus trap, Escape, focus restored, body scroll locked, auto-closes past 1000px. - Hero: H1 "Mahjong, made portable." Copy first on mobile (H1, one line, button, text link) so the primary CTA sits at about 305–350px. Then a full-width postcard of the Green/Blush flat with a 76px paper band above the photo (100px on desktop) that carries the stamp and postmark, so nothing touches the collar or shoulder. The beach hand-fan postcard (30% wide, taped, tilted) sits over the bottom-right corner of the sweater card, below the hem and to the right of the caption (caption
max-width:60%, beach card from 70%), so it covers neither product nor text. Desktop: copy left (5/12); sweater card 62% wide with the beach card beside it from 60%, overlapping only the paper margin. The sweater image is the onlyfetchpriority="high"image and is also preloaded. - Shop by category: four straight postcards, 2×2 on mobile, 4 across on desktop: on-model storefront, boat hand-fan, two decks in a kraft gift box, racks with bag.
- Colorway wall (the bold spend): "One sweater, seven postcards." Seven straight postcards, each a real deep link
/products/mahj-knit-sweater?variant=<id>. Flats for six, folded for Light Blue/White (the only image). Captions lead with the colour name (1.3rem Fredoka) with the travel line second at .8rem, muted. 2 columns on mobile with the seventh centred; 4 + 3 on desktop. "From $95 · Sizes S to XXL" and one CTA beneath. - Complete your table: three postcard backs; Complete Set is default (2px navy border, real roundel stamp, "Most tables", scaled 1.04 on desktop). Cards only $62.95 · Complete Set $85 to $100 · Table Set $165 with struck
$199.90(hidden "was"). "What's in the box" lists on all three. Mobile: a three-cell price strip (all prices visible at once, tap to jump) above a snap row that lands on the Complete Set on load (instantscrollTo). Desktop: three columns. - Choose your edition: Beach, Classic, Modern, Texas, Summit, open-tray series, 76vw with peek on mobile, five across on desktop, no arrows. Whole card is the link.
- Cards, not tiles: a real
<table>on a postcard back with the five rows and a footnote; the three "Easy to…" benefits as H3 microcopy, no icons; then two Learn to play postcards with read times. - Sent back from the table: six review postcard backs (
data-placeholder="review") with stars (role="img"), a small postmark whose ring text is drawn from the quote, quote, name, product; three photo postcards (Italy, kitchen table, Philadelphia) interleaved. Two columns at 375px (postmark 66px, photo cards 4:5), three columns at 900px+. Aggregate "4.9 from 212 reviews" markeddata-placeholder="aggregate". - Join the table: a postcard back; message side "Get first access to the fall sweater drop", address side the labelled email input, "Send me the next postcard", roundel in the stamp box. Trust strip with ✓ glyphs.
- Footer: navy; five groups: brand, Shop (six categories + Gift cards), Help (Contact, Returns, Shipping, FAQ), Wholesale (its own group, one line and a link), Stay in touch (Instagram, second labelled newsletter form). Payment methods once as a text list. No raw URLs.
Interactions
Both forms preventDefault, console.log('[shopify] …') and show a 2s role="status" toast; an empty or invalid email focuses the field and toasts a nudge. Collection and product links are real hrefs.
Performance
- Fonts are non-blocking:
<link rel="preload" as="style" … onload="this.onload=null;this.rel='stylesheet'">with a<noscript>stylesheet fallback,display=swapkept. Verified with a headless DOM dump: the link readsrel="stylesheet"after load. Text paints in the system fallback first and swaps. - LCP:
<link rel="preload" as="image" imagesrcset imagesizes>for the hero sweater, matching the<img>'s srcset and sizes. - -400 twins: every one of the 29
<img>srcsets carries the-400.webptwin with its true width (300w for portrait files, 400w for square). The five images rendered under 100px (header logo, footer logo, hero stamp, Complete Set stamp, email stamp) use the 400 twin as their only source (18KB instead of 67KB for the roundel). Withsizesset per tile, the 2-column wall and category tiles at 375px resolve to the 400 twins even on a 2× screen; editions and learn cards still need the 800 twins. - Above the fold at 375px: roundel-400 (18KB) and the sweater (400 twin at 1×, 800 twin at 2×, 82KB) plus the two font families and ~55KB of HTML. Every other image is
loading="lazy", including the hero's beach postcard (below a 667px fold) and the drawer image (inside ahiddensubtree). - Length: 10,590px at 375px (down from 11,538); reviews are now ~1.5 viewports.
- Lighthouse was not run here (not installed); the coordinator re-runs it.
Assumptions
- "Packed in Texas" / "Packed and shipped from Texas": the brief confirms shipping from Texas; no city is claimed.
- Cards-only tier links to the Beach Edition PDP. Complete Set →
/collections/bundles-gifts. Table Set →/products/table-setwithdata-placeholder="sku". - Review postmark ring text and the photo-postcard captions are editorial framing for placeholder content, to be replaced when reviews are sourced.
- The comparison footnote hedges the tile weights and prices supplied by the brief.
srcsetdescriptors are the real pixel widths of each file rather than a literal400w/800w/1600w;width/heightattributes are the full-size intrinsic dimensions.- Images inside links whose text already names the product (colorway tiles, edition tiles, the drawer tile) use
alt=""; every other image has descriptive alt text.
Photography gaps and how they were handled
- No sweater worn at a table. The hero uses the flat as the postcard object and the green-sleeve beach hand-fan as the second postcard. People appear via the storefront on-model shot and the kitchen-table portrait.
- No Summit lifestyle shot or roundel. Summit uses the open-tray shot like the others; nothing was faked. No edition roundels are used as stamps (each is a 43–65KB file for a decoration); the primary roundel is the only stamp image.
- No exploded Table Set / all-components shot. The ladder is text on postcard backs, so nothing is composited or implied.
- No floral-table review photo used: the only floral overhead is the heaviest file in the set; Italy, Philadelphia and the kitchen table stand in.
- No Needlepoint Hat anywhere.
Revision (after CRITIQUE.md)
Changed in index.html:
- Straightened the page. Removed rotation from every
.pc/.pcb(no--tiltvariables remain), deleted the pin pseudo-element, scoped the tape rule to.hero .tape, and strippedtape/pin/pc--r/pcb--r/*--flatclasses from all non-hero markup. Only the two hero postcards are tilted; only the beach card is taped. Removed the wall's vertical stagger. - Stamp and postmark moved onto a paper band above the hero photo (
padding-top:76pxmobile /100pxdesktop); nothing covers the sweater. Hero recomposed: sweater card full width on mobile, beach card 30% wide at the bottom-right below the hem and clear of the caption; on desktop the beach card overlaps only the paper margin. - Reviews two columns at 375px (compact padding, 66px postmark, 4:5 photo cards); three columns from 900px. Page length 11,538 → 10,590px. Mobile section padding 56 → 48px.
- Fredoka 500 for h1 and h2 with
letter-spacing:-.02em; the h1 dropped from 700. - Wall captions: colour name first at 1.3rem, travel line second at .8rem muted.
- Wholesale is its own footer group; removed from Help. Footer grid is five columns on desktop.
- H1 kept: "Mahjong, made portable."
- Performance: non-blocking Google Fonts with
<noscript>fallback; hero image preload withimagesrcset/imagesizes;-400.webptwins added to all 29 srcsets with true widths; logos and stamps use the 400 twin only;sizesretuned for the new hero and two-column reviews. - Accessibility:
.barisrole="region" aria-label="Announcements"; the hero beach image is nowloading="lazy"(26 lazy, 3 eager). mix-blend-mode:multiplyon the hero and wall sweater flats;.pc__phbackground is the paper colour.
Deliberately not taken:
- Dropping the three photo postcards (the critic's alternative to two columns): the shared brief requires real customer-style photos mixed into social proof, so the section went to two columns instead.
- Removing the travel captions on the wall: the critic notes they do not help a buyer choose. They are kept as the brand voice the brief asks for, but demoted to a small muted second line under the colour name.
- Lazy-loading the hero stamp image: the linter flags two images beyond the third without
loading="lazy"; the beach image got it, the stamp did not, because it is above the fold and is the same 18KB file already loaded for the header logo.
QA performed
- Self-check: one
<h1>; zerouppercase; zerocarousel/autoplay; exactly onefetchpriority="high"; 29<img>all withsrcset(incl.-400),sizes,width,height,alt; 26 lazy, 3 eager; sixdata-placeholder="review"; seven distinct variant ids; all 70 referenced image paths exist; all srcset descriptors match real pixel widths;rotate(appears only in hero rules; accent variable used only in.btn,.tlink,.skip,:focus-visible. - Rendered with headless Chrome at 1440px (including a 1440×900 fold) and at a true 375px layout via a 375px iframe (Chrome clamps desktop windows to ~500px), with a virtual-time budget so the postmark's delayed fade has completed. No JavaScript console errors. Bar + header under 112px; primary CTA above a 667px fold; Complete Set centred on load; Beach first with peek; drawer opens with tile and six items.