Theming
Every colour, corner radius and font on the generated pages uses a --pyd-* custom property. To
retint the pages, set a handful of these properties. Anything the tokens do not cover is a normal
CSS rule, and your rules win by default. See Overriding rules.
To replace a whole renderer, for example to build signature blocks or member tables with your own Astro component, see Component overrides instead.
Loading your stylesheet
Section titled “Loading your stylesheet”The plugin’s stylesheet is starlight-pydocs/styles. Under Starlight, the plugin appends it to
Starlight’s customCss for you. Pass injectStyles: false if you want to import it yourself.
Add your own CSS to that same list. Register it like any other Starlight custom stylesheet:
starlight({ title: 'My project', plugins: [starlightPydocs({ packages: [{ name: 'mypkg', search: ['../src'] }] })], customCss: ['./src/styles/custom.css'],});Both kinds of change go in that file. To retint, set tokens. To override, write a rule:
:root { --pyd-accent: #b45309; --pyd-radius: 0;}/* Unlayered, so this rule beats the plugin stylesheet with no !important. */.pyd-signature { background: transparent; border-inline-start: 3px solid var(--pyd-accent);}In a plain Astro project nothing is injected. The plugin’s built-in page layout imports the
stylesheet itself, so the generated pages are styled either way. Import it yourself when you pass a
layout of your own as layout, or in a hand-written page that embeds API docs with
<Autodoc>:
---import 'starlight-pydocs/styles';---Overriding rules
Section titled “Overriding rules”Every rule in the stylesheet is wrapped in @layer starlight-pydocs. An unlayered rule always beats
a layered rule, whatever its specificity. So a plain selector in your own stylesheet wins, with no
specificity fight and no !important. You do not need to increase selector specificity to override
the plugin’s styles.
Keep your overrides unlayered, and this holds however you load them. If you put your own rules in a
cascade layer, the comparison changes. The plugin appends its stylesheet after your customCss
entries, so its layer is declared last, and later-declared layers win.
One exception: inside Starlight’s own .sl-markdown-content, Starlight’s unlayered rules apply too.
There, give your rule enough specificity to beat Starlight’s. The plugin’s stylesheet does the same
in the few places it must.
Class hooks are stable and prefixed pyd-. The ones worth knowing:
| Class | What it wraps |
|---|---|
.pyd-module |
A whole module page’s body |
.pyd-member |
One documented object, including its members |
.pyd-heading, .pyd-anchor |
An object’s heading and its permalink |
.pyd-badges, .pyd-badge |
Kind, label and deprecation badges |
.pyd-signature |
A signature block (--overload, --attribute variants) |
.pyd-type |
A name inside a signature or annotation (linked or not) |
.pyd-provenance, .pyd-source-link |
The line under a heading |
.pyd-section, .pyd-section-title |
One docstring section and its heading |
.pyd-params, .pyd-summary-table |
Parameter tables and member summaries |
.pyd-aside |
An admonition (--tip, --caution, --danger) |
.pyd-inherited |
The <details> block of members from a base class |
.pyd-search… |
The symbol search element and its results |
.pyd-page, .pyd-toc |
The built-in vanilla layout and its table of contents |
For anything more selective, use the data attributes instead. They use the model’s own vocabulary:
[data-pydocs-kind="class"], [data-pydocs-path="mypkg.Report"],
[data-pydocs-group="methods"], [data-pydocs-section="parameters"],
[data-pydocs-badge="deprecated"] and [data-pydocs-aside="note"].
Tokens
Section titled “Tokens”Set any of these on :root, or on a narrower selector to retint one part of the site. The values
shown are the defaults. Each uses Starlight’s token first, then a static fallback for sites without
Starlight.
| Token | Default | Used for |
|---|---|---|
--pyd-text |
var(--sl-color-text, #24292f) |
Body text in signatures and tables |
--pyd-text-muted |
var(--sl-color-gray-3, #57606a) |
Badges, provenance, section titles, source links |
--pyd-text-invert |
var(--sl-color-black, #ffffff) |
Text on an accent background (the kind badge) |
--pyd-bg |
var(--sl-color-bg, #ffffff) |
Page background in the built-in layout |
--pyd-surface |
var(--sl-color-gray-6, #f6f8fa) |
Signature blocks, badges, search results |
--pyd-border |
var(--sl-color-hairline-light, #d8dee4) |
Member rules, table borders, input borders |
--pyd-accent |
var(--sl-color-text-accent, #2563eb) |
Linked types, anchors, the kind badge |
--pyd-accent-low |
var(--sl-color-accent-low, #dbeafe) |
Accent backgrounds |
--pyd-note |
var(--sl-color-blue, #3b82f6) |
Note asides |
--pyd-note-low |
var(--sl-color-blue-low, #dbeafe) |
Note aside background |
--pyd-tip |
var(--sl-color-purple, #8b5cf6) |
Tip asides |
--pyd-tip-low |
var(--sl-color-purple-low, #ede9fe) |
Tip aside background |
--pyd-caution |
var(--sl-color-orange, #ea580c) |
Caution asides and deprecation badges |
--pyd-caution-low |
var(--sl-color-orange-low, #ffedd5) |
Caution aside background |
--pyd-danger |
var(--sl-color-red, #dc2626) |
Danger asides |
--pyd-danger-low |
var(--sl-color-red-low, #fee2e2) |
Danger aside background |
--pyd-radius |
0.375rem |
Corner radius on signatures and inputs |
--pyd-mono |
var(--sl-font-mono, …) |
Every monospaced string |
Because the defaults reference Starlight’s tokens, changing your site’s accent colour also changes the API pages. To make the API pages differ, retint one token at a time.
Dark mode
Section titled “Dark mode”Under Starlight, there is nothing to do. The tokens follow --sl-*, and Starlight flips those.
Without Starlight, the stylesheet defines a dark palette twice, so both ways of choosing a theme
work: once under @media (prefers-color-scheme: dark), guarded by
:root:not([data-theme='light']), and once under :root[data-theme='dark']. Set
data-theme="dark" or data-theme="light" on <html> from your own theme toggle, and the API
pages follow it. Leave it unset, and they follow the operating system.
Code inside docstrings
Section titled “Code inside docstrings”The site’s own pipeline highlights fenced blocks and doctest transcripts in a docstring, because docstring prose renders through the Markdown processor your project configured. There is no separate Shiki configuration here, and nothing to keep in sync.
Under Starlight, that means Expressive Code. A docstring’s code block gets the same frame, copy
button and themes as a code block in a hand-written page. In a plain Astro project, it is Shiki’s
.astro-code output, configured by markdown.shikiConfig:
export default defineConfig({ markdown: { shikiConfig: { themes: { light: 'github-light', dark: 'github-dark' }, defaultColor: false, }, },});Two themes with defaultColor: false emit both palettes as custom properties. This is what lets a
single stylesheet work in both light and dark. This site uses that configuration.
Signatures are not Shiki output, but they are Shiki coloured. The model builds them, so that every
type can be a link, and the tokens are then given the colours of the same themes you configure
above. A cross-reference keeps its own .pyd-type colour through that pass, so a linked
dict[str, Report] still reads as a link rather than disappearing into the grammar.
The colours are worked out once, at astro:config:done, and stored beside the extracted dump.
Shiki cannot resolve a theme from inside the page-rendering bundle, where Astro has tree-shaken the
theme registry away, so nothing is highlighted at render time.