All components / Disclosure
disclosure-panel
Native disclosure, coordinated with attributes and events. Useful for help, installation instructions, and expandable documentation.
Version 1.0.0 · MIT · Light DOM · Zero dependencies
Element module on GitHub · Registration module on GitHub · Custom Elements Manifest · License
Live example
Try the disclosure
This content is ordinary HTML. Open it with a pointer, or focus the summary and press Enter or Space.
Use the summary to open or close the panel.
The outer controls change the attribute. The inner button sends a bubbling request; the status listens for the outgoing change event. The summary works without JavaScript.
Start with HTML
<script type="module" src="/vendor/disclosure-panel/index.js"></script>
<disclosure-panel>
<details>
<summary>Installation help</summary>
<p>Use your browser’s Add to Home Screen menu.</p>
</details>
</disclosure-panel>Keep index.js beside disclosure-panel.js. No stylesheet, build step, or shadow root is required. Put initial open on the native details too if it should start expanded without JavaScript.
<disclosure-panel open>
<details open>
<summary>Help</summary>
<p>Visible before and after upgrade.</p>
</details>
</disclosure-panel>Attributes and events
| Attribute | Meaning |
|---|---|
open | Boolean presence opens the details. Remove to close. open="false" is still present and opens it. Native changes reflect back to the host. |
| Event | Direction and payload |
|---|---|
disclosure-change | Emitted up after state changes: detail: { open: boolean }. Bubbles, composed, not cancelable. No event for initialization or unchanged state. |
disclosure-request | Listened for from children: detail: { open: boolean }. Dispatch with bubbles: true; add composed: true to cross a shadow root. Requests must reach the nearest panel. |
const panel = document.querySelector('disclosure-panel');
panel.addEventListener('disclosure-change', (event) => {
console.log('Open:', event.detail.open);
});
panel.setAttribute('open', '');
panel.removeAttribute('open');
// From a button inside the panel; no parent selector required.
button.addEventListener('click', () => {
button.dispatchEvent(new CustomEvent('disclosure-request', {
detail: { open: false }, bubbles: true, composed: true,
}));
});Here button is a reference to your child button. Hide enhancement-only controls until their listeners are installed, as the live demo does. Do not announce content twice: the component preserves native disclosure semantics and adds no ARIA.
The manifest uses the Custom Elements Manifest schema. Incoming events use the documented x-listens extension because the standard events field describes emitted events. There are no custom public methods, properties, slots, CSS parts, or CSS custom properties.
Install from GitHub
There is no npm registry publication. Clone the GitHub repository, check out a reviewed commit, and vendor the component directory into your project. Replace COMMIT_SHA with that commit’s full SHA.
git clone https://github.com/jessehattabaugh/jessehattabaugh-com.git component-source
git -C component-source checkout COMMIT_SHA
mkdir -p vendor
cp -R component-source/client/components/disclosure-panel vendor/disclosure-panelCommit the vendored files, including the license, and record the upstream SHA when updating. GitHub raw-file URLs are source downloads; serve vendored modules from your own origin with a JavaScript MIME type.
npm install ./vendor/disclosure-panel
# Or:
pnpm add ./vendor/disclosure-panel
# Or:
yarn add ./vendor/disclosure-panelKeep the vendor directory and package lockfile in version control. These commands install a local ES module package, without publishing to another repository. Do not install this whole GitHub repository as the component package: its root package belongs to the website.
import '@jessehattabaugh/disclosure-panel';Import once from your browser entry. Vite and webpack resolve the package export. The registration entry is marked as a side effect so production builds keep it.
// In your browser entry; Rollup follows these relative ES module imports.
import './vendor/disclosure-panel/index.js';For bare package imports in Rollup, use its node-resolve plugin. The relative import needs no plugin. All three approaches register the same element; no framework-specific wrapper is needed.
import { DisclosurePanel } from '@jessehattabaugh/disclosure-panel/element';
customElements.define('disclosure-panel', DisclosurePanel);The /element export has no registration side effect. Choose manual registration or the default auto-registration entry. Load one version per document; with automatic registration the first definition wins. For server rendering, output the HTML on the server and import the module only in the browser: HTMLElement is a browser API.
npm pack ./vendor/disclosure-panel --ignore-scripts
npm install ./jessehattabaugh-disclosure-panel-1.0.0.tgzA tarball is optional for local distribution or a future GitHub release asset. No GitHub release is required by these instructions. See the npm install reference.
Behavior and limits
- Use one direct
detailschild with a first-childsummary. Additional details are left alone. Missing details is inert; direct children inserted later are discovered. - At upgrade or reconnection, host
openwins when present; otherwise the native state is adopted. Initialization emits no change event. - Native toggle notifications are asynchronous and can coalesce rapid changes. Attribute requests report changes immediately; do not rely on one event per rapid native toggle.
- Malformed requests, prevented requests, and requests without a details child are ignored. Nested panels handle their own requests; request events still bubble for other listeners.
- No JavaScript, failed module loading, or unsupported Custom Elements leaves an ordinary keyboard-accessible details element. Only state coordination requires JavaScript.
- Use current browsers with Custom Elements, MutationObserver, and native details support. Style
disclosure-panel,details, andsummaryin your own CSS; the wrapper is unstyled and inline by default.
This component also wraps the site’s app-install instructions. It makes no network requests and stores no data.