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:

css
@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:

app/layout.tsx
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:

Before (stylesheet defaults)
:root {
  --pd-foreground: #111111;
  --pd-border: #e4e4e4;
}

.dark {
  --pd-foreground: #ececec;
  --pd-border: #242424;
}
After (app/globals.css)
: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:

docs.config.ts
theme: {
  radius: 6,
  layout: { contentWidth: "80ch" },
  colors: {
    light: { foreground: "#000000", border: "#dddddd" },
    dark: { foreground: "#ffffff", border: "#2a2a2a" },
  },
}

When to use which#

  • Use theme in the config when PzzaDocs is the only consumer of the values. Everything about the site lives in one typed object, the tokens are emitted by ThemeStyles on the server, and you get autocomplete for every key.
  • Use globals.css when 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#

ClassElement
pd-layoutOuter wrapper rendered by DocsLayout.
pd-headerSticky header.
pd-header-innerCentered header content row.
pd-header-leftLogo, wordmark and nav tabs.
pd-header-rightSearch trigger, theme toggle, repository link and call to action.
pd-header-brandLink wrapping the logo and wordmark.
pd-logoLogo image or element.
pd-nav-tabsHorizontal navigation tab list.
pd-nav-tabOne navigation tab.
pd-layout-bodyRow holding the sidebar column and the main area.
pd-sidebar-columnSticky column that contains the sidebar.
pd-mainMain area that contains the page.
ClassElement
pd-sidebarSidebar container.
pd-sidebar-listA list of items at one depth.
pd-sidebar-itemA page link.
pd-sidebar-folderA folder with its children.
pd-sidebar-folder-rowThe folder's clickable row.
pd-sidebar-folder-labelFolder name inside the row.
pd-sidebar-folder-toggleChevron button that expands or collapses the folder.
pd-sidebar-separatorSection separator from ---Label--- entries.
pd-sidebar-rootsRoot switcher shown when the tree has root folders.
pd-sidebar-rootOne entry in the root switcher.

Page#

ClassElement
pd-pagePage wrapper rendered by DocsPage.
pd-articleThe article column.
pd-article-topBreadcrumbs and page actions above the title.
pd-breadcrumbsBreadcrumb trail.
pd-titlePage h1.
pd-descriptionDescription under the title.
pd-proseRendered MDX body.
pd-tocTable of contents column or popover.
pd-toc-listList of TOC items.
pd-toc-linkOne TOC link.
pd-page-footerFooter with the last updated time and edit link.
pd-paginationPrevious and next page row.
pd-pagination-cardOne previous or next card.
pd-page-metaLast updated and edit link group.
pd-page-actionsCopy Markdown and open-in menu.

Components#

ClassElement
pd-calloutCallout container.
pd-codeCode block container.
pd-code-headerCode block title row.
pd-code-bodyScrollable code area.
pd-preThe pre element.
pd-code-tabsGrouped code blocks rendered as tabs.
pd-tabsTabs container.
pd-tabs-listTab trigger row.
pd-tab-triggerOne tab button.
pd-tab-panelOne tab panel.
pd-stepsSteps container.
pd-stepOne step.
pd-cardsCards grid.
pd-cardOne card.
pd-accordionsAccordions container.
pd-accordionOne accordion.
pd-table-wrapperScroll wrapper around Markdown tables.
pd-tableMarkdown table.
pd-type-tableType table.
pd-filesFile tree.
pd-inline-tocInline table of contents.
pd-bannerTop banner.
pd-search-overlayBackdrop behind the search dialog.
pd-search-dialogSearch dialog.
pd-search-inputSearch text input.
pd-search-itemOne search result row.
pd-mobile-drawerSidebar drawer on small screens.

State attributes#

AttributeSet onMeaning
data-activepd-sidebar-item, pd-nav-tab, pd-toc-linkMatches the current route or visible heading.
data-openpd-sidebar-folder, the TOC popover, collapse wrappersExpanded.
data-state="active" | "inactive"pd-tab-triggerSelected tab.
data-depthpd-sidebar-list, TOC itemsNesting depth, starting at 0.
data-typepd-calloutinfo, warn, error or success.
data-fullpd-pageThe page is in full-width mode (full: true in frontmatter).
data-transparentpd-headerThe header is currently transparent (header.transparentMode).
data-copiedCopy buttonsA copy just succeeded; cleared after a short delay.
data-selectedpd-search-itemKeyboard-highlighted result.
data-directionpd-pagination-cardprevious or next.
app/globals.css
.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:

  • DocsLayout takes tree, config, children, and optionally className, sidebarFooter (rendered under the sidebar tree), sidebarBanner (rendered above it), headerStart, headerEnd, and labels (translated UI strings, see Localization).
  • Header takes config, and optionally tree, transparentMode, icons, className, start (rendered after the nav tabs) and end (rendered before the search trigger).
  • Sidebar, TOC and DocsPage accept className. DocsPage also takes locale (date format of the footer timestamp) and labels.
  • RootProvider takes theme, search, and labels.

The footer attribution line is part of the framework and is always rendered.