// Thank you to https://github.com/daviddarnes/heading-anchors // Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/ class HeadingAnchors extends HTMLElement { static register(tagName) { if ("customElements" in window) { customElements.define(tagName || "heading-anchors", HeadingAnchors); } } static attributes = { exclude: "data-ha-exclude", content: "data-ha-text" } static classes = { anchor: "ha", heading: "ha-h", // only used for nested method } static defaultSelector = "h2,h3,h4,h5,h6"; static featureTest() { return ; } static css = ` .${HeadingAnchors.classes.anchor} { position: absolute; text-decoration: none; font-weight: 400; opacity: 0; transition: opacity .15s; padding-left: .25em; padding-right: .25em; } /* nested */ :is(h1,h2,h3,h4,h5,h6):is(:focus-within, :hover) .${HeadingAnchors.classes.anchor}, /* sibling */ :is(h1,h2,h3,h4,h5,h6) + .${HeadingAnchors.classes.anchor}:is(:focus-within, :hover), :is(h1,h2,h3,h4,h5,h6):is(:focus-within, :hover) + .${HeadingAnchors.classes.anchor} { opacity: 1; } @supports (anchor-name: none) { /* purely for anchoring */ .${HeadingAnchors.classes.heading}:after { content: ""; anchor-name: var(--ha_anchor); } .${HeadingAnchors.classes.anchor} { left: anchor(right); top: anchor(top); } }`; get supportsTest() { return "replaceSync" in CSSStyleSheet.prototype; } get supportsAnchorPosition() { return CSS.supports("anchor-name: none"); } constructor() { super(); if(!this.supportsTest) { return; } let sheet = new CSSStyleSheet(); sheet.replaceSync(HeadingAnchors.css); document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; } connectedCallback() { if (!this.supportsTest) { return; } this.headings.forEach((heading, index) => { if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) { let anchor = this.getAnchorElement(heading); // Prefers anchor position approach for better accessibility // https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/ if(this.supportsAnchorPosition) { let anchorName = `--ha_${index}`; heading.style.setProperty("--ha_anchor", anchorName); anchor.style.positionAnchor = anchorName; let fontSize = parseInt(getComputedStyle(heading).getPropertyValue("font-size"), 10); anchor.style.fontSize = `${fontSize / 16}em`; heading.classList.add(HeadingAnchors.classes.heading); heading.after(anchor); } else { heading.appendChild(anchor); } } }); } getText(heading) { return heading.getAttribute(HeadingAnchors.attributes.content) || "#"; } getAnchorElement(heading) { let anchor = document.createElement("a"); anchor.href = `#${heading.id}`; anchor.classList.add(HeadingAnchors.classes.anchor); let text = this.getText(heading); if(this.supportsAnchorPosition) { anchor.innerHTML = `Jump to section titled: ${heading.textContent}`; } else { anchor.innerHTML = ``; } return anchor; } get headings() { return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`)); } get selector() { return this.getAttribute("selector") || HeadingAnchors.defaultSelector; } } HeadingAnchors.register();