Rich Elements
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:
<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>
| 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:
<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>
:::info
This is a note.
:::
:::warning[Breaking Change]
This API has been removed in v2.
:::
Renders as:
This is a note.
Every page built.
Be careful with this.
Never edit .htmlpress/build: every build replaces it.
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>.
<details>
<summary>Click to expand</summary>
<p>Hidden content here with <strong>HTML</strong> support.</p>
</details>
:::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
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:
<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>
```js tab="JavaScript" sync="lang"
console.log("hello")
```
```python tab="Python"
print("hello")
```
Renders as:
console.log("hello")
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.
<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>
:::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 install htmlpress
npm install -g gethtmlpress
pip install htmlpress
From crates.io, built on your machine.
A prebuilt binary per platform.
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.
<h3 data-step>Install</h3>
<p>…</p>
<h3 data-step>Write a page</h3>
<p>…</p>
### 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.
<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>
:::card{title="Getting Started" href="/introduction/" icon="rocket"}
Learn how to set up your first project.
:::
:::card{title="Plain HTML"}
…
:::
Renders as:
Getting Started
Learn how to set up your first project.
Configuration
Configure your htmlpress project.
GitHub
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.
<htmlpress-badge>New</htmlpress-badge>
<htmlpress-badge variant="positive">Stable</htmlpress-badge>
:badge[New] :badge[Stable]{variant="positive"}
Renders as:
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.
<htmlpress-button href="/introduction/" label="Get Started"></htmlpress-button>
<htmlpress-button href="…" label="GitHub" variant="secondary"></htmlpress-button>
::button[Get Started]{href="/introduction/"}
::button[GitHub]{href="…" variant="secondary"}
Renders as:
Icons
<htmlpress-icon name="…"> draws a built-in
Lucide 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:
<htmlpress-icon name="rocket"></htmlpress-icon> Get started
:icon{name="rocket"} Get started
File Tree
A nested list inside <htmlpress-filetree>; an item holding a list is
a folder.
<htmlpress-filetree>
<ul>
<li>docs<ul><li>index.html</li></ul></li>
<li>htmlpress.yaml</li>
</ul>
</htmlpress-filetree>
:::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:
<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>
:::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
descriptionbelow it, after thelogowhen set, linking home. <htmlpress-banner>- The
bannerfromhtmlpress.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 sidebarlinksclose it, under "Links". <htmlpress-links>-
The header sections, the open one marked, then the header
linksfromhtmlpress.yaml. <htmlpress-page-toc>-
The page's
<h2>and<h3>, highlighting the one in view. A heading without anidgets one from its text. <htmlpress-pager>- The previous and next pages in navigation order.
<htmlpress-edit>-
An "Edit on GitHub" link from
editinhtmlpress.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-highlightand 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.
<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.
Created with ❤ and Htmlpress