Documentation Style Guide
.
Not present in production builds. Safe no-op in prod. */
body {
padding: 0 !important;
}
/* Colors Used
#3CB540 - Jade Green
#2b9a66 - Light Green
#18794E - Dark Green
Complementary Greens
See https://coolors.co/004225-1a794e-08a045-3cb540-62ba4f
#004225 - Deep Forrest
#1A794E - Turf Green
#08A045 - Medium Jungle
#3CB540 - Jade Green
#6BBF59 - Moss Green
See https://coolors.co/0c0c0c-073b3a-1a794e-08a045-6bbf59
#0C0C0C - Onyx Black
#073B3A - Dark Teal
#1A794E - Turf Green
#08A045 - Medium Jungle
#6BBF59 - Moss Green
See https://coolors.co/fffffa-073b3a-1a794e-08a045-6bbf59
#FFFFFA - Porcelain
#073B3A - Dark Teal
#1A794E - Turf Green
#08A045 - Medium Jungle
#6BBF59 - Moss Green
Pink Offset Colour
See https://coolors.co/073b3a-1a794e-f61067-08a045-6bbf59
#F61067 - Razzmatazz Pink
#073B3A - Dark Teal
#1A794E - Turf Green
#08A045 - Medium Jungle
#6BBF59 - Moss Green
*/
/* ============================================
GLOBAL THEME VARIABLES
Component governance source of truth
============================================ */
:root {
--lp-color-accent: #3cb540;
--lp-color-accent-strong: #18794e;
--lp-color-accent-soft: #6bbf59;
--lp-color-accent-bright: #5dd662;
--lp-color-accent-brightest: #a0f0a5;
--lp-color-arbitrum: #3ea6f8;
--lp-color-text-primary: #181c18;
--lp-color-text-secondary: #717571;
--lp-color-text-muted: #9ca3af;
--lp-color-bg-page: #ffffff;
--lp-color-bg-card: #f9fafb;
--lp-color-bg-elevated: #f3f6f4;
--lp-color-bg-subtle: rgba(24, 28, 24, 0.04);
--lp-color-bg-overlay: rgba(12, 12, 12, 0.5);
--lp-color-border-default: #e5e7eb;
--lp-color-border-strong: rgba(24, 28, 24, 0.18);
--lp-color-border-inverse: rgba(255, 255, 255, 0.5);
--lp-color-on-accent: #ffffff;
--lp-color-link: #18794e;
--lp-color-link-hover: #004225;
--lp-color-brand-discord: #5865f2;
--lp-color-brand-forum: #00aeef;
--lp-color-brand-github: #181c18;
--lp-color-brand-x: #181c18;
--lp-color-brand-globe: #00c0ff;
--lp-color-brand-twitch: #9048ff;
--lp-color-brand-youtube: #ff0034;
--lp-color-brand-instagram: #dc2275;
--lp-color-brand-linkedin: #0189df;
--lp-color-brand-preview: #b636dd;
--lp-color-brand-coming-soon: #ef1a73;
--lp-color-brand-linux: #ff9a0e;
--lp-color-brand-windows: #14bbf7;
--lp-color-brand-macos: #60ba47;
--lp-color-status-good: #22c55e;
--lp-color-status-warn: #fbbf24;
--lp-color-status-bad: #ef4444;
--lp-spacing-1: 0.25rem;
--lp-spacing-2: 0.5rem;
--lp-spacing-3: 0.75rem;
--lp-spacing-4: 1rem;
--lp-spacing-6: 1.5rem;
--lp-spacing-8: 2rem;
--lp-spacing-px-3: 3px;
--lp-spacing-px-4: 4px;
--lp-spacing-px-6: 6px;
--lp-spacing-px-8: 8px;
--lp-spacing-px-12: 12px;
--lp-font-sans: 'Inter', 'Segoe UI', sans-serif;
--lp-font-mono: 'SFMono-Regular', 'SF Mono', 'Menlo', monospace;
--lp-radius-sm: 0.25rem;
--lp-radius-md: 0.5rem;
--lp-radius-lg: 0.75rem;
--lp-shadow-card: 0 8px 24px rgba(24, 28, 24, 0.08);
--lp-z-base: 1;
--lp-z-overlay: 10;
--lp-z-modal: 50;
/* Legacy aliases maintained during migration */
--accent: var(--lp-color-accent);
--accent-dark: var(--lp-color-accent-strong);
--hero-text: var(--lp-color-text-primary);
--text: var(--lp-color-text-secondary);
--text-secondary: var(--lp-color-text-secondary);
--muted-text: var(--lp-color-text-muted);
--background: var(--lp-color-bg-page);
--card-background: var(--lp-color-bg-card);
--background-highlight: var(--lp-color-bg-subtle);
--border: var(--lp-color-border-default);
--button-text: var(--lp-color-on-accent);
--page-header-description-color: var(--lp-color-text-secondary);
--arbitrum: var(--lp-color-arbitrum);
}
.dark {
--lp-color-accent: #2b9a66;
--lp-color-accent-strong: #18794e;
--lp-color-accent-soft: #3cb540;
--lp-color-accent-bright: #5dd662;
--lp-color-accent-brightest: #7fe584;
--lp-color-text-primary: #e0e4e0;
--lp-color-text-secondary: #a0a4a0;
--lp-color-text-muted: #6b7280;
--lp-color-bg-page: #0d0d0d;
--lp-color-bg-card: #1a1a1a;
--lp-color-bg-elevated: #141a16;
--lp-color-bg-subtle: rgba(255, 255, 255, 0.1);
--lp-color-bg-overlay: rgba(0, 0, 0, 0.5);
--lp-color-border-default: #333333;
--lp-color-border-strong: rgba(255, 255, 255, 0.3);
--lp-color-border-inverse: rgba(255, 255, 255, 0.5);
--lp-color-on-accent: #ffffff;
--lp-color-link: #5dd662;
--lp-color-link-hover: #a0f0a5;
--lp-color-brand-github: #f0f0f0;
/* Legacy aliases maintained during migration */
--accent: var(--lp-color-accent);
--accent-dark: var(--lp-color-accent-strong);
--hero-text: var(--lp-color-text-primary);
--text: var(--lp-color-text-secondary);
--text-secondary: var(--lp-color-text-secondary);
--muted-text: var(--lp-color-text-muted);
--background: var(--lp-color-bg-page);
--card-background: var(--lp-color-bg-card);
--background-highlight: var(--lp-color-bg-subtle);
--border: var(--lp-color-border-default);
--button-text: var(--lp-color-on-accent);
--page-header-description-color: var(--lp-color-text-secondary);
--arbitrum: var(--lp-color-arbitrum);
}
/* ============================================ */
/* Code block themes
hiki codeblock themes:
Popular Dark Themes:
github-dark (what you have now)
github-dark-dimmed
github-dark-high-contrast
dracula
dracula-soft
monokai
nord
one-dark-pro
poimandres
rose-pine
everforest-dark
vitesse-dark
Popular Light Themes:
github-light (what you have now)
github-light-high-contrast
solarized-light
rose-pine-dawn
everforest-light
vitesse-light */
/* img[alt="dark logo"],
img[alt="light logo"] {
max-width: 180px;
} */
/* V2 TEST */
/* a.nav-tabs-item[href="/pages/resources/resources_hub.mdx"],
a.nav-tabs-item[href="/pages/08_help/README"] {
color: rgba(255, 90, 90, 0.342) !important;
} */
/* Make the nav-tabs container full width */
.nav-tabs {
width: 100%;
justify-content: flex-start;
}
/* Fix Mintlify content width and centering.
Regular pages: balance padding + widen inner cap.
Portal/frame pages: balance padding (smaller) + widen inner cap for full-width hero. */
@media (min-width: 1024px) {
/* Regular pages */
#content-container:not(:has(.frame-mode-hero-full)):not(
:has(.frame-mode-container)
) {
padding-left: 3rem !important;
padding-right: 3rem !important;
}
#content-container:not(:has(.frame-mode-hero-full)):not(
:has(.frame-mode-container)
)
> .max-w-5xl {
max-width: 72rem !important;
}
/* Portal/frame pages — tighter balanced padding, wider inner cap */
#content-container:has(.frame-mode-hero-full),
#content-container:has(.frame-mode-container) {
padding-left: 2rem !important;
padding-right: 2rem !important;
}
#content-container:has(.frame-mode-hero-full) > .max-w-5xl,
#content-container:has(.frame-mode-container) > .max-w-5xl {
max-width: 80rem !important;
}
}
#navbar > div.z-10.mx-auto.relative > div.hidden.lg\:flex.px-12.h-12 > div {
column-gap: 2rem !important;
}
a.nav-tabs-item[href*='/internal/'] {
margin-left: 1rem;
margin-right: -1rem;
padding-right: 0;
border-bottom-color: transparent !important;
}
/* .gap-x-6 {
column-gap: 2rem !important;
} */
/* .nav-tabs h-full flex text-sm gap-x-6 {
column-gap: 2rem !important;
} */
/* Push Resource HUB to the right and style as outlined button */
a.nav-tabs-item[href$='/resources/redirect'],
a.nav-tabs-item[href$='/resources/portal'],
a.nav-tabs-item[href$='/07_resources/redirect'],
a.nav-tabs-item[href$='/07_resources/portal'] {
margin-left: auto;
background-color: transparent;
border: 1px solid var(--accent) !important;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.7rem;
height: auto !important;
align-self: center;
margin-right: -2rem;
}
/* Color the text */
/* a.nav-tabs-item[href="/v2/resources/resources_hub"] {
color: #2b9a66 !important;
} */
/* Shrink & color the icon */
a.nav-tabs-item[href$='/resources/redirect'] svg,
a.nav-tabs-item[href$='/resources/portal'] svg,
a.nav-tabs-item[href$='/07_resources/redirect'] svg,
a.nav-tabs-item[href$='/07_resources/portal'] svg,
a.nav-tabs-item[href$='/07_resources/resources_hub'] svg {
height: 0.75rem;
width: 0.75rem;
/* background-color: #2b9a66 !important; */
}
/* Hide the underline on the button */
a.nav-tabs-item[href$='/resources/redirect'] > div:last-child,
a.nav-tabs-item[href$='/resources/portal'] > div:last-child,
a.nav-tabs-item[href$='/07_resources/redirect'] > div:last-child,
a.nav-tabs-item[href$='/07_resources/portal'] > div:last-child,
a.nav-tabs-item[href$='/07_resources/resources_hub'] > div:last-child {
display: none;
}
/* Stack footer links vertically */
#footer .flex-col .flex.gap-4 {
flex-direction: column !important;
gap: 0rem !important;
}
/* Reduce footer padding */
#footer > div {
padding-top: 2rem !important;
padding-bottom: 1rem !important;
}
/* Accessibility: prevent hidden assistant sheet from receiving focus */
#chat-assistant-sheet[aria-hidden='true'] {
display: none !important;
}
/* Accessibility: ensure CTA buttons meet minimum target size */
button.text-left.text-gray-600.text-sm.font-medium {
min-height: 24px;
padding-top: 4px;
padding-bottom: 4px;
}
/* #footer > div > div:first-child {
display: flex;
flex-direction: row !important;
color: red !important;
}
#footer > div > div:first-child > div {
display: flex;
flex-direction: row !important;
color: green !important;
} */
/* Fix bad styling of cards with arrows */
[data-component-part='card-content-container'] {
padding-right: 2.5rem; /* Creates space for the arrow */
}
/* Reposition View component dropdown */
/*
To find the correct selector:
1. Open your page with View components in the browser
2. Right-click on the dropdown in the top-right corner
3. Select "Inspect Element"
4. Find the class name or data attribute
5. Replace the selector below with the actual one
*/
/* Common possible selectors - uncomment and adjust the one that works */
/* Option 1: If it has a data attribute */
/* [data-view-dropdown] {
position: relative !important;
top: 60px !important;
right: 20px !important;
} */
/* Option 2: If it's in a fixed container */
/* .fixed [class*="view"] {
position: relative !important;
top: 60px !important;
} */
/* Option 3: Target by position (fixed elements in top-right) */
/* .fixed.top-0.right-0 [class*="select"],
.fixed.top-0.right-0 [class*="dropdown"] {
position: relative !important;
top: 60px !important;
margin-right: 20px !important;
} */
/* Option 4: Move it inline with content instead of fixed position */
/* Replace 'ACTUAL_SELECTOR' with the real class name from browser inspection */
/* ACTUAL_SELECTOR {
position: static !important;
display: inline-block !important;
margin-bottom: 20px !important;
} */
.code-block > div > div > svg {
background-color: #18794e !important;
}
/* Error 404 Styling */
#error-description > span > div > div {
border: 1px solid #18794e !important;
}
body
> div.relative.antialiased.text-gray-500.dark\:text-gray-400
> div.peer-\[\.is-not-custom\]\:lg\:flex.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:sm\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:md\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:lg\:\!hidden.peer-\[\.is-custom\]\:\[\&\>div\:first-child\]\:xl\:\!hidden
> div.flex.flex-col.items-center.justify-center.w-full.max-w-lg.overflow-x-hidden.mx-auto.py-48.px-5.text-center.\*\:text-center.gap-y-8.not-found-container
> div {
margin-top: -5rem;
}
#error-description
> span
> div
> div
> div.relative.rounded-xl.overflow-hidden.flex.justify-center
> img {
width: 500px;
aspect-ratio: 4 / 3;
object-fit: cover;
/* border: 1px solid #fff; */
}
/* Step List Color Icons Styling */
/* #content > div.steps > div > div.absolute.ml-\[-13px\].py-2 > div {
background-color: #18794e !important;
} */
/* Step List Color Titles */
#content
> div.steps.ml-3\.5.mt-10.mb-6
> div
> div.w-full.overflow-hidden.pl-8.pr-px
> p {
color: #2b9a66 !important;
}
/* View Dropdown */
/* #radix-_R_5slubt9fen9fdb_ */
/* Turn off bg-white in dark mode for multi-view dropdown (PALM THEME BUG) */
.dark .bg-white\/\[0\.95\].multi-view-dropdown-trigger {
background-color: transparent !important;
background: none !important;
}
/* Sidebar collapse button - bigger and easier to click */
/* #sidebar button.absolute {
min-width: 2.5rem !important;
min-height: 2.5rem !important;
padding: 0.75rem !important;
z-index: 100 !important;
} */
/* Override US flag with UK flag in language selector */
/* Hide the original img and use background-image instead */
/* #localization-select-trigger img[alt="US"],
#localization-select-item-en img[alt="US"],
img[alt="US"][src*="flags/US.svg"] {
opacity: 0 !important;
position: relative !important;
}
#localization-select-trigger img[alt="US"]::before,
#localization-select-item-en img[alt="US"]::before,
img[alt="US"][src*="flags/US.svg"]::before {
content: "" !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background-image: url("/snippets/assets/media/images/site/united-kingdom-flag-icon.svg") !important;
background-size: cover !important;
background-position: center !important;
border-radius: 50% !important;
opacity: 1 !important;
} */
/* Hide the panel on frame mode pages (MINTLIFY SUCKS) */
/* Hide empty table of contents layout only when it's empty */
#table-of-contents-layout:empty,
#content-side-layout:has(#table-of-contents-layout:empty) {
display: none;
}
/* DynamicTable: force fixed layout so columnWidths prop values take effect.
Mintlify's Tailwind prose resets table-layout to auto — !important required. */
[data-docs-dynamic-table] {
table-layout: fixed !important;
}
/* StyledTable should sit flush inside its own border shell.
Mint wraps rendered tables in a scroll container with vertical padding,
which creates a false gap above/below the header row. */
[data-docs-styled-table-shell] > div {
padding-top: 0 !important;
padding-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* BorderedBox should own its internal spacing.
Trim default block margins on the first/last rendered child so headings
and paragraphs do not add a false gap inside the padded shell. */
[data-docs-bordered-box] > :first-child {
margin-top: 0 !important;
}
[data-docs-bordered-box] > :last-child {
margin-bottom: 0 !important;
}
[data-docs-bordered-box][data-accent-bar]::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 4px;
background-color: var(--accent-bar-color);
border-radius: inherit;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
/* Frame mode container - 80% of #content-container width, centered */
/* Breaks out of #content padding to center in full #content-container */
.frame-mode-container {
width: calc(100% + 96px + 20px); /* 976px */
margin-left: -96px;
margin-right: -20px;
margin-bottom: 2rem;
padding-left: 15%; /* Adjust this for desired content width */
padding-right: 15%; /* Adjust this for desired content width */
box-sizing: border-box;
}
/* Frame mode container inside hero - already broken out, so reset */
.frame-mode-hero-full .frame-mode-container {
width: 100%;
margin-left: 0;
margin-right: 0;
padding-left: 0%;
padding-right: 0%;
}
/* Pagination on frame mode pages ONLY - match container padding */
[data-page-mode='frame'] #pagination {
width: calc(100% + 96px + 20px);
margin-left: -96px;
margin-right: -20px;
padding-left: calc((100% + 96px + 20px) * 0.1 + 96px);
padding-right: calc((100% + 96px + 20px) * 0.1 + 20px);
box-sizing: border-box;
}
/* Hero full width - breaks out of #content padding to fill #content-container */
.frame-mode-hero-full {
width: calc(100% + 96px + 20px);
margin-left: -96px;
margin-right: -20px;
position: relative;
}
@media (max-width: 1023px) {
.frame-mode-container {
width: 100%;
margin-left: 0;
margin-right: 0;
padding-left: 1rem;
padding-right: 1rem;
}
[data-page-mode='frame'] #pagination {
width: 100%;
margin-left: 0;
margin-right: 0;
padding-left: 1rem;
padding-right: 1rem;
}
.frame-mode-hero-full {
width: 100%;
margin-left: 0;
margin-right: 0;
}
}
#starfield {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
/* Target the card content container */
.frame-mode-hero-full [data-component-part='card-content-container'] {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-left: 1rem;
padding-right: 2.5rem; /* Space for arrow icon (0.75rem right + icon width ~1rem + margin) */
}
/* Target the arrow icon */
.frame-mode-hero-full #card-link-arrow-icon {
top: 0.75rem;
right: 0.75rem;
}
/* #content > div.frame-mode-hero-full > div.frame-mode-container > div > div:nth-child(2) > div > div > div:nth-child(4) > a > div {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
#content > div.frame-mode-hero-full > div.frame-mode-container > div > div:nth-child(2) > div > div > div:nth-child(4) > a > div > #card-link-arrow-icon {
top: 0.75rem;
right: 0.75rem;
} */
/* ============================================
ACCESSIBILITY — Focus indicators
============================================ */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid var(--accent) !important;
outline-offset: 2px;
}
/* ============================================
ACCESSIBILITY — Responsive breakpoints
============================================ */
@media (max-width: 767px) {
.frame-mode-hero-full {
width: 100%;
max-width: 100%;
overflow-x: hidden;
}
}
@media (max-width: 480px) {
#content {
padding-left: 1rem;
padding-right: 1rem;
}
}
/* ============================================
UTILITY CLASSES — inline element styling
Used where components can't replace inline spans
(e.g., inside Mintlify
, components)
============================================ */
.lp-inline-flex {
display: flex;
align-items: center;
}
.lp-text-muted {
color: var(--lp-color-text-secondary);
}
.lp-text-italic-muted {
font-style: italic;
color: var(--lp-color-text-secondary);
}
.lp-inline-flex-gap {
display: flex;
align-items: center;
gap: 0.2rem;
}
.lp-link-underline {
border-bottom: 1.5px solid var(--lp-color-text-primary);
color: var(--lp-color-text-primary);
padding-bottom: 0.25rem;
}
styling conventions, Mintlify-specific gotchas, and best practices for contributing to the Livepeer documentation.
Production-Grade Styling for Mintlify
Framework: CSS Custom Properties (CSS Variables)
Mintlify uses CSS Custom Properties (CSS Variables) as the production-grade styling framework. This is the official, supported approach for theming in Mintlify documentation.
Theme Architecture
The documentation uses CSS Custom Properties defined in style.css at the repository root. This is the ONLY production-grade approach for styling Mintlify documentation.
DO NOT USE:
❌ JavaScript theme objects (ThemeData, themeStyles.jsx) - DEPRECATED
❌ Inline style objects with hardcoded colours
❌ JavaScript-based theme switching
USE INSTEAD:
✅ CSS Custom Properties (--variable-name)
✅ Global CSS variables in style.css
✅ Mintlify theme configuration in docs.json
Colour System
All theme colours are defined as CSS Custom Properties in style.css:
Variable Light Mode Dark Mode Usage --accent#3CB540 (Jade Green)#2b9a66 (Dark Jade)Highlights, icons, links --accent-dark#18794E#18794EStep icons, emphasis --hero-text#181C18#E0E4E0Headings, titles --text#717571#A0A4A0Body text --muted-text#9ca3af#6b7280Secondary text --background#ffffff#0d0d0dPage background --card-background#f9fafb#1a1a1aCards, containers --border#e5e7eb#333333Borders, dividers --button-text#ffffff#ffffffButton text
Using CSS Custom Properties
Inline Styles (Recommended)
< div style = { {
color: "var(--accent)" ,
border: "1px solid var(--border)" ,
background: "var(--card-background)"
} } >
Content
</ div >
Component-Level CSS
export const MyComponent = () => {
return (
<>
< div className = "my-component" >
Content
</ div >
< style > { `
.my-component {
color: var(--text);
background: var(--card-background);
border: 1px solid var(--border);
}
` } </ style >
</>
);
};
Global CSS in style.css
.my-custom-class {
color : var ( --accent );
background : var ( --card-background );
}
How Theme Switching Works
Mintlify automatically adds a .dark class to the <html> element when dark mode is active. CSS variables automatically switch based on the theme:
:root {
--accent : #3CB540 ; /* Light mode */
}
.dark {
--accent : #2b9a66 ; /* Dark mode */
}
No JavaScript required - theme switching is handled entirely by CSS.
Colour Rules
Use Case Approach Brand colours (green) Use --accent or --accent-dark Headings Use --hero-text Body text Use --text Secondary text Use --muted-text Backgrounds Use --background or --card-background Borders Use --border Semantic colours (error, warning, success) Keep fixed (don’t theme) White text on green headers Keep fixed as #fff
Deprecated Approaches
DO NOT USE THESE:
ThemeData Object - snippets/styles/themeStyles.jsx is DEPRECATED
// ❌ WRONG - DO NOT USE
import { ThemeData } from "/snippets/styles/themeStyles.jsx" ;
< div style = { { color: ThemeData . light . accent } } > Content </ div >
Hardcoded Colours - Never hardcode hex values that should adapt to theme
// ❌ WRONG - DO NOT USE
< div style = { { color: "#3CB540" } } > Content </ div >
JavaScript Theme Switching - Not needed, CSS handles this automatically
What NOT to Do
❌ Don’t import or use ThemeData from themeStyles.jsx
❌ Don’t hardcode hex colours that should adapt to theme
❌ Don’t use generic grays without checking theme compatibility
❌ Don’t make semantic colours (trust scores, error states) theme-dependent
❌ Don’t override white text on intentionally coloured backgrounds
❌ Don’t create custom JavaScript theme objects
Styling Framework Architecture
The Livepeer documentation uses a three-layer styling framework designed to work within Mintlify’s constraints while maintaining consistency and maintainability.
Framework Layers
Layer 1: Global CSS (style.css)
Purpose : Theme variables and framework-level overrides only
What belongs here:
✅ CSS Custom Properties (theme variables)
✅ Mintlify component overrides (navigation, footer, etc.)
✅ Frame mode container classes
✅ Utility classes for patterns used 5+ times across pages
What does NOT belong here:
❌ Page-specific styles
❌ Component-specific styles (belong in JSX)
❌ One-off styling needs
Structure:
/* ============================================
THEME VARIABLES (CSS Custom Properties)
============================================ */
:root { /* Light mode */ }
.dark { /* Dark mode */ }
/* ============================================
FRAMEWORK OVERRIDES (Mintlify-specific)
============================================ */
/* Navigation, footer, frame mode containers */
/* ============================================
UTILITY CLASSES (Reusable patterns)
============================================ */
/* Only for patterns used 5+ times across pages */
Layer 2: JSX Components (snippets/components/**/*.jsx)
Purpose : Self-contained components with internal styling
Rules:
✅ Use CSS Custom Properties (var(--accent), var(--text), etc.)
✅ Styles must be within the component file
✅ Use <style> tags for complex styling (pseudo-classes, media queries)
✅ Use inline style objects for simple styling
❌ Import external CSS files
❌ Hardcode theme colours (use CSS variables)
❌ Use className without corresponding <style> tag
Pattern A: Inline Style Objects (Simple components)
export const MyComponent = () => {
return (
< div style = { {
color: "var(--text)" ,
backgroundColor: "var(--card-background)" ,
border: "1px solid var(--border)" ,
padding: "1rem"
} } >
Content
</ div >
);
};
Pattern B: Style Tag (Complex components)
export const MyComponent = () => {
return (
<>
< div className = "my-component" >
Content
</ div >
< style > { `
.my-component {
color: var(--text);
background: var(--card-background);
}
.my-component:hover {
border-color: var(--accent);
}
` } </ style >
</>
);
};
Pattern C: Style Constants (Reusable within component)
export const MyComponent = ({ variant = "default" }) => {
const baseStyle = {
color: "var(--text)" ,
padding: "1rem"
};
const variantStyles = {
default: { border: "1px solid var(--border)" },
accent: { border: "1px solid var(--accent)" }
};
return (
< div style = { { ... baseStyle , ... variantStyles [ variant ] } } >
Content
</ div >
);
};
Layer 3: MDX Files (v2/**/*.mdx)
Purpose : Content only - ZERO inline styles
Rules:
✅ Use component primitives for all styling needs
✅ Use Mintlify global components (Card, Tabs, Steps, etc.)
✅ Import custom components from /snippets/components/
❌ NO inline style={{}} attributes
❌ NO hardcoded colours
❌ NO custom className attributes
Before (❌ WRONG):
< div style = { { display: "flex" , gap: "1rem" } } >
< Card > Content 1 </ Card >
< Card > Content 2 </ Card >
</ div >
After (✅ CORRECT):
import { FlexContainer } from '/snippets/components/wrappers/containers/Layout.jsx' ;
< FlexContainer gap = "1rem" >
< Card > Content 1 </ Card >
< Card > Content 2 </ Card >
</ FlexContainer >
Decision Tree: Where Does This Style Go?
Is it a theme colour? → Add to style.css as CSS Custom Property
Is it used in a component? → Put in JSX component file (inline or <style> tag)
Is it needed in MDX? → Create/use a component primitive
Is it used 5+ times globally? → Add utility class to style.css
Is it page-specific? → Create a component primitive (don’t put in style.css)
Component Primitives Library
For common styling needs in MDX files, use component wrappers:
Layout : FlexContainer, GridContainer (/snippets/components/wrappers/containers/Layout.jsx), Spacer (/snippets/components/elements/spacing/Divider.jsx)
Tables : StyledTable, TableRow, TableCell (/snippets/components/displays/tables/Tables.jsx)
Containers : BorderedBox, CenteredContainer, FullWidthContainer (/snippets/components/wrappers/containers/Containers.jsx)
These are hard constraints imposed by the Mintlify platform. They cannot be worked around.
CSS file loading
Mintlify auto-loads all .css files in the repository root. Any classes defined in any .css file are available globally in all MDX files. The repo uses style.css as the single source of truth for all design tokens and framework overrides.
Mintlify also auto-loads all .js files from the content directory on every page. Be aware of this for security – do not place scripts in the content directory unless they are intended to run on every page.
Tailwind CSS v3
Mintlify ships Tailwind CSS v3 and it is available in both MDX files and JSX components. However:
Arbitrary values are NOT supported – w-[200px], bg-[#3CB540] will not work
Predefined classes only – flex, gap-4, grid-cols-2, p-4, rounded etc.
Responsive prefixes work – md:grid-cols-2, lg:grid-cols-3
Dark mode prefixes work – dark:hidden, dark:block, dark:bg-gray-800
When to use Tailwind: Layout structure (flex, grid, spacing, visibility toggling). Tailwind responsive classes (md:, lg:) are the recommended approach for responsive layouts.
When NOT to use Tailwind: Colours (use CSS Custom Properties instead – Tailwind arbitrary values are not supported, and our colour system uses --lp-* tokens).
Layout shift warning: Mintlify explicitly warns that using the style prop (inline styles) can cause a layout shift on page load , especially on custom mode pages. Use Tailwind CSS classes or CSS Custom Properties in style.css instead of style={{}} to avoid shifts or flickering.
Constraint Detail No CSS imports in JSX JSX component files cannot import .css files. Styles must be inline, in <style> blocks, or in global style.css No JS theme detection Mintlify adds .dark class to <html> automatically. Do not use JavaScript to detect or switch themes JSX cannot import JSX Components cannot import other component files. Import both in the MDX file that uses them Mermaid cannot use CSS variables Mermaid’s theming engine only accepts hex colour values, not var(--) references. Use MermaidColours.jsx for centralised hex definitions Constants inside function bodies All constants in JSX files must be defined inside the function body, not at top level React hooks are global useState, useEffect, useRef etc. are available without import. Do not import ReactMintlify components are global Card, Tabs, Steps, Icon, Badge, Accordion, Columns etc. are available without import. Do not import them
Token Reference
Colour tokens
All theme colours are defined as CSS Custom Properties in style.css. Use --lp-* tokens for new work.
Token Light Dark Usage --lp-color-accent#3CB540#2b9a66Primary accent (buttons, links, highlights) --lp-color-accent-strong#18794E#18794EEmphasis, step icons --lp-color-accent-soft#6BBF59#3CB540Soft accent --lp-color-text-primary#181C18#E0E4E0Headings, titles --lp-color-text-secondary#717571#A0A4A0Body text --lp-color-text-muted#9CA3AF#6b7280Muted/secondary (note: borderline WCAG AA on white – 4.2:1) --lp-color-bg-page#FFFFFF#0d0d0dPage background --lp-color-bg-card#F9FAFB#1a1a1aCard background --lp-color-border-default#E5E7EB#333333Default border --lp-color-on-accent#FFFFFF#FFFFFFText on accent backgrounds --lp-color-arbitrum#3EA6F8#3EA6F8Arbitrum branding --lp-color-status-good#22C55E#22C55ESuccess/positive --lp-color-status-warn#FBBF24#FBBF24Warning --lp-color-status-bad#EF4444#EF4444Error/negative
Legacy aliases (deprecated)
These older aliases still work but should be replaced with --lp-* tokens in new work:
Legacy alias Replacement --accent--lp-color-accent--accent-dark--lp-color-accent-strong--hero-text--lp-color-text-primary--text--lp-color-text-secondary--muted-text--lp-color-text-muted--background--lp-color-bg-page--card-background--lp-color-bg-card--border--lp-color-border-default--button-text--lp-color-on-accent--arbitrum--lp-color-arbitrum
Spacing tokens
Token Value When to use --lp-spacing-10.25remTight gaps (icon-to-text) --lp-spacing-20.5remSmall gaps (badge rows, inline elements) --lp-spacing-30.75remStandard component padding --lp-spacing-41remDefault section spacing --lp-spacing-61.5remComfortable section spacing --lp-spacing-82remLarge section gaps --lp-spacing-px-33pxFine borders, thin lines --lp-spacing-px-44pxSmall borders, button padding --lp-spacing-px-66pxMedium borders --lp-spacing-px-88pxStandard border radius --lp-spacing-px-1212pxLarge border radius
Brand colour tokens
Token Value Platform --lp-color-brand-linux#ff9a0eLinux (orange) --lp-color-brand-windows#14bbf7Windows (blue) --lp-color-brand-macos#60ba47macOS (green)
Component Styling Conventions
These patterns exist across all component files and should be followed when authoring new components.
Style prop merging
Every component accepts style and className props and merges them with defaults:
export const MyComponent = ({ children , style = {}, className = "" , ... rest }) => {
const defaultStyle = { color: "var(--lp-color-text-secondary)" , padding: "1rem" }
return (
< div className = { className } style = { { ... defaultStyle , ... style } } { ... rest } >
{ children }
</ div >
)
}
Variant pattern
Use preset style objects keyed by variant name:
export const MyComponent = ({ variant = "default" , style = {}, ... rest }) => {
const variants = {
default: { border: "1px solid var(--lp-color-border-default)" },
accent: { border: "1px solid var(--lp-color-accent)" },
}
return < div style = { { ... variants [ variant ], ... style } } { ... rest } />
}
Scoped <style> injection
When a component needs pseudo-classes, media queries, or must override Mintlify built-in component styles, use a scoped <style> block with a generated ID:
export const MyComponent = ({ children }) => {
const id = `my-component- ${ Math . random (). toString ( 36 ). slice ( 2 , 8 ) } `
return (
<>
< div id = { id } > { children } </ div >
< style > { `
# ${ id } :hover { border-color: var(--lp-color-accent); }
@media (max-width: 768px) { # ${ id } { padding: 0.5rem; } }
` } </ style >
</>
)
}
Content/Style Separation Quick-Reference
When writing MDX pages, use this table to find the correct approach for common layout needs:
Need Correct approach NOT this Flex row <div className="flex gap-4"> or <FlexContainer>style={{ display: "flex", gap: "1rem" }}Responsive grid <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">style={{ display: "grid" }}Spacing between sections <Spacer size="2rem" /> or className="mt-8"style={{ marginTop: "2rem" }}Bordered container <BorderedBox variant="accent">style={{ border: "1px solid var(--accent)" }}Brand colour text <span style={{ color: "var(--lp-color-accent)" }}>style={{ color: "var(--lp-color-accent)" }}Show/hide for dark mode <div className="block dark:hidden"> / <div className="hidden dark:block">JavaScript theme detection
Rule: Single-property CSS variable usage inline is acceptable (e.g. style={{ color: "var(--lp-color-accent)" }}). Multi-property layout styles must use a component or Tailwind classes.
Icon Usage
How icons render
Source Renders as Colour styleable? Example Lucide/FontAwesome name Inline SVG Yes – color prop works <Icon icon="check" color="var(--lp-color-accent)" />Custom SVG path <img> elementNo – CSS color has no effect <Icon icon="/path/to/icon.svg" />
Icon library: FontAwesome is configured in docs.json ("icons.library": "fontawesome").
Brand icons
Two custom brand icons use a mask-image technique that makes them colour-styleable:
import { ArbitrumIcon , LivepeerIcon } from '/snippets/components/elements/icons/Icons.jsx'
< ArbitrumIcon size = { 16 } color = "var(--lp-color-arbitrum)" />
< LivepeerIcon size = { 16 } color = "var(--lp-color-accent)" />
<Icon icon="ethereum" /> is a standard FontAwesome icon – no custom component needed.
The full semantic icon reference lives at docs-guide/tooling/reference-maps/icon-map.mdx with usage guidance and scan counts.
Badge Usage
Badge colour vocabulary
<Badge> is a Mintlify global component – no import needed.
Colour Meaning greenWorkload: Dual/Realtime AI. Ownership: Livepeer Product. State: confirmed blueWorkload: Video. Ownership: public purpleWorkload: AI. Ownership: commercial product yellowStatus: draft/conditional. Funding: SPE-funded grayConfiguration values, neutral reference metadata surfaceNeutral states, compound icon badges, metadata orangeWarnings, caution states redCritical/destructive actions
Green is context-dependent: in solutions it means “Livepeer Product”, in gateways it means “Dual workload”, in pricing it means “ETH(wei)”. Context determines meaning.
On-chain / off-chain markers
< Icon icon = "link" size = { 16 } /> { /* on-chain */ }
< Icon icon = "floppy-disk" size = { 16 } /> { /* off-chain */ }
Workload type triad
< Badge color = "blue" > Video </ Badge >
< Badge color = "purple" > AI </ Badge >
< Badge color = "green" > Dual </ Badge >
The full badge reference lives at docs-guide/tooling/reference-maps/badge-map.mdx.
Responsive Design
Use Tailwind responsive prefixes for breakpoint-based layouts. These work in both MDX and JSX.
Prefix Breakpoint Viewport (none) < 640pxMobile-first default sm:>= 640pxSmall tablets md:>= 768pxTablets lg:>= 1024pxDesktop xl:>= 1280pxLarge desktop
Test at three viewports minimum: 375px (iPhone SE), 768px (iPad), 1024px+ (Desktop).
Accessibility Checklist
Required for every page
Single H1 – set via frontmatter title: property, not in content
Sequential heading hierarchy – H2 under H1, H3 under H2, no gaps
Descriptive alt text on all images
Meaningful link text – no “click here” or bare URLs
Language declared on code blocks
Required for components
Never remove focus indicators – do not use outline: "none" or outline: 0
Keyboard accessible – all interactive elements reachable via Tab
ARIA labels on icon-only buttons and inputs
Colour contrast – body text minimum 4.5:1, large text minimum 3:1
mint a11y # Full check
mint a11y --skip-contrast # Alt text only
mint a11y --skip-alt-text # Contrast only
Typography
Headings
Use standard Markdown headings (#, ##, ###, etc.) for most content. Mintlify automatically styles these.
For frame mode pages , use custom heading components:
import { H1 , H2 , PageHeader } from "/snippets/components/scaffolding/frame-mode/FrameMode.jsx" ;
< PageHeader > Main Title </ PageHeader >
< H1 > Section Title </ H1 >
< H2 > Subsection Title </ H2 >
Note: Frame mode components use CSS Custom Properties internally - no ThemeData import needed.
Text Styling
Use bold (**text**) for emphasis
Use italic (*text*) sparingly
Use code (backticks) for inline code
Use code blocks for multi-line code
Punctuation
Do not use em dashes in English docs prose. Replace — with spaced en dash – or rewrite the sentence if a comma or colon reads more.
Mathematical Expressions
Mintlify supports LaTeX for rendering mathematical expressions. Use proper syntax to ensure equations render correctly.
Inline Math
Use single dollar signs $...$ for inline mathematical expressions within text:
The voting power is calculated as $V_i = \frac { B_i }{ B_T } $ where $B_i$ is bonded stake.
Result: The voting power is calculated as V i = B i B T V_i = \frac{B_i}{B_T} V i = B T B i where B i B_i B i is bonded stake.
Block Equations
Use double dollar signs $$...$$ for standalone equations on their own line:
$$
R_t = S_t \cdot r_t
$$
Result:
R t = S t ⋅ r t R_t = S_t \cdot r_t R t = S t ⋅ r t
Common LaTeX Syntax
Expression Syntax Result Fractions $\frac{a}{b}$a b \frac{a}{b} b a Subscripts $B_i$B i B_i B i Superscripts $x^2$x 2 x^2 x 2 Greek letters $\alpha, \beta, \theta$α , β , θ \alpha, \beta, \theta α , β , θ Summation $\sum_{i=1}^{n} x_i$∑ i = 1 n x i \sum_{i=1}^{n} x_i ∑ i = 1 n x i Square root $\sqrt{x}$x \sqrt{x} x Proportional $\propto$∝ \propto ∝ Greater/less than or equal $\geq, \leq$≥ , ≤ \geq, \leq ≥ , ≤
Critical: Do NOT Use Backslash Delimiters
WRONG - These will cause MDX errors:
{ /* ❌ WRONG - backslash delimiters break MDX */ }
\( B_i \) for inline math
\[ R_t = S_t \cdot r_t \] for block math
CORRECT - Use dollar sign delimiters:
{ /* ✅ CORRECT - dollar sign delimiters */ }
$B_i$ for inline math
$$ R_t = S_t \cdot r_t $$ for block math
LaTeX Configuration
You can configure LaTeX rendering in docs.json under styles.latex to override automated detection if needed. See Mintlify LaTeX docs for details.
Spacing & Layout
Consistent Spacing
Use consistent spacing between sections
Group related content together
Use dividers (<CustomDivider />) to separate major sections
Page Layouts
Portals - Use CardGroups for key entry points
Guides - Use Steps for sequential instructions
References - Use Tables or Accordions for organised data
Quickstarts - Use Tabs for different paths (OS, on-chain/off-chain)
Component Usage
When to Use Components
Tabs - Separate content by context (OS, workflow type, user type)
Views - Show different content based on operating system or user path
Steps - Sequential instructions for processes
Card Groups - Visual groupings for portals, hubs, and related content
Accordions - Expandable sections for detailed information
Callouts - Important notes, tips, warnings, and information boxes
Callout Types
<Info> - General information and tips
<Tip> - Helpful suggestions
<Warning> - Important cautions
<Danger> - Critical warnings
<Note> - Additional context
Prefer Custom Components for Links and Navigation
Preference : Use custom components for links, cards, quotes, and other visually appealing elements instead of plain Mintlify links.
Why : Custom components provide better visual design, consistent theming, enhanced user experience, and better integration with the Livepeer documentation design system.
Custom Components to Use:
Links : Use <GotoLink> and <GotoCard> instead of plain markdown links or Mintlify <Card> with href
Quotes : Use <Quote> and <FrameQuote> instead of plain blockquotes
Cards : Use <GotoCard> for navigation cards with better styling
Callouts : Use <CustomCallout> and <TipWithArrow> for enhanced visual callouts
External Links : Use <DoubleIconLink> for external links (GitHub, etc.)
Examples:
{ /* ❌ Plain markdown link */ }
[ Getting Started ]( /get-started )
{ /* ✅ Custom component with better styling */ }
import { GotoLink } from '/snippets/components/elements/links/Links.jsx' ;
< GotoLink label = "Getting Started" relativePath = "/get-started" icon = "arrow-right" />
{ /* ❌ Plain blockquote */ }
> This is a quote
{ /* ✅ Custom quote component with attribution */ }
import { FrameQuote } from '/snippets/components/displays/quotes/Quote.jsx' ;
< FrameQuote author = "John Doe" source = "Livepeer Blog" href = "https://livepeer.org/blog" >
This is a quote with better visual design.
</ FrameQuote >
{ /* ❌ Basic Mintlify Card with href */ }
< Card title = "API Reference" href = "/api/reference" >
API documentation
</ Card >
{ /* ✅ Custom GotoCard with enhanced styling */ }
import { GotoCard } from '/snippets/components/elements/links/Links.jsx' ;
< GotoCard
label = "API Reference"
relativePath = "/api/reference"
icon = "book"
text = "Complete API documentation with examples"
cta = "View Docs"
/>
When to Use Plain Links:
Inline links within paragraphs (markdown links are fine)
Links in code examples or technical references
Links that don’t need visual emphasis
See the Component Library for all available custom components.
Mintlify Overrides & Best Practices
Our styling framework intentionally overrides some Mintlify default recommendations to work better within Mintlify’s constraints and maintain consistency.
Override: “Use Tailwind classes”
Mintlify suggests : Use Tailwind utility classes
Our approach : ❌ Don’t use Tailwind - use component primitives
Reason : Tailwind classes in MDX create maintenance burden and reduce semantic meaning. Component primitives are more maintainable and self-documenting.
Example:
{ /* ❌ Don't use Tailwind */ }
< div className = "flex gap-4 items-center" >
< Card > Content </ Card >
</ div >
{ /* ✅ Use component primitives */ }
< FlexContainer gap = "1rem" align = "center" >
< Card > Content </ Card >
</ FlexContainer >
Override: “Inline styles are fine for quick fixes”
Mintlify suggests : Inline styles acceptable in MDX
Our approach : ❌ No inline styles in MDX, only in JSX components
Reason : Consistency and maintainability. Inline styles in MDX make it harder to maintain theme consistency and create visual inconsistencies.
Example:
{ /* ❌ Don't use inline styles in MDX */ }
< div style = { { display: "flex" , gap: "1rem" } } >
< Card > Content </ Card >
</ div >
{ /* ✅ Use component primitives */ }
< FlexContainer gap = "1rem" >
< Card > Content </ Card >
</ FlexContainer >
Override: “Use global CSS for everything”
Mintlify suggests : Put all styles in style.css
Our approach : ✅ Only theme variables and framework overrides in style.css
Reason : Mintlify only allows one global CSS file. Putting everything there makes it unmaintainable. Component-specific styles belong in JSX components.
What goes in style.css:
✅ Theme variables (CSS Custom Properties)
✅ Mintlify component overrides (nav, footer)
✅ Frame mode container classes
✅ Utility classes used 5+ times globally
What does NOT go in style.css:
❌ Component-specific styles (put in JSX)
❌ Page-specific styles (create component primitives)
❌ One-off styling needs (create component primitives)
Override: “Component styles can be external”
Mintlify suggests : External CSS files for components
Our approach : ❌ Styles must be within JSX component files
Reason : Mintlify doesn’t support CSS imports in components reliably. Inline styles and <style> tags within components work consistently.
Pattern:
{ /* ✅ Styles within component */ }
export const MyComponent = () => {
return (
<>
< div className = "my-component" > Content </ div >
< style > { `
.my-component {
color: var(--text);
}
` } </ style >
</>
);
};
Mintlify Gotchas & Limitations
Critical Limitations
1. Import Paths Must Be Absolute
// ✅ Correct - absolute path from repo root
import { MyComponent } from '/snippets/components/MyComponent.jsx' ;
// ⚠️ Avoid - relative paths resolve but are harder to validate and grep
import { MyComponent } from '../components/MyComponent.jsx' ;
2. File Extensions Required
// ✅ Include extension
import { Component } from '/snippets/Component.jsx' ;
// ❌ May not resolve
import { Component } from '/snippets/Component' ;
3. Cannot Import into Component Files
You CANNOT import data or other components into a JSX component file:
// ❌ WRONG - This will fail
// snippets/components/MyComponent.jsx
import { themeColor } from '/snippets/styles/themeStyles.jsx' ;
export const MyComponent = () => {
return < div style = { { color: themeColor . light . accent } } > Hello </ div > ;
};
Solution: Import in the MDX file that uses the component:
// ✅ CORRECT
// MyPage.mdx
import { MyComponent } from '/snippets/components/MyComponent.jsx' ;
import { ThemeData } from '/snippets/styles/themeStyles.jsx' ;
< MyComponent />
// MyComponent can access ThemeData from parent scope
4. JSX Files Cannot Import Other JSX Files
Mintlify does not allow JSX files to import other JSX files. This is why we use MDX-in-MDX patterns instead.
5. MDX Scope Inheritance
When importing MDX files into other MDX files:
✅ Child MDX inherits parent scope for props - Parent’s imports work when used as component props
❌ Child MDX may NOT inherit parent scope for direct JSX interpolation - Variables used as {variable} may need re-import
✅ Child can import its own variables - If the child needs something the parent doesn’t import
Example:
// Parent.mdx
import { DOCKER_CODE } from '/snippets/data/gateways/code.jsx'
import ChildView from '/snippets/pages/ChildView.mdx'
< ChildView />
// ChildView.mdx
{ /* Can use DOCKER_CODE as props */ }
< CustomCodeBlock { ... DOCKER_CODE . install } /> { /* ✅ Works */ }
{ /* But direct interpolation may need re-import */ }
< Badge > { latestVersion } </ Badge > { /* ❌ May need import */ }
6. React Hooks Are Global
Mintlify provides React hooks globally - no imports needed:
// ✅ Works - hooks available without import
export function MyComponent () {
const [ count , setCount ] = useState ( 0 );
useEffect (() => { /* ... */ }, []);
return < div > { count } </ div > ;
}
// ❌ Not needed - will cause errors
import React , { useState , useEffect } from 'react' ;
7. Icon Component Behaviour
CRITICAL: Mintlify’s <Icon> component renders custom icons as <img alt="Image"> elements, NOT inline SVG.
// ❌ This will NOT work - color styling has no effect
< span style = { { color: "#3CB540" } } >
< Icon icon = "/path/to/icon.svg" size = { 20 } />
</ span >
Solution: Use theme-aware SVG files with internal CSS, or use different files for each theme.
8. Mintlify Global Components
These components are available globally - do not import them :
React, Frame, Card, Icon, Steps, Step, Tabs, Tab
Note, Warning, Info, Tip, Danger
Accordion, Columns, CardGroup, CodeBlock, Expandable, Badge, Tooltip
// ✅ Correct - use directly
< Card title = "Title" > Content </ Card >
< Tabs >
< Tab title = "Tab 1" > Content </ Tab >
</ Tabs >
// ❌ Wrong - don't import
import { Card , Tabs } from "@mintlify/components" ;
CRITICAL: Mintlify global components cannot be stored in variables - they must be used directly as JSX:
// ❌ WRONG - Will cause "ReferenceError: Expandable is not defined"
const componentMap = {
expandable: Expandable ,
accordion: Accordion
};
const Component = componentMap [ component ];
// ✅ CORRECT - Use conditional rendering with direct JSX
if ( component === "expandable" ) {
return < Expandable { ... props } > { content } </ Expandable > ;
}
return < Accordion { ... props } > { content } </ Accordion > ;
CRITICAL: JSX comments ({/* */}) in MDX files do NOT prevent MDX from parsing the content inside them. MDX will still try to evaluate JSX components and expressions within comments.
{ /* ❌ WRONG - MDX will still try to parse CustomCodeBlock */ }
{ /*
<CustomCodeBlock codeString="test" />
*/ }
{ /* ✅ CORRECT - Remove the entire section, don't comment it */ }
{ /* Code components temporarily unavailable - see component-bugs.md */ }
If you need to temporarily disable a component section:
Remove the entire section from the MDX file
Add a comment explaining why it was removed
Document in docs/PLAN/errors/component-bugs.md if it’s a component bug
Do NOT use JSX comments to “comment out” component usage
9. Frame Mode Limitations
Frame mode (mode: frame in frontmatter) removes all default Mintlify styling. When using frame mode:
Default markdown headings may not render correctly
Use custom heading components from frameMode.jsx
All styling must be custom
Mintlify components still work but lose default styles
Keep responsive layout primitives in style.css (.frame-mode-container, .frame-mode-hero-full, frame pagination)
Keep portal/page-specific structure in shared JSX components (for example /snippets/components/scaffolding/portals/Portals.jsx)
Do not use fixed breakout constants directly in component styles (for example hardcoded 96px, 20px, or fixed % widths)
Prefer CSS variables + breakpoints for frame mode layout, and CSS custom properties (var(--...)) for theming
Import Patterns
Correct Pattern: Import in MDX, Use in Component
// ✅ MyPage.mdx
import { MyComponent } from '/snippets/components/MyComponent.jsx' ;
import { DOCKER_CODE } from '/snippets/data/gateways/code.jsx' ;
< MyComponent />
// ✅ MyComponent.jsx - uses CSS Custom Properties (production-grade)
export const MyComponent = () => {
return (
< div style = { { color: "var(--accent)" } } >
< CustomCodeBlock { ... DOCKER_CODE . install } />
</ div >
);
};
Git Workflow
Branch Management
ALWAYS create a new branch from docs-v2:
git checkout docs-v2
git pull
git checkout -b docs-plan/XX-task-name
Never work directly on:
docs-v2 (active docs branch)
main or master
Any branch another agent is using
Branch naming: Use pattern docs-plan/XX-task-name where XX is the task number.
Best Practices
Code Organisation
Keep components in /snippets/components/ organised by purpose:
primitives/ - Basic UI elements
layout/ - Layout components
display/ - Media and embeds
content/ - Content display
integrations/ - External services
domain/ - Domain-specific components
Keep data in /snippets/data/ for reusable code strings and variables
Use /snippets/pages/ for modular MDX content that’s imported into main pages
Writing Style
Be Clear and Concise - Write for users with varying technical backgrounds
Use Examples - Include code examples and real-world scenarios
Provide Context - Explain why, beyond how
Link Related Content - Help users discover related information
Test Both Themes - Verify content looks good in both light and dark modes
Component Guidelines
Use CSS Custom Properties ONLY - Never use ThemeData or hardcode colours
Reference Variables from style.css - All theme colours are in style.css as CSS variables
Test Components - Ensure components render correctly
Handle Children Properly - Always handle children as arrays when mapping
Document Props - Include JSDoc comments for component props
Provide Examples - Add examples in the examples/ folder for each component
Component Immutability
CRITICAL RULE: Components in snippets/components/ are IMMUTABLE
NEVER modify files in snippets/components/ - These components are used across many pages. Any changes could break existing functionality.
Allowed:
Creating new components
Modifying MDX files that use components
Fixing MDX imports and usage
Forbidden:
Modifying existing component files
Changing component function signatures
Adding/removing component exports
Changing component logic
Exception: Only if explicitly requested by user AND after confirming impact assessment.
If a component appears to have a bug:
Comment out the component section in the MDX file where it’s used
Verify the page renders without that section
If page renders correctly → Component is the issue
Document the error in docs/PLAN/errors/component-bugs.md with:
Component name and file path
Error message from console
Page where error occurs
Verification that commenting out fixes the page
Recommendation for component fix (but do not implement)
DO NOT fix the component - Components are immutable without explicit user permission.
File Naming
Use kebab-case for file names: my-component.mdx
Use PascalCase for component names: MyComponent
Use descriptive names that indicate purpose
Testing Checklist
Before submitting documentation:
Content renders correctly in dark mode (default)
Content renders correctly in light mode
All links work and point to correct pages
Code examples are accurate and tested
Images load and have appropriate alt text
Components use theme-aware colours
No hardcoded colours that should adapt to theme
Components render correctly
No console errors in browser dev tools
MDX syntax errors checked and fixed
All pages verified in headless browser (see Verification Requirements below)
Verification Requirements
MDX Syntax Checking
Before declaring work complete, check MDX files:
Use linting tools to check all modified MDX files
Check for:
Unclosed JSX tags
Invalid import syntax
Missing frontmatter
Syntax errors
Fix any MDX errors before considering work complete
Headless Browser Verification
Before declaring work complete, verify each page in a headless browser:
Use Puppeteer or similar tool to load each page
Wait for network idle
Check for console errors (filtering out test script artifacts)
Verify content length > 500 chars
Verify H1 element exists
Check for 404 errors
Filter out false positives:
Ignore “require is not defined” from test scripts
Ignore “puppeteer” related errors
Ignore “fs has already been declared” errors
Focus on actual component errors
Report must show:
Page URL
Content length
H1 text
List of actual console errors (if any)
Status: ✅ OK or ❌ ERRORS
URL Structure Verification
Mintlify pages use full path structure:
Page path in docs.json: v2/resources/documentation-guide/component-library/primitives
URL: /v2/resources/documentation-guide/component-library/primitives
Do not assume URL patterns - verify by testing actual URLs.
Mintlify Theme Configuration
Mintlify also supports theme configuration in docs.json:
{
"theme" : "palm" ,
"colors" : {
"primary" : "#3CB540" ,
"light" : "#2b9a66" ,
"dark" : "#3CB540"
}
}
This controls Mintlify’s built-in components (buttons, links, etc.). For custom styling, always use CSS Custom Properties in style.css.
Pre-Commit Hooks
This repository uses Git pre-commit hooks to automatically enforce the style guide rules. These hooks are mandatory and will block commits that violate the style guide.
What Gets Checked
The pre-commit hooks automatically check for:
Deprecated ThemeData Usage - Blocks imports of ThemeData from snippets/styles/themeStyles.jsx
Hardcoded Theme Colours - Warns about direct hex colour codes that should use CSS Custom Properties
Relative Snippets Imports - Flags imports from snippets/ that use relative paths instead of absolute paths
Unnecessary Imports - Warns about explicit imports for Mintlify’s globally available components and React hooks
Syntax Validation - Checks MDX, JSON, Shell, and JavaScript syntax
Browser Validation - Tests that MDX pages actually render correctly in a headless browser (if mint dev is running)
Installation
MANDATORY : You must install the hooks before making any commits:
What Happens on Violation
If you attempt to commit code that violates the style guide:
The commit is blocked
You receive a detailed error message listing all violations
You must fix the violations before committing again
Protected .allowlist Edits (Human-Only)
The .allowlist file is protected by pre-commit checks.
If a human intentionally needs to edit .allowlist, use:
git commit -m "Update .allowlist" --trailer "allowlist-edit=true"
This override still runs all other pre-commit checks.
If a human intentionally needs to allow file deletions, use:
git commit -m "Remove obsolete files" --trailer "allow-deletions=true"
This deletion override also runs all other pre-commit checks.
Example Error Output
╔═══════════════════════════════════════════════════════════════╗
║ STYLE GUIDE VIOLATIONS DETECTED - COMMIT BLOCKED ║
╚═══════════════════════════════════════════════════════════════╝
Found 2 violation(s):
❌ my-component.jsx: Uses deprecated ThemeData - use CSS Custom Properties instead
⚠️ my-page.mdx: Contains hardcoded theme colors - use CSS Custom Properties (var(--accent), etc.)
📖 MANDATORY: Read the Style Guide before committing:
v2/resources/documentation-guide/style-guide.mdx
Browser Validation
The hooks include headless browser validation that tests MDX files actually render in the browser. This catches:
Runtime errors in components
Failed imports
Console errors
Render failures
Note : Browser validation requires mint dev to be running. If it’s not running, the check is skipped (doesn’t block commit).
Comprehensive Test Suite
The repository includes a comprehensive test suite that validates all style guide rules and more:
Running Tests
# Run all tests
npm test
# Run specific test suites
npm run test:style # Style guide validation
npm run test:mdx # MDX syntax validation
npm run test:spell # UK English spelling
npm run test:quality # Quality checks (alt text, links, frontmatter)
npm run test:browser # Browser rendering tests
What Gets Tested
Style Guide Tests (test:style):
CSS Custom Properties usage (no ThemeData, no hardcoded colours)
No inline styles in MDX files
No em dashes in English v2 docs prose (use – or rewrite the sentence)
No Tailwind classes
Absolute import paths
File naming conventions
Component immutability warnings
MDX Validation (test:mdx):
Frontmatter validation
Unclosed JSX tags
Invalid import syntax
MDX scope inheritance issues
Spelling Tests (test:spell):
UK English spelling validation
Custom dictionary for technical terms (Livepeer, Arbitrum, etc.)
Excludes code blocks and frontmatter
Quality Checks (test:quality):
Image alt text presence
Frontmatter completeness
Internal link validation
SEO metadata validation
Browser Tests (test:browser):
Page rendering in headless browser
Console error detection
Theme testing (light/dark)
Content validation (H1, content length)
The test suite runs automatically in pre-commit hooks (staged/fast mode) and in CI/CD.
In pull request CI, static checks are changed-file scoped for blocking, while browser sweeps keep full-route coverage.
For full details on the hooks, see the Git Hooks Documentation .
Resources
Next Steps
Last modified on April 8, 2026