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.

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):

FileUse
TVC logo.svgThe colour mark, vector — same artwork as TVC logo.png, now scalable.
TVC logo white.svgKnockout 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.svgMark + “Tamarind Valley Collective” wordmark, side by side, colour version for light backgrounds.
TVC logo horizontal white.svgSame lockup, using the corrected white-mark-with-orange-seeds above, for dark backgrounds.
TVC logo horizontal footer white.svgA 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:

TokenHexRole
Green#3d6e52Primary — links, active nav, brand fills
Green (vivid)#18723CThe 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#bd7248Everyday accent (muted) — hover, highlights, badges, borders. --tvc-orange on the main site.
Orange (vivid)#f78520CTA-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:

TokenHexRole
Earth Brown#6F4A2ESoil/earth — warm callouts, section accents
Sky Blue#4E8FB5Sky — 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) had warning recoloured to Earth Brown and info/todo to 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--sky classes in global.css, generalized from .rainfall-summary’s existing bordered-card pattern (WeatherView.astro) rather than inventing a new look: white background, --tvc-line border, 4px colour-accented left border, .eyebrow label inside. .callout defaults to Earth Brown (matching .button/.button--outline’s convention of an unmodified default plus a modifier class); .callout--sky swaps 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-summary itself 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):

TokenHexRole
Ground#faf7eePage background
Hairline#e2ddc9Borders, rules, table lines
Muted#57604fMuted/secondary text
Ink#22291fBody text, headings, strong text

Typography

RoleFaceNotes
Headings / displayFrauncesOrganic, humanist serif that echoes the botanical logo — friendly, not corporate. Already shared by both systems.
Body / UIInter, self-hostedSelf-hosting avoids a Google Fonts CDN call at runtime — the main site’s existing rationale, now shared.
CodeIBM Plex Mono, self-hostedClear, 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 full 999px pill 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:

TokenValue
--space-14px
--space-28px
--space-312px
--space-416px
--space-520px
--space-624px
--space-732px
--space-848px

--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 uses 1.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:

TokenValueCovers
--transition-fast150ms easeHover/interactive feedback — buttons, links, cards (the single most common value already in use)
--transition-base300ms easeMore 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.farmtvc.farm (main site)
Extra typefaceNone 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 modeYes — 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 deliveryMust 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.
ComponentsRelies 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 filesCanonical 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)

TokenDark 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 slotLightDarkFrom
light#faf7ee#141613Ground
lightgray#e2ddc9#2B2E27Hairline
gray#57604f#788779Muted
darkgray#22291f#CBD2C7Ink (body weight)
dark#22291f#EAF0E7Ink (heading weight)
secondary#3d6e52#5CB37DGreen
tertiary#f78520#F79A3COrange
highlightrgba(61,110,82,0.10)rgba(92,179,125,0.15)Green tint (internal-link / code bg)
textHighlight#f7852040#F79A3C40Orange tint (==marker==)

Accessibility

A WCAG 2.1 contrast audit of the palette above (sRGB relative luminance), run 2026-08-13:

PairRatioAA normal text (4.5:1)AA large text/UI (3:1)
Ink on Ground13.95:1PassPass
Muted on Ground6.14:1PassPass
Green on Ground, as link text5.52:1PassPass
Green (vivid) on Ground5.58:1PassPass
Orange (muted, #bd7248) on Ground3.46:1FailPass
Orange (vivid, #f78520) on Ground, as text2.35:1FailFail
Ink (dark, body weight) on Ground (dark)11.77:1PassPass
Ink (dark, heading weight) on Ground (dark)15.70:1PassPass
Muted (dark) on Ground (dark)4.80:1 (fixed 2026-08-13, was 3.26:1)PassPass
Green (light, dark-ground) on Ground (dark)7.11:1PassPass
Orange (light, dark-ground) on Ground (dark)8.36:1PassPass

Two real, live findings:

  • The main site’s .eyebrow label fails AA. Fixed 2026-08-13. It was set in --tvc-orange (#bd7248, muted) at 0.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 .eyebrow at it instead of --tvc-orange. --tvc-orange itself 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 .eyebrow was in scope). The photo-background .eyebrow--light variant (#ffd8a8 on 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-faint was #5f6b60 (3.26:1 against dark Ground); lightened to #788779 (same hue/near-zero saturation, 4.80:1) in both quartz.config.yaml and 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 .eyebrow contrast failure — done 2026-08-13, see Accessibility.
  • Spacing 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 --tvc-orange into one unified Orange valuerejected 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.
  • Docs 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-vivid and 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.