--- version: alpha name: rootstock-design description: "A warm seed-packet cream canvas (#f6f1e4) with dark soil ink (#2f2a1e), a moss green (#4c6b3d) for growth and primary action, and a clay terracotta (#9c521f) for roots and grounded attention states. Lora carries every heading at weight 600; Mulish carries the surrounding prose. The system reads as an actual community farm's newsletter, plainly printed, never a glossy nonprofit annual-report site." --- ## Brand Positioning Rootstock is for neighbors who want to know exactly where a share or a volunteer hour goes, not a donation platform optimized for a single big ask. It exists because most nonprofit sites borrow fundraising-platform chrome (a donation-amount slider, a progress-bar-to-goal, stock photography of hands holding soil) that has nothing to do with an actual working farm co-op. Rootstock's bet: the one piece of visual drama should be the same thing that happens in the ground, a vine growing and forking, roots spreading, real numbers counting up from an actual season, not a fundraising target. Every visual decision below serves that one bet. If a component doesn't help someone join a share or sign up for a shift, or doesn't come from a real co-op interaction (shift table, share status, signup form), it doesn't belong in this system. colors: accent: "#4c6b3d" accent-alt: "#9c521f" ink: "#2f2a1e" ink-muted: "#6b5f47" canvas: "#f6f1e4" surface: "#fffcf5" surface-raised: "#ede4d0" border: "#ddd0b0" focus-ring: "#4c6b3d" ## Logo / Wordmark System The mark is a rounded-square card containing a small forking vine with a root node at its base, literally a 1:1 miniature of the homepage's `#vine-lines` growth mechanic frozen mid-grow. ```svg ``` - **Lockup**: mark, then a `0.5rem` gap, then the wordmark "Rootstock" in Lora/600, mark always left of the wordmark, never stacked or reversed. - **Minimum size**: mark not smaller than `16px` square, below that the two branches and the root dot merge into the stem. 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 dark background, it only ever renders on `--panel`/`--bg`. Don't add a leaf or flower to the mark itself, the homepage's leaf unfurl is a motion detail, not part of the static mark. Don't swap the root dot to green, clay marks "underground" consistently across the system. Don't rotate or stretch it. ## Color System ### Roles | Role | Token | Value | Used for | |---|---|---|---| | Canvas | `--bg` | `#f6f1e4` | Page background | | Surface | `--panel` | `#fffcf5` | Panels, nav, stat cards, table header | | Surface raised | `--surface-raised` | `#ede4d0` | Table header/hover, input focus bg | | Border | `--border` | `#ddd0b0` | Panel edges, dividers, input outlines, table rules | | Ink | `--ink` | `#2f2a1e` | Primary text | | Ink muted | `--dim` | `#6b5f47` | Secondary text, helper text, full-shift status | | Growth / above ground | `--accent` | `#4c6b3d` | Vine lines, leaves, primary button fill, open-shift status, focus ring | | Roots / below ground | `--accent-alt` | `#9c521f` | Root node, root-system diagram, waitlisted state | There is deliberately no red anywhere. A full volunteer shift or a waitlisted share isn't a failure, it's the co-op working as intended, so both are communicated in the same clay tone the root diagram uses, grounded, not alarmed. ### Contrast (computed from the hex values above via WCAG relative luminance) | Pairing | Ratio | Passes | |---|---|---| | `--ink` text on `--bg` | ~12.7:1 | AAA | | `--dim` text on `--bg` | ~5.6:1 | AA (body text) | | `--accent` text on `--bg` (open status, links) | ~5.4:1 | AA (body text) | | `--accent-alt` text on `--bg` (waitlist copy) | ~5.1:1 | AA (body text) | | Light label on `--accent` fill (primary button) | ~5.4:1 | AA | | `--ink` on `--panel` | ~13.9:1 | AAA | | `--ink` on `--surface-raised` | ~11.3:1 | AAA | The rule this table exists to enforce: `--accent-alt` (clay) is tuned specifically to clear AA at body-text size on `--bg`, it started as a lighter terracotta that only cleared large-text contrast and was darkened until the waitlist copy itself, not just a badge, could carry the color directly. typography: display: fontFamily: Lora fontWeight: 600 body: fontFamily: Mulish fontWeight: 400 ## Type Scale | Token | Size | Weight | Family | Line-height | Letter-spacing | Used for | |---|---|---|---|---|---|---| | `--text-display` | `clamp(2.5rem, 6vw, 4.25rem)` | 600 | Lora | 1.05 | 0 | Hero H1 only | | `--text-h2` | `1.6rem` (homepage) / `1.4rem` (second page) | 600 | Lora | 1.2 | 0 | Section headings | | `--text-stat` | `clamp(1.5rem, 4vw, 2.25rem)` | 600 | Lora | 1.1 | 0 | The three impact-stat numbers, the one place a number gets a display-scale step | | `--text-body` | `1rem` | 400 | Mulish | 1.6 | 0 | Paragraph copy | | `--text-small` | `0.85rem` | 400 | Mulish | 1.5 | 0.01em | Footer, nav utility links, helper text, table cells | Rule for Lora vs. Mulish: Lora is reserved for the hero headline, section headings, and the impact-stat numbers, moments meant to read like a printed newsletter headline. Everything else, nav, buttons, form labels, table cells, is Mulish, chosen for staying warm and legible at small sizes without competing with the serif. ## 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: 10px` for panels, stat cards, inputs, and table containers. `--radius-pill: 999px` for buttons and the confirmed-share badge. Nothing else rounds. ## Iconography No icon library. The only non-text glyphs are the logo mark, the vine/leaf/root line art, and one Unicode glyph, `✓` (share confirmed), set at the same weight as the text beside it. Shift and share status is communicated with a plain-language phrase ("4 open", "Full") paired with color, never an icon standing in for the word. 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 grows a vine up the hero, forking twice, with small leaves unfurling at each fork (a quick `back.out` scale-and-fade, not a wilt or droop), the whole sequence tied to scroll position and reversible by scrolling back up (`toggleActions: play none none reverse`). Further down, the same scroll-in pattern draws a root-system diagram below the season's impact numbers while those numbers count up from zero, roots and numbers finishing together. These are the only scripted animations 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 fully grown vine, unfurled leaves, drawn roots, and final stat numbers immediately, with no animation. ### Never-list (written before the impeccable-pass rebuild) 1. No centered hero sitting a full section above the vine, the vine grows in the same first viewport as the headline. 2. No two-button CTA row floating free of the drawing, the primary action sits beside the intro copy that introduces it. ## Voice & Tone warm, grounded, community-first, concrete numbers not adjectives. In practice: - **Button label**: "Join a share", not "Support Our Mission Today" - **Error message**: "Waitlisted. Full-share signups are closed, half-shares still open.", not "Sorry, we're full!" - **Empty state**: "No shares left for this season.", not "Oops, nothing available right now!" - **Success status**: "Share confirmed for this week", not "You're officially part of the family! 🌱" ## Accessibility Baseline - Focus ring: `2px solid var(--accent)`, `2px` offset, applied via `:focus-visible` on every interactive element (links, buttons, inputs), never removed without a visible replacement. - Reduced motion: the vine growth, leaf unfurl, root reveal, and stat count-up all check `prefers-reduced-motion: reduce` and render fully resolved with no animation when it's set. - Minimum tap target: `44px` in the smallest dimension for every button and input on touch viewports. - Color is never the only signal: shift status pairs color with a specific count ("4 open") or the word "Full", the waitlist state pairs clay with a full sentence naming the alternative (half-shares), not a bare badge. ## 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 Get Involved 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 accent: - **Primary**: `--accent` fill, light label, used once per view for the single real action ("Join a share"). - **Secondary**: transparent fill, `1px solid --border`, `--ink` label. "See half-shares" and similar secondary actions, form submits that aren't the primary conversion. - **Ghost**: text-only, `--dim` label, `--ink` on hover, bold weight matching the rest of the button set. Tertiary actions like "watch it grow." All variants define `:hover` (brightness/border shift), `:active` (`transform: scale(0.98)`), `:disabled` (`opacity: .4`, `pointer-events: none`), and `:focus-visible` (accent outline). ### Vine growth diagram An SVG of stroke-only paths (`#vine-lines`) revealed via `stroke-dasharray`/`stroke-dashoffset`, with small filled leaf shapes fading and scaling in at each fork. Purely illustrative, not interactive, exists only to make the "growth" metaphor literal. ### Impact stats Three flat `.stat` cards, each a count-up number in the display serif over a plain label. The one place on the page a number is allowed the display type scale, everywhere else numbers stay body-sized. ### Table Header row in `--surface-raised`/`--dim` bold labels, body rows in `--panel`, one `--border` rule between rows, row hover to `--surface-raised`. Status cells are bold text in `--accent` (open, with a specific count) or `--dim` (full), never a bare colored dot. ### Forms Label above input, `--text-small`/`--dim`/700 weight. 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 the 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 alternative, 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: co-op name/tagline on the left, real page links on the right (Get Involved, back to gallery). No invented legal or tax-deduction text. ## Do's and Don'ts ### Do - Let the vine growth and root diagram be the only scroll-scrubbed, illustrative elements on the page. - Reserve `--accent` for above-ground/growth meaning and `--accent-alt` for below-ground/roots meaning, keep that split consistent everywhere. - State real, specific numbers (shift counts, season totals), never vague claims like "tons of impact." - Use a light label on any `--accent`-filled surface. - Treat a full shift or closed share as ordinary information, not a dead end, always pair it with the actual alternative. ### Don't - Don't add a donation-amount slider, a fundraising progress bar, or stock photography of hands in soil, this is a working co-op site, not a fundraising platform. - Don't introduce red for "full" or "waitlisted" states, route those to `--accent-alt` with specific, helpful copy instead. - Don't replay the vine-growth or root reveal on every scroll pass, reveal once per view, reversible only by scrolling back up. - 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, get involved), still no member account or payment flow for shares, no responsive breakpoint table beyond a single-column mobile collapse, no authenticated volunteer login (the "Join a share" and signup form are not wired to anything real, this is a concept demo).