Skip to main content

html_outliner/
lib.rs

1/*!
2# HTML Outliner
3
4Outline HTML documents for better SEO.
5
6## Outline Algorithm
7
8This crate builds a practical SEO and table-of-contents outline.
9It uses the parsed HTML tree, but it only keeps the parts that are useful for a document outline.
10It starts from the document `body` when a `body` element is available.
11If no `body` element is found, it falls back to the parsed root element.
12
13The algorithm treats `article`, `aside`, `nav`, and `section` as sectioning content.
14Each sectioning content element becomes a nested outline item.
15If a sectioning content element has no heading, the output uses `Untitled article`, `Untitled aside`, `Untitled nav`, or `Untitled section`.
16The algorithm treats `blockquote`, `body`, `details`, `dialog`, `fieldset`, `figure`, and `td` as sectioning roots inside the current walk.
17Inner sectioning roots are skipped so their headings do not become part of the current outline.
18
19The algorithm treats `h1` through `h6` as headings.
20The first heading found in a section names that section.
21Later headings in the same section start new implied outline items.
22Heading levels decide nesting for these implied outline items.
23A lower-level heading such as `h3` becomes a child of the nearest previous heading with a smaller level number such as `h2`.
24A heading with the same or a higher level closes earlier implied heading groups and becomes a sibling.
25
26The algorithm looks through normal wrapper elements such as `header` and `div`.
27A heading inside a normal wrapper can still name the current section.
28After a wrapper contributes a heading or a nested outline, later sibling headings are treated as later headings instead of replacing the section heading.
29
30Heading text is collected from descendant text nodes.
31Whitespace is collapsed so inline markup such as `<h1>Design <em>notes</em></h1>` becomes `Design notes`.
32For compatibility with earlier versions of this crate, `hgroup` joins its heading children with ` — `.
33
34The `max_depth` argument limits how deeply the parser walk can read.
35Nodes deeper than `max_depth` are ignored by the outline builder.
36
37## Difference From A Browser Accessibility Tree
38
39A browser accessibility tree is built for assistive technologies and includes roles, names, states, focus behavior, and visibility rules.
40This crate does not build that kind of tree.
41It only reads the HTML elements and text needed for a simple document outline.
42Use this crate for content structure checks, not as a replacement for accessibility testing.
43
44## Difference From The Old HTML5 Outline Algorithm
45
46The old HTML5 outline algorithm tried to create a full section model for the whole document.
47This crate is smaller and more predictable.
48It returns a displayable outline from sectioning content and heading levels.
49It does not expose a current outlinee model or associate every DOM node with a generated section.
50
51## Using This Crate For SEO
52
53This crate can help SEO work by showing whether a page has a clear heading structure.
54Use the generated outline to check whether the main topic appears early and whether the subtopics are easy to scan.
55Look for missing headings, untitled sections, confusing heading levels, duplicated heading text, or headings that do not match the visible content.
56You can also use the outline to generate a table of contents or to validate pages in CI.
57This does not guarantee better rankings, but it helps search engines and users understand the page more easily.
58
59## Examples
60
61```rust
62use html_outliner::Outline;
63
64const MAX_DEPTH: usize = 50;
65
66let outline = Outline::parse_html(r"
67<h1>Header Top</h1>
68<h1>Header 1</h1>
69<h2>Header 2</h2>
70<h3>Header 3</h3>
71<h4>Header 4</h4>
72<h5>Header 5</h5>
73<h6>Header 6</h6>
74", MAX_DEPTH);
75
76println!("{}", outline);
77
78/*
791. Header Top
802. Header 1
81    1. Header 2
82        1. Header 3
83            1. Header 4
84                1. Header 5
85                    1. Header 6
86*/
87```
88*/
89
90mod heading;
91mod outline;
92mod outline_structure;
93mod sectioning_type;
94
95pub use heading::Heading;
96pub use outline::Outline;
97pub use outline_structure::OutlineStructure;
98pub use sectioning_type::SectioningType;