Skip to content

Instantly share code, notes, and snippets.

@cferdinandi
Created July 10, 2025 15:36
Show Gist options
  • Save cferdinandi/712a89a687ff2e255d700bc87e8ab252 to your computer and use it in GitHub Desktop.
Save cferdinandi/712a89a687ff2e255d700bc87e8ab252 to your computer and use it in GitHub Desktop.
Watch the tutorial: https://youtu.be/AwAcchY28yA
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kelp</title>
<meta name="description" content="A UI library for people who love HTML, powered by modern CSS and Web Components.">
<!-- Mobile Screen Resizing -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Kelp CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/kelpui@0/css/kelp.css">
</head>
<body>
<main class="container">
<h1>kelp-toc - debug</h1>
<kelp-toc></kelp-toc>
</main>
<script>
document.addEventListener('kelp-debug', function (event) {
console.log(event.target, event.detail);
});
</script>
<script src="./toc.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kelp</title>
<meta name="description" content="A UI library for people who love HTML, powered by modern CSS and Web Components.">
<!-- Mobile Screen Resizing -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Kelp CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/kelpui@0/css/kelp.css">
</head>
<body>
<main class="container">
<h1>kelp-toc</h1>
<kelp-toc></kelp-toc>
<h2>Cat O'Nine Tails</h2>
<h3 id="the-brig">The Brig</h3>
<h4>Privateer</h4>
<h2>Ahoy</h2>
<h3 id="man-of-war">Man-of-War</h3>
<h2 id="corsair">Corsair</h2>
<h3 id="shiver-me-timbers">Shiver Me Timbers</h3>
<h2 id="scurvy-dog">Scurvy Dog</h2>
<h4>Sea Legs</h4>
<h2 id="quarterdeck">On the Quarterdeck</h2>
<h3 id="jolly-roger">Jolly Roger</h3>
<h3 id="sloop">Sloop</h3>
<h4 id="swab">Swab</h4>
<h5>Grog</h5>
<div id="empty"></div>
</main>
<!-- Component JS -->
<script src="./toc.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kelp</title>
<meta name="description" content="A UI library for people who love HTML, powered by modern CSS and Web Components.">
<!-- Mobile Screen Resizing -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Kelp CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/kelpui@0/css/kelp.css">
<!-- Component JS -->
<script>
document.addEventListener('kelp-debug', function (event) {
console.log(event.target, event.detail);
});
</script>
<script src="./toc.js"></script>
</head>
<body>
<main class="container">
<h1>kelp-toc - load in header</h1>
<kelp-toc></kelp-toc>
<h2>Cat O'Nine Tails</h2>
<h3 id="the-brig">The Brig</h3>
<h4>Privateer</h4>
<h2>Ahoy</h2>
<h3 id="man-of-war">Man-of-War</h3>
<h2 id="corsair">Corsair</h2>
<h3 id="shiver-me-timbers">Shiver Me Timbers</h3>
<h2 id="scurvy-dog">Scurvy Dog</h2>
<h4>Sea Legs</h4>
<h2 id="quarterdeck">On the Quarterdeck</h2>
<h3 id="jolly-roger">Jolly Roger</h3>
<h3 id="sloop">Sloop</h3>
<h4 id="swab">Swab</h4>
<h5>Grog</h5>
<div id="empty"></div>
</main>
</body>
</html>
customElements.define('kelp-toc', class extends HTMLElement {
// Initialize on connect
connectedCallback () {
if (document.readyState !== 'loading') {
this.init();
return;
}
document.addEventListener('DOMContentLoaded', () => this.init(), {once: true});
}
// Initialize the component
init () {
// Don't run if already initialized
if (this.hasAttribute('is-ready')) return;
// Get settings
this.level = this.getAttribute('level') || 'h2';
this.heading = this.getAttribute('heading');
this.target = this.getAttribute('target') || '';
this.listClass = this.getAttribute('list-class') || 'list-inline';
// Render
if (!this.render()) {
this.debug('No matching headings were found');
return;
}
// Ready
this.emit();
this.setAttribute('is-ready', '');
}
// Render the TOC
render () {
// Generate list items
const navList = Array.from(document.querySelectorAll(`${this.target} ${this.level}`)).map((heading) => {
if (!heading.id) {
heading.id = `h_${crypto.randomUUID()}`;
}
return `<li><a class="link-subtle" href="#${heading.id}">${heading.textContent}</a></li>`;
}).join('');
// Make sure a navList exists
if (navList.length < 1) return;
// Render the HTML
this.innerHTML = `<ul class="${this.listClass}">${this.heading ? `<li><strong>${this.heading}</strong></li>` : ''}${navList}</ul>`;
return true;
}
// Emit a custom event
emit () {
// Create a new event
const event = new CustomEvent('kelp:toc-ready', {
bubbles: true
});
// Dispatch the event
return this.dispatchEvent(event);
}
// Emit a debug event
debug (detail = '') {
// Create a new event
const event = new CustomEvent('kelp-debug', {
bubbles: true,
detail
});
// Dispatch the event
return this.dispatchEvent(event);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment