Table of contents
An in-page outline of real hash links. The host passes the headings and, optionally, the current one; turn on the spy to follow the page's scroll instead. The last heading wins at the bottom of the page.
Introduction
Scroll the page: the outline follows the heading you are reading, and a link jumps to its section.
Installation
Scroll the page: the outline follows the heading you are reading, and a link jumps to its section.
With Cargo
Scroll the page: the outline follows the heading you are reading, and a link jumps to its section.
Usage
Scroll the page: the outline follows the heading you are reading, and a link jumps to its section.
Configuration
Scroll the page: the outline follows the heading you are reading, and a link jumps to its section.
Usage
use hydroxy_leptos::table_of_contents::{Item, TableOfContents};
use leptos::prelude::*;
pub fn demo() -> AnyView {
let items = vec![
Item::new("toc-demo-intro", "Introduction", 1),
Item::new("toc-demo-install", "Installation", 1),
Item::new("toc-demo-cargo", "With Cargo", 2),
Item::new("toc-demo-usage", "Usage", 1),
Item::new("toc-demo-config", "Configuration", 1),
];
let heading = |id: &'static str, text: &'static str| {
view! {
<h3 id=id style="margin: 0 0 var(--space-3); font: var(--text-h4); scroll-margin-top: var(--ox-toc-heading-offset, 72px);">
{text}
</h3>
<p style="margin: 0 0 var(--space-8); min-height: 140px; color: var(--text-2);">
"Scroll the page: the outline follows the heading you are reading, and a link jumps to its section."
</p>
}
};
view! {
<div style="display: grid; grid-template-columns: minmax(0, 1fr) 200px; gap: var(--space-7); align-items: start;">
<div>
{heading("toc-demo-intro", "Introduction")} {heading("toc-demo-install", "Installation")}
{heading("toc-demo-cargo", "With Cargo")} {heading("toc-demo-usage", "Usage")}
{heading("toc-demo-config", "Configuration")}
</div>
<div style="position: sticky; top: 72px;">
<TableOfContents items=items spy=true />
</div>
</div>
}
.into_any()
}use dioxus::prelude::*;
use hydroxy_dioxus::table_of_contents::{Item, TableOfContents};
#[component]
pub fn TableOfContentsDemo() -> Element {
let items = vec![
Item::new("toc-demo-intro", "Introduction", 1),
Item::new("toc-demo-install", "Installation", 1),
Item::new("toc-demo-cargo", "With Cargo", 2),
Item::new("toc-demo-usage", "Usage", 1),
Item::new("toc-demo-config", "Configuration", 1),
];
rsx! {
div { style: "display: grid; grid-template-columns: minmax(0, 1fr) 200px; gap: var(--space-7); align-items: start;",
div {
for item in items.clone() {
h3 {
key: "{item.id}",
id: "{item.id}",
style: "margin: 0 0 var(--space-3); font: var(--text-h4); scroll-margin-top: var(--ox-toc-heading-offset, 72px);",
"{item.label}"
}
p { style: "margin: 0 0 var(--space-8); min-height: 140px; color: var(--text-2);",
"Scroll the page: the outline follows the heading you are reading, and a link jumps to its section."
}
}
}
div { style: "position: sticky; top: 72px;", TableOfContents { items, spy: true } }
}
}
}
Anatomy
| Component | data-part | Renders |
|---|---|---|
TableOfContents | root | A nav landmark named On this page |
(inside) | list | An ordered list of items |
(inside) | item | data-level is the outline depth, which indents the link |
(inside) | link | A plain #hash anchor. aria-current=true on the current heading |
Keyboard
| Key | Does |
|---|---|
Enter | On a link: jump to the heading |
Tab | Moves through the links |
Props
| Prop | Type | Notes |
|---|---|---|
items | Vec<Item> | id, label and level for each heading. Blank and duplicate ids are dropped |
active_id | Option<String> | The current heading, owned by the host. Wins over the spy |
label | Option<String> | Accessible name. Default On this page |
heading_offset | f64 | Sticky-header clearance in pixels: the spy's reading line. Default 72 |
spy | bool | Follow the page's scroll position |
on_navigate | Callback<String> | A link was followed; the value is the heading id |