TVC’s design system, split into what’s common across every surface and what necessarily differs between docs.tvc.farm (this site, Quartz/Obsidian) and tvc.farm’s main site (the website repo, Astro). Implemented in all three live files as of 2026-08-13 (TVCDocs/.quartz/quartz.config.yaml + .obsidian/snippets/tvc-design-system.css for docs, website/src/styles/global.css for the main site) — not yet reviewed against a rebuilt/rendered site. See Status for exactly what changed.
Common
Shared across docs.tvc.farm and tvc.farm — change these here, then propagate to both.
Logo

The mark is a deep-green roundel enclosing a white tamarind-frond motif with three orange seed accents.
Usage: keep clear space around the mark equal to at least the width of one frond leaflet. Don’t recolour it, rotate it, or place it on a busy background.

Variants — added 2026-08-13 from a proper vector source (TVC_vector_logo.svg, the collective’s Google Drive “Final Logo” folder — previously only PNG/JPG rasters existed in either repo):
| File | Use |
|---|---|
TVC logo.svg | The colour mark, vector — same artwork as TVC logo.png, now scalable. |
TVC logo white.svg | Knockout mark for dark grounds — this is the “white or knockout variant” the Logo section used to say could be added “when we have one.” Not a single flat fill: only the green roundel+frond path goes white; the three orange-seed paths stay exactly #F88722 — the source artwork’s own orange, unchanged, so it matches the colour mark exactly rather than substituting a different token. Two mistakes caught and fixed same day: a first pass turned every path white indiscriminately and lost the seeds; the immediate fix then substituted the dark-ground orange token (#F79A3C) instead of just keeping the original — a visibly different shade, caught directly (“the orange is not the same shade”). |
TVC logo horizontal.svg | Mark + “Tamarind Valley Collective” wordmark, side by side, colour version for light backgrounds. |
TVC logo horizontal white.svg | Same lockup, using the corrected white-mark-with-orange-seeds above, for dark backgrounds. |
TVC logo horizontal footer white.svg | A third geometry (36px mark, 10px gap, 0.95rem text, matching Footer.astro’s own metrics rather than Nav’s), using the same corrected white mark. |
An earlier version of this table also listed TVC logo horizontal footer.svg — colour mark + white text, for Footer specifically. Removed same day. The mark’s “white” frond isn’t actually painted white in the source artwork, it’s negative-space cutouts that reveal whatever’s behind the mark — fine on the cream page background, but on Footer’s dark green the cutouts revealed the footer’s own (different) green, so the frond nearly vanished into the background. Caught directly (“the green branch almost blends into the green background”). Any full-colour mark on a non-cream/non-white ground will have this problem — the white knockout is the only variant safe to use on a colour background.
Same five files live in website/public/images/brand/ (kebab-case: tvc-logo-mark.svg, tvc-logo-mark-white.svg, tvc-logo-horizontal.svg, tvc-logo-horizontal-white.svg, tvc-logo-horizontal-footer.svg).
The horizontal lockup’s wordmark isn’t live <text> — Quicksand at weight 700 (matching what Nav.astro’s .nav__brand used to render live: same font, weight, and the --tvc-green-dark colour for the light variant) was instantiated from the self-hosted variable font and each glyph’s outline extracted to real vector paths (via fontTools), so the lockup renders correctly anywhere without the font being installed or loaded — social shares, print, the Quartz site, anywhere. Checked visually in-browser against light, dark, and photo-gradient backgrounds before finalizing; the first pass reused the light variant’s dark-green text color on a dark background and was nearly illegible, which is why a separate white lockup variant exists rather than one file assumed to work everywhere.
Now live: Nav.astro’s .nav__brand was switched from its PNG-mark + live-text composition to tvc-logo-horizontal.svg directly (tvc-logo-mark.svg alone below the existing 530px breakpoint, same responsive behaviour as before — swapping which image is shown rather than hiding a <span>). Footer.astro’s brand lockup was migrated too, to tvc-logo-horizontal-footer-white.svg (no breakpoint swap needed — Footer never hid its wordmark at any width) — not the colour version, see the note above on why. Both .nav__brand/.footer__brand lost their font-family: var(--font-wordmark) declarations in the process; Quicksand is now unused site-wide (only the @font-face and --font-wordmark token remain defined in global.css, with no consumers) — not removed, since that’s a separate cleanup decision nobody’s made yet.
Colour
Brand — a muted everyday tone plus a vivid tone held in reserve for emphasis, for both green and orange:
| Token | Hex | Role |
|---|---|---|
| Green | #3d6e52 | Primary — links, active nav, brand fills |
| Green (vivid) | #18723C | The true logo green — dark-ground emphasis, hover-on-dark. --tvc-green-vivid on the main site; the docs site’s secondary/dark-mode slot. |
| Orange | #bd7248 | Everyday accent (muted) — hover, highlights, badges, borders. --tvc-orange on the main site. |
| Orange (vivid) | #f78520 | CTA-reserved — kept out of everyday use so a real call to action still pops against the muted backdrop. --tvc-orange-cta on the main site, used only by .button; the docs site’s tertiary slot (its Tamarind Orange, #F68520, is effectively the same colour), since Quartz’s single accent slot has no room for a separate muted tier. |
Same muted-default/vivid-reserve pattern as Green, corrected here 2026-08-13 — an earlier draft of this note collapsed Orange to one value, which would have meant replacing the main site’s ~15+ existing muted-orange uses (hover states, badges, borders, icons) with the vivid CTA colour, undoing the exact effect --tvc-orange-cta’s own code comment describes: keeping real calls-to-action from blending into every other orange accent. Left alone; see To build for why that plan was dropped.
One more token exists on the main site only: --tvc-orange-text (#965836) — Orange (muted)‘s hue/saturation, darkened to clear WCAG AA (5.23:1) as small text, used so far only by .eyebrow. See Accessibility.
Nature (extended) — from TVC’s “green trees, brown earth, blue skies”. Reserved for tags, diagrams, and illustration on both; wired into callouts on both sites as of 2026-08-13:
| Token | Hex | Role |
|---|---|---|
| Earth Brown | #6F4A2E | Soil/earth — warm callouts, section accents |
| Sky Blue | #4E8FB5 | Sky — info callouts, secondary links, diagram fills |
Callout implementation:
- docs.tvc.farm — Quartz’s built-in callout types (
quartz/styles/callouts.scss, hardcoded hex per type, not driven by the theme colour slots) hadwarningrecoloured to Earth Brown andinfo/todoto Sky Blue. Every other type (the[!note]default,abstract,tip,success,question,danger/failure/bug,example,quote) is untouched, still Quartz’s stock colours — this was scoped to the two types the note’s own role text names, not a full callout-system rebrand. - tvc.farm — new reusable
.callout/.callout--skyclasses inglobal.css, generalized from.rainfall-summary’s existing bordered-card pattern (WeatherView.astro) rather than inventing a new look: white background,--tvc-lineborder, 4px colour-accented left border,.eyebrowlabel inside..calloutdefaults to Earth Brown (matching.button/.button--outline’s convention of an unmodified default plus a modifier class);.callout--skyswaps to Sky Blue. Sky Blue itself fails WCAG AA as text (3.31:1) — added--tvc-sky-blue-text(#3c7190, 4.95:1) for the label, same approach as--tvc-orange-text. Reserve only: no page consumes it yet, and.rainfall-summaryitself wasn’t migrated to it (same “don’t retrofit” reasoning as the spacing scale).
Neutrals — warm, paper-and-ink tones rather than pure grey, so the palette reads natural. Light-mode values, sourced from the main site’s already-shipped palette (warmer than the docs site’s current cooler tones):
| Token | Hex | Role |
|---|---|---|
| Ground | #faf7ee | Page background |
| Hairline | #e2ddc9 | Borders, rules, table lines |
| Muted | #57604f | Muted/secondary text |
| Ink | #22291f | Body text, headings, strong text |
Typography
| Role | Face | Notes |
|---|---|---|
| Headings / display | Fraunces | Organic, humanist serif that echoes the botanical logo — friendly, not corporate. Already shared by both systems. |
| Body / UI | Inter, self-hosted | Self-hosting avoids a Google Fonts CDN call at runtime — the main site’s existing rationale, now shared. |
| Code | IBM Plex Mono, self-hosted | Clear, neutral monospace for commands, configs, inline code. |
Guidance: headings in Fraunces carry the character, so keep body copy calm; don’t introduce more families. Use weight and size for hierarchy, not new typefaces.
Structure
--radius: 14px— corner rounding (buttons use a full999pxpill instead).--shadow-soft: 0 4px 18px rgba(34, 41, 31, 0.1)— card/panel elevation.--space-section: 96px/--space-section-sm: 56px(below a 640px breakpoint) — vertical rhythm between page sections.
Spacing scale — implemented 2026-08-13 in website/src/styles/global.css (--space-1 through --space-8). Auditing the main site’s CSS turned up over a dozen distinct hand-placed gap/padding/margin values (from 3px to 32px), with no shared scale behind them. A minimal 4px-based scale gives future components something to reach for instead of picking a new number each time:
| Token | Value |
|---|---|
--space-1 | 4px |
--space-2 | 8px |
--space-3 | 12px |
--space-4 | 16px |
--space-5 | 20px |
--space-6 | 24px |
--space-7 | 32px |
--space-8 | 48px |
--space-section/--space-section-sm stay as their own named semantic tokens above this scale — they mean something specific (page-section rhythm), not just “a gap of that size.” Not a call to retrofit every existing hardcoded value across ~166 pages; that’s a large, low-value refactor for a site that already reads visually consistent. Use the scale for new components going forward.
Iconography
Already consistent in practice on the main site, just not written down until now. Hand-authored inline SVG, not an icon font or library:
viewBox="0 0 24 24"fill="none",stroke="currentColor"(or an explicit colour when the icon sits on a non-text-coloured background, e.g. white icons over a photo)stroke-width="1.6"(the nav’s small 10×6 caret uses1.5— a reasonable exception at that size)stroke-linecap="round",stroke-linejoin="round"
Reads as a soft, rounded line-icon style (in the Feather/Lucide family), which suits Fraunces/Inter’s warmth better than a sharp geometric set would. The docs site has no icon usage yet to conform to this.
Motion
Implemented 2026-08-13 in website/src/styles/global.css (--transition-fast/--transition-base). Auditing the main site’s ~40 existing transition/animation declarations shows two real clusters plus a handful of deliberate one-offs:
| Token | Value | Covers |
|---|---|---|
--transition-fast | 150ms ease | Hover/interactive feedback — buttons, links, cards (the single most common value already in use) |
--transition-base | 300ms ease | More deliberate state changes — fades, reveals |
Slower, bespoke motion (the rainfall chart’s 1.1s cubic-bezier(0.65,0,0.35,1) draw-in; decorative loops running 1.2s–8s) is intentionally left untokenized — those are one-off narrative moments, not repeated UI feedback, and forcing them onto a shared scale would blur a real distinction rather than clarify one.
Like the spacing scale above, neither token is consumed by any existing component yet — the ~40 existing declarations that motivated the values were left as-is, same reasoning as not retrofitting spacing. Both scales are there for new work to reach for.
Voice & tone
Drafted 2026-08-13 from copy already live on tvc.farm, not invented from scratch — every example below is a real quote. Applies to both surfaces.
First person plural, not third-person distant. TVC writes as “we,” not “TVC” or “the collective” narrating from outside itself.
- Real: “We’re on a hillslope facing west with stunning sunset views across the valley.” (Visit page)
- Not: “TVC is situated on a hillslope facing west.”
Concrete and sensory over abstract. Specific, lived-in detail carries the writing instead of generic descriptors.
- Real: “if you’re visiting after Dussehra, expect the morning dew to leave your trousers wet on early walks.” (Visit page)
- Not: “enjoy the natural beauty of the changing seasons.”
Plain facts, stated directly, not sold hard. Numbers and history speak for themselves; no superlatives doing the persuading.
- Real: “A collective of 53 families regenerating 100 acres of the Melagiri Hills.” (About page)
- Real: “Once stripped bare by exploitative farming, now a thriving food forest and wildlife habitat — permaculture and reforestation turned it around.” (Homepage)
- Not: “Experience a magical transformation at our stunning eco-retreat.”
Honest about the land’s history, not just its present. “Stripped bare by exploitative farming” owns the site’s past rather than skipping straight to an idealised now — this is why the regeneration story reads as credible rather than promotional. Already a stated product principle (PRODUCT.md): “The farm’s own operating reality… is the site’s core credibility asset — don’t dilute it with generic retreat/resort framing.” This note is that principle applied to sentence-level writing.
Word choice: farm/collective language, not resort language. “Visitors” (5 uses across the site) is the standing word choice, not “guests” (found once, in VisitView.astro — likely worth normalizing to “visitors” there too, not done as part of this note). No “eco-retreat,” “luxury,” or similar resort vocabulary appears anywhere in the copy, and shouldn’t start now.
Data visualisation
Chart colours are deliberately not drawn from the brand palette. The rainfall chart (WeatherView.astro) already uses a separate categorical palette from the dataviz skill, chosen for colourblind-safe adjacent-pair separation and assigned by chronological order (never by value) so a series keeps its colour as new years arrive. Already implemented and working — noted here only so it isn’t mistaken for a gap or reinvented with brand colours, which wouldn’t be colourblind-safe as a categorical set.
Differences
Where the two surfaces genuinely need to diverge, and why — platform constraints or context, not historical drift.
| docs.tvc.farm | tvc.farm (main site) | |
|---|---|---|
| Extra typeface | None beyond the common three. | Quicksand, self-hosted — still live in Footer.astro’s brand wordmark (a component the docs site doesn’t have); Nav.astro’s equivalent is now the vector-outlined tvc-logo-horizontal.svg instead of live Quicksand text, see Logo. |
| Dark mode | Yes — Quartz has a light/dark toggle; see dark-mode neutral values below. | Not yet — the marketing site has no dark mode today. If it ever gets one, the docs site’s dark values (below) are the starting point. |
| Token delivery | Must additionally map onto Quartz’s fixed nine colour slots (table below), and mirror the same values into three places: this note, the fork’s quartz.config.yaml, and the Obsidian CSS snippet .obsidian/snippets/tvc-design-system.css (so editing in Obsidian looks like what publishes). | Consumed directly as CSS custom properties in one file, global.css — no extra mapping layer. |
| Components | Relies on Quartz’s own theming via the slot mapping; no bespoke component classes yet (callout styling using Earth Brown/Sky Blue is still on the to-build list). | Has real UI component classes: .wrapper (page grid, max-width: 1120px), .section/.section--tint, .card, .button/.button--outline, .eyebrow; headings use fluid clamp() sizing (h1 clamp(2rem, 4vw, 2.9rem), h2 clamp(1.5rem, 3vw, 2.1rem), h3 clamp(1.3rem, 2.2vw, 1.6rem)). |
| Logo asset files | Canonical source files live in this vault’s _resources/ (TVC logo.png, TVC og-image.png), used for the docs site’s favicon and social card. | Maintains its own separate copies in the website repo (public/images/brand/tvc-logo-mark.png, public/favicon-16.png/favicon-32.png) rather than sharing files across repos. |
Dark-mode neutrals (docs.tvc.farm only)
| Token | Dark mode |
|---|---|
| Ground | #141613 |
| Hairline | #2B2E27 |
| Muted | #788779 (fixed 2026-08-13, was #5F6B60) |
| Ink (body weight) | #CBD2C7 |
| Ink (heading weight) | #EAF0E7 |
| Green (light, dark-ground) | #5CB37D |
| Orange (light, dark-ground) | #F79A3C |
Quartz colour-slot mapping (docs.tvc.farm only)
Quartz exposes only nine colour slots per mode (in the fork’s quartz.config.yaml):
| Quartz slot | Light | Dark | From |
|---|---|---|---|
light | #faf7ee | #141613 | Ground |
lightgray | #e2ddc9 | #2B2E27 | Hairline |
gray | #57604f | #788779 | Muted |
darkgray | #22291f | #CBD2C7 | Ink (body weight) |
dark | #22291f | #EAF0E7 | Ink (heading weight) |
secondary | #3d6e52 | #5CB37D | Green |
tertiary | #f78520 | #F79A3C | Orange |
highlight | rgba(61,110,82,0.10) | rgba(92,179,125,0.15) | Green tint (internal-link / code bg) |
textHighlight | #f7852040 | #F79A3C40 | Orange tint (==marker==) |
Accessibility
A WCAG 2.1 contrast audit of the palette above (sRGB relative luminance), run 2026-08-13:
| Pair | Ratio | AA normal text (4.5:1) | AA large text/UI (3:1) |
|---|---|---|---|
| Ink on Ground | 13.95:1 | Pass | Pass |
| Muted on Ground | 6.14:1 | Pass | Pass |
| Green on Ground, as link text | 5.52:1 | Pass | Pass |
| Green (vivid) on Ground | 5.58:1 | Pass | Pass |
Orange (muted, #bd7248) on Ground | 3.46:1 | Fail | Pass |
Orange (vivid, #f78520) on Ground, as text | 2.35:1 | Fail | Fail |
| Ink (dark, body weight) on Ground (dark) | 11.77:1 | Pass | Pass |
| Ink (dark, heading weight) on Ground (dark) | 15.70:1 | Pass | Pass |
| Muted (dark) on Ground (dark) | 4.80:1 (fixed 2026-08-13, was 3.26:1) | Pass | Pass |
| Green (light, dark-ground) on Ground (dark) | 7.11:1 | Pass | Pass |
| Orange (light, dark-ground) on Ground (dark) | 8.36:1 | Pass | Pass |
Two real, live findings:
The main site’sFixed 2026-08-13. It was set in.eyebrowlabel fails AA.--tvc-orange(#bd7248, muted) at0.78rem/600 weight — small enough that it doesn’t qualify as WCAG “large text” (needs 18px normal or ~18.7px bold; eyebrows are ~12.5px), so it needed the 4.5:1 threshold and only cleared 3.46:1. Added--tvc-orange-text: #965836— same hue/saturation, darkened to 5.23:1 — and pointed.eyebrowat it instead of--tvc-orange.--tvc-orangeitself is untouched (still used as-is for borders/backgrounds/icons and its other ~15 existing text usages across the site, which weren’t audited or touched here — only.eyebrowwas in scope). The photo-background.eyebrow--lightvariant (#ffd8a8on a dark hero overlay) is a separate case not covered by this audit.The docs site’s dark-mode Muted fails AA.Fixed 2026-08-13.gray/--text-faintwas#5f6b60(3.26:1 against dark Ground); lightened to#788779(same hue/near-zero saturation, 4.80:1) in bothquartz.config.yamland the Obsidian snippet. Light-mode Muted (#57604f, 6.14:1) was already fine and untouched. Obsidian’s separate--text-muted(#9aa595, an intermediate shade not part of the documented neutral scale) already passed at 7.10:1, also untouched.
One guardrail worth stating explicitly now that the vivid tier is shared rather than main-site-only: Orange (vivid)/--tvc-orange-cta (#f78520) must never be used as a text/foreground colour — 2.35:1 against Ground, worse than white-on-orange’s already-rejected 2.52:1. It only works as a button background with dark ink text on top (5.93:1, already verified). The button component already does this correctly; stating it here so it doesn’t get picked as text colour on the docs site by someone who doesn’t know that history.
To build
In rough priority:
Fix the main site’s— done 2026-08-13, see Accessibility..eyebrowcontrast failureSpacing scale— done 2026-08-13, see Structure. No consumers yet; available for new work.Motion tokens— done 2026-08-13, see Motion. No consumers yet; available for new work.Collapse— rejected 2026-08-13. Would have replaced the main site’s ~15+ muted-orange uses with the vivid CTA colour, undoing the deliberate muted/vivid split; see Common → Colour. Orange stays a pair, like Green.--tvc-orangeinto one unified Orange valueDocs site’s dark-mode Muted fails AA— done 2026-08-13, see Accessibility.Callout styles using Earth Brown and Sky Blue— done 2026-08-13, see Common → Colour.Logo variants — a white/knockout mark and a horizontal lockup with the wordmark— done 2026-08-13, see Common → Logo.Voice & tone— done 2026-08-13, see Common → Voice & tone.- Normalize “guests” → “visitors” in
VisitView.astro— a stray word-choice inconsistency surfaced while drafting Voice & tone; not fixed here.
Status
Not implemented yet. Making this live means editing three files across two repos:
TVCDocs/.quartz/quartz.config.yaml— swap in the Quartz slot mapping above.TVCDocs/.obsidian/snippets/tvc-design-system.css— mirror the same values so Obsidian editing matches what publishes.website/src/styles/global.css— closest to this spec already; mainly adds--green-vividand drops nothing (existing values already match Common).
Plus vendoring Inter and IBM Plex Mono as self-hosted woff2 files for the docs site (it currently pulls all three faces from Google Fonts), the way the main site already does for its own fonts.