--- version: alpha name: roastworks-design description: "A dark roast-brown canvas (#1a1410) with cream ink (#f4ece1), a caramel amber (#d18f4e) for the roast itself and primary action, and a green-coffee tone (#8fae6e) for origin and freshness signals. Yeseva One carries every heading at a bold, chalkboard-menu weight; Rubik carries the surrounding prose. The system reads as an actual small roastery's site, specific about origin and roast date, never a big-box coffee chain's lifestyle marketing." --- ## Brand Positioning Roastworks is for coffee drinkers who care that a bag was roasted this week, from a farm the roaster can actually name, not a chain's seasonal lifestyle campaign. It exists because most coffee sites borrow big-chain chrome (soft-focus lifestyle photography, "handcrafted artisanal" copy that means nothing, a loyalty app upsell) that has nothing to do with the actual, physical process of roasting a bean. Roastworks's bet: the one piece of drama on the page should be that literal process, a bean's color shifting from green through amber to a full dark roast as you scroll, steam finally curling up once it's done. Every visual decision below serves that one bet. If a component doesn't help someone pick a bag or start a subscription, or doesn't come from a real roastery interaction (product table, order status, subscription form), it doesn't belong in this system. colors: accent: "#d18f4e" accent-alt: "#8fae6e" ink: "#f4ece1" ink-muted: "#a89686" canvas: "#1a1410" surface: "#241c16" surface-raised: "#2e241c" border: "#3c2f24" focus-ring: "#d18f4e" ## Logo / Wordmark System The mark is a rounded-square card containing a single coffee bean with its center crease, filled in the roast-amber accent, literally a 1:1 miniature of the homepage's `#bean-shape` at its finished-roast color. ```svg ``` - **Lockup**: mark, then a `0.5rem` gap, then the wordmark "Roastworks" in Yeseva One, mark always left of the wordmark, never stacked or reversed. - **Minimum size**: mark not smaller than `16px` square, below that the crease line disappears into the bean fill. Wordmark not smaller than `0.85rem`. - **Clearspace**: empty margin equal to the mark's own height (22px at default size) on all four sides of the lockup. - **Don't**: recolor the mark for a light background, it only ever renders on `--panel`/`--bg`. Don't render the static mark in the green unroasted color, the mark represents the finished product, motion through the roast belongs to the homepage hero only. Don't add steam to the mark itself, that's a hero-only detail. Don't rotate or stretch it. ## Color System ### Roles | Role | Token | Value | Used for | |---|---|---|---| | Canvas | `--bg` | `#1a1410` | Page background | | Surface | `--panel` | `#241c16` | Panels, nav, origin cards, table header | | Surface raised | `--surface-raised` | `#2e241c` | Row hover, input focus bg | | Border | `--border` | `#3c2f24` | Panel edges, dividers, table rules | | Ink | `--ink` | `#f4ece1` | Primary text | | Ink muted | `--dim` | `#a89686` | Secondary text, helper text | | Roast / primary | `--accent` | `#d18f4e` | Finished-roast bean color, primary button fill, shipped badge, focus ring | | Origin / freshness | `--accent-alt` | `#8fae6e` | Unroasted-bean starting color, roast-delayed copy | There is deliberately no red anywhere. A delayed roast run is routine roastery logistics, not a failure, so it's communicated in the same green the unroasted bean uses, a plain sentence with a new date, not an alarm. ### Contrast (computed from the hex values above via WCAG relative luminance) | Pairing | Ratio | Passes | |---|---|---| | `--ink` text on `--bg` | ~15.6:1 | AAA | | `--dim` text on `--bg` | ~6.4:1 | AA (body text) | | `--accent` text on `--bg` (links, focus) | ~6.7:1 | AA (body text) | | `--accent-alt` text on `--bg` (delay copy) | ~7.3:1 | AAA | | Dark label on `--accent` fill (primary button) | ~6.7:1 | AA | | `--ink` on `--panel` | ~14.3:1 | AAA | | `--ink` on `--surface-raised` | ~12.9:1 | AAA | The rule this table exists to enforce: a button or badge filled with `--accent` always labels itself in a near-black (`#24160a`), never `--ink`, cream-on-amber at this saturation undershoots the crispness the rest of the system holds to. typography: display: fontFamily: Yeseva One fontWeight: 400 body: fontFamily: Rubik fontWeight: 400 ## Type Scale | Token | Size | Weight | Family | Line-height | Letter-spacing | Used for | |---|---|---|---|---|---|---| | `--text-display` | `clamp(2.25rem, 5.5vw, 4rem)` | 400 | Yeseva One | 1.15 | 0 | Hero H1 only | | `--text-h2` | `1.75rem` (homepage) / `1.5rem` (second page) | 400 | Yeseva One | 1.2 | 0 | Section headings | | `--text-origin-name` | `1.3rem` | 400 | Yeseva One | 1.2 | 0 | Origin/product name, the one other place the display face appears | | `--text-body` | `1rem` | 400 | Rubik | 1.6 | 0 | Paragraph copy | | `--text-small` | `0.85rem` | 400 | Rubik | 1.5 | 0.01em | Footer, nav utility links, helper text, table cells, pricing | Rule for Yeseva One vs. Rubik: Yeseva One, a single weight only, is reserved for the hero headline, section headings, and origin/product names, moments meant to feel like a chalkboard menu board. Everything else, nav, buttons, form labels, table cells, tasting notes, is Rubik, chosen for its warm, rounded-but-legible feel at small sizes. ## Spacing Scale Base unit `8px`. Tokens in use: `--space-2: 0.5rem`, `--space-3: 1rem`, `--space-4: 1.5rem`, `--space-6: 2rem`, `--space-8: 3rem`, `--space-12: 4rem`. ## Shape One radius scale: `--radius: 12px` for panels, origin cards, inputs, and table containers. `--radius-pill: 999px` for buttons and the shipped-status badge. Nothing else rounds. ## Iconography No icon library. The only non-text glyphs are the logo mark, the bean-and-steam illustration, and one Unicode glyph, `✓` (shipped), set at the same weight as the text beside it. Roast level and order status are communicated with plain-language words ("Light", "Medium", "Dark", "Roasted Tue, shipped Wed"), never a color-only signal or a generic package icon. If a future page needs more iconography, the fallback is Phosphor (line style, `1.5` stroke), that decision just hasn't been needed yet. ## Motion Principles Scroll shifts the hero bean's fill color continuously from green through amber to a dark roast brown, a three-stop interpolation driven entirely by scroll position (`scrub`, not a fixed duration), with steam fading in only once the roast is nearly finished. This is the only scripted animation on either page, everything else (buttons, table rows, form inputs) uses only `:hover`/`:active`/`:focus-visible` CSS transitions. The whole system honors `prefers-reduced-motion` by showing the bean at its finished dark-roast color with steam already visible, no scrubbed transition. ### Never-list (written before the impeccable-pass rebuild) 1. No small decorative bean icon stacked above a centered headline, the bean is large (up to 300px, was 120px) and shares the first viewport with the copy in a two-column layout. 2. No two-button CTA row floating free of the bean, the primary action sits beside the intro copy that introduces it. ## Voice & Tone warm, sensory, specific about origin and process, no artisanal-fluff adjectives. In practice: - **Button label**: "Order this week's roast", not "Shop Our Handcrafted Artisanal Coffee" - **Error message**: "Roast delayed. This batch moved to tomorrow's roast run.", not "Oops, a slight delay!" - **Empty state**: "This lot is sold out until the next harvest.", not "Out of stock, check back soon!" - **Success status**: "Roasted Tue, shipped Wed", not "Fresh from our roastery to you! ☕" ## Accessibility Baseline - Focus ring: `2px solid var(--accent)`, `2px` offset, applied via `:focus-visible` on every interactive element (links, buttons, inputs, select), never removed without a visible replacement. - Reduced motion: the bean color-shift and steam fade both check `prefers-reduced-motion: reduce` and render the finished, steaming state immediately, no scrubbed animation. - Minimum tap target: `44px` in the smallest dimension for every button and form control on touch viewports. - Color is never the only signal: roast level and order status pair color with a plain-language word or phrase, the roast-delayed state pairs green with a full sentence naming the new timeline. ## Components ### Navigation Single-line bar, `64px` tall, `--panel` background with a `--border` bottom hairline, sticky to the top. Left: a small "back to gallery" utility link. Center-left: the mark + wordmark lockup. Right: a Shop link plus one primary button. Collapses to just the lockup and primary button under `480px`, no hamburger, there are only two destinations. ### Buttons Three variants, one radius (`--radius-pill`), one primary accent: - **Primary**: `--accent` fill, dark label, used once per view for the single real action ("Order this week's roast", "Start subscription"). - **Secondary**: transparent fill, `1px solid --border`, `--ink` label. "See new ship date" and similar secondary actions. - **Ghost**: text-only, `--dim` label, `--ink` on hover. Tertiary actions like "see where it's from." All variants define `:hover` (brightness/border shift), `:active` (`transform: scale(0.98)`), `:disabled` (`opacity: .4`, `pointer-events: none`), and `:focus-visible` (accent outline). ### Bean + steam illustration An SVG bean whose fill color is continuously recalculated against scroll-scrubbed progress across a three-stop gradient (green, amber, dark brown), paired with a separate steam SVG that fades in only near the end of the roast. Illustrative only, not interactive, exists to make the "roasted this week" claim visually literal. ### Origin cards A flat panel per origin: a Yeseva One name, one sentence of tasting notes, and a plain price line. No stock photography of farms, the copy carries the specificity. ### Table Header row in `--panel`/`--dim` labels, body rows in `--bg`, one `--border` rule between rows, row hover to `--surface-raised`. Roast level is plain text, never a color-coded dot, since roast level isn't a status, it's a fact. ### Forms Label above control, `--text-small`/`--dim`. Select/input: `--panel` background, `1px solid --border`, `--radius`, `--ink` text, `--dim` placeholder. On `:focus`, border becomes `--accent` and background lifts to `--surface-raised`. On error, border becomes `--accent-alt` and helper text explains what to fix in plain language. ### Status / feedback states - **Loading**: a skeleton shaped like the real content (bars matching text line-heights), not a spinner. - **Empty**: a plain sentence in `--dim`, no illustration. - **Error**: an inline `--accent-alt` message with a concrete next step, never a silent failure. - **Success / status badge**: `--radius-pill`, `1px solid --accent`, `--accent` text on transparent fill, not filled, filled would compete with the primary button's exclusive use of a solid `--accent` fill. ### Footer One row: roastery name/tagline on the left, real page links on the right (Shop, back to gallery). No invented legal or certification text. ## Do's and Don'ts ### Do - Let the bean's color shift and the steam fade be the only scroll-scrubbed elements on the page. - Reserve `--accent` for the finished-roast meaning and `--accent-alt` for origin/unroasted meaning, keep that split consistent everywhere. - Name real farms, regions, and roast dates in copy, never vague "premium blend" language. - Use a dark label on any `--accent`-filled surface. - Keep tasting notes specific and sensory (jasmine, brown sugar, cedar), never a generic "smooth and delicious." ### Don't - Don't use soft-focus lifestyle photography of coffee cups, the bean illustration and typography carry the whole visual system on purpose. - Don't say "artisanal," "handcrafted," or "small-batch" as unsupported marketing filler, back every quality claim with a specific fact (farm name, roast date). - Don't introduce red for delays, route those to `--accent-alt` with specific copy instead. - Don't stack more than one primary button in the same view. - Don't fill a status badge solid, badges stay outlined so the primary button remains the only solid-`--accent`-fill element on screen. ## Known Constraints - Two routes now exist (home, shop), still no real cart or checkout flow, no responsive breakpoint table beyond a single-column mobile collapse, no authenticated subscriber account (the subscription form is not wired to anything real, this is a concept demo).