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

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, 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. Knowing this helps explain why a group can show three cards when four were asked for.

cols meanscols={1}cols={4} in the reading column
Columnsexactly this manyone full-width cellfour narrow cells
CardGroupat most this manyone full-width cardthree, then four in a wider layout

CardGroup packs as many ~210px cards as fit and stops at cols. This keeps each card at a readable width, and no page needs its own breakpoint. Columns follows the exact layout the author wrote. 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

Default green outlined 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.

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 occupies the sticky right rail on desktop, and returns to the article on smaller screens.

Set rightRail: custom in frontmatter only on a page that deliberately replaces the table of contents with those components. Such a page uses the same layout as an OpenAPI endpoint page: the same wider three-track frame, and the same sticky panel. Both pages share one shape: prose in the middle, examples alongside. Below 1280px, the build hides the rail column either way.

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

A ```mermaid fence is the usual way to add a diagram. See Code and diagrams. Use the <Mermaid> component for one-liners and 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

Every icon on the site comes from Lucide through <Icon name="…" />, never a raw emoji or Unicode glyph. A plain character renders without error, but it looks inconsistent next to a Lucide icon: the weight, baseline, and style differ.

name is a short alias, not the Lucide name directly. See the icons map in src/components/Icon.astro for what is available. If the icon you need is not there, import it from @lucide/astro and add one line to that map.

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.