Duvlify
GitHubGet started
ReferenceComponents

Components

The complete component vocabulary, rendered on the page that documents it, including callouts, cards, steps, tabs, and trees.

Prefer a semantic PascalCase tag over styling content with className. The complete vocabulary is declared in src/components/mdx-components.ts, with one line per author-facing name. An unknown tag fails the build and names itself in the error.

Everything on this page is live. Each example shows exactly what the component does.

The vocabulary

GroupTags
StructureTabs, Tab, CodeGroup, CodeBlock, Steps, Step, Columns, Column, Panel, Frame
EmphasisCallout, Note, Warning, Info, Tip, Check, Danger, Banner, Badge, Update, Tooltip
Content and APIPrompt, ParamField, ResponseField, RequestExample, ResponseExample, Examples, Endpoint
DisclosureAccordion, AccordionGroup, Expandable
Audience and variantsView, Visibility
Navigation and visualsCard, CardGroup, Tile, Tree, TreeFolder, TreeFile, Icon, Mermaid, Color, ColorRow, ColorItem
ActionsCTA, Button

Some tags have shorter aliases. Duvlify keeps them because they match the notation most documentation already uses: FileTree/Folder/File for the tree, Response for ResponseField, LinkButton for Button, and Color.Row/Color.Item and Tree.Folder/Tree.File as member expressions of their parent. Each alias is the same component as its full name.

Callouts

A callout is one bordered, tinted aside. The tone prop selects both the tint and the icon. Note, Tip, Info, Check, Warning, and Danger are one-line wrappers that each pick a tone, so a page never has to name a colour directly.

MDX
<Callout title="A callout with a title" tone="info">
  Reach for a callout when the statement is true of one passage.
</Callout>

<Tip>A tip, written as `<Tip>` rather than `<Callout tone="tip">`.</Tip>

Available tones: info, note, success, tip, check, warning, danger.

Cards and tiles

A linked card

With href set, the card renders as an anchor and grows an arrow.

An unlinked card

Without href, the card is a plain titled block.

MDX
<CardGroup cols={2}>
  <Card title="A linked card" href="/getting-started">
    <Icon slot="icon" name="rocket" size={22} />
    Given `href` it renders as an anchor and grows an arrow.
  </Card>
</CardGroup>

Two meanings of cols

Both <Columns> and <CardGroup> take cols, but the prop means different things for each.

cols meanscols={1}cols={4} in the reading column
Columnsexactly this manyone full-width cellfour narrow cells
CardGroupat most this manyone full-width cardthree; the reading column is not wide enough for a fourth ~210px card

Columns follows the exact layout the author wrote. CardGroup never packs more than cols, but packs fewer once the column is too narrow for that many ~210px cards — which is the one case where four genuinely becomes three. Two and three do not have that excuse: asked for two, a group renders two, whatever the container’s width. Five cards at cols={3} below are the proof, run rather than described:

One

Two

Three

Four

Five

Three cards, then two: never four, and never one alone stretched to fill the row. Both props take values 1 to 4 and collapse to one column on a phone.

Steps

Numbered automatically

The marker counts steps for you. Pass icon to replace the number. Pass stepNumber to continue a sequence that started elsewhere.

Any content inside

Including code fences, callouts and tables.

MDX
<Steps>
  <Step title="Numbered automatically">The marker counts for you.</Step>
  <Step title="Any content inside">Including code fences and callouts.</Step>
</Steps>

Tabs

Tab panels can hold anything a page can.
Including fences, callouts and tables.
Three panels here.

Tab groups synchronize when they offer the same labels in the same order. For example, every “npm / pnpm / yarn” group on a site moves together, and the site remembers the choice for the next page. Groups that share only one label do not synchronize. A group offering “REST” alongside two other options is a different group from one offering “REST” alongside four options. Treating them as the same group made unrelated sections jump together.

PropEffect
sync={false}Keeps this group local, on Tabs, CodeGroup or RequestExample
defaultTabIndex={1}Starts on the second panel. A reader’s remembered choice takes priority over it
dropdownOn CodeGroup, for when a horizontal language strip would be crowded

