diff --git a/README.md b/README.md
index 77e95e8e3..49da9a573 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,7 @@ The accessibility column indicates whether the functionality is accessible in it
| [dialog](https://lion-web-components.netlify.app/?path=/docs/overlays-dialog--main) | [](https://www.npmjs.com/package/@lion/dialog) | Dialog element | ✔️ |
| [tooltip](https://lion-web-components.netlify.app/?path=/docs/overlays-tooltip--main) | [](https://www.npmjs.com/package/@lion/tooltip) | Tooltip element | [#175][i175] |
| **-- [Navigation System](https://lion-web-components.netlify.app/?path=/docs/navigation-intro--page) --** | | Components which are used to guide users | |
+| [accordion](https://lion-web-components.netlify.app/?path=/docs/navigation-accordion--main) | [](https://www.npmjs.com/package/@lion/accordion) | Accordion | ✔️ |
| [steps](https://lion-web-components.netlify.app/?path=/docs/navigation-steps--main) | [](https://www.npmjs.com/package/@lion/steps) | Multi Step System | n/a |
| [tabs](https://lion-web-components.netlify.app/?path=/docs/navigation-tabs--main) | [](https://www.npmjs.com/package/@lion/tabs) | Move between a small number of equally important views | n/a |
| **-- [localize System](https://lion-web-components.netlify.app/?path=/docs/localize-intro--page) --** | | Localize text, numbers, dates and a way to store/fetch these data. | |
diff --git a/packages/accordion/README.md b/packages/accordion/README.md
new file mode 100644
index 000000000..6c35da962
--- /dev/null
+++ b/packages/accordion/README.md
@@ -0,0 +1,275 @@
+[//]: # 'AUTO INSERT HEADER PREPUBLISH'
+
+# Accordion
+
+`lion-accordion` is a component used to toggle the display of sections of content.
+Its purpose is to reduce the need to scroll when presenting multiple sections of content on a single page. Accordions often allow users to get the big picture before focusing on details.
+
+```js script
+import { LitElement } from 'lit-element';
+import { html } from 'lit-html';
+import './lion-accordion.js';
+
+export default {
+ title: 'Navigation/Accordion',
+};
+```
+
+```js preview-story
+export const main = () => html`
+
+
+
+
+
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit.
+
+
+
+
+
+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde
+ labore reiciendis saepe, iure, optio officiis obcaecati quibusdam.
+
+
+`;
+```
+
+## How to use
+
+### Installation
+
+```bash
+npm i --save @lion/accordion
+```
+
+```js
+import { LionAccordion } from '@lion/accordion';
+// or
+import '@lion/accordion/lion-accordion.js';
+```
+
+### Usage
+
+```html
+
+
+
+
+
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit.
+
+
+
+
+
+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde
+ labore reiciendis saepe, iure, optio officiis obcaecati quibusdam.
+
+
+```
+
+> An accordion exists off a list of expandable headings (of the same level). To get this behavior you need to add a slot="invoker" to the heading and place a button as the content.
+
+## Examples
+
+### Expanded
+
+You can set `expanded` to pre-expand a certain invoker.
+
+```js preview-story
+export const expanded = () => html`
+
+
+
+
+
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit.
+
+
+
+
+
+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde
+ labore reiciendis saepe, iure, optio officiis obcaecati quibusdam.
+
+
+`;
+```
+
+### Slots Order
+
+The invoker and content slots are ordered by DOM order.
+
+This means you must locate your content before it's invoker.
+
+```js preview-story
+export const slotsOrder = () => html`
+
+
+
+
+
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit.
+
+
+
+
+
+ Laboriosam sequi odit cumque, enim aut assumenda itaque quis voluptas est quos fugiat unde
+ labore reiciendis saepe, iure, optio officiis obcaecati quibusdam.
+
+
+`;
+```
+
+### Distribute New Elements
+
+Below, we demonstrate on how you could dynamically add new invoker + content.
+
+```js preview-story
+export const distributeNewElement = () => {
+ const tagName = 'demo-accordion-add-dynamically';
+ if (!customElements.get(tagName)) {
+ customElements.define(
+ tagName,
+ class extends LitElement {
+ static get properties() {
+ return {
+ __collection: { type: Array },
+ };
+ }
+ render() {
+ return html`
+
Append
+
+
+
+
+
content 1
+
+
+
+
content 2
+
+
+
+
Push
+
+
+
+
+
content 1
+
+
+
+
content 2
+ ${this.__collection.map(
+ item => html`
+
+
${item.content}
+ `,
+ )}
+
+
+ `;
+ }
+ constructor() {
+ super();
+ this.__collection = [];
+ }
+ __handleAppendClick() {
+ const accordionElement = this.shadowRoot.querySelector('#appendAccordion');
+ const c = 2;
+ const n = Math.floor(accordionElement.children.length / 2);
+ for (let i = n + 1; i < n + c; i += 1) {
+ const invoker = document.createElement('h4');
+ const button = document.createElement('button');
+ button.innerText = `header ${i}`;
+ invoker.setAttribute('slot', 'invoker');
+ invoker.appendChild(button);
+ const content = document.createElement('p');
+ content.setAttribute('slot', 'content');
+ content.innerText = `content ${i}`;
+ accordionElement.append(invoker);
+ accordionElement.append(content);
+ }
+ }
+ __handlePushClick() {
+ const accordionElement = this.shadowRoot.querySelector('#pushTabs');
+ const i = Math.floor(accordionElement.children.length / 2) + 1;
+ this.__collection = [
+ ...this.__collection,
+ {
+ invoker: `header ${i}`,
+ content: `content ${i}`,
+ },
+ ];
+ }
+ },
+ );
+ }
+ return html` `;
+};
+```
+
+One way is by creating the DOM elements and appending them as needed.
+
+Inside your `lion-accordion` extension, an example for appending nodes on a certain button click:
+
+```js
+__handleAppendClick() {
+ const accordionAmount = this.children.length / 2;
+ const invoker = document.createElement('h4');
+ const button = document.createElement('button');
+ button.innerText = `header ${accordionAmount + 1}`;
+ invoker.setAttribute('slot', 'invoker');
+ invoker.appendChild(button);
+ const content = document.createElement('p');
+ content.setAttribute('slot', 'content');
+ content.innerText = `content ${accordionAmount + 1}`;
+ this.append(invoker);
+ this.append(content);
+}
+```
+
+The other way is by adding data to a Lit property where you loop over this property in your template.
+You then need to ensure this causes a re-render.
+
+```js
+__handlePushClick() {
+ const accordionAmount = this.children.length;
+ myCollection = [
+ ...myCollection,
+ {
+ invoker: `header ${accordionAmount + 1}`,
+ content: `content ${accordionAmount + 1}`,
+ },
+ ];
+ renderMyCollection();
+}
+```
+
+Make sure your template re-renders when myCollection is updated.
+
+```html
+
+ ${myCollection.map(item => html`
+
+
+
+
${item.content}
+ `)}
+
+```
+
+## Rationale
+
+### Contents are not focusable
+
+Focusable elements should have a means to interact with them. Contents themselves do not offer any interactiveness.
+If there is a button or a form inside the tab panel then these elements get focused directly.
diff --git a/packages/accordion/index.js b/packages/accordion/index.js
new file mode 100644
index 000000000..275dd49aa
--- /dev/null
+++ b/packages/accordion/index.js
@@ -0,0 +1 @@
+export { LionAccordion } from './src/LionAccordion.js';
diff --git a/packages/accordion/lion-accordion.js b/packages/accordion/lion-accordion.js
new file mode 100644
index 000000000..b113c589d
--- /dev/null
+++ b/packages/accordion/lion-accordion.js
@@ -0,0 +1,3 @@
+import { LionAccordion } from './src/LionAccordion.js';
+
+customElements.define('lion-accordion', LionAccordion);
diff --git a/packages/accordion/package.json b/packages/accordion/package.json
new file mode 100644
index 000000000..08542881b
--- /dev/null
+++ b/packages/accordion/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@lion/accordion",
+ "version": "0.0.0",
+ "description": "Vertically stacked list of invokers that can be clicked to reveal or hide content associated with them.",
+ "author": "ing-bank",
+ "homepage": "https://github.com/ing-bank/lion/",
+ "license": "MIT",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ing-bank/lion.git",
+ "directory": "packages/accordion"
+ },
+ "scripts": {
+ "prepublishOnly": "../../scripts/npm-prepublish.js",
+ "start": "cd ../../ && yarn dev-server --open packages/tabs/README.md",
+ "test": "cd ../../ && yarn test:browser --grep \"packages/tabs/test/**/*.test.js\"",
+ "test:watch": "cd ../../ && yarn test:browser:watch --grep \"packages/tabs/test/**/*.test.js\""
+ },
+ "keywords": [
+ "lion",
+ "web-components",
+ "accordion"
+ ],
+ "main": "index.js",
+ "module": "index.js",
+ "files": [
+ "docs",
+ "src",
+ "test",
+ "translations",
+ "*.js"
+ ],
+ "sideEffects": [
+ "lion-accordion.js"
+ ],
+ "dependencies": {
+ "@lion/core": "0.7.1"
+ }
+}
diff --git a/packages/accordion/src/LionAccordion.js b/packages/accordion/src/LionAccordion.js
new file mode 100644
index 000000000..ab5a772e2
--- /dev/null
+++ b/packages/accordion/src/LionAccordion.js
@@ -0,0 +1,293 @@
+import { LitElement, css, html } from '@lion/core';
+
+const uuid = () => Math.random().toString(36).substr(2, 10);
+
+const setupContent = ({ element, uid, index }) => {
+ element.style.setProperty('order', index + 1);
+ element.setAttribute('id', `content-${uid}`);
+ element.setAttribute('aria-labelledby', `invoker-${uid}`);
+};
+
+const setupInvoker = ({ element, uid, index, clickHandler, keydownHandler }) => {
+ element.style.setProperty('order', index + 1);
+ element.firstElementChild.setAttribute('id', `invoker-${uid}`);
+ element.firstElementChild.setAttribute('aria-controls', `content-${uid}`);
+ element.firstElementChild.addEventListener('click', clickHandler);
+ element.firstElementChild.addEventListener('keyup', keydownHandler);
+};
+
+const cleanInvoker = (element, clickHandler, keydownHandler) => {
+ element.firstElementChild.removeAttribute('id');
+ element.firstElementChild.removeAttribute('aria-controls');
+ element.firstElementChild.removeEventListener('click', clickHandler);
+ element.firstElementChild.removeEventListener('keyup', keydownHandler);
+};
+
+const focusInvoker = element => {
+ element.firstElementChild.focus();
+ element.firstElementChild.setAttribute('focused', true);
+};
+
+const unfocusInvoker = element => {
+ element.firstElementChild.removeAttribute('focused');
+};
+
+const expandInvoker = element => {
+ element.setAttribute('expanded', true);
+ element.firstElementChild.setAttribute('expanded', true);
+ element.firstElementChild.setAttribute('aria-expanded', true);
+};
+
+const collapseInvoker = element => {
+ element.removeAttribute('expanded');
+ element.firstElementChild.removeAttribute('expanded');
+ element.firstElementChild.setAttribute('aria-expanded', false);
+};
+
+const expandContent = element => {
+ element.setAttribute('expanded', true);
+};
+
+const collapseContent = element => {
+ element.removeAttribute('expanded');
+};
+
+/**
+ * # webcomponent
+ *
+ * @customElement lion-accordion
+ * @extends LitElement
+ */
+export class LionAccordion extends LitElement {
+ static get properties() {
+ return {
+ /**
+ * index number of the focused accordion
+ */
+ focusedIndex: {
+ type: Number,
+ },
+ /**
+ * array of indices of the expanded accordions
+ */
+ expanded: {
+ type: Array,
+ },
+ };
+ }
+
+ static get styles() {
+ return [
+ css`
+ .accordion {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .accordion ::slotted([slot='invoker']) {
+ margin: 0;
+ }
+
+ .accordion ::slotted([slot='invoker'][expanded]) {
+ font-weight: bold;
+ }
+
+ .accordion ::slotted([slot='content']) {
+ margin: 0;
+ visibility: hidden;
+ display: none;
+ }
+
+ .accordion ::slotted([slot='content'][expanded]) {
+ visibility: visible;
+ display: block;
+ }
+ `,
+ ];
+ }
+
+ render() {
+ return html`
+