# Rich Elements

> Tables, callouts, tabs, steps, cards, badges, buttons, file trees, the theme tokens and the layout.

A few `htmlpress-*` elements cover what HTML has no element for. They are light-DOM custom elements, so their content stays plain HTML that reads well even before the script loads. Each one's styles and script are linked only on pages that use it. Put block content (`<p>`, `<h3>`, `<pre>`) inside a component, not bare text.

## Tables

A `<table>` is styled as it is, and one with a `<thead>` sorts by any header clicked:

**HTML**

```html
<table>
  <thead><tr><th>Language</th><th>Year</th><th>Typing</th></tr></thead>
  <tbody><tr><td>TypeScript</td><td>2012</td><td>Static</td></tr>…</tbody>
</table>
```

**Markdown**

```md
| Language | Year | Typing |
| --- | --- | --- |
| TypeScript | 2012 | Static |
```

Renders as:

| Language   | Year | Typing  |
| ---------- | ---- | ------- |
| TypeScript | 2012 | Static  |
| Python     | 1991 | Dynamic |
| Rust       | 2010 | Static  |
| JavaScript | 1995 | Dynamic |

## Callouts

Callouts highlight important information. `type` is `info` (the default), `note`, `tip`, `warning` or `danger`, and `title` replaces the heading the type gives:

**HTML**

```html
<htmlpress-callout><p>This is a note.</p></htmlpress-callout>

<htmlpress-callout type="warning" title="Breaking Change">
  <p>This API has been removed in v2.</p>
</htmlpress-callout>
```

**Markdown**

```md
:::info
This is a note.
:::

:::warning[Breaking Change]
This API has been removed in v2.
:::
```

Renders as:

> **Info**
>
> This is a note.

> **Tip**
>
> Every page built.

> **Warning**
>
> Be careful with this.

> **Danger**
>
> Never edit `.htmlpress/build`: every build replaces it.

> **Breaking Change**
>
> This API has been removed in v2.

Headings inside a callout stay out of the page's table of contents.

## Expandable

No component needed: the theme styles a plain `<details>`.

**HTML**

```html
<details>
  <summary>Click to expand</summary>
  <p>Hidden content here with <strong>HTML</strong> support.</p>
</details>
```

**Markdown**

```md
:::details[Click to expand]
Hidden content here with **Markdown** support.
:::
```

Renders as:

Click to expand

Hidden content here with **HTML** support.

You can nest any HTML inside, including code blocks, lists and callouts:

Configuration example

```yaml
site: https://htmlpress.dev
title: Htmlpress
```

## Code Tabs

Tabs that hold only code blocks join into one framed block. Each `<section>` is a tab, named by its `title`:

**HTML**

```html
<htmlpress-tabs sync="lang">
  <htmlpress-tab title="JavaScript"><pre><code>console.log("hello")</code></pre></htmlpress-tab>
  <htmlpress-tab title="Python"><pre><code>print("hello")</code></pre></htmlpress-tab>
</htmlpress-tabs>
```

**Markdown**

````md
```js tab="JavaScript" sync="lang"
console.log("hello")
```

```python tab="Python"
print("hello")
```
````

Renders as:

**JavaScript**

```
console.log("hello")
```

**Python**

```
print("hello")
```

Add `sync="key"` to keep tab groups in sync, as described under Content Tabs below.

## Content Tabs

Group arbitrary content into tabs with `<htmlpress-tabs>`, one `<htmlpress-tab title>` per tab.

Add a `sync` attribute to synchronize tab selection across tab groups. When a reader selects a tab in one group, every other group with the same `sync` key switches to the matching tab, and the choice is remembered across pages in `localStorage`.

**HTML**

```html
<htmlpress-tabs sync="channel">
  <htmlpress-tab title="cargo"><p>From crates.io, built on your machine.</p></htmlpress-tab>
  <htmlpress-tab title="npm"><p>A prebuilt binary per platform.</p></htmlpress-tab>
  <htmlpress-tab title="pip"><p>A binary wheel.</p></htmlpress-tab>
</htmlpress-tabs>
```

