CSS overrides
Cascade layers, token overrides in globals.css, and the stable class names and state attributes you can target.
When the theme config is not enough, write CSS. The stylesheet is built so that plain, unlayered CSS from your app always wins, and every rendered element carries a stable pd-* class and a small set of state attributes you can rely on.
Cascade layers#
pzzadocs/styles.css wraps all of its rules in three cascade layers, declared in this order:
@layer pzzadocs.base, pzzadocs.components, pzzadocs.utilities;Layered rules always lose to unlayered rules, regardless of specificity. That means a one-class selector in your globals.css overrides anything the framework ships without !important and without matching its selectors. Import the framework stylesheet first so the layer order is established before your own CSS runs:
import "pzzadocs/styles.css";
import "./globals.css";If your own styles also use layers, declare them after pzzadocs.* or keep the overrides unlayered.
Token overrides#
The theme config and a global stylesheet reach the same custom properties. Here is the stylesheet default for two tokens, and the same tokens overridden from app/globals.css:
:root {
--pd-foreground: #111111;
--pd-border: #e4e4e4;
}
.dark {
--pd-foreground: #ececec;
--pd-border: #242424;
}:root {
--pd-foreground: #000000;
--pd-border: #dddddd;
--pd-radius: 6px;
--pd-content-width: 80ch;
}
.dark {
--pd-foreground: #ffffff;
--pd-border: #2a2a2a;
}Keep light values on :root and dark values under .dark (the framework also recognises [data-theme="dark"], see Dark mode). The full token list with defaults is in the design tokens reference.
The identical override through the config looks like this:
theme: {
radius: 6,
layout: { contentWidth: "80ch" },
colors: {
light: { foreground: "#000000", border: "#dddddd" },
dark: { foreground: "#ffffff", border: "#2a2a2a" },
},
}When to use which#
- Use
themein the config when PzzaDocs is the only consumer of the values. Everything about the site lives in one typed object, the tokens are emitted byThemeStyleson the server, and you get autocomplete for every key. - Use
globals.csswhen the values already exist in your design system (for example--pd-foreground: var(--color-text)), when you need media queries or container queries around a token, or when you are overriding non-token rules anyway and want the overrides in one place.
Both can be combined. A globals.css declaration on :root and the ThemeStyles declaration on :root have the same specificity, so the one that appears later in the document wins; ThemeStyles is rendered inside <body>, after your linked stylesheets, so config values win over globals.css for the same token.
Class reference#
Class names are part of the public styling API and only change in a major release. Combine them with the state attributes below to target a specific state.
Layout#
| Class | Element |
|---|---|
pd-layout | Outer wrapper rendered by DocsLayout. |
pd-header | Sticky header. |
pd-header-inner | Centered header content row. |
pd-header-left | Logo, wordmark and nav tabs. |
pd-header-right | Search trigger, theme toggle, repository link and call to action. |
pd-header-brand | Link wrapping the logo and wordmark. |
pd-logo | Logo image or element. |
pd-nav-tabs | Horizontal navigation tab list. |
pd-nav-tab | One navigation tab. |
pd-layout-body | Row holding the sidebar column and the main area. |
pd-sidebar-column | Sticky column that contains the sidebar. |
pd-main | Main area that contains the page. |
Sidebar#
| Class | Element |
|---|---|
pd-sidebar | Sidebar container. |
pd-sidebar-list | A list of items at one depth. |
pd-sidebar-item | A page link. |
pd-sidebar-folder | A folder with its children. |
pd-sidebar-folder-row | The folder's clickable row. |
pd-sidebar-folder-label | Folder name inside the row. |
pd-sidebar-folder-toggle | Chevron button that expands or collapses the folder. |
pd-sidebar-separator | Section separator from ---Label--- entries. |
pd-sidebar-roots | Root switcher shown when the tree has root folders. |
pd-sidebar-root | One entry in the root switcher. |
Page#
| Class | Element |
|---|---|
pd-page | Page wrapper rendered by DocsPage. |
pd-article | The article column. |
pd-article-top | Breadcrumbs and page actions above the title. |
pd-breadcrumbs | Breadcrumb trail. |
pd-title | Page h1. |
pd-description | Description under the title. |
pd-prose | Rendered MDX body. |
pd-toc | Table of contents column or popover. |
pd-toc-list | List of TOC items. |
pd-toc-link | One TOC link. |
pd-page-footer | Footer with the last updated time and edit link. |
pd-pagination | Previous and next page row. |
pd-pagination-card | One previous or next card. |
pd-page-meta | Last updated and edit link group. |
pd-page-actions | Copy Markdown and open-in menu. |
Components#
| Class | Element |
|---|---|
pd-callout | Callout container. |
pd-code | Code block container. |
pd-code-header | Code block title row. |
pd-code-body | Scrollable code area. |
pd-pre | The pre element. |
pd-code-tabs | Grouped code blocks rendered as tabs. |
pd-tabs | Tabs container. |
pd-tabs-list | Tab trigger row. |
pd-tab-trigger | One tab button. |
pd-tab-panel | One tab panel. |
pd-steps | Steps container. |
pd-step | One step. |
pd-cards | Cards grid. |
pd-card | One card. |
pd-accordions | Accordions container. |
pd-accordion | One accordion. |
pd-table-wrapper | Scroll wrapper around Markdown tables. |
pd-table | Markdown table. |
pd-type-table | Type table. |
pd-files | File tree. |
pd-inline-toc | Inline table of contents. |
pd-banner | Top banner. |
pd-search-overlay | Backdrop behind the search dialog. |
pd-search-dialog | Search dialog. |
pd-search-input | Search text input. |
pd-search-item | One search result row. |
pd-mobile-drawer | Sidebar drawer on small screens. |
State attributes#
| Attribute | Set on | Meaning |
|---|---|---|
data-active | pd-sidebar-item, pd-nav-tab, pd-toc-link | Matches the current route or visible heading. |
data-open | pd-sidebar-folder, the TOC popover, collapse wrappers | Expanded. |
data-state="active" | "inactive" | pd-tab-trigger | Selected tab. |
data-depth | pd-sidebar-list, TOC items | Nesting depth, starting at 0. |
data-type | pd-callout | info, warn, error or success. |
data-full | pd-page | The page is in full-width mode (full: true in frontmatter). |
data-transparent | pd-header | The header is currently transparent (header.transparentMode). |
data-copied | Copy buttons | A copy just succeeded; cleared after a short delay. |
data-selected | pd-search-item | Keyboard-highlighted result. |
data-direction | pd-pagination-card | previous or next. |
.pd-sidebar-item[data-active] {
font-weight: 600;
}
.pd-callout[data-type="warn"] {
border-left: 2px solid var(--pd-border-strong);
}
.pd-tab-trigger[data-state="active"] {
color: var(--pd-foreground);
}Custom chrome#
For changes that CSS alone cannot express, the layout components accept extra classes and slots:
DocsLayouttakestree,config,children, and optionallyclassName,sidebarFooter(rendered under the sidebar tree),sidebarBanner(rendered above it),headerStart,headerEnd, andlabels(translated UI strings, see Localization).Headertakesconfig, and optionallytree,transparentMode,icons,className,start(rendered after the nav tabs) andend(rendered before the search trigger).Sidebar,TOCandDocsPageacceptclassName.DocsPagealso takeslocale(date format of the footer timestamp) andlabels.RootProvidertakestheme,search, andlabels.
The footer attribution line is part of the framework and is always rendered.