import pandas as pd
df = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})
df| x | y | |
|---|---|---|
| 0 | 1 | 4 |
| 1 | 2 | 5 |
| 2 | 3 | 6 |
Great Docs sites are built from .qmd files (Quarto Markdown). If you’ve written Markdown before, you’re already most of the way there. The .qmd format extends standard Markdown with YAML frontmatter, executable code blocks, cross-references, callouts, and more. This page teaches you everything you need to write rich documentation pages.
.qmd File?A .qmd file is a plain-text file with three parts:
--- fencesHere’s the simplest possible .qmd file:
When Great Docs builds the site, Quarto converts each .qmd file into a styled HTML page with navigation, search, and theming applied automatically.
Understanding this three-part structure is all you need to start writing pages. The rest of this guide explores each part in detail so you can take full advantage of .qmd capabilities.
As you build out your documentation site, you’ll author .qmd files in several contexts:
great-docs.yml (see Custom Sections)All of these use the same Markdown syntax, frontmatter, callouts, tabsets, and other features covered on this page. Once you learn the authoring basics here, they transfer directly to every part of your site.
Much of what you learn here also applies to writing Python docstrings. Great Docs renders docstrings through Quarto, so formatting like bold, inline code, lists, tables, and even callouts works inside your docstrings as well. You can even embed executable code cells in docstrings, which means your API examples can display rich outputs (tables, plots, and more) right on the reference page. See Writing Docstrings for the full guide on structuring docstrings for Great Docs.
Every .qmd file starts with a YAML block delimited by --- lines. This block sets metadata that controls how the page is rendered and where it appears in navigation. Think of it as the control panel for the page: it never appears in the body text, but it shapes everything about how the page looks and behaves.
At minimum, a page needs a title:
For User Guide pages, you’ll also want guide-section to control sidebar grouping:
The title becomes both the page heading and the label readers see in sidebar navigation, so make it concise and descriptive.
| Field | Purpose | Example |
|---|---|---|
title |
Page heading and sidebar label | "Quick Start" |
guide-section |
Sidebar section grouping | "Getting Started" |
tags |
Page tags for filtering and discovery | [Setup, Configuration] |
toc |
Show table of contents | true (default) |
toc-depth |
Max heading depth in the TOC | 3 |
The Table of Contents (TOC) is the “On this page” sidebar that appears on the right side of every page. It lists the headings on the current page as clickable links, letting readers jump directly to the section they need. By default the TOC is enabled and shows headings down to level 3 (i.e., ## and ###). Setting toc-depth: 2 would limit it to only ## headings, making the sidebar shorter for pages with many subsections. Setting toc: false hides it entirely, which can be useful for very short pages that don’t benefit from a navigation outline.
These fields give you fine-grained control over each page’s behavior without changing its content.
YAML is whitespace-sensitive. Here are the patterns you’ll use most:
---
# Strings (quotes optional unless the value contains special characters)
title: "My Title"
guide-section: Getting Started
# Booleans
toc: true
# Lists (two equivalent forms)
tags: [Setup, Config]
tags:
- Setup
- Config
---YAML uses spaces, never tabs. Inconsistent indentation will cause a build error. Use 2 spaces per indent level.
You won’t need deep YAML knowledge for most pages. The patterns above cover the vast majority of frontmatter you’ll write. When in doubt, copy the frontmatter from an existing page and adjust the values.
The body of a .qmd file is written in Markdown, a lightweight markup language designed to be readable as plain text while converting cleanly to HTML. If you’ve never used Markdown, this section covers everything you need to start writing documentation.
Use # characters to create headings. The number of # symbols determines the level:
In a .qmd file, the title in frontmatter is the page’s top-level heading, so your body content typically starts at ##:
my-page.qmd
Headings automatically appear in the Table of Contents sidebar and serve as anchor targets for linking. Choose heading levels to reflect the logical structure of your content. Don’t skip levels (e.g., jumping from ## to ####) as this breaks the outline hierarchy.
Paragraphs are separated by blank lines. For example, the following Markdown source:
This is the first paragraph. It can span
multiple lines in the source file and will
be rendered as a single flowing paragraph.
This is a second paragraph.…renders as two paragraphs, with the line breaks within the first paragraph collapsed into spaces.
Markdown provides several ways to emphasize text. Here’s the syntax alongside what it produces:
| Syntax | Result | Use For |
|---|---|---|
*italic* or _italic_ |
italic | Introducing terms, emphasis |
**bold** or __bold__ |
bold | Strong emphasis, important terms |
***bold italic*** |
bold italic | Rare, combined emphasis |
`code` |
code |
Function names, variables, file paths |
~~strikethrough~~ |
Deprecated or removed content |
Here’s a live example: writing **Great Docs** makes it *easy* to document your ~~old~~ package produces the following: Great Docs makes it easy to document your old package.
Use emphasis purposefully. Bold works well for key terms on first introduction, italic for slight emphasis, and inline code for anything a programmer would type. Overusing emphasis dilutes its effect and makes prose harder to scan.
Unordered lists use -, *, or +. The syntax:
Renders as:
Ordered lists use numbers followed by a period:
Which produces:
The actual numbers don’t matter because Markdown renumbers them automatically. Writing 1. for every item is a common shorthand that keeps diffs clean when you reorder items.
Definition lists pair a term with its definition:
This renders as:
Lists are one of the most effective tools for organizing information. Use unordered lists for collections where order doesn’t matter, ordered lists for sequences and procedures, and definition lists for glossaries or option descriptions.
Most common are inline links, where you would put the URL right next to the text:
This renders as: Visit Quarto’s website for more.
For links to other pages in your documentation, use relative paths:
This renders as: See the Configuration page.
Liberal cross-linking between pages helps readers navigate your documentation. Reference-style links are especially handy when the same URL appears multiple times or when long URLs would clutter your prose.
The basic syntax is:
For more control over sizing and layout, use Quarto’s figure syntax:
The attributes in curly braces let you set width, height, alignment, and more. Always include meaningful alt text for accessibility. Screen readers rely on it, and it also displays as a placeholder if the image fails to load.
Images and diagrams break up walls of text and can communicate complex ideas far more efficiently than prose. Store images in a subdirectory (e.g., images/) to keep your project organized.
Prefix lines with > to create blockquotes:
This renders as:
Documentation is a love letter that you write to your future self.
— Damian Conway
Blockquotes work well for attributing quotes, reproducing terminal output in narrative context, or visually setting apart a passage that you’re commenting on.
Three or more hyphens, asterisks, or underscores on a line create a visual divider:
Which renders as a horizontal line:
Because --- is also used for YAML frontmatter fences, it’s safest to use *** or ___ for horizontal rules in the body of your document to avoid any ambiguity.
Horizontal rules are useful for separating thematically distinct sections within a page when a heading would be too heavy-handed.
Use pipes and hyphens to create tables:
| Feature | Status |
|------------|-------------|
| Dark mode | Supported |
| Search | Built-in |
| Mobile | Responsive |This renders as:
| Feature | Status |
|---|---|
| Dark mode | Supported |
| Search | Built-in |
| Mobile | Responsive |
Control column alignment with colons in the separator row:
| Left | Center | Right |
|:-----------|:-----------:|------------:|
| aligned | aligned | aligned |Which produces:
| Left | Center | Right |
|---|---|---|
| aligned | aligned | aligned |
For tables with long content, the pipes don’t need to line up perfectly. Markdown is forgiving about column widths in the source.
Tables are the best way to present structured comparisons, option listings, and reference data. They’re much easier to scan than the equivalent information written as prose.
Taken together, the Markdown fundamentals covered in this section are enough to write clear, well-structured documentation pages. The remaining sections introduce Quarto-specific features that take your pages further.
Code blocks are essential for technical documentation. Quarto offers several forms, each suited to different purposes: from showing syntax-highlighted snippets to running live code and embedding its output.
Wrap code in triple backticks. Specify the language after the opening fence for syntax highlighting:
This renders with full syntax highlighting:
Quarto supports syntax highlighting for many languages: python, javascript, typescript, r, bash, yaml, toml, json, html, css, sql, rust, go, java, cpp, and more.
Syntax highlighting makes code dramatically easier to read by visually distinguishing keywords, strings, comments, and other language elements. Always specify the language tag. There’s no reason to show unhighlighted code.
Add a filename label to give readers context about where the code belongs:
```{.python filename="build.py"}
from great_docs import GreatDocs
docs = GreatDocs()
docs.build()
```This renders as:
The .python syntax (with the dot prefix) is Quarto’s way of specifying syntax highlighting for non-executable blocks. It’s equivalent to plain python but allows you to add attributes like filename.
Filename labels are a small touch that adds important context. They tell readers where a piece of code lives, which is especially valuable in tutorials that span multiple files.
Use curly braces around the language name to make a code block executable. Quarto will run the code and embed the output directly in the page:
Here’s that same block running live:
Control execution with hash-pipe (#|) options. For example, the following block hides its source code and produces only the figure output with a caption:
```{python}
#| echo: false
#| fig-cap: "A simple plotnine chart"
from plotnine import ggplot, aes, geom_col, theme_minimal, labs
import pandas as pd
df = pd.DataFrame({"category": ["A", "B", "C", "D"], "value": [4, 7, 3, 8]})
(
ggplot(df, aes(x="category", y="value"))
+ geom_col(fill="#4682B4")
+ theme_minimal()
+ labs(x="Category", y="Value")
)
```And here it is rendered (notice only the plot appears because echo: false hides the code):

Common hash-pipe options:
| Option | Effect |
|---|---|
#| echo: false |
Hide the source code, show only output |
#| eval: false |
Show the code but don’t run it |
#| output: false |
Run the code but hide the output |
#| warning: false |
Suppress warning messages |
#| fig-cap: "..." |
Add a caption to figure output |
#| label: fig-name |
Create a referenceable label for the figure |
Executable code blocks are what make .qmd files more powerful than plain Markdown. They ensure that your documentation’s code examples are always tested: if the code breaks, the build fails, so your docs stay in sync with your actual API.
Use single backticks for inline code. For example, writing The `build()` method generates the HTML output renders as: The build() method generates the HTML output.
Inline code is the workhorse of technical writing. Use it every time you mention a function name, variable, command, file path, or any other literal value. It tells readers “this is something you’d type, not just a word in a sentence”.
Code blocks, whether static or executable, are the backbone of any technical documentation page. They let you show readers exactly what to type and exactly what to expect.
Callouts are colored boxes that draw attention to important information. They stand out from the surrounding prose, making them impossible to miss. Quarto provides five built-in types, each with a distinct color and icon:
Here are all five types rendered live:
Supplementary information that adds context. Use notes for background details, clarifications, or “good to know” asides.
A helpful suggestion or best practice. Use tips for workflow shortcuts, performance advice, or recommended approaches.
Something the reader should be careful about. Use warnings for common mistakes, breaking changes, or compatibility issues.
Critical information the reader must not miss. Use important callouts for security considerations, data integrity, or required prerequisites.
Potential for data loss or irreversible actions. Use caution callouts for destructive operations, production deployments, or anything that can’t be undone.
Add a heading inside the callout to give it a custom title:
This renders as:
You can chain great-docs init and great-docs build in CI.
Custom titles let you summarize the callout’s message at a glance, which is especially helpful when a page has several callouts.
Add collapse="true" to make a callout collapsible (readers click to expand):
This renders as:
This content is hidden by default. Collapsible callouts are perfect for supplementary details that most readers can skip, like lengthy configuration examples or edge-case explanations.
Collapsible callouts keep pages scannable by hiding secondary detail behind a click. They’re ideal for verbose examples, troubleshooting steps, or background context that would otherwise interrupt the flow.
Callouts are one of the most effective tools for guiding readers through your documentation. Use them to flag what matters, but use them sparingly. If everything is highlighted, nothing stands out.
Tabsets present alternative content in switchable tabs. They’re perfect for showing platform-specific instructions or code in multiple languages without cluttering the page with content most readers will skip:
::: {.panel-tabset}
## pip
```bash
pip install great-docs
```
## conda
```bash
conda install great-docs
```
## pipx
```bash
pipx install great-docs
```
:::This renders as a set of clickable tabs:
Each ## heading inside the ::: {.panel-tabset} block becomes a tab label. Readers click tabs to switch content (no page reload needed).
Tabsets solve a common documentation problem: when you need to present the same information in multiple variants (different operating systems, package managers, programming languages, or framework versions), tabs keep all variants accessible without forcing readers to scroll past the ones they don’t need.
Quarto uses a fenced div syntax (borrowed from Pandoc) to apply classes and attributes to blocks of content. The ::: fences you’ve already seen in callouts and tabsets are examples of divs. This section covers the general mechanism so you can use it for layout and styling beyond those built-in features.
Wrap content in ::: with a class name:
This renders as:
This content has a border around it.
Nest divs by using more colons on the outer fence:
:::: {.columns}
::: {.column width="50%"}
Left column content.
:::
::: {.column width="50%"}
Right column content.
:::
::::This renders as:
Left column: content placed here appears on the left half of the page.
Right column: content placed here appears on the right half of the page.
Fenced divs give you a structural building block for any layout that Markdown alone can’t express. Because they’re plain text, they stay readable in source and don’t require writing raw HTML.
Apply classes to inline content with square brackets and curly braces:
This renders as: This is important text in a sentence.
Spans are the inline counterpart to divs. Use them when you need to style or annotate a word or phrase without affecting the surrounding block.
Divs and spans together give you a complete system for applying structure and styling at any granularity, from full-page layouts down to individual words, all without leaving Markdown.
Cross-references let you link to specific figures, tables, sections, and other elements by label. Unlike plain links that break when content moves, cross-references resolve by label during the build, so they stay valid as your documentation evolves.
Any heading can be referenced by its auto-generated ID (lowercase, hyphens for spaces):
This renders as: See YAML Frontmatter above.
Section links are the simplest form of cross-reference. They help readers jump to related content within the same page without scrolling.
Label a figure with #fig- prefix and reference it:
```{python}
#| label: fig-revenue
#| fig-cap: "Quarterly revenue"
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [10, 15, 13, 17])
plt.show()
```
As shown in @fig-revenue, revenue grew steadily.The @fig-revenue syntax creates a numbered link like “Figure 1” that stays correct even if you add more figures. This means you never have to manually update figure numbers; Quarto handles the bookkeeping.
Use relative paths for links within your documentation:
See the [Configuration](configuration.qmd) guide for all options.
For API details, see the [GreatDocs](../reference/GreatDocs.qmd) class.These links are validated during the build, so broken references are caught before your site goes live.
Cross-references are essential for any documentation longer than a single page. They weave your pages into a connected whole, letting readers follow their own path through the material.
Quarto supports multi-column layouts for side-by-side content. This is useful for comparisons, before/after examples, or pairing explanatory text with a visual:
:::: {.columns}
::: {.column width="40%"}
### Input
Raw Markdown text goes here.
:::
::: {.column width="60%"}
### Output
The rendered result appears here.
:::
::::This renders as:
Input: the left column holds source text or code.
Output: the right column holds the rendered result or a larger visual.
You can also use the layout attribute on figures for grid arrangements:
Multi-column layouts let you use horizontal space effectively. They’re particularly valuable for tutorials where readers benefit from seeing input and output side by side, or for image galleries that would waste space displayed one-per-row.
Break large pages into smaller, reusable pieces with the include shortcode:
This inserts the contents of _shared-setup.qmd directly into the page at build time. By convention, included files start with _ to indicate they aren’t standalone pages.
Includes are useful for:
Includes promote the “write once, use everywhere” principle. When a paragraph or setup block appears in multiple pages, extracting it into an include file means you only need to update it in one place.
For technical documentation that involves math, Quarto renders LaTeX equations natively.
Inline math uses single dollar signs. For example, $E = mc^2$ renders as \(E = mc^2\).
Display math uses double dollar signs for centered, standalone equations:
This renders as:
\[ L = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2 \]
Math rendering uses KaTeX, so most LaTeX math commands are supported, from simple expressions like \(\alpha + \beta\) to complex formulas with fractions, summations, and matrices.
Math support is indispensable for packages that deal with statistics, machine learning, physics, or any domain where precise notation matters. Equations embedded directly in your docs are more readable and maintainable than screenshot images of formulas.
When Markdown doesn’t offer enough control, you can embed raw HTML directly:
<details>
<summary>Click to expand</summary>
This content is hidden by default but still written in Markdown.
- List item one
- List item two
</details>This renders as:
This content is hidden by default but still written in Markdown.
Use raw HTML sparingly. Markdown content is portable and easier to maintain. Reserve HTML for cases where Markdown and Quarto’s built-in features can’t achieve the layout you need.
Raw HTML is an escape hatch for the rare cases where Markdown’s abstractions aren’t sufficient. The <details> element shown above is a good example: it creates a native browser disclosure widget that’s simpler than a collapsible callout for short asides.
Here’s a realistic User Guide page that combines many of these features:
user_guide/04-getting-started.qmd
---
title: "Getting Started"
guide-section: "Tutorials"
tags: [Setup, Tutorial]
---
## Prerequisites
Before you begin, make sure you have:
- Python 3.9 or later
- A Python package with a `pyproject.toml`
::: {.callout-tip}
## Virtual Environments
We recommend using a virtual environment to keep your dependencies isolated.
:::
## Installation
::: {.panel-tabset}
## pip
```bash
pip install great-docs
```
## pipx
```bash
pipx install great-docs
```
:::
## Your First Build
Run these two commands from your project root:
```{.bash filename="Terminal"}
great-docs init
great-docs build
```
The generated site is in `great-docs/_site/`. Open `index.html` to preview it.
## What's Next?
| Topic | Page |
|-------|------|
| Customize settings | [Configuration](configuration.qmd) |
| Add narrative docs | [User Guides](user-guides.qmd) |
| Deploy your site | [Deployment](deployment.qmd) |This example uses frontmatter, a list, a callout, a tabset, a code block with a filename label, and a table (all features covered on this page). As you write your own pages, mix and match these building blocks to create documentation that’s clear, scannable, and engaging.
A compact cheat sheet for the most common .qmd authoring patterns:
| Element | Syntax |
|---|---|
| Heading | ## My Heading |
| Bold | **bold text** |
| Italic | *italic text* |
| Inline code | `my_function()` |
| Link | [text](url) |
| Image |  |
| Unordered list | - item |
| Ordered list | 1. item |
| Code block | ```python ... ``` |
| Executable block | ```{python} ... ``` |
| Callout | ::: {.callout-note} ... ::: |
| Tabset | ::: {.panel-tabset} ... ::: |
| Cross-ref to section | [Section Name](#section-name) |
| Cross-ref to figure | @fig-label |
| Inline math | $E = mc^2$ |
| Display math | $$ ... $$ |
| Include | {{< include file.qmd >}} |
Keep this table close at hand as you write your first few pages. After a short while, the syntax becomes second nature.
The syntax covered here (frontmatter, Markdown, callouts, tabsets, executable code, cross-references) applies everywhere in Great Docs: user guide pages, docstrings, recipes, and custom sections. Once it’s second nature, you’re well-prepared to dive into any of these topics:
Comments
Add comments that don’t appear in the rendered output:
Comments are invisible to readers but visible to anyone editing the source file. Use them to leave notes for yourself or other authors: TODO reminders, explanations of why content is structured a certain way, or placeholders for sections you plan to fill in later. They’re a lightweight coordination tool that costs nothing in the rendered output.