Skip to main content

Component Governance Framework

Repository: livepeer/docs, docs-v2 branch
Scope: snippets/components/ — the governed component library
Platform: Mintlify (MDX-based)
Version: 1.0
Date: 7 March 2026
Author: Wonderland (Alison Haire) / Claude collaboration
Source deliverables: D1–D6 of the Component Governance Framework plan

Quick Start

You’re a contributor. You want to build, use, or modify a component. Here’s everything you need in 60 seconds.

Find a component

Use the Component Registry or the published Component Library pages. Every component has a status badge, props table, and copy-paste example.

Use a component

Import with the full explicit path in your MDX page:
import { CustomDivider } from '/snippets/components/primitives/divider.jsx'

<CustomDivider />
Mintlify requires full paths with file extensions. No barrel imports. If you need multiple components, import them all at the MDX page level. Components must not import other .jsx component files. Shared non-visual runtime logic may only be kept inline in the exported component or imported from a colocated non-component .js helper module.

Decide which category

Follow the decision tree (first match wins):
  1. Bound to an external data source or pipeline?data/
  2. Only makes sense on frame-mode or portal pages?page-structure/
  3. Accepts children and arranges them spatially?layout/
  4. Formats or renders content for the reader?content/
  5. Otherwiseprimitives/

Build a new component

  1. Create your .jsx file in the correct category folder.
  2. Use arrow function syntax (export const X = () => ...).
  3. Add the full JSDoc block (14 governance fields + @param per prop + @example).
  4. Use var(--lp-*) CSS tokens for all colours. No hardcoded hex. No ThemeData.
  5. Add defensive rendering — guard all props, never throw. A crash kills the entire page.
  6. Create an example in {category}/examples/{file}-examples.mdx.
  7. Write unit tests in tests/unit/components/{category}/{file}.test.js.
  8. Verify visually in both light and dark mode via mintlify dev.
  9. Commit. Pre-commit validates JSDoc, regenerates the registry, and checks styling rules.

Component checklist (full)

JSDoc
  □ All 14 governance fields present
  □ @param for every prop
  □ @example with primary usage

Props
  □ camelCase names
  □ Boolean props: is/has prefix
  □ Handler props: on prefix
  □ Defaults in destructuring
  □ children for slot content, named props for data
  □ Spread last (...rest)
  □ Required props: no default. Optional: always have default.

Composition
  □ No cross-file imports from other `.jsx` component files
  □ Shared runtime logic for MDX-facing components stays inline or comes from a colocated `.js` helper import
  □ Renders correctly standalone and inside Mintlify globals

Styling
  □ Colours use var(--lp-*) tokens
  □ No ThemeData, no hardcoded hex, no !important
  □ Dark/light mode works (tokens + Tailwind dark:)

Error Handling
  □ Null-safe prop access throughout
  □ Required props guarded — returns null if missing
  □ Array operations guarded: (items ?? []).map()
  □ Try-catch wraps complex logic
  □ console.warn on degradation
  □ Never throws

Accessibility
  □ Semantic HTML elements
  □ Images have alt text
  □ [If interactive] ARIA attributes present
  □ [If interactive] Keyboard operable

Testing
  □ Visual verification: light and dark mode
  □ Browser test coverage: page renders
  □ Unit tests: valid props, no props, null data, missing required

1. Classification & Purpose Model

1.1 Category Taxonomy

Five categories. Every component belongs to exactly one.
CategoryFolderCountPurpose
Primitivesprimitives/29Standalone visual atoms — icons, text, callouts, links, images, math, dividers, a11y
Layoutlayout/23Spatial arrangement — containers, grids, steps, lists, tables, carousels
Contentcontent/25Content formatting and media — code blocks, quotes, video, embeds, API fields, diagrams
Datadata/13Pipeline/API-bound display — blog, forum, Discord, Luma, CoinGecko, showcase
Page Structurepage-structure/23Frame-mode and portal scaffolding — headers, sections, enhanced cards

1.2 Decision Tree

Classify by answering in order. First match wins.
Q1: Bound to an external data source or automation pipeline?
    YES → DATA

