{"id":16427860,"url":"https://github.com/cferdinandi/houdini","last_synced_at":"2025-07-02T17:34:02.209Z","repository":{"id":6563299,"uuid":"7805163","full_name":"cferdinandi/houdini","owner":"cferdinandi","description":" A simple, accessible show-and-hide/accordion script.","archived":false,"fork":false,"pushed_at":"2022-12-02T17:23:02.000Z","size":1398,"stargazers_count":160,"open_issues_count":20,"forks_count":16,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-20T03:06:25.101Z","etag":null,"topics":["a11y","accessibility","accordion","collapse","disclosure","javascript","javascript-plugin","no-dependencies","vanilla-js"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cferdinandi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/contributing.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-01-24T19:23:11.000Z","updated_at":"2024-11-24T23:56:29.000Z","dependencies_parsed_at":"2023-01-13T14:02:13.412Z","dependency_job_id":null,"html_url":"https://github.com/cferdinandi/houdini","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/cferdinandi/houdini","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cferdinandi%2Fhoudini","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cferdinandi%2Fhoudini/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cferdinandi%2Fhoudini/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cferdinandi%2Fhoudini/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cferdinandi","download_url":"https://codeload.github.com/cferdinandi/houdini/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cferdinandi%2Fhoudini/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261218635,"owners_count":23126419,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a11y","accessibility","accordion","collapse","disclosure","javascript","javascript-plugin","no-dependencies","vanilla-js"],"created_at":"2024-10-11T08:14:18.568Z","updated_at":"2025-07-02T17:34:02.184Z","avatar_url":"https://github.com/cferdinandi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Houdini [![Build Status](https://travis-ci.org/cferdinandi/houdini.svg)](https://travis-ci.org/cferdinandi/houdini)\nA simple, accessible show-and-hide/accordion script.\n\nHoudini progressively enhances your markup when it loads. You provide the content, and Houdini layers in the toggle buttons, ARIA attributes, and interactivity for you.\n\n**[View the Demo on CodePen \u0026rarr;](https://codepen.io/cferdinandi/pen/JeByZQ)**\n\n[Getting Started](#getting-started) | [Accordions](#accordions) | [Demos](#demos) | [API](#api) | [What's New?](#whats-new) | [Browser Compatibility](#browser-compatibility) | [License](#license)\n\n\n\u003chr\u003e\n\n### Want to learn how to write your own vanilla JS plugins? Check out my [Vanilla JS Pocket Guides](https://vanillajsguides.com/) or join the [Vanilla JS Academy](https://vanillajsacademy.com) and level-up as a web developer. 🚀\n\n\u003chr\u003e\n\n\n\n## Getting Started\n\nCompiled and production-ready code can be found in the `dist` directory. The `src` directory contains development code.\n\n### 1. Include Houdini on your site.\n\nHoudini has two required files: JavaScript and CSS.\n\nThere are two versions of the Houdini JavaScript file: the standalone version, and one that comes preloaded with polyfills for `matches()`, `closest()`, `classList`, and `CustomEvent()`, which are only supported in newer browsers.\n\nIf you're including your own polyfills or don't want to enable this feature for older browsers, use the standalone version. Otherwise, use the version with polyfills.\n\n**Direct Download**\n\nYou can [download the files directly from GitHub](https://github.com/cferdinandi/houdini/archive/master.zip).\n\n```html\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"/path/to/houdini.min.css\"\u003e\n\u003cscript src=\"path/to/houdini.polyfills.min.js\"\u003e\u003c/script\u003e\n```\n\n**CDN**\n\nYou can also use the [jsDelivr CDN](https://cdn.jsdelivr.net/gh/cferdinandi/houdini/dist/). I recommend linking to a specific version number or version range to prevent major updates from breaking your site. Houdini uses semantic versioning.\n\n```html\n\u003c!-- Always get the latest version --\u003e\n\u003c!-- Not recommended for production sites! --\u003e\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini/dist/css/houdini.min.js\"\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini/dist/js/houdini.polyfills.min.js\"\u003e\u003c/script\u003e\n\n\u003c!-- Get minor updates and patch fixes within a major version --\u003e\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini@10/dist/css/houdini.min.js\"\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini@11/dist/js/houdini.polyfills.min.js\"\u003e\u003c/script\u003e\n\n\u003c!-- Get patch fixes within a minor version --\u003e\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini@10.0/dist/css/houdini.min.js\"\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini@11.0/dist/js/houdini.polyfills.min.js\"\u003e\u003c/script\u003e\n\n\u003c!-- Get a specific version --\u003e\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini@10.0.0/dist/css/houdini.min.js\"\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/cferdinandi/houdini@11.0.0/dist/js/houdini.polyfills.min.js\"\u003e\u003c/script\u003e\n```\n\n**NPM**\n\nYou can also use NPM (or your favorite package manager).\n\n```bash\nnpm install houdinijs\n```\n\n### 2. Add the markup to your HTML.\n\n1. Wrap your content in a `div` element and assign it a unique ID.\n2. Add a selector (the example below uses the `[data-houdini]` attribute, but this could be a class or ID instead).\n3. Add a `[data-houdini-button]` attribute with the text you want for your button. Houdini will create the button and add any required ARIA attributes automatically when it loads.\n\n```html\n\u003cdiv data-houdini data-houdini-button=\"Show More\" id=\"show-me\"\u003e\n\t\u003cp\u003eNow you see me, now you don't.\u003c/p\u003e\n\u003c/div\u003e\n```\n\n*__Note:__ The ID can serve as the selector. However, if you'll be including multiple disclosures with the same options and settings, it's better to use a shared selector like a class or data attribute for all of them.*\n\n### 3. Initialize Houdini.\n\nIn the footer of your page, after the content, initialize Houdini by passing in the selector for your disclosure component(s). And that's it, you're done. Nice work!\n\n```html\n\u003cscript\u003e\n\tvar disclosure = new Houdini('[data-houdini]');\n\u003c/script\u003e\n```\n\n[Here's a simple demo you can play with.](https://codepen.io/cferdinandi/pen/JeByZQ)\n\n\n\n## Accordions\n\nHoudini also supports accordion groups.\n\n### Accordion Markup\n\nFor semantic reasons, these should have a heading/content relationship. You can use heading elements, data lists, and more.\n\nThe heading should have a `[data-houdini-toggle]` attribute, with a value equal to the ID of the content it toggles.\n\n```html\n\u003ch2 data-houdini-toggle=\"yo-ho-ho\"\u003eYo, ho ho!\u003c/h2\u003e\n\u003cdiv data-houdini-group id=\"yo-ho-ho\"\u003eYo, ho ho and a bottle of rum!\u003c/div\u003e\n\n\u003ch2 data-houdini-toggle=\"ahoy\"\u003eAhoy, there!\u003c/h2\u003e\n\u003cdiv data-houdini-group id=\"ahoy\"\u003eAhoy there, matey!\u003c/div\u003e\n```\n\n### Accordion Initialization\n\nInitialize an accordion by passing in the `isAccordion` option with a value of `true`.\n\n```js\nvar accordion = new Houdini('[data-houdini-group]', {\n\tisAccordion: true\n});\n```\n\nIf opening one accordion section should close any others in the group that are open, also include the `collapseOthers` option, with a value of `true`.\n\nIt's recommended that you give each group a unique selector if using this option.\n\n```js\nvar accordion = new Houdini('[data-houdini-group=\"pirates\"]', {\n\tisAccordion: true,\n\tcollapseOthers: true\n});\n```\n\n\n\n## Demos\n\n- [Basic Demo](https://codepen.io/cferdinandi/pen/JeByZQ)\n- [Button after the content](https://codepen.io/cferdinandi/pen/yQqoRE)\n- [Adding extra info for screen readers](https://codepen.io/cferdinandi/pen/bQQXjG)\n- [Using your own button](https://codepen.io/cferdinandi/pen/jQQgKb)\n- [Accordion](https://codepen.io/cferdinandi/pen/PxBKxp)\n- [Accordion with only one open content area](https://codepen.io/cferdinandi/pen/NEBvew)\n- [Accordion with datalist](https://codepen.io/cferdinandi/pen/LXBjaE)\n\n\n\n## API\n\nHoudini includes smart defaults and works right out of the box. But if you want to customize things, it also has a robust API that provides multiple ways for you to adjust the default options and settings.\n\n### Expanded by Default\n\nIf you want specific disclosures or accordions to be expanded by default, add the `.is-expanded` class to your markup.\n\n**Expanded Disclosure**\n\n```html\n\u003cdiv data-houdini class=\"is-expanded\" id=\"show-me\"\u003e\n\t\u003cp\u003eNow you see me, now you don't.\u003c/p\u003e\n\u003c/div\u003e\n```\n\n**Expanded Accordion**\n\n```html\n\u003ch2 data-houdini-toggle=\"yo-ho-ho\"\u003eYo, ho ho!\u003c/h2\u003e\n\u003cdiv data-houdini-group class=\"is-expanded\" id=\"yo-ho-ho\"\u003eYo, ho ho and a bottle of rum!\u003c/div\u003e\n\n\u003ch2 data-houdini-toggle=\"ahoy\"\u003eAhoy, there!\u003c/h2\u003e\n\u003cdiv data-houdini-group id=\"ahoy\"\u003eAhoy there, matey!\u003c/div\u003e\n```\n\nTo expand all items by default, pass in the `expanded` option with a value of `true`.\n\n```js\n// Disclosure expanded by default\nvar disclosure = new Houdini('[data-houdini]', {\n\texpanded: true\n});\n\n// Accordions expanded by default\nvar accordion = new Houdini('[data-houdini-group]', {\n\tisAccordion: true,\n\texpanded: true\n});\n```\n\n### Extra Info for Screen Readers\n\nIf you want to add extra information to your button for screen reader users, include the `[data-houdini-label]` attribute. This will add an `aria-label` to the button.\n\n```html\n\u003cdiv data-houdini data-houdini-button=\"Show More\" data-houdini-label=\"Show more about pirates\" id=\"show-me\"\u003e\n\t\u003cp\u003eNow you see me, now you don't.\u003c/p\u003e\n\u003c/div\u003e\n```\n\n### Using Your Own Buttons\n\nIf you want to have more control over the toggle buttons, you can include your own instead.\n\nMake sure your button has a `[data-houdini-toggle]` attribute with a value that matches the ID of the content it's supposed to toggle. You should also add the `[hidden]` attribute to hide the button until the script loads (Houdini will make it visible automatically).\n\n```html\n\u003cbutton data-houdini-toggle=\"show-me-too\" aria-label=\"Show more about pirates, too\" hidden\u003e\n\tShow me, too\n\u003c/button\u003e\n```\n\n*__Note:__ You DO NOT need to include the `[data-houdini-button]` or `[data-houdini-label]` attributes, since you're creating your own button and can add that content directly.*\n\n### Options and Settings\n\nYou can override the default settings by passing in user options as a second argument when instantiating.\n\n```javascript\nvar disclosure = new Houdini('[data-houdini]', {\n\n\t// Content\n\tcontentClass: 'houdini',\n\texpanded: false,\n\texpandedClass: 'is-expanded',\n\n\t// Toggle Buttons\n\tbtnAfter: false, // If true, load toggle button after the content\n\tbtnClass: 'houdini-toggle', // The class to add to toggle buttons\n\tbtnAttribute: 'data-houdini-toggle', // The data attribute to use for toggle buttons\n\tbtnTextAttribute: 'data-houdini-button', // The data attribute for the button text\n\tbtnLabelAttribute: 'data-houdini-label', // The data attribute for aria-label text\n\tbtnPreexisting: 'data-houdini-button-preexisting', // The data attribute added to pre-existing buttons\n\n\t// Accordion\n\tisAccordion: false, // If true, treat as an accordion\n\tcollapseOthers: false, // If true, only allow on open piece of content at a time\n\theadingClass: 'houdini-heading', // The class to add to the heading element\n\n\t// Icons\n\ticon: -1, // If true, include an expand/collapse icon\n\ticonClass: 'houdini-toggle-icon', // The class to use for the expand/collapse icon\n\ticonAttribute: 'data-houdini-icon', // The data attribute to use for the expand/collapse icon\n\ticonShow: '+', // The icon to expand an accordion\n\ticonHide: '\u0026ndash;', // The icon to collapse an accordion\n\n\t// Custom Events\n\temitEvents: true // If true, emit custom events\n\n});\n```\n\n### Custom Events\n\nHoudini emits five custom events:\n\n- `houdiniExpand` is emitted on a content element after it's expanded.\n- `houdiniCollapse` is emitted on a content element after it's collapsed.\n- `houdiniInitialize` is emitted on the `document` when the script is initialized, but before the DOM is setup.\n- `houdiniSetup` is emitted on the `document` after the DOM is setup.\n- `houdiniDestroy` is emitted on the `document` after an initialization is destroyed.\n\nOn the `houdiniExpand` and `houdiniCollapse` event, the `event.detail` object includes the content and button. For the `houdiniInitialize`, `houdiniSetup`, and `houdiniDestroy` event, it includes the `settings` object.\n\nAll five events bubble, and can be captured with event delegation.\n\n```js\n// Log scroll events\nvar logHoudiniEvent = function (event) {\n\n\t// The event type\n\tconsole.log('type:', event.type);\n\n\t// The content being expanded or collapsed\n\tconsole.log('content:', event.detail.content);\n\n\t// The button for the content\n\tconsole.log('button:', event.detail.button);\n\n};\n\n// Listen for scroll events\ndocument.addEventListener('houdiniExpand', logHoudiniEvent, false);\ndocument.addEventListener('houdiniCollapse', logHoudiniEvent, false);\n```\n\n### Methods\n\nYou can also call Houdini's methods in your own scripts.\n\n#### toggle()\nToggle the visibility of a content area. Accepts an element or selector string as an argument. Can be the toggle *or* content.\n\n```javascript\nvar disclosure = new Houdini();\n\n// Selector string\ndisclosure.toggle('#yo-ho-ho');\n\n// Content element\nvar content = document.querySelector('#yo-ho-ho');\ndisclosure.toggle(content);\n\n// Button element\nvar btn = document.querySelector('[data-houdini-toggle=\"yo-ho-ho\"]');\ndisclosure.toggle(btn);\n```\n\n#### expand()\nExpand a content area. Accepts an element or selector string as an argument. Can be the toggle *or* content.\n\n```javascript\nvar disclosure = new Houdini();\n\n// Selector string\ndisclosure.expand('#yo-ho-ho');\n\n// Content element\nvar content = document.querySelector('#yo-ho-ho');\ndisclosure.expand(content);\n\n// Button element\nvar btn = document.querySelector('[data-houdini-toggle=\"yo-ho-ho\"]');\ndisclosure.expand(btn);\n```\n\n#### collapse()\nCollapse a content area. Accepts an element or selector string as an argument. Can be the toggle *or* content.\n\n```javascript\nvar disclosure = new Houdini();\n\n// Selector string\ndisclosure.collapse('#yo-ho-ho');\n\n// Content element\nvar content = document.querySelector('#yo-ho-ho');\ndisclosure.collapse(content);\n\n// Button element\nvar btn = document.querySelector('[data-houdini-toggle=\"yo-ho-ho\"]');\ndisclosure.collapse(btn);\n```\n\n#### setup()\nAdds the required markup to the DOM. This runs automatically when you initialize Houdini, but if you add new elements to the DOM later, you should run it again.\n\n```js\nvar disclosure = new Houdini('[data-houdini]');\n\n// Some time later...\ndisclosure.setup();\n```\n\n#### destroy()\nDestroy an instantiation of Houdini and restore the markup to its original state.\n\n```js\nvar disclosure = new Houdini('[data-houdini]');\n\n// Some time later...\ndisclosure.destroy();\n```\n\n\n\n## What's New?\n\n- Supports multiple instantiations at once.\n- Better accessibility and semantics.\n- Automatically progressively enhances your markup for you.\n- Deprecated callbacks in favor of custom events.\n\n### Migrating to Houdini 11 from Houdini 10\n\nBased on feedback from accessibility experts, Houdini no longer supports changing button text or using the same text for all buttons.\n\n- Every content area now needs a `[data-houdini-button]` attribute or a `button` element with a `[data-houdini-toggle]` attribute that matches the content ID.\n- The `btnShow` and `btnHide` options no longer exist.\n\n### Migrating to Houdini 10 from Older Versions\n\nThe entire markup and initialization process has changed in Houdini 10. To migrate:\n\n- Remove existing toggle buttons from your markup.\n- Switch the `.active` to `.is-expanded` for content you want expanded by default.\n- Accordions should use a semantic heading/content relational structure instead of anchor links.\n- Instantiate Houdini with `new Houdini()` instead of `houdini.init()`.\n- Move any callbacks to custom event listeners.\n\n\n\n## Kudos 👏\n\nMajor kudos to [Scott O'Hara](https://www.scottohara.me/) for walking me through the nuances of accordion accessibility and giving me tons of feedback along the way.\n\n\n\n## Browser Compatibility\n\nHoudini works in all modern browsers, and IE 9 and above.\n\nHoudini is built with modern JavaScript APIs, and uses progressive enhancement. If the JavaScript file fails to load, or if your site is viewed on older and less capable browsers, all of your content will be displayed as-is.\n\n### Polyfills\n\nSupport back to IE9 requires polyfills for `matches()`, `closest()`, `classList`, and `CustomEvent()`. Without them, support starts with Edge.\n\nUse the included polyfills version of Houdini, or include your own.\n\n\n\n## License\n\nThe code is available under the [MIT License](LICENSE.md).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcferdinandi%2Fhoudini","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcferdinandi%2Fhoudini","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcferdinandi%2Fhoudini/lists"}