Disclosure

An accordionWith a description line

Closed by default. Pass defaultOpen to start it open. Pass icon for a glyph beside the label.

A second one

Grouped in an <AccordionGroup>, so the accordions share one border.

<Expandable> is the lighter variant. Use it to reveal a nested object’s fields inside a parameter list, rather than a section of prose.

Parameter fields

page_idstringrequired

The page identifier. path, query, header, and body each set where the parameter lives, and the label reflects the choice.

limitintegerdefault: 20

Records per page. The API clamps requests above the maximum instead of rejecting them.

MDX
<ParamField path="page_id" type="string" required>
  The page identifier.
</ParamField>

On an OpenAPI endpoint page you do not write these by hand. The framework generates them from the spec. Write them yourself only for an API this framework does not drive, or for a configuration object.

Badges, updates and tooltips

A badge is filled by default, takes a colour, and can be outlined or carry an icon: Default, green, outlined, and with an icon.

A tooltip wraps inline text.

What changed

docsframework

<Update> is the changelog entry: a dated block with optional tags, meant to stack down a release-notes page. The label becomes the anchor, in slugified form. This gives a date written for a reader a usable permalink too.

Banners

<Banner> states something true of one page, in the reading column:

For an announcement on every page, set site.banner in src/docs.config.ts instead. This creates the strip fixed above the navbar, which this site is using right now. There is deliberately no per-page equivalent of that strip. A fixed bar states something true of the whole documentation set. Something true of one page belongs in the page instead.

The site remembers a dismissal against the exact text of the banner. Publishing new wording shows the banner again to everyone who dismissed the previous version. This same behaviour makes the strip unsuitable for anything a reader must not be able to hide.

Calls to action

<CTA> is a block asking the reader to do one thing. The default layout puts the headline and its supporting line on the left and the action on the right:

MDX
<CTA
  title="Ready to deploy?"
  description="One command puts this site on Cloudflare."
  href="/guides/deployment"
  label="Deploy it"
/>

Three layouts

Each answers a different question about where the block sits.

row is the default and belongs mid-page: it costs about the height of a paragraph and reads as one line of intent. A mark can sit at its left:

stack centres everything for the end of a page, where the reader has finished and the block is the next step rather than an aside:

split gives an image half the block, with the text and action in the other half. Use it when the picture is doing part of the persuading:

This one carries actionWidth="auto", so narrow the window and its button stays at its own size, flush with the text, rather than stretching. Every other block on this page keeps the default. See how wide the action goes for when each is right.

Variants

surface is the default sunken panel, accent fills with the brand colour for the one call to action on a landing page, and outline is a hairline and nothing else for a quiet nudge in a long page:

More than one action

Pass buttons to the actions slot instead of using href and label. The slot takes <Button>, so each action chooses its own weight:

MDX
<CTA variant="accent" title="Two ways in" description="…">
  <Fragment slot="actions">
    <Button href="/getting-started">Get started</Button>
    <Button href="/reference/architecture" variant="secondary">How it works</Button>
  </Fragment>
</CTA>

Buttons on their own

<Button> works outside a CTA. It is always a link: a <button> in static documentation would need JavaScript to mean anything, and a control that looks pressable and does nothing is worse than prose. Three variants and three sizes:

Primary, large Secondary Ghost, small

How wide the action goes

Once the block stacks (on a phone, or in stack), the action takes the full width by default, because a 36px pill alone on a narrow line reads as orphaned rather than as the thing to tap. actionWidth="auto" keeps it at its natural size, aligned with the text above it, which is right for a block that is an aside rather than the point:

MDX
<CTA actionWidth="auto" title="…" href="/why" label="Read why" />

Colours outside the theme

background takes any CSS colour, for a block that has to match something the theme does not know about.

On a filled block the button is the topbar’s secondary button, --surface behind --text. Those two invert together with the colour mode, so the pill is light with dark text on a light page and dark with light text on a dark one, which a literal grey could not do.

