|
| 1 | +# Heading Level 📦 |
| 2 | + |
| 3 | +Used by the [`<Heading>`](/3-ui/heading.md) component, and available for use in all frameworks (React, Svelte, Ember, etc). `getSectionHeadingLevel` is the primary function exported by this utility library and correctly determines which of the `<h1>` through `<h6>` [Section Heading][mdn-h] elements to use, where the **level** is determined _automatically_ based on how the DOM has rendered. |
| 4 | + |
| 5 | +This enables distributed teams to correctly produce appropriate section heading levels without knowledge of where their work will be rendered in the overall document -- and extra helpful for design systems teams where is _is not possible_ to know the appropriate heading level ahead of time. |
| 6 | + |
| 7 | +[mdn-h]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/Heading_Elements |
| 8 | + |
| 9 | +## Install |
| 10 | + |
| 11 | +```bash |
| 12 | +pnpm add which-heading-do-i-need |
| 13 | +``` |
| 14 | + |
| 15 | +## Usage |
| 16 | + |
| 17 | +In your app, you can use any of `<section>`, `<article>`, and `<aside>` elements to denote when the [_Section Heading_][mdn-h] element should change its level. |
| 18 | +Note that this demo starts with `h3`, because this docs page already has an `h1`, and _this_ section (Usage) uses an `h2`. |
| 19 | + |
| 20 | +In this example, we dynamically create a TextNode and Element, where, since the TextNode is rendered first, the Element can traverse from the TextNode up the existing DOM to determine which h-level to use. We expect an `h3` and `h4` in the demo, since this is the `Usage` section, which has a section heading of `h2`. |
| 21 | + |
| 22 | +<section class="featured-demo"> |
| 23 | + |
| 24 | +```gjs live preview |
| 25 | +import Component from "@glimmer/component"; |
| 26 | +import { element } from "ember-element-helper"; |
| 27 | +import { getSectionHeadingLevel } from "which-heading-do-i-need"; |
| 28 | +
|
| 29 | +class Heading extends Component { |
| 30 | + constructor(owner, args) { |
| 31 | + super(owner, args); |
| 32 | +
|
| 33 | + this.markerNode = document.createTextNode(""); |
| 34 | + } |
| 35 | +
|
| 36 | + get hLevel() { |
| 37 | + return `h${getSectionHeadingLevel(this.markerNode)}`; |
| 38 | + } |
| 39 | +
|
| 40 | + <template> |
| 41 | + {{this.markerNode}} |
| 42 | +
|
| 43 | + {{#let (element this.hLevel) as |El|}} |
| 44 | + <El ...attributes> |
| 45 | + {{yield}} |
| 46 | + </El> |
| 47 | + {{/let}} |
| 48 | + </template> |
| 49 | +} |
| 50 | +
|
| 51 | +// Output for demo uses the above |
| 52 | +<template> |
| 53 | + <Heading>hello there</Heading> |
| 54 | +
|
| 55 | + <section> |
| 56 | + <Heading>hello there</Heading> |
| 57 | +
|
| 58 | + </section> |
| 59 | +
|
| 60 | + <style> |
| 61 | + h1, h2, h3, h4, h5, h6 { margin: 0; } |
| 62 | +
|
| 63 | + h1::before, h2::before, h3::before, h4::before, h5::before, h6::before { |
| 64 | + position: absolute; |
| 65 | + margin-left: -1.2em; |
| 66 | + font-size: 0.7em; |
| 67 | + text-align: right; |
| 68 | + opacity: 0.8; |
| 69 | + } |
| 70 | +
|
| 71 | + h1 { font-size: 2.5rem; } |
| 72 | + h2 { font-size: 2.25rem; } |
| 73 | + h3 { font-size: 2rem; } |
| 74 | + h4 { font-size: 1.5rem; } |
| 75 | + h5 { font-size: 1.25rem; } |
| 76 | + h6 { font-size: 1rem; } |
| 77 | + a { color: white; } |
| 78 | +
|
| 79 | + h1::before { content: 'h1'; } |
| 80 | + h2::before { content: 'h2'; } |
| 81 | + h3::before { content: 'h3'; } |
| 82 | + h4::before { content: 'h4'; } |
| 83 | + h5::before { content: 'h5'; } |
| 84 | + h6::before { content: 'h6'; } |
| 85 | +
|
| 86 | + article, section, aside, nav, main, footer { |
| 87 | + border: 1px dotted; |
| 88 | + padding: 0.25rem 1.5rem; |
| 89 | + padding-left: 2rem; |
| 90 | + padding-right: 0.5rem; |
| 91 | + position: relative; |
| 92 | +
|
| 93 | + &::before { |
| 94 | + position: absolute; |
| 95 | + right: 0.5rem; |
| 96 | + top: -1rem; |
| 97 | + font-size: 0.7rem; |
| 98 | + text-align: right; |
| 99 | + opacity: 0.8; |
| 100 | + } |
| 101 | + } |
| 102 | +
|
| 103 | + section, article { |
| 104 | + display: flex; |
| 105 | + flex-direction: column; |
| 106 | + gap: 0.75rem; |
| 107 | + } |
| 108 | +
|
| 109 | + article::before { content: '<article>'; } |
| 110 | + section::before { content: '<section>'; } |
| 111 | + aside::before { content: '<aside>'; } |
| 112 | + nav::before { content: '<nav>'; } |
| 113 | + main::before { content: '<main>'; } |
| 114 | + footer::before { content: '<footer>'; } |
| 115 | +
|
| 116 | + main { |
| 117 | + display: grid; |
| 118 | + gap: 2.5rem; |
| 119 | + grid-template-columns: max-content 1fr; |
| 120 | + grid-template-areas: |
| 121 | + "heading heading" |
| 122 | + "nav content" |
| 123 | + "nav content" |
| 124 | + "nav content"; |
| 125 | +
|
| 126 | + } |
| 127 | +
|
| 128 | + >:first-child { grid-area: heading; } |
| 129 | + nav { grid-area: nav; } |
| 130 | + article { grid-area: content; } |
| 131 | +
|
| 132 | + </style> |
| 133 | +</template> |
| 134 | +``` |
| 135 | + |
| 136 | +</section> |
| 137 | + |
| 138 | +## API Reference |
| 139 | + |
| 140 | +```gjs live no-shadow |
| 141 | +import { APIDocs } from 'kolay'; |
| 142 | +
|
| 143 | +<template> |
| 144 | + <APIDocs @package="which-heading-do-i-need" @module="declarations/index" @name="getSectionHeadingLevel" /> |
| 145 | +</template> |
| 146 | +``` |
0 commit comments