Q2: Only makes sense on frame-mode or portal pages?
    YES → PAGE STRUCTURE

Q3: Accepts children and arranges them spatially?
    YES → LAYOUT

Q4: Formats or renders content for the reader?
    YES → CONTENT

Q5: Default → PRIMITIVES
Order rationale: Data and Page Structure are checked first because they’re the most constrained (fewest false positives). Layout’s “accepts children spatially” test is concrete and binary. Content catches everything that formats reader-facing material. Primitives is the default.

1.3 Compositional Tiers

Every component carries a @tier tag. Tiers describe the component’s role in MDX-level composition (not internal dependencies — Mintlify forbids cross-file imports between JSX files).
TierMeaning
primitiveStandalone — used independently, solves one visual need
compositeDesigned to work alongside other components at the MDX page level
patternOrchestrates a page section — backbone of a major page area

1.4 Library Boundary

Inside: snippets/components/ and its five category subdirectories. Every exported component here is governed. Outside (not governed by this framework):
PatternGoverned by
Mintlify globals (Card, Tabs, Accordion, etc.)Mintlify platform
Content snippets (snippets/*.mdx outside components/)Composable Content Structure
Data snippets (snippets/data/, snippets/automations/)Data integration layer
Mintlify enforces that all importable JSX lives under /snippets/. No “section-local components” are possible outside this path.

1.5 Content Affinity

The @contentAffinity field connects components to page types from the Page Taxonomy: landing, overview, tutorial, how_to, concept, reference, faq, troubleshooting, changelog, glossary, universal. This is an affinity signal, not a hard restriction. universal means the component works on any page type.

2. Repo Structure & Documentation Architecture

2.1 Folder Layout

snippets/components/
├── primitives/
│   ├── divider.jsx
│   ├── icons.jsx
│   ├── ...
│   └── examples/
│       ├── divider-examples.mdx
│       └── ...
├── layout/
│   ├── containers.jsx
│   ├── ...
│   └── examples/
├── content/
│   ├── code.jsx
│   ├── video.jsx
│   ├── ...
│   └── examples/
├── data/
│   ├── data.jsx
│   ├── ...
│   └── examples/
├── display/
│   ├── frame-mode.jsx
│   ├── ...
│   └── examples/
└── page-structure/
    ├── heroGif.jsx
    ├── portals.jsx
    └── ...
Six category folders. Flat within each (no tier subfolders). One examples/ subdirectory per category where the category uses runnable examples.

2.2 Naming Conventions

ElementConventionExample
FilesCanonical camelCase .jsx outside display/; display/ keeps kebab-caseframeMode.jsx, heroGif.jsx
ExportsPascalCase, named, arrow functionexport const PageHeader = () => ...
Example fileskebab-case -examples.mdxframe-mode-examples.mdx

2.3 File Granularity

Grouped files with per-export JSDoc. Related components share a file. Each export gets its own 14-field JSDoc block. Non-exported helpers are implementation details.

2.4 Import Paths

Full explicit paths. Mintlify requires file extensions. No barrel imports.
import { CustomDivider } from '/snippets/components/primitives/divider.jsx'
import { StyledTable, TableRow } from '/snippets/components/layout/tables.jsx'

2.5 Documentation Architecture

JSDoc (SOT for metadata + props)

docs-guide/component-registry.json (generated by pre-commit)

Published MDX pages (generated, LLM editorial + template fallback)
Per-folder READMEs are deprecated. The registry and published pages are the two documentation surfaces.

2.6 Registry

Single JSON at docs-guide/component-registry.json. Generated by pre-commit hook when files in snippets/components/ are staged. Contains all 14 governance fields per component plus derived fields (file, importPath).

3. Styling Architecture & Standards

3.1 Three-Layer Hierarchy

Layer 1: Mintlify Theme (docs.json) — platform colours, layout, dark mode toggle
Layer 2: style.css (repo root) — --lp-* CSS custom properties, Mintlify overrides
Layer 3: Component styles — consume tokens, scoped vars if documented
Components consume var(--lp-*) tokens. They rarely define new variables (only for computed/dynamic values, with documentation).

3.2 Token Namespace

All tokens use --lp- prefix: --lp-color-*, --lp-spacing-*, --lp-font-*, --lp-radius-*, --lp-shadow-*, --lp-z-*. Every --lp-color-* token has both light and dark values defined in style.css.

3.3 Banned Patterns (Pre-commit Blocks)

PatternWhy
ThemeDataDeprecated, causes bugs
Hardcoded hex/rgbBreaks dark mode, prevents centralised updates
!importantIf you need it, the hierarchy is wrong

3.4 Advisory Patterns (Code Review Flags)

PatternGuidance
Static inline style={}”Could this be a var(--) token?”
Direct Mintlify class overridesShould go through style.css, not component JSX

3.5 Dark/Light Mode

ConcernMechanism
Colour valuesvar(--lp-color-*) tokens — automatic light/dark swap
Visibility/layout togglesTailwind dark: classes (dark:hidden, hidden dark:block)
JS theme detectionNever. No reading document.documentElement in components.

3.6 Mintlify Override Registry

Overrides in style.css are documented technical debt. Each entry tracks: what’s overridden, which Mintlify limitation necessitates it, introduction date, review date, owner, status. Reviewed quarterly against Mintlify release notes.

4. Component Development Standards

4.1 Props Conventions (7 Rules)

  1. camelCase prop names
  2. is/has prefix for booleans (isCompact, hasIcon)
  3. on prefix for handlers (onClick, onToggle)
  4. Defaults in destructuring (({ variant = 'default' }) => ...)
  5. children for slot content, named props for data
  6. Spread last (<div className={...} {...rest}>)
  7. Required props: no default. Optional: always have default.

4.2 Composition

No inter-component composition. Mintlify does not support nested imports between JSX files. Components are standalone. The MDX page is the composer. Same-file references are allowed (co-located components share scope). Cross-file composition happens only at the MDX page level.

4.3 Accessibility

All components: Semantic HTML, alt text on images, heading hierarchy, descriptive link text. Interactive components (SearchTable, CardCarousel, ShowcaseCards, CopyText, DownloadButton, ScrollBox): additionally require ARIA roles/attributes, keyboard operability (Tab, Enter/Space, Escape), and visible focus indicators.

4.4 Error Handling (Mandatory)

Mintlify has no error boundary per component. A component crash kills the entire page. Defensive rendering is non-negotiable.
RuleImplementation
Null-safe prop accessOptional chaining, default values
Guard required propsReturn null + console.warn if missing
Guard arrays(items ?? []).map(...)
Try-catch complex logicWrap computed values, data transforms
Console.warn on degradationconsole.warn('[ComponentName] reason')
Never throwCatch internally, degrade gracefully
MDX-facing component rule: if a .jsx file is imported directly by a routable MDX page, exported components in that file must not rely on private file-scope helpers. Keep defensive logic inline in the exported component or import it from a colocated non-component .js helper module. Do not hoist the logic into top-level private locals inside the .jsx file.

4.5 Testing (Three Tiers)

TierWhatHowRequired for
Visual verificationLight + dark mode renderingManual, mintlify devAll components
Browser testsPage renders without errorCI, existing infrastructureAll used components
Unit testsProps, edge cases, defensive renderingCI, tests/unit/components/All components
Core unit test cases (every component): renders with valid props, renders with no props, handles null/undefined data, handles missing required props, handles invalid prop types.

5. Documentation & Example Standards

5.1 JSDoc Template

Every exported component carries this block:
/**
 * @component ComponentName
 * @category primitives
 * @tier primitive
 * @status stable
 * @description One-line purpose statement.
 * @contentAffinity landing, overview, tutorial
 * @owner @livepeer/docs-team
 * @dependencies none
 * @usedIn v2/developers/index.mdx
 * @breakingChangeRisk low
 * @decision KEEP
 * @dataSource none
 * @duplicates none
 * @lastMeaningfulChange 2026-03-07
 *
 * @param {string} [variant='default'] - Visual variant. One of: 'default', 'compact'.
 * @param {boolean} [isCompact=false] - Compact rendering mode.
 *
 * @example
 * <ComponentName />
 * <ComponentName variant="compact" />
 */

5.2 Metadata Schema (14 Fields, All Required)

#TagTypeValues
1@componentstringPascalCase export name
2@categoryenumprimitives · layout · content · data · page-structure
3@tierenumprimitive · composite · pattern
4@statusenumstable · experimental · deprecated · broken · placeholder
5@descriptionstringOne-line purpose
6@contentAffinityenum[]Page types from taxonomy + universal
7@ownerstringGitHub handle or team
8@dependenciesstring[]Same-file co-dependencies or none
9@usedInstring[]MDX page paths
10@breakingChangeRiskenumlow · medium · high
11@decisionenumKEEP · MOVE · SPLIT · MERGE · REMOVE
12@dataSourcestringPipeline/API source or none
13@duplicatesstring[]Overlapping components or none
14@lastMeaningfulChangedateISO 8601

5.3 Props Table Format (Published Docs)

PropTypeDefaultRequiredDescription
hrefstringYesDestination URL
iconstring'livepeer'NoBrand icon
Generated from @param tags. Five columns.

5.4 Examples

One rendered MDX example per exported component in {category}/examples/{file}-examples.mdx. Copy-paste ready with import statement. Required for stable components only.

5.5 Published Docs Generation

Fully automated. Zero human maintenance.
  1. Registry generation (pre-commit): JSDoc → component-registry.json
  2. Docs generation (manual/chained): registry + @param + @example + OpenRouter LLM → published MDX pages
  3. LLM failure fallback: template-generated prose from metadata (deterministic)

5.6 Deprecation

/**
 * @status deprecated
 * @deprecated Use GotoCard instead. Scheduled for removal Q3 2026.
 * @see GotoCard
 */
Published docs show deprecation banner. Component section moved to bottom of category page.

6. Lifecycle & Governance

6.1 Lifecycle States

StateMeaningBadge
stableProduction-ready, tested, documentedGreen
experimentalUsable, API may changeYellow
deprecatedScheduled for removal, replacement identifiedRed
brokenKnown defect, do not useRed
placeholderStub/empty, not functionalGrey

6.2 Transitions

Free transitions — any state to any state. Update @status in JSDoc. Document reason in commit message. Registry captures the change automatically.

6.3 Governance Taxonomy

Category-level. One governance label per folder. The current metadata field is owner, but it is taxonomy, not reviewer assignment or gatekeeper authority. Historical GitHub review maps may remain archived for reference, but tests, generated registries, and repair commands are the active contract.

6.4 Modification Rules

No immutability rule. Automated validation and the existing test infrastructure (58-script suite, 17 CI workflows, pre-commit hooks, browser tests) are the gates. Human review is collaborative, not ownership-based.

6.5 Deprecation & Removal

Usage-gated. A deprecated component is removed only when @usedIn is empty — no pages reference it. The registry tracks consumers. Published docs surface remaining consumers as migration prompts.

7. Enforcement Summary

What’s enforced, where, and how.
WhatEnforced byBlocks commit?
14 JSDoc governance fields presentPre-commit validationYes
@param coverage matches propsPre-commit validationYes
@category matches folderPre-commit validationYes
@status is valid enumPre-commit validationYes
@deprecated present when status=deprecatedPre-commit validationYes
No ThemeDataPre-commit regex scanYes
No hardcoded hex/rgbPre-commit regex scanYes
No !importantPre-commit regex scanYes
Registry regenerationPre-commit auto-generationAuto-staged
Static inline stylesCopilot code reviewNo (advisory)
Mintlify class overridesCopilot code reviewNo (advisory)
Props conventionsCode review (human + Copilot)No
Accessibility (ARIA)Code review + unit testsPartially
Defensive renderingUnit testsYes (CI)
Page-level renderingBrowser testsYes (CI)
Visual light/dark modeManual verificationNo

8. Generation Pipeline

8.1 Registry Generation

Trigger:  pre-commit (staged files in snippets/components/)
Script:   tools/scripts/generate-component-registry.js
Input:    all JSDoc blocks in snippets/components/**/*.jsx
Output:   docs-guide/component-registry.json (auto-staged)
Errors:   missing/invalid fields → commit blocked

8.2 Published Docs Generation

Trigger:  manual or chained after registry update
Script:   tools/scripts/generate-component-docs.js
Input:    registry + @param + @example + examples/ MDX files
Process:  OpenRouter LLM for editorial prose, template fallback on failure
Output:   6 published MDX pages (overview + per-category)
Caching:  LLM output cached per component hash

9. Decision Register (All 33 Decisions)

D1: Classification & Purpose Model

#DecisionChoice
1Category taxonomyFive: Primitives, Layout, Content (absorbs Media), Data, Page Structure
2Compositional modelMetadata only — flat files, tier as JSDoc tag
3aMetadata locationJSDoc block in each JSX file
3bMetadata schemaFull 14 fields, all required
4Decision mechanismDecision tree — ordered yes/no, first match wins
5Library boundarysnippets/components/ only

D2: Repo Structure & Documentation Architecture

#DecisionChoice
1Naming conventionscamelCase files, PascalCase exports
2File granularityGrouped files with per-export JSDoc
3Canonical docs homeHybrid — JSDoc SOT for metadata, published MDX SOT for editorial
4Registry formatSingle JSON at docs-guide/component-registry.json
5Generation pipelinePre-commit hook auto-regeneration
6Examples conventionCo-located examples/ per category
7Import pathsFull explicit paths (Mintlify constraint)

D3: Styling Architecture & Standards

#DecisionChoice
1Style system authorityLayered — components may define scoped vars if documented
2Forbidden patternsStrict ban (3) + advisory (2)
3Dark/light modeCSS tokens + Tailwind dark: utility
4Inline stylesAdvisory flag sufficient
5Design tokensRationalise and document with --lp-* namespace
6Mintlify override policyOverride registry — documented, justified, review-dated

D4: Component Development Standards

#DecisionChoice
1Props conventionsStandard 7 rules
2CompositionNo inter-component composition (Mintlify constraint)
3AccessibilitySemantic HTML + ARIA for interactive components
4Error handlingDefensive rendering mandatory — page crash prevention
5TestingVisual + browser + unit tests (three tiers)

D5: Documentation & Example Standards

#DecisionChoice
1JSDoc scopeGovernance + @param + @example
2Props table formatStandard 5-column
3Example requirementsMinimal — one per component, copy-paste ready
4Published docsLLM-generated (OpenRouter) + template fallback, zero maintenance
5Deprecation docs@deprecated + @see pointer

D6: Lifecycle & Governance Model

#DecisionChoice
1Lifecycle statesFive: stable, experimental, deprecated, broken, placeholder
2TransitionsFree — any to any, documented in commit
3Governance taxonomyCategory-level metadata; reviewer routing is not the contract
4ImmutabilityNo rule — code review + tests are the gate
5Deprecation processUsage-gated removal — only when @usedIn is empty

10. Upstream Dependencies

This framework consumes but does not redefine:
DocumentWhat it provides
Page Taxonomy (SOT-00)10 page types — components map via @contentAffinity
Content TaxonomyStructural patterns per page type — components are the rendering layer
Composable Content StructureFour reuse patterns — this framework governs UI components only
Mintlify ConsiderationsPlatform constraints: /snippets/ paths, no nested imports, arrow function syntax

11. Open Items (D8 Audit + D9 Migration)

ItemDeliverable
Classify every component (status, category, tier)D8
Populate all 14 JSDoc fields on every exportD8
Add @param tags for every propD8
Add @example for every stable componentD8
Create example MDX filesD8/D9
Write unit tests for all componentsD8/D9
Audit style.css tokens → rationalise to --lp-*D8
Populate Mintlify override registryD8
Visual verify all components in both themesD8
Create generate-component-registry.jsD9
Create generate-component-docs.js + OpenRouter integrationD9
Extend pre-commit hookD9
Create unit test infrastructureD9
Retire legacy GitHub review mappingD9
Update components.instructions.mdD9
Migrate file locations to target folder structureD9
Rename tokens to --lp-* namespaceD9
Remove ThemeData from all componentsD9
Update all import paths across MDX pagesD9
Create import scanner for @usedIn populationD9
Last modified on March 16, 2026