That pairing holds against the theme’s own colours, and cannot hold against one it has never seen: a fixed background stays put while --surface flips, so a dark custom fill that reads well in light mode gets a near-black pill on it in dark mode. Measured on the block above before its action colours were set: 1.26:1, a button you could lose against its own block. So a custom background is a commitment to actionBackground and actionForeground as well: the same bargain as foreground, for the same reason.

File trees

app
index.mdx
guides
authoring.mdx
astro.config.ts
MDX
<Tree>
  <TreeFolder name="app">
    <TreeFile name="index.mdx" />
  </TreeFolder>
</Tree>

FileTree, Folder, and File are aliases for the same three components. Tree.Folder and Tree.File are the same components again, written as member expressions of their parent. The tree above uses this member-expression form. All three notations leave identical Markdown in the page’s twin, so an agent reading it sees a folder listing rather than component syntax.

Colours

Brand
Accent#16866a / #68b09e
Accent strong#0f5e4a

<Color.Item> takes either one value or a { light, dark } pair. It captions the swatch with exactly what you wrote, not a sanitised version of it. Use variant="table" for a labelled palette. Leave it off for a compact row.

Columns

Exactly two cells. Columns follows the layout you drew.
Collapses to one column on a phone.

The right rail

Top-level Panel, RequestExample, and ResponseExample content can occupy the sticky right rail instead of the reading column. The rightRail field in frontmatter decides. A panel placed on an ordinary page stays inline, which is the default rather than a special case:

Page-wide variants

Use <View> for one page-wide language or framework selector. Use <Tabs> for alternatives local to one passage. Two or more views make the selector appear. One view on its own renders as an ordinary section. This is what makes it safe to write a page one view at a time.

With two or more views on a page, a single selector appears above them and switches the whole page at once. This is the TypeScript variant.

This is the Python variant. Only one view is visible at a time. The selector is the platform’s own <select> element, so an option cannot carry an icon. This is why a View is labelled by its title alone.

For content that differs between readers and models, rather than between frameworks, use <Visibility>:

You are reading the rendered page, so this paragraph appears. The Markdown twin of this page carries a different paragraph in its place.

Full explanation in Authoring.

Diagrams

<Mermaid> is the component form of a diagram. A fenced ```mermaid block is the usual way to write one, and Code and diagrams covers both. Use the component for a one-liner, or for the interactive controls:

flowchart LR A["content/page.mdx"] --> B["HTML"] A --> C["page.md"] A --> D["share card"] C --> E["fetch tool"]
Where a page's bytes end up

actions={true} adds pan, zoom, reset, and a fullscreen viewer on hover or keyboard focus. placement positions the inline controls. Set actions={false} to make a deliberately static diagram.

Icons

<Icon> places any icon the site knows about, and every icon slot on a Card or a Tile takes the same names:

MDX
<Icon name="rocket" size={20} />

Code and diagrams explains where the names come from, and why a page should never use a raw emoji instead.

Use these docs with your AI tools

An AI agent can read this documentation directly. You do not need an account or an API key. Everything here is public and read-only.

Query these docs via MCP

Recommended

Add this server to Claude, Claude Code, Cursor, Mistral, or any tool that supports MCP. Your agent can then search Duvlify documentation and read it in full, instead of answering from memory.

https://duvlify.dev/mcp
  • searchFind the passages that answer a question.
  • fetchRead one page in full, as Markdown.
  • list_pagesSee every page in this documentation.

Query these docs over HTTP

The same tools also work as plain web requests. Use this for scripts, or for any tool that does not support MCP. There is one endpoint per tool. Arguments go in the query string, and the answer comes back as JSON.

https://duvlify.dev/api/docs/search?query=custom+domain

Read the OpenAPI description. It is built from the same definitions as the tools, so it always matches what the endpoints do.

Read these docs as Markdown

Add .md to any page URL to get its Markdown source. You can also send the headerAccept: text/markdown to the page URL itself.

To read the whole documentation in one file, open llms-full.txt. For a short index of every page, open llms.txt.