**Markdown**

```md
:::tab{title="cargo" sync="channel"}
From crates.io, built on your machine.
:::

:::tab{title="npm"}
A prebuilt binary per platform.
:::

:::tab{title="pip"}
A binary wheel.
:::
```

Renders as (both groups share `sync="channel"`; switch one):

**cargo**

```sh
cargo install htmlpress
```

**npm**

```
npm install -g gethtmlpress
```

**pip**

```sh
pip install htmlpress
```

**cargo**

From crates.io, built on your machine.

**npm**

A prebuilt binary per platform.

**pip**

A binary wheel.

## Steps

A heading with `data-step` is a numbered step, the steps counting up through the page; its content follows it as usual. Those headings stay out of the page's table of contents.

**HTML**

```html
<h3 data-step>Install</h3>
<p>…</p>
<h3 data-step>Write a page</h3>
<p>…</p>
```

**Markdown**

```md
### Install [step]

…

### Write a page [step]

…
```

Renders as:

### Install

Pick a channel from the tabs above.

### Write a page

Any `.html` file in the folder.

### Start

Run `htmlpress start` and open the URL it prints.

## Cards

Cards in `<htmlpress-cards>` fill two columns, one on narrow screens. A card's `title` is its heading, an `href` makes the whole card clickable, an `icon` shows before the title in the accent colour, and its content is the card's body.

**HTML**

```html
<htmlpress-cards>
  <htmlpress-card title="Getting Started" href="/introduction/" icon="rocket">
    <p>Learn how to set up your first project.</p>
  </htmlpress-card>
  <htmlpress-card title="Plain HTML"><p>…</p></htmlpress-card>
</htmlpress-cards>
```

**Markdown**

```md
:::card{title="Getting Started" href="/introduction/" icon="rocket"}
Learn how to set up your first project.
:::

:::card{title="Plain HTML"}
…
:::
```

Renders as:

### [Getting Started](/introduction/)

Learn how to set up your first project.

### [Configuration](/customization/configuration-file/)

Configure your htmlpress project.

### [GitHub](https://github.com/datisthq/htmlpress)

View the source code on GitHub.

### Plain HTML

A card without a link is not clickable.

Headings inside a card stay out of the page's table of contents.

## Badges

An inline label. `variant` is `secondary` or `outline` for a plainer style, or `positive`, `warning` or `destructive` for a tone; without it, solid in the accent.

**HTML**

```html
<htmlpress-badge>New</htmlpress-badge>
<htmlpress-badge variant="positive">Stable</htmlpress-badge>
```

**Markdown**

```md
:badge[New] :badge[Stable]{variant="positive"}
```

Renders as: New Secondary Stable Beta Removed Outline

## Buttons

`<htmlpress-button href label>` is a call to action linking to its `href`. `variant` is `secondary`, `outline`, `ghost` or `link` for a quieter one, and `positive`, `warning` or `destructive` for a toned one; `size` is `sm` or `lg`.

**HTML**

```html
<htmlpress-button href="/introduction/" label="Get Started"></htmlpress-button>
<htmlpress-button href="…" label="GitHub" variant="secondary"></htmlpress-button>
```

**Markdown**

```md
::button[Get Started]{href="/introduction/"}

::button[GitHub]{href="…" variant="secondary"}
```

Renders as:

