Relevant source files
The following files were used as context for generating this wiki page:
- packages/base-ui/css/preset.css
- packages/radix-ui/css/preset.css
- packages/radix-ui/css/vitepress.css
- packages/base-ui/css/vitepress.css
- packages/tailwind/src/typography/styles.ts
- packages/shadcn/src/manual-installation.ts
- packages/tailwind/src/typography/index.ts
- packages/preview/src/styles/globals.css
- packages/radix-ui/css/preset-legacy.css
- packages/openapi/css/preset.css
- packages/base-ui/css/style.css
- packages/radix-ui/css/style.css
- packages/api-docs/css/preset.css
- packages/base-ui/css/solar.css
- packages/preview/src/pages/_root.tsx
- packages/radix-ui/css/solar.css
- packages/asyncapi/css/preset.css
- packages/radix-ui/css/catppuccin.css
- packages/base-ui/css/shadcn.css
- packages/python/styles/preset.css
- packages/radix-ui/css/shadcn.css
- packages/radix-ui/css/lib/shiki.css
- packages/base-ui/css/catppuccin.css
- packages/base-ui/css/lib/shiki.css
- packages/preview/waku.config.ts
- packages/base-ui/tsdown.config.ts
- packages/radix-ui/src/tailwind/typography.ts
- packages/base-ui/src/tailwind/typography.ts
- packages/radix-ui/css/ruby.css
- packages/base-ui/css/lib/base.css
Overview
Fumadocs provides a comprehensive styling system built around Tailwind CSS, custom design tokens, and modular stylesheets that configure layouts, typography, and code blocks across base-ui and radix-ui packages. This architecture bridges component primitives with preconfigured or custom theme variants such as Catppuccin, Shadcn, Vitepress, Solar, and Ruby.
Sources: packages/base-ui/css/preset.css:1-11, packages/radix-ui/css/preset.css:1-11, packages/radix-ui/css/vitepress.css:1-40, packages/radix-ui/css/catppuccin.css:1-39, packages/radix-ui/css/solar.css:1-16, packages/radix-ui/css/ruby.css:1-39, packages/radix-ui/css/shadcn.css:1-21
Preset Stylesheets and Base CSS
Preset CSS Structures
The styling architecture relies on preset stylesheets located inside the base-ui and radix-ui packages packages/base-ui/css/preset.css, packages/radix-ui/css/preset.css. Each preset consolidates base imports, typography plugins, and generated layout modules into a single entry point.
| Preset File | Primary Imports & Plugins | Layout / Generated Modules |
|---|---|---|
packages/base-ui/css/preset.css | ./lib/base.css, ./lib/shiki.css, ../dist/tailwind/typography.js | flux.css, docs.css, home.css, notebook.css, shared.css |
packages/radix-ui/css/preset.css | ./lib/base.css, ./lib/shiki.css, ../dist/tailwind/typography.js | docs.css, home.css, notebook.css, flux.css, shared.css |
packages/radix-ui/css/preset-legacy.css | ./lib/base.css, ./lib/shiki.css, ../dist/tailwind/typography.js | docs.css, home.css, notebook.css, flux.css, shared.css, legacy theme block |
Sources: packages/base-ui/css/preset.css:1-11, packages/radix-ui/css/preset.css:1-11, packages/radix-ui/css/preset-legacy.css:1-22
Base CSS Primitives and Animation Keyframes
The core imported primitives in packages/base-ui/css/lib/base.css define direction-aware drawer offsets, theme variables, and custom animation keyframes packages/base-ui/css/lib/base.css.
@theme {
--animate-fd-fade-in: fd-fade-in 300ms ease;
--animate-fd-fade-out: fd-fade-out 300ms ease;
--animate-fd-dialog-in: fd-dialog-in 300ms cubic-bezier(0.16, 1, 0.3, 1);
--animate-fd-dialog-out: fd-dialog-out 300ms cubic-bezier(0.16, 1, 0.3, 1);
--animate-fd-popover-in: fd-popover-in 100ms ease;
--animate-fd-popover-out: fd-popover-out 100ms ease;
--animate-fd-sidebar-in: fd-sidebar-in 250ms ease;
--animate-fd-sidebar-out: fd-sidebar-out 250ms ease;
@keyframes fd-sidebar-in {
from {
transform: translateX(var(--fd-sidebar-drawer-offset));
}
}
@keyframes fd-sidebar-out {
to {
transform: translateX(var(--fd-sidebar-drawer-offset));
}
}
@keyframes fd-dialog-in {
from {
transform: scale(1.06);
opacity: 0;
}
to {
transform: scale(1);
}
}
@keyframes fd-dialog-out {
from {
transform: scale(1);
}
to {
transform: scale(1.04);
opacity: 0;
}
}
@keyframes fd-popover-in {
from {
opacity: 0;
transform: scale(0.9);
}
}
@keyframes fd-popover-out {
to {
opacity: 0;
transform: scale(0.9);
}
}
@keyframes fd-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fd-fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
}Sources: packages/base-ui/css/lib/base.css:9-85
Note
The sidebar drawer offset variable --fd-sidebar-drawer-offset switches dynamically between 100% and -100% based on the document direction attribute [dir='rtl'] to ensure slide transitions occur in the correct inline orientation.
Sources: packages/base-ui/css/lib/base.css:1-8
Legacy Custom Theme Variables and Utilities
The legacy Radix UI preset provides specific theme tokens and utilities that define container widths, layout offsets, and custom scrollbar or step counters packages/radix-ui/css/preset-legacy.css, packages/base-ui/css/lib/base.css.
@theme {
--spacing-fd-container: 1400px;
--fd-page-width: 1200px;
--fd-sidebar-width: 0px;
--fd-toc-width: 0px;
--fd-layout-width: 1600px;
--fd-banner-height: 0px;
--fd-nav-height: 0px;
--fd-tocnav-height: 0px;
}
@layer base {
:root,
#nd-docs-layout {
--fd-layout-offset: max(calc(50vw - var(--fd-layout-width) / 2), 0px);
}
}
@utility container {
margin-inline: auto;
padding-inline: 1rem;
@apply 2xl:max-w-(--spacing-fd-container);
}
/* Apply styles to :root */
@custom-variant on-root {
:root:has(&) {
@slot;
}
}Sources: packages/radix-ui/css/preset-legacy.css:13-43
Warning
When using legacy layout definitions, the root layout offset calculation utilizes calc(50vw - var(--fd-layout-width) / 2), clamping values at 0px to handle wide-screen alignment correctly.
Sources: packages/radix-ui/css/preset-legacy.css:24-29
Theme Color Palettes
Catppuccin Theme
The Catppuccin theme variant establishes distinct pastel and deep background color tokens inside both packages/base-ui/css/catppuccin.css and packages/radix-ui/css/catppuccin.css packages/base-ui/css/catppuccin.css, packages/radix-ui/css/catppuccin.css. Each variant imports ./lib/default-colors.css and configures custom HSL color properties for both light mode (@theme) and dark mode (.dark) along with specialized sidebar variables packages/base-ui/css/catppuccin.css, packages/radix-ui/css/catppuccin.css.
@theme {
--color-fd-popover: hsl(220, 22%, 92%);
--color-fd-popover-foreground: hsl(234, 16%, 35%);
--color-fd-secondary-foreground: hsl(234, 16%, 35%);
--color-fd-border: hsl(223, 16%, 83%);
--color-fd-primary: hsl(266, 85%, 58%);
--color-fd-primary-foreground: hsl(220, 22%, 92%);
--color-fd-muted: hsl(220, 22%, 92%);
--color-fd-card: hsl(220, 22%, 92%);
--color-fd-accent: hsl(223, 16%, 83%);
--color-fd-accent-foreground: hsl(234, 16%, 35%);
--color-fd-card-foreground: hsl(234, 16%, 35%);
--color-fd-muted-foreground: hsl(233, 10%, 47%);
--color-fd-foreground: hsl(234, 16%, 35%);
--color-fd-secondary: hsl(220, 22%, 92%);
--color-fd-background: hsl(220, 23%, 95%);
--color-fd-ring: hsl(267, 84%, 81%);
}Sources: packages/base-ui/css/catppuccin.css:3-20
Note
In Catppuccin dark mode, #nd-sidebar overrides both --color-fd-secondary and --color-fd-muted to hsl(237, 16%, 23%), matching the dark border and accent tone.
Sources: packages/base-ui/css/catppuccin.css:46-49, packages/radix-ui/css/catppuccin.css:46-49
Shadcn and Vitepress Themes
The Shadcn theme variant maps Fumadocs CSS variables directly to underlying --background, --foreground, --muted, --popover, --card, --border, --primary, --secondary, --accent, and --ring properties across :root and .dark selectors packages/base-ui/css/shadcn.css, packages/radix-ui/css/shadcn.css. Additionally, it configures #nd-sidebar bindings and declares a static theme variable --color-fd-error: var(--destructive); packages/base-ui/css/shadcn.css, packages/radix-ui/css/shadcn.css.
#nd-sidebar {
background-color: var(--sidebar);
color: var(--sidebar-foreground);
border-color: var(--sidebar-border);
--color-fd-accent: var(--sidebar-accent);
--color-fd-accent-foreground: var(--sidebar-accent-foreground);
--color-fd-ring: var(--sidebar-ring);
--color-fd-primary-foreground: var(--sidebar-primary-foreground);
}
@theme static {
--color-fd-error: var(--destructive);
}Sources: packages/base-ui/css/shadcn.css:23-36, packages/radix-ui/css/shadcn.css:23-36
The Vitepress theme variant defines explicit neutral HSL values for light and dark modes, customizes prose links and code background color-mix styles, and modifies #nd-sidebar background colors packages/base-ui/css/vitepress.css, packages/radix-ui/css/vitepress.css.
--color-fd-background: hsl(0, 0%, 100%);
--color-fd-card: hsl(0, 0%, 100%);
--color-fd-foreground: hsl(240, 6%, 25%);
--color-fd-muted-foreground: hsl(240, 6%, 50%);
--color-fd-secondary: hsl(240, 6%, 97%);
--color-fd-accent-foreground: hsl(240, 6%, 25%);
--color-fd-primary: hsl(226, 55%, 45%);
}Sources: packages/base-ui/css/vitepress.css:14-21, packages/radix-ui/css/vitepress.css:14-21
Solar and Ruby Themes
The Solar theme variant utilizes OKLCH color definitions for primary and article tokens alongside strict letter-spacing adjustments and responsive layout rules packages/base-ui/css/solar.css, packages/radix-ui/css/solar.css. Under @variant md, Solar injects absolute pseudo-elements before #nd-page with rounded borders, shadows, and margins for both #nd-docs-layout and #nd-notebook-layout packages/base-ui/css/solar.css, packages/radix-ui/css/solar.css.
.dark {
--color-fd-primary: oklch(0.902 0.0461 259.51);
--color-fd-article: hsl(0, 0%, 7.04%);
--color-fd-background: hsl(0, 0%, 5.04%);
}Sources: packages/base-ui/css/solar.css:12-16, packages/radix-ui/css/solar.css:12-16
The Ruby theme variant implements deep crimson, rose, and pink palettes tailored for light and dark environments packages/radix-ui/css/ruby.css. It establishes primary colors using hsl(348, 85%, 45%) in light mode and hsl(348, 90%, 62%) in dark mode, with custom translucent border alphas set to 0.25 packages/radix-ui/css/ruby.css.
@theme {
--color-fd-background: hsl(0, 0%, 98%);
--color-fd-foreground: hsl(350, 45%, 14%);
--color-fd-primary: hsl(348, 85%, 45%);
--color-fd-primary-foreground: hsl(0, 0%, 100%);
--color-fd-border: hsla(348, 40%, 60%, 0.25);
--color-fd-ring: hsl(348, 85%, 45%);
--color-fd-accent: hsl(348, 70%, 95%);
--color-fd-accent-foreground: hsl(348, 70%, 22%);
--color-fd-muted: hsl(0, 0%, 94%);
--color-fd-muted-foreground: hsl(348, 25%, 42%);
--color-fd-secondary: hsl(348, 55%, 92%);
--color-fd-secondary-foreground: hsl(348, 50%, 20%);
--color-fd-card: hsl(0, 0%, 100%);
--color-fd-card-foreground: hsl(350, 45%, 14%);
--color-fd-popover: hsl(0, 0%, 100%);
--color-fd-popover-foreground: hsl(350, 45%, 14%);
}Sources: packages/radix-ui/css/ruby.css:3-20
Caution
When integrating the Solar theme, ensure that table of contents popover positioning rules calculate top offsets using --fd-docs-row-1 or --fd-docs-row-2 variables plus 2.5 * var(--spacing), or popover headers will detach from trigger elements.
Sources: packages/base-ui/css/solar.css:34-68, packages/radix-ui/css/solar.css:34-68
Tailwind Typography Plugin Engine
Selector Manipulation and Scoping
The typography plugin architecture in packages/tailwind dynamically generates scoped prose component styles and custom Tailwind variants packages/tailwind/src/typography/index.ts. Built with Tailwind's plugin.withOptions, the plugin accepts an optional configuration object specifying a custom CSS class name (defaulting to prose) and a disableRoundedTable boolean flag packages/tailwind/src/typography/index.ts. It registers variants across a defined set of selectors using addVariant and injects component styles via addComponents packages/tailwind/src/typography/index.ts.
export const typography: ReturnType<typeof plugin.withOptions<Options>> =
plugin.withOptions<Options>(({ className = 'prose', ...styleOptions } = {}) => {
return ({ addVariant, addComponents, ...rest }) => {
const prefix = (rest as unknown as { prefix: Context['prefix'] }).prefix;
for (const [name, ...values] of SELECTORS) {
const selectors = values.length === 0 ? [name] : values;
const selector = selectors.join(', ');
addVariant(
`${className}-${name}`,
`& :is(${inWhere(selector, {
prefix,
className,
})})`,
);
}
addComponents({
[`.${className}`]: configToCss(
{
...styles.DEFAULT,
css: [
...(styles.DEFAULT.css ?? []),
styleOptions.disableRoundedTable ? styles.normalTable : styles.roundedTable,
],
},
{
className,
modifier: 'DEFAULT',
prefix,
},
),
});
};
});Sources: packages/tailwind/src/typography/index.ts:149-184
Selector generation relies on inWhere and commonTrailingPseudos to construct robust CSS rules packages/tailwind/src/typography/index.ts, packages/tailwind/src/typography/index.ts. The commonTrailingPseudos function parses selectors with postcss-selector-parser, extracts pseudo-elements into a column-major 2D array, and hoists identical trailing pseudo elements to the end of the selector packages/tailwind/src/typography/index.ts. The inWhere wrapper wraps the selector in a :where() pseudo-class and appends a :not(:where([class~="not-className"],[class~="not-className"] *)) exclusion to support nested unprose exceptions packages/tailwind/src/typography/index.ts.
function inWhere(selector: string, { className, prefix, modifier }: Options & Context) {
const prefixedNot = prefix(`.not-${className}`).slice(1);
const selectorPrefix = selector.startsWith('>')
? `${modifier === 'DEFAULT' ? `.${className}` : `.${className}-${modifier}`} `
: '';
// Parse the selector, if every component ends in the same pseudo element(s) then move it to the end
const [trailingPseudo, rebuiltSelector] = commonTrailingPseudos(selector);
if (trailingPseudo) {
return `:where(${selectorPrefix}${rebuiltSelector}):not(:where([class~="${prefixedNot}"],[class~="${prefixedNot}"] *))${trailingPseudo}`;
}
return `:where(${selectorPrefix}${selector}):not(:where([class~="${prefixedNot}"],[class~="${prefixedNot}"] *))`;
}Sources: packages/tailwind/src/typography/index.ts:21-35
The plugin registers variants for thirty distinct target identifiers using SELECTORS packages/tailwind/src/typography/index.ts, packages/tailwind/src/typography/index.ts.
| Variant Name | Target Elements / Selectors |
|---|---|
prose-headings | h1, h2, h3, h4, h5, h6, th |
prose-h1 | h1 |
prose-h2 | h2 |
prose-h3 | h3 |
prose-h4 | h4 |
prose-h5 | h5 |
prose-h6 | h6 |
prose-p | p |
prose-a | a |
prose-blockquote | blockquote |
prose-figure | figure |
prose-figcaption | figcaption |
prose-strong | strong |
prose-em | em |
prose-kbd | kbd |
prose-code | code |
prose-pre | pre |
prose-ol | ol |
prose-ul | ul |
prose-li | li |
prose-table | table |
prose-thead | thead |
prose-tr | tr |
prose-th | th |
prose-td | td |
prose-img | img |
prose-video | video |
prose-hr | hr |
prose-lead | [class~="lead"] |
const SELECTORS = [
['headings', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'th'],
['h1'],
['h2'],
['h3'],
['h4'],
['h5'],
['h6'],
['p'],
['a'],
['blockquote'],
['figure'],
['figcaption'],
['strong'],
['em'],
['kbd'],
['code'],
['pre'],
['ol'],
['ul'],
['li'],
['table'],
['thead'],
['tr'],
['th'],
['td'],
['img'],
['video'],
['hr'],
['lead', '[class~="lead"]'],
];Sources: packages/tailwind/src/typography/index.ts:117-147
Calculated Styles and Tables
Calculated typographic styles use helper functions round, rem, and em defined in packages/tailwind/src/typography/styles.ts to convert pixel values into relative rem and em units packages/tailwind/src/typography/styles.ts. The engine merges default typography configurations with either roundedTable or normalTable based on the disableRoundedTable option packages/tailwind/src/typography/index.ts. The roundedTable export configures separated borders, background colors using var(--color-fd-card), border radii from var(--radius-lg), and custom inline border start styles packages/tailwind/src/typography/styles.ts.
export const roundedTable = {
table: {
borderCollapse: 'separate',
borderSpacing: '0',
background: 'var(--color-fd-card)',
borderRadius: 'var(--radius-lg)',
border: '1px solid var(--color-fd-border)',
overflow: 'hidden',
},
th: {
textAlign: 'start',
padding: 'calc(var(--spacing) * 2.5)',
'border-inline-start': '1px solid var(--color-fd-border)',
background: 'var(--color-fd-muted)',
},
'th:first-child': {
'border-inline-start': 'none',
},
'th:not(tr:last-child *), td:not(tr:last-child *)': {
'border-bottom': '1px solid var(--color-fd-border)',
},
td: {
textAlign: 'start',
'border-inline-start': '1px solid var(--color-fd-border)',
padding: 'calc(var(--spacing) * 2.5)',
},
'td:first-child': {
'border-inline-start': 'none',
},
'tfoot th, tfoot td': {
borderTopWidth: '1px',
borderTopColor: 'var(--tw-prose-th-borders)',
},
'thead th, thead td': {
borderBottomWidth: '1px',
borderBottomColor: 'var(--tw-prose-th-borders)',
},
};Sources: packages/tailwind/src/typography/styles.ts:35-72
Note
When disableRoundedTable is enabled, the plugin applies normalTable rules instead of roundedTable, setting explicit bottom borders on table headers and alternating body rows while omitting explicit card background wrappers.
Sources: packages/tailwind/src/typography/index.ts:167-182, packages/tailwind/src/typography/styles.ts:74-113
Syntax Highlighting and Shiki Styling
Shiki Theme Variables and Selectors
Fumadocs formats code blocks and inline code using Shiki via CSS stylesheets located in packages/radix-ui/css/lib/shiki.css and packages/base-ui/css/lib/shiki.css. The engine maps syntax highlighting tokens to CSS variables for light and dark themes. For block-level code elements matching .shiki:not(.not-fumadocs-codeblock *), token spans inherit --shiki-light colors and --shiki-light-font-style in light mode, while .dark .shiki:not(.not-fumadocs-codeblock *) selectors assign --shiki-dark colors and --shiki-dark-font-style. Inline code elements matching code.shiki:not(.not-fumadocs-code *) apply the identical theme variable mapping for both light and dark contexts.
.shiki:not(.not-fumadocs-codeblock *) {
--padding-left: calc(var(--spacing) * 4);
--padding-right: calc(var(--spacing) * 4);
code span {
color: var(--shiki-light);
font-style: var(--shiki-light-font-style);
}Sources: packages/radix-ui/css/lib/shiki.css:1-8, packages/base-ui/css/lib/shiki.css:1-8
.dark .shiki:not(.not-fumadocs-codeblock *) {
code span {
color: var(--shiki-dark);
font-style: var(--shiki-dark-font-style);
}
}
/* inline code */
code.shiki:not(.not-fumadocs-code *) span {
color: var(--shiki-light);
font-style: var(--shiki-light-font-style);
}
.dark code.shiki:not(.not-fumadocs-code *) span {
color: var(--shiki-dark);
font-style: var(--shiki-dark-font-style);
}Sources: packages/radix-ui/css/lib/shiki.css:92-108, packages/base-ui/css/lib/shiki.css:92-108
Note
The :not(.not-fumadocs-codeblock *) and :not(.not-fumadocs-code *) exclusions prevent Shiki theme overrides from applying inside custom blocks that opt out of Fumadocs styling.
Sources: packages/radix-ui/css/lib/shiki.css:1-1, packages/radix-ui/css/lib/shiki.css:100-100
Line Decoration and Code Block Features
Code block layout is governed by variables setting padding-left and padding-right to 4 times the spacing scale (--padding-left and --padding-right). Empty code lines are forced to a height of 1lh. Advanced features like line focus states, git diff markers, line highlights, and line numbers are implemented through specific CSS classes and pseudo-elements.
| Class Selector | Applied Styles & Variables | Purpose |
|---|---|---|
&.has-focused .line:not(.focused) | filter: blur(2px); transition: filter 200ms; | Applies blur to non-focused lines when focus mode is active. |
&.has-focused:hover .line:not(.focused) | filter: blur(0); | Clears blur when hovering over a code block with active focus lines. |
.diff.remove | --fd-counter-color: var(--color-fd-diff-remove-symbol); @apply bg-fd-diff-remove; opacity: 0.7; | Styles deleted diff lines with background color and opacity. |
.diff.remove::before | content: '-'; @apply text-fd-diff-remove-symbol; | Inserts the minus symbol before removed lines. |
.diff.add | --fd-counter-color: var(--color-fd-diff-add-symbol); @apply bg-fd-diff-add; | Styles added diff lines with background color. |
.diff.add::before | content: '+'; @apply text-fd-diff-add-symbol; | Inserts the plus symbol before added lines. |
.highlighted | --fd-counter-color: var(--color-fd-primary); @apply bg-fd-primary/10; | Highlights entire code lines with primary theme tint. |
.highlighted::after | position: absolute; content: ''; @apply inset-y-0 left-0 border-l-2 border-fd-primary/50; | Renders a left accent border on highlighted lines. |
.highlighted-word | padding: 1px; @apply border -my-px border-fd-primary/30 bg-fd-primary/10 rounded-md font-medium; | Styles specific inline highlighted words within code spans. |
.line {
position: relative;
padding-left: var(--padding-left);
padding-right: var(--padding-right);
}
.line:empty {
height: 1lh;
}
&.has-focused .line:not(.focused) {
filter: blur(2px);
transition: filter 200ms;
}
&.has-focused:hover .line:not(.focused) {
filter: blur(0);
}
.diff::before {
position: absolute;
left: calc(var(--spacing) * 1.5);
}
.diff.remove {
opacity: 0.7;
--fd-counter-color: var(--color-fd-diff-remove-symbol);
@apply bg-fd-diff-remove;
}
.diff.remove::before {
content: '-';
@apply text-fd-diff-remove-symbol;
}
.diff.add {
--fd-counter-color: var(--color-fd-diff-add-symbol);
@apply bg-fd-diff-add;
}
.diff.add::before {
content: '+';
@apply text-fd-diff-add-symbol;
}
.highlighted {
--fd-counter-color: var(--color-fd-primary);
@apply bg-fd-primary/10;
}
.highlighted::after {
position: absolute;
content: '';
@apply inset-y-0 left-0 border-l-2 border-fd-primary/50;
}
.highlighted-word {
padding: 1px;
@apply border -my-px border-fd-primary/30 bg-fd-primary/10 rounded-md font-medium;
}Sources: packages/radix-ui/css/lib/shiki.css:10-69, packages/base-ui/css/lib/shiki.css:10-69
When data-line-numbers is present on the container, line numbers are generated using CSS counters (counter-increment: line), positioning counter values inside a 7-space left column with a color mixed in oklab between --fd-counter-color and transparent.
&[data-line-numbers] .twoslash-meta-line {
padding-left: calc(var(--padding-left) + 7 * var(--spacing));
}
&[data-line-numbers] .line {
counter-increment: line;
padding-left: calc(var(--padding-left) + 7 * var(--spacing));
}
&[data-line-numbers] .line::after {
position: absolute;
content: counter(line);
color: color-mix(
in oklab,
var(--fd-counter-color, var(--color-fd-muted-foreground)) 60%,
transparent
);
@apply inset-y-0 left-0 w-7 text-right;
}
}Sources: packages/radix-ui/css/lib/shiki.css:70-90, packages/base-ui/css/lib/shiki.css:70-90
Specialized Extension Stylesheets
OpenAPI, AsyncAPI, and API Documentation Presets
Supplementary preset stylesheets configure styling rules for API documentation packages by importing shared base definitions and companion package styles. The OpenAPI preset (packages/openapi/css/preset.css) imports ./generated/shared.css and @import '@fumadocs/api-docs/css/preset.css'. Similarly, the AsyncAPI preset (packages/asyncapi/css/preset.css) imports ./generated/shared.css and @import '@fumadocs/api-docs/css/preset.css'. The core API docs preset (packages/api-docs/css/preset.css) imports only ./generated/shared.css.
@import './generated/shared.css';
@import '@fumadocs/api-docs/css/preset.css';Sources: packages/openapi/css/preset.css:1-2, packages/api-docs/css/preset.css:1-1, packages/asyncapi/css/preset.css:1-2
Note
Both OpenAPI and AsyncAPI stylesheets reuse @fumadocs/api-docs/css/preset.css, ensuring uniform presentation across all API specification rendering packages.
Sources: packages/openapi/css/preset.css:2-2, packages/asyncapi/css/preset.css:2-2
Python Module Theme Configuration
The Python module preset (packages/python/styles/preset.css) sets up Tailwind scanning paths and defines custom inline theme variables for Python syntax elements. It scans distribution output files via @source '../dist/**/*.js' and configures color variables inside an @theme inline block.
| Theme Variable | Value | Purpose |
|---|---|---|
--color-fdpy-func | oklch(79.2% 0.209 151.711) | Defines the color token for Python functions. |
--color-fdpy-class | red | Defines the color token for Python classes. |
--color-fdpy-attribute | oklch(74% 0.238 322.16) | Defines the color token for Python attributes. |
--color-fdpy-method | blue | Defines the color token for Python methods. |
@source '../dist/**/*.js';
@theme inline {
--color-fdpy-func: oklch(79.2% 0.209 151.711);
--color-fdpy-class: red;
--color-fdpy-attribute: oklch(74% 0.238 322.16);
--color-fdpy-method: blue;
}Sources: packages/python/styles/preset.css:1-8
Tailwind V4 Integration and Bundling
Global Style Entry Points and Imports
Tailwind CSS v4 integration across Fumadocs relies on @import 'tailwindcss' statements coupled with layout presets and neutralization layers. The preview package style entry point (packages/preview/src/styles/globals.css) imports Tailwind alongside neutral and preset configurations.
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';Sources: packages/preview/src/styles/globals.css:1-3
Both packages/base-ui/css/style.css and packages/radix-ui/css/style.css follow an identical import structure while additionally defining a base layer rule that configures the body element for flex column layout with a minimum height spanning the viewport.
@import 'tailwindcss';
@import './neutral.css';
@import './preset.css';
@layer base {
body {
@apply flex flex-col min-h-screen;
}
}Sources: packages/base-ui/css/style.css:1-9, packages/radix-ui/css/style.css:1-9
Note
The root application layout (packages/preview/src/pages/_root.tsx) imports globals.css and attaches the identical flex column layout utility classes directly to the body element alongside hydration warning suppressions.
Sources: packages/preview/src/pages/_root.tsx:3-11
Vite and Waku Configuration
Build tooling for the preview package is handled via Waku configuration (packages/preview/waku.config.ts), which integrates Vite plugins including @tailwindcss/vite and an internal Fumadocs configuration loader.
vite: {
resolve: {
tsconfigPaths: true,
},
plugins: [
tailwindcss(),
{
name: 'internal',
config() {
return getConfig({ root: process.cwd() });
},
},
],
},Sources: packages/preview/waku.config.ts:7-21
Oxide Scanner and Dynamic CSS Compilation
The tsdown build configuration for packages/base-ui/tsdown.config.ts incorporates @tailwindcss/oxide via a programmatic Scanner implementation in compileInline(). This scanner extracts utility names from source code patterns and generates layout-specific inline source stylesheets inside css/generated/.
export async function compileInline() {
await mkdir('css/generated', { recursive: true });
const scanner = new Scanner({
sources: [
{
base: path.resolve('src'),
pattern: 'components/**/*.{ts,tsx}',
negated: false,
},
{
base: path.resolve('src'),
pattern: 'layouts/shared/**/*.{ts,tsx}',
negated: false,
},
{
base: path.resolve('src'),
pattern: '*.{ts,tsx}',
negated: false,
},
],
});
const commonNames = scanner.scan();
await writeFile('css/generated/shared.css', namesToFile(commonNames));
Sources: packages/base-ui/tsdown.config.ts:50-74
The compilation loop iterates through distinct layout identifiers (flux, notebook, home, and docs), filters out utilities already included in the shared scanner results, and writes out individual layout CSS files.
const layouts = ['flux', 'notebook', 'home', 'docs'];
const commonNameSet = new Set(commonNames);
for (const layout of layouts) {
const scanner = new Scanner({
sources: [
{
base: path.resolve('src'),
pattern: `layouts/${layout}/**/*.{ts,tsx}`,
negated: false,
},
],
});
await writeFile(
`css/generated/${layout}.css`,
namesToFile(scanner.scan().filter((name) => !commonNameSet.has(name))),
);
}
console.log('generated CSS files');
}Sources: packages/base-ui/tsdown.config.ts:75-96
Caution
The Oxide scanner explicitly excludes negated patterns and separates layout components so that shared utility names are compiled once into shared.css while layout-specific classes remain isolated in their respective generated files.
Sources: packages/base-ui/tsdown.config.ts:52-70, packages/base-ui/tsdown.config.ts:78-92
Related
On This Page
OverviewPreset Stylesheets and Base CSSPreset CSS StructuresBase CSS Primitives and Animation KeyframesLegacy Custom Theme Variables and UtilitiesTheme Color PalettesCatppuccin ThemeShadcn and Vitepress ThemesSolar and Ruby ThemesTailwind Typography Plugin EngineSelector Manipulation and ScopingCalculated Styles and TablesSyntax Highlighting and Shiki StylingShiki Theme Variables and SelectorsLine Decoration and Code Block FeaturesSpecialized Extension StylesheetsOpenAPI, AsyncAPI, and API Documentation PresetsPython Module Theme ConfigurationTailwind V4 Integration and BundlingGlobal Style Entry Points and ImportsVite and Waku ConfigurationOxide Scanner and Dynamic CSS CompilationRelated