[Get Started](/introduction/) [GitHub](https://github.com/datisthq/htmlpress) [Outline](#buttons) [Link](#buttons)

## Icons

`<htmlpress-icon name="…">` draws a built-in [Lucide](https://lucide.dev) icon inline, in the text's color and size; the same names go in the `icon` of a link or a section. An unknown name fails the build. These are the 114 built in:

**HTML**

```html
<htmlpress-icon name="rocket"></htmlpress-icon> Get started
```

**Markdown**

```md
:icon{name="rocket"} Get started
```

activity

archive

arrow-right

badge-check

bell

blocks

book

book-marked

book-open

book-text

bookmark

bot

box

braces

brain

bug

calendar

chart-column

check

circle-help

clock

cloud

cloud-upload

code

codepen

coffee

compass

cpu

database

download

external-link

feather

figma

file

file-code

file-pen-line

file-text

flag

flask-conical

folder

git-branch

git-commit-horizontal

git-pull-request

github

gitlab

globe

graduation-cap

hammer

heading

heart

heart-handshake

history

house

image

info

key

layers

layout-grid

layout-panel-left

library

life-buoy

lightbulb

link

linkedin

list

lock

mail

map

megaphone

message-circle

monitor

newspaper

notebook-pen

package

palette

pencil

pencil-ruler

play

plug

presentation

puzzle

rocket

route

rss

scale

scroll

search

server

settings

shield

sliders-horizontal

smartphone

sparkles

square-dashed

square-stack

star

table

tag

terminal

triangle

trophy

twitter

type

upload

user

users

video

webhook

wind

workflow

wrench

x

youtube

zap

## File Tree

A nested list inside `<htmlpress-filetree>`; an item holding a list is a folder.

**HTML**

```html
<htmlpress-filetree>
  <ul>
    <li>docs<ul><li>index.html</li></ul></li>
    <li>htmlpress.yaml</li>
  </ul>
</htmlpress-filetree>
```

**Markdown**

```md
:::filetree
- docs
  - index.html
- htmlpress.yaml
:::
```

Renders as:

- docs

  - index.html
  - guides
    - build.html

- htmlpress.yaml

## Columns

`<htmlpress-columns>` lays out its children side by side, two by default and up to four with `cols`, each `<htmlpress-column>` one column:

**HTML**

```html
<htmlpress-columns cols="2">
  <htmlpress-column><p><strong>Left column</strong> with content.</p></htmlpress-column>
  <htmlpress-column><p><strong>Right column</strong> with more content.</p></htmlpress-column>
</htmlpress-columns>
```

**Markdown**

```md
:::column
**Left column** with content.
:::

:::column
**Right column** with more content.
:::
```

Renders as:

**Left column** with content, including lists:

- Item one
- Item two

**Right column** with more content:

> A blockquote works here too.

## Theme tokens

Colors, fonts and widths are CSS custom properties on `:root`, with a dark set under `prefers-color-scheme` or the header's theme toggle: `--htmlpress-accent`, `--htmlpress-accent-text`, `--htmlpress-banner`, `--htmlpress-banner-text`, `--htmlpress-highlight`, `--htmlpress-highlight-text`, `--htmlpress-prose`, `--htmlpress-prose-strong`, `--htmlpress-prose-marker`, `--htmlpress-bg`, `--htmlpress-surface`, `--htmlpress-text`, `--htmlpress-muted`, `--htmlpress-border`, `--htmlpress-info`, `--htmlpress-success`, `--htmlpress-warning`, `--htmlpress-danger`, `--htmlpress-font`, `--htmlpress-mono`, `--htmlpress-radius`, `--htmlpress-shadow`, `--htmlpress-width` and `--htmlpress-nav-width`. Override them from a page's own stylesheet:

```
:root {
  --htmlpress-accent: oklch(0.6 0.18 30);
  --htmlpress-font: "Inter", system-ui, sans-serif;
  --htmlpress-width: 52rem;
}
```

## Layout

The layout is plain HTML. Htmlpress fills its `<head>` (charset, viewport, title, description, social preview tags, canonical link, favicon, the theme, the styles and scripts of the components the page uses, the `head` tags from `htmlpress.yaml`, then the page's own head tags) and replaces these elements at build time:

- `<htmlpress-content>`

  The page's content.

- `<htmlpress-brand>`

  The site title with the `description` below it, after the `logo` when set, linking home.

- `<htmlpress-banner>`

  The `banner` from `htmlpress.yaml`.

- `<htmlpress-breadcrumbs>`

  Home, every folder above the page (named and linked by its index page when it has one), then the page.

- `<htmlpress-nav>`

  The open section's pages (every page without `sections`), grouped by top folder under an "Articles" label ("Posts" in a blog, "Releases" in a changelog); a folder's group is named by its index page and links to it. The sidebar sections open it and the sidebar `links` close it, under "Links".

- `<htmlpress-links>`

  The header sections, the open one marked, then the header `links` from `htmlpress.yaml`.

- `<htmlpress-page-toc>`

  The page's `<h2>` and `<h3>`, highlighting the one in view. A heading without an `id` gets one from its text.

- `<htmlpress-pager>`

  The previous and next pages in navigation order.

- `<htmlpress-edit>`

  An "Edit on GitHub" link from `edit` in `htmlpress.yaml`, on its own; the default layout has it inside `<htmlpress-toolbar>`.

- `<htmlpress-search>`

  Search over every page, with the script: a header button, also opened by `S`, `/` or `⌘K` / `Ctrl K`. The build writes the index to `_htmlpress/search.json`, split at each page's headings, and the dialog loads it on first open. Any word of the query matches, as a word's start or within one typo; a result opens its page with `?highlight=` the query, whose matches are painted in `--htmlpress-highlight` and scrolled to. Below 56rem the field takes over the header instead of a dialog.

- `<htmlpress-toolbar>`

  The page's actions under an "Actions" title: the source link, then a link to the page's Markdown copy, which the script swaps for "Copy Markdown", "Open in ChatGPT" and "Open in Claude".

- `<htmlpress-footer>`

  A "Created with Htmlpress" credit.

- `<htmlpress-language>`

  The language picker, linking to this page in each language, dropped down or, on a phone, in a sheet; only on a site with [translations](/customization/translations/).

- `<htmlpress-shortcuts>`

  A header menu of the keyboard shortcuts, with the script, listing every key and disabling the ones the page does not answer to, such as `L` without a next page; a row also presses its key. Hidden below 56rem, where there is no keyboard.

- `<htmlpress-skip>`

  The "Skip to content" link, shown only on focus.

Every string these elements and their scripts show comes in the page's language. The scripts read theirs from a `<script type="application/json" id="htmlpress-messages">` htmlpress writes into the head, and fall back to English without it.

An element with nothing to show is removed. `<htmlpress-nav-toggle>` (the sidebar as a drawer on a phone; wider, it folds the sidebar away and remembers that), `<htmlpress-toc-bar>` (narrower than the table of contents' column, a bar under the header naming the heading in view, which opens a sheet of the page's contents and actions), `<htmlpress-theme-toggle>` (a menu of light, dark and the system's choice, dropped down or, on a phone, in a sheet) and `<htmlpress-back-to-top>` are built by the script in the browser. Their keys match Livemark's: `M` toggles the sidebar, `T` flips light and dark and `U` goes back to the top, while `J` and `K` scroll any page down and up and `D` goes to its bottom. With a `<htmlpress-pager>` on the page, `L` and `H` go to the next and previous page. None fires while typing in a field.

Every phone sheet, the contents, the theme, the language and the sidebar's section picker, is one `<htmlpress-sheet>` the scripts add to the body. It closes on a swipe down, a tap on the backdrop, `Esc` or its close button, so escaping `components/sheet/sheet.css` restyles all of them.

The frame around them is made of elements too. They hold no content of their own, only style and place what is inside them, so a layout keeps the ones it wants and gives them landmark roles as attributes:

- `<htmlpress-shell>`

  The sidebar beside the rest; stacked on a phone.

- `<htmlpress-sidebar>`

  The brand and the navigation; a drawer on a phone.

- `<htmlpress-main>`

  The column beside the sidebar.

- `<htmlpress-header>`

  The sticky bar on top, showing its brand and menu button only on a phone.

- `<htmlpress-page>`

  The `<main>` it holds beside the aside.

- `<htmlpress-aside>`

  The column beside the content, hidden when empty or narrow.

A `.htmlpress/layout.html` in the project replaces the built-in layout, and a page can pick its own from `.htmlpress/layouts/`. Every component's styles and script can be replaced or added the same way: see [Overriding and Escaping](/customization/overriding-escaping/).
