-Lion web components is a set of highly performant, accessible and flexible Web Components.
-They provide an unopinionated, white label layer that can be extended to your own layer of components.
+
-For some more details see the [announcement blog post](https://medium.com/ing-blog/ing-open-sources-lion-a-library-for-performant-accessible-flexible-web-components-22ad165b1d3d).
+
-[](https://www.tickgit.com/browse?repo=github.com/ing-bank/lion)
+**Lion is a set of highly performant, accessible and flexible Web Components.!**
-## Demos
+They provide an unopinionated, white-label layer that can be extended to your own layer of components.
-We do have a [live Storybook](http://lion-web-components.netlify.com) which shows all our components.
+- **High Performance:** Focused on great performance in all relevant browsers with a minimal number of dependencies.
+- **Accessibility:** Aimed at compliance with the WCAG 2.1 AA standard to create components that are accessible for everybody.
+- **Flexibility:** Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs.
+- **Modern Code:** Lion is distributes as pure es modules.
+- **Exposes functions/classes and Web Components:** Ships a functionality in it's most appropriate form.
-**Please note:** This project uses Yarn [Workspaces](https://classic.yarnpkg.com/en/docs/workspaces). If you want to run all demos locally you need to get [Yarn](https://classic.yarnpkg.com/en/docs/install) and install all dependencies by executing `yarn install`.
-
-The code examples make use of [Javascript tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) which are a key component of the [lit-html engine](https://lit-html.polymer-project.org/) used in Lion.
-Additionally imports like `import '@lion/form/lion-form.js'` need to be transformed somehow, for example by [es-dev-server](https://open-wc.org/developing/es-dev-server.html#node-resolve).
-
-## Features
-
-- High Performance - Focused on great performance in all relevant browsers with a minimal number of dependencies
-- Accessibility - Aimed at compliance with the WCAG 2.1 AA standard to create components that are accessible for everybody
-- Flexibility - Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs
-- Pure ES Modules
-- Exposes functions/classes and Web Components
-
-> Note: These demos may look a little bland but that is on purpose. They only come with functional stylings.
+> Note: Our demos may look a little bland but that is on purpose. They only come with functional stylings.
> This makes sense as the main use case is to extend those components and if you do you do not want to override existing stylings.
+
+
## How to install
```bash
@@ -82,7 +104,7 @@ You can also use the lion elements directly, although this is likely not a commo
```html
@@ -150,18 +172,17 @@ The accessibility column indicates whether the functionality is accessible in it
## Technologies
-Lion Web Components aims to be future proof and use well-supported proven technology. The stack we have chosen should reflect this.
+Lion Web Components aims to be future-proof and use well-supported proven technology. The stack we have chosen should reflect this.
- [lit-html](https://lit-html.polymer-project.org) and [lit-element](https://lit-element.polymer-project.org)
- [npm](http://npmjs.com)
- [yarn](https://yarnpkg.com)
-- [open-wc](https://open-wc.org)
-- [Karma](https://karma-runner.github.io)
+- [Open Web Components](https://open-wc.org)
+- [Modern Web](https://modern-web.dev)
- [Mocha](https://mochajs.org)
- [Chai](https://www.chaijs.com)
- [ESLint](https://eslint.org)
- [prettier](https://prettier.io)
-- [Storybook](https://storybook.js.org)
- [ES modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
- Lots and lots of tests
@@ -180,6 +201,8 @@ Check out our [coding guidelines](https://lion-web-components.netlify.app/?path=
## How to contribute
+**Please note:** This project uses Yarn [Workspaces](https://classic.yarnpkg.com/en/docs/workspaces). If you want to run all demos locally you need to get [Yarn](https://classic.yarnpkg.com/en/docs/install) and install all dependencies by executing `yarn install`.
+
Lion Web Components are only as good as its contributions.
Read our [contribution guide](https://github.com/ing-bank/lion/blob/master/CONTRIBUTING.md) and feel free to enhance/improve Lion. We keep feature requests closed while we're not working on them.
diff --git a/assets/logo.png b/assets/logo.png
new file mode 100644
index 000000000..b4528d72d
Binary files /dev/null and b/assets/logo.png differ
diff --git a/demo/docs/20-lea-tabs.md b/demo/docs/20-lea-tabs.md
deleted file mode 100644
index 5c3154b10..000000000
--- a/demo/docs/20-lea-tabs.md
+++ /dev/null
@@ -1,231 +0,0 @@
-# lea Tabs
-
-> This is not a real implementation!
->
-> It is an example of how you can reuse the functionality of Lion to create your own Style System
-
-`lea-tabs` implements tabs view to allow users to quickly move between a small number of equally important views.
-
-```js script
-import { html } from '@lion/core';
-import { LitElement } from '@lion/core';
-
-import '../lea-tabs.js';
-import '../lea-tab.js';
-import '../lea-tab-panel.js';
-
-export default {
- title: 'Intro/Tabs Example',
-};
-```
-
-```js preview-story
-export const main = () => html`
-
- Info
- Info page with lots of information about us.
- Work
- Work page that showcases our work.
-
-`;
-```
-
-## How to use
-
-### Installation
-
-```sh
-npm i --save @lion/tabs;
-```
-
-### Usage
-
-```js
-import '@lion/tabs/lea-tabs.js';
-```
-
-```html
-
- Info
- Info page with lots of information about us.
- Work
- Work page that showcases our work.
-
-```
-
-## Examples
-
-### Selected Index
-
-You can set the `selectedIndex` to select a certain tab.
-
-```js preview-story
-export const selectedIndex = () => html`
-
- Info
- Info page with lots of information about us.
- Work
- Work page that showcases our work.
-
-`;
-```
-
-### Slots Order
-
-The tab and panel slots are ordered by DOM order.
-
-This means you can switch the grouping in your `lea-tabs` from tab + panel to all tabs first or all panels first.
-
-```js preview-story
-export const slotsOrder = () => html`
-
- Info
- Work
- Info page with lots of information about us.
- Work page that showcases our work.
-
-`;
-```
-
-### Distribute New Elements
-
-Below, we demonstrate on how you could dynamically add new tab + panels.
-
-```js preview-story
-export const distributeNewElements = () => {
- const tagName = 'lea-tabs-experimental';
- if (!customElements.get(tagName)) {
- customElements.define(
- tagName,
- class extends LitElement {
- static get properties() {
- return {
- __collection: { type: Array },
- };
- }
- render() {
- return html`
-
Append
-
-
- tab 1
- panel 1
- tab 2
- panel 2
-
-
-
Push
-
-
- tab 1
- panel 1
- tab 2
- panel 2
- ${this.__collection.map(
- item => html`
- ${item.button}
- ${item.panel}
- `,
- )}
-
- `;
- }
- constructor() {
- super();
- this.__collection = [];
- }
- __handleAppendClick() {
- const tabsElement = this.shadowRoot.querySelector('#appendTabs');
- const c = 2;
- const n = Math.floor(tabsElement.children.length / 2);
- for (let i = n + 1; i < n + c; i += 1) {
- const tab = document.createElement('lea-tab');
- tab.setAttribute('slot', 'tab');
- tab.innerText = `tab ${i}`;
- const panel = document.createElement('lea-tab-panel');
- panel.setAttribute('slot', 'panel');
- panel.innerText = `panel ${i}`;
- tabsElement.append(tab);
- tabsElement.append(panel);
- }
- }
- __handlePushClick() {
- const tabsElement = this.shadowRoot.querySelector('#pushTabs');
- const i = Math.floor(tabsElement.children.length / 2) + 1;
- this.__collection = [
- ...this.__collection,
- {
- button: `tab ${i}`,
- panel: `panel ${i}`,
- },
- ];
- }
- },
- );
- }
- return html` `;
-};
-```
-
-One way is by creating the DOM elements and appending them as needed.
-
-Inside your `lea-tabs` extension, an example for appending nodes on a certain button click:
-
-```js
-__handleAppendClick() {
- const tabsAmount = this.children.length / 2;
- const tab = document.createElement('lea-tab');
- tab.setAttribute('slot', 'tab');
- tab.innerText = `tab ${tabsAmount + 1}`;
- const panel = document.createElement('lea-tab-panel');
- panel.setAttribute('slot', 'panel');
- panel.innerText = `panel ${tabsAmount + 1}`;
- this.append(tab);
- this.append(panel);
-}
-```
-
-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 tabsAmount = this.children.length;
- myCollection = [
- ...myCollection,
- {
- button: `tab ${tabsAmount + 1}`,
- panel: `panel ${tabsAmount + 1}`,
- },
- ];
- renderMyCollection();
-}
-```
-
-Make sure your template re-renders when myCollection is updated.
-
-```html
-
- ${myCollection.map(item => html`
- ${item.button}
- ${item.panel}
- `)}
-
-```
-
-## Rationale
-
-### No separate active/focus state when using keyboard
-
-We will immediately switch content as all our content comes from light dom (e.g. no latency)
-
-See Note at
-
-> It is recommended that tabs activate automatically when they receive focus as long as their
-> associated tab panels are displayed without noticeable latency. This typically requires tab
-> panel content to be preloaded.
-
-### Panels are not focusable
-
-Focusable elements should have a means to interact with them. Tab panels 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/demo/lea-tab-panel.js b/demo/lea-tab-panel.js
deleted file mode 100644
index c9b048d53..000000000
--- a/demo/lea-tab-panel.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { LeaTabPanel } from './src/LeaTabPanel.js';
-
-customElements.define('lea-tab-panel', LeaTabPanel);
diff --git a/demo/lea-tab.js b/demo/lea-tab.js
deleted file mode 100644
index 5fb7621ab..000000000
--- a/demo/lea-tab.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { LeaTab } from './src/LeaTab.js';
-
-customElements.define('lea-tab', LeaTab);
diff --git a/demo/lea-tabs.js b/demo/lea-tabs.js
deleted file mode 100644
index 29092c758..000000000
--- a/demo/lea-tabs.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { LeaTabs } from './src/LeaTabs.js';
-
-customElements.define('lea-tabs', LeaTabs);
diff --git a/docs/404.md b/docs/404.md
new file mode 100644
index 000000000..9bdb1a891
--- /dev/null
+++ b/docs/404.md
@@ -0,0 +1,4 @@
+---
+permalink: 404.html
+layout: layout-404
+---
diff --git a/docs/README.md b/docs/README.md
deleted file mode 100644
index 74610c7a8..000000000
--- a/docs/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Coding Guidelines
-
-```js script
-export default {
- title: 'Guidelines/Intro',
-};
-```
-
-First be sure to understand our [definitions](?path=/docs/guidelines-definitions--page).
-
-- [Guidelines for Styling](?path=/docs/guidelines-styling--page)
-- [Guidelines for Scoped Elements](?path=/docs/guidelines-scoped-elements--page)
-- [Guidelines for SubClasser APIs](?path=/docs/guidelines-subclasser-apis--page)
diff --git a/docs/_assets/_static/icons/android-chrome-192x192.png b/docs/_assets/_static/icons/android-chrome-192x192.png
new file mode 100644
index 000000000..3de4ce367
Binary files /dev/null and b/docs/_assets/_static/icons/android-chrome-192x192.png differ
diff --git a/docs/_assets/_static/icons/android-chrome-512x512.png b/docs/_assets/_static/icons/android-chrome-512x512.png
new file mode 100644
index 000000000..2f4b2547d
Binary files /dev/null and b/docs/_assets/_static/icons/android-chrome-512x512.png differ
diff --git a/docs/_assets/_static/icons/apple-touch-icon.png b/docs/_assets/_static/icons/apple-touch-icon.png
new file mode 100644
index 000000000..bc556fc6f
Binary files /dev/null and b/docs/_assets/_static/icons/apple-touch-icon.png differ
diff --git a/docs/_assets/_static/icons/favicon-16x16.png b/docs/_assets/_static/icons/favicon-16x16.png
new file mode 100644
index 000000000..da06d9604
Binary files /dev/null and b/docs/_assets/_static/icons/favicon-16x16.png differ
diff --git a/docs/_assets/_static/icons/favicon-32x32.png b/docs/_assets/_static/icons/favicon-32x32.png
new file mode 100644
index 000000000..31c2edecd
Binary files /dev/null and b/docs/_assets/_static/icons/favicon-32x32.png differ
diff --git a/docs/_assets/_static/icons/mstile-150x150.png b/docs/_assets/_static/icons/mstile-150x150.png
new file mode 100644
index 000000000..58eec7e9a
Binary files /dev/null and b/docs/_assets/_static/icons/mstile-150x150.png differ
diff --git a/docs/_assets/_static/icons/safari-pinned-tab.svg b/docs/_assets/_static/icons/safari-pinned-tab.svg
new file mode 100644
index 000000000..78295723e
--- /dev/null
+++ b/docs/_assets/_static/icons/safari-pinned-tab.svg
@@ -0,0 +1,39 @@
+
+
+
diff --git a/docs/_assets/logo.svg b/docs/_assets/logo.svg
new file mode 100644
index 000000000..dec159884
--- /dev/null
+++ b/docs/_assets/logo.svg
@@ -0,0 +1,31 @@
+
diff --git a/docs/_assets/style.css b/docs/_assets/style.css
new file mode 100644
index 000000000..04e56e465
--- /dev/null
+++ b/docs/_assets/style.css
@@ -0,0 +1,23 @@
+body[layout='layout-home'] .markdown-body .call-to-action:nth-of-type(2) {
+ --primary-color: #222;
+ --primary-color-lighter: #333;
+ --primary-color-darker: #000;
+}
+
+.markdown-body img {
+ width: 100%;
+}
+
+mdjs-preview {
+ margin-bottom: 20px;
+}
+
+#main-header a[href="/blog/"] {
+ display: none;
+}
+
+@media screen and (min-width: 1024px) {
+ #main-header a[href="/blog/"] {
+ display: block;
+ }
+}
diff --git a/docs/_assets/variables.css b/docs/_assets/variables.css
new file mode 100644
index 000000000..181a4b183
--- /dev/null
+++ b/docs/_assets/variables.css
@@ -0,0 +1,41 @@
+html {
+ --primary-color: #a07f23;
+ --primary-color-lighter: #d1a62f;
+ --primary-color-darker: #705918;
+ --primary-color-accent: #cee5f6;
+ --primary-text-color: #2c3e50;
+ --primary-lines-color: #ccc;
+
+ /* Contrast colors */
+ --contrast-color-light: #fff;
+ --contrast-color-dark: #1d3557;
+
+ /* background-colors */
+ --page-background: white;
+ --footer-background: rgba(0, 0, 0, 0.1);
+
+ --text-color: black;
+}
+
+html.dark {
+ --primary-color: #e63946;
+ --primary-color-lighter: #e25761;
+ --primary-color-darker: #a22831;
+ --primary-color-accent: #cee5f6;
+ --primary-text-color: #eee;
+
+ /* Contrast colors */
+ --contrast-color-light: #fff;
+ --contrast-color-dark: #1d3557;
+
+ /* background-colors */
+ --page-background: #333;
+ --footer-background: #4f4f4f;
+
+ --text-color: white;
+
+ --markdown-octicon-link: white;
+ --markdown-syntax-background-color: #a0a0a0;
+ --markdown-link-color: #fb7881;
+ --markdown-blockquote-color: #c9e3ff;
+}
diff --git a/docs/_data/footer.json b/docs/_data/footer.json
new file mode 100644
index 000000000..c5053c70b
--- /dev/null
+++ b/docs/_data/footer.json
@@ -0,0 +1,41 @@
+[
+ {
+ "name": "Discover",
+ "children": [
+ {
+ "text": "Blog",
+ "href": "/blog/"
+ },
+ {
+ "text": "Help and Feedback",
+ "href": "https://github.com/ing-bank/lion/issues"
+ }
+ ]
+ },
+ {
+ "name": "Follow",
+ "children": [
+ {
+ "text": "Github",
+ "href": "https://github.com/ing-bank/lion"
+ },
+ {
+ "text": "Twitter",
+ "href": "https://twitter.com/daKmoR"
+ },
+ {
+ "text": "Slack",
+ "href": "/about/slack/"
+ }
+ ]
+ },
+ {
+ "name": "Support",
+ "children": [
+ {
+ "text": "Contribute",
+ "href": "https://github.com/ing-bank/lion/blob/master/CONTRIBUTING.md"
+ }
+ ]
+ }
+]
diff --git a/docs/_data/site.cjs b/docs/_data/site.cjs
new file mode 100644
index 000000000..7cf118bc8
--- /dev/null
+++ b/docs/_data/site.cjs
@@ -0,0 +1,22 @@
+module.exports = async function () {
+ return {
+ dir: 'ltr',
+ lang: 'en',
+ name: 'Lion',
+ description: 'Fundamental white label web components for building your design system',
+ socialLinks: [
+ {
+ name: 'GitHub',
+ url: 'https://github.com/ing-bank/lion',
+ },
+ ],
+ gitSiteUrl: 'https://github.com/ing-bank/lion',
+ gitBranch: 'master',
+ helpUrl: 'https://github.com/ing-bank/lion/issues',
+ logoAlt: 'Lion Logo',
+ iconColorMaskIcon: '#3f93ce',
+ iconColorMsapplicationTileColor: '#1d3557',
+ iconColorThemeColor: '#1d3557',
+ analytics: 'G-151V7YV71K',
+ };
+};
diff --git a/docs/about/slack.md b/docs/about/slack.md
new file mode 100644
index 000000000..90b4a8b8d
--- /dev/null
+++ b/docs/about/slack.md
@@ -0,0 +1,5 @@
+# Slack
+
+You can also find us on the Polymer Slack in the [#lion](https://polymer.slack.com/archives/CJGFWJN9J) channel.
+
+You can join the Polymer Slack by visiting [https://www.polymer-project.org/slack-invite](https://www.polymer-project.org/slack-invite).
diff --git a/docs/blog/controlling-exports.md b/docs/blog/controlling-exports.md
new file mode 100644
index 000000000..1500627fc
--- /dev/null
+++ b/docs/blog/controlling-exports.md
@@ -0,0 +1,173 @@
+---
+title: Controlling exports
+published: true
+description: Maintainer can now define their public api of a package itself.
+date: 2021-03-09
+tags: [javascript, exports]
+cover_image: /blog/images/controlling-exports-cover-image.jpg
+---
+
+When publishing npm packages it can often be hard to understand what users are actually using.
+
+Basically, JavaScript allows you to write imports like this
+
+```js
+import { addLeadingZero } '@lion/localize/src/date/utils/addLeadingZero.js';
+```
+
+We as the maintainers of that package however consider this internal code, so any changes to it will not result in a new breaking change update.
+So if you depend on this directly then your code may break with any minor or patch update.
+
+So why would we even "allow" such imports? Because so far there was no way to actually define and enforce what a maintainer considers to be the public API of the package. Now, with the introduction of node's [Package Entry Points](https://nodejs.org/api/packages.html#packages_package_entry_points) and the adoption of it in [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve#package-entrypoints) it can now be used in node, [@web/dev-server](https://modern-web.dev/docs/dev-server/overview/) and [rollup](https://rollupjs.org/).
+
+How can you use those `Package Entry Points`?
+
+Let's assume you have these two files in your `src` directory.
+
+```js
+// src/index.js
+export { foo } from './public.js';
+
+// src/public.js
+export const foo = 'public foo';
+
+// src/internal.js
+export const bar = 'internal bar';
+```
+
+If you publish the package "normally" then users will be able to write imports like this
+
+```js
+import { foo } from 'my-pkg';
+import { foo } from 'my-pkg/src/public.js';
+import { bar } from 'my-pkg/src/internal.js';
+```
+
+This has multiple issues, described in use cases:
+
+1. Case 1: For maintenance purposes, we want to split `public.js` in `featureA.js` and `helpers.js`. Now all imports that use `import { foo } from 'my-pkg/src/public.js';` will break.
+2. Case 2: We found a package that solved what we did in `internal.js` in a more generic way. We don't treat it as public API, so we actually go ahead and get rid of this file. Now all imports for `import { bar } from 'my-pkg/src/internal.js';` will break.
+
+Instead, what we actually want is all our consumers using the intended public API, which is
+
+```js
+import { foo } from 'my-pkg';
+```
+
+This way, above cases 1 and 2 just don't have any effect and we can freely refactor our codebase without introducing breaking changes. This means we can keep improving our code without disturbing our users. It's a win-win situation 🎉
+
+Now, if someone tries to use a not defined export, like
+
+```js
+import { bar } from 'my-pkg/src/internal.js';
+```
+
+Then an error will be thrown
+
+```
+Could not resolve import "my-pkg/src/internal.js"
+```
+
+If a users needs access to `bar` then a GitHub Issue/Discussion should be opened to request it.
+Maintainers can then have a discussion if they want to make this part of the public API or not.
+
+## Using consumer import in your own code
+
+An additional benefit of using Package Entry Points is that you can write imports in the same way as your consumers.
+
+So instead of writing demos or tests like
+
+```js
+import { LionInput } from '../src/LionInput.js';
+```
+
+we can now write
+
+```js
+import { LionInput } from '@lion/input';
+```
+
+This has the following benefits:
+
+- We can make sure everything we are demoing/testing is actually part of the public API
+- Users can read / copy our demo code and it just works
+- We can move files around without needing to adjust our demos/docs/tests
+
+## Exports for a single web component
+
+Usage:
+
+```js
+// only the classes
+import { MyElement } from 'my-element';
+
+// OR
+
+// execute customElements.define
+import 'my-element/define';
+```
+
+Package Entry Points:
+
+```json
+"exports": {
+ ".": "./src/index.js",
+ "define": "./src/my-element.js",
+}
+```
+
+## Exports for multiple web components
+
+Usage:
+
+```js
+// only the classes
+import { MyElement, SubElement } from 'my-element';
+
+// OR
+
+// execute customElements.define for all elements
+import 'my-element/define';
+
+// execute customElements.define for a single element
+import 'my-element/define-my-element';
+import 'my-element/define-sub-element';
+```
+
+Package Entry Points:
+
+```json
+"exports": {
+ ".": "./src/index.js",
+ "define": "./src/define.js",
+ "define-my-element": "./src/my-element.js",
+ "define-sub-element": "./src/sub-element.js",
+}
+```
+
+in this case, the `src/define.js` should not contain any `customElements.define`, but instead it just imports the other define files
+
+```js
+import 'my-element/define-my-element';
+import 'my-element/define-sub-element';
+```
+
+## What does it mean for Lion?
+
+Imports that worked before will need be be adjusted as they will no longer work.
+This is a breaking change.
+
+```js
+// no longer works
+import '@lion/input/lion-input';
+import '@lion/input/lion-input.js';
+import { LionInput } from '@lion/input/src/LionInput.js';
+
+// works
+import '@lion/input/define';
+import { LionInput } from '@lion/input';
+```
+
+---
+
+Photo by Curology on Unsplash
diff --git a/docs/extending-documentation.md b/docs/blog/extending-documentation.md
similarity index 88%
rename from docs/extending-documentation.md
rename to docs/blog/extending-documentation.md
index 29f4b4e00..e8caad715 100644
--- a/docs/extending-documentation.md
+++ b/docs/blog/extending-documentation.md
@@ -1,12 +1,13 @@
-# Extending Lion Documentation
+---
+title: Extending Lion Documentation
+published: true
+description: Extending a component library and its documentation for a speedy design system
+date: 2020-12-01
+tags: javascript, documentation, demos
+cover_image: https://miro.medium.com/max/2000/1*NZ6tdtJHHJjxtPmIFxWpGw.jpeg
+---
-```js script
-export default {
- title: 'Guidelines/Extending documentation',
-};
-```
-
-If you extend [Lion](https://lion-web-components.netlify.app/) components, you don't only want to reuse the components, but you probably want to reuse the documentation (Storybook demos) as well. Wouldn't it be nice to just take it all from lion, but replace it with your own design system extension, so you don't have the extra maintenance of essentially copying the docs from `Lion` for your own design system implementation?
+If you extend [Lion](https://lion-web.netlify.app/) components, you don't only want to reuse the components, but you probably want to reuse the documentation (Storybook demos) as well. Wouldn't it be nice to just take it all from lion, but replace it with your own design system extension, so you don't have the extra maintenance of essentially copying the docs from `Lion` for your own design system implementation?
In this blog we will explain how `Lion` supports this use case, and allows you to extend not just the components, but also the documentation.
@@ -29,13 +30,13 @@ For step 1, we can use `@open-wc/demoing-storybook` version 2 or higher, which u
Install it manually (and see the docs for configuring):
-```sh
+```bash
npm i @open-wc/demoing-storybook --save-dev
```
Or scaffold it with basic configuration by doing
-```sh
+```bash
npm init @open-wc
```
@@ -66,11 +67,11 @@ This step alone should already give you the `LionTabs` docs inside your own Stor
Potentially the hardest part is to analyse your extension `LeaTabs`, and to figure out how we should transform the import paths for `LionTabs` to new paths to your `LeaTabs`.
-To do this we make use of [Providence](https://lion-web-components.netlify.app/?path=/docs/tools-providence-main--run-providence). This tool has a command that creates a full map of all the import paths of a reference project (`Lion`) and can replace them with the correct paths of a target project (`Lea`).
+To do this we make use of [Providence](https://lion-web.netlify.app/?path=/docs/tools-providence-main--run-providence). This tool has a command that creates a full map of all the import paths of a reference project (`Lion`) and can replace them with the correct paths of a target project (`Lea`).
So lets install it:
-```sh
+```bash
npm i providence-analytics --save-dev
```
@@ -86,7 +87,7 @@ The `--prefix-from` and `--prefix-to` are the prefixes of the project you extend
If you know you only use a single component from lion, you can reduce the time the tool needs for analysis, by specifying this package `-r 'node_modules/@lion/tabs'`.
-Running the script will create a `providence-extend-docs-data.json` file, with all from/to information. You can change the name / location of the output file, refer to [Providence Documentation](https://lion-web-components.netlify.app/?path=/docs/tools-providence-main--run-providence) for this.
+Running the script will create a `providence-extend-docs-data.json` file, with all from/to information. You can change the name / location of the output file, refer to [Providence Documentation](https://lion-web.netlify.app/?path=/docs/tools-providence-main--run-providence) for this.
#### Running it automatically when upgrading lion dependency
@@ -103,13 +104,13 @@ Inside ING, our design system also makes use of this providence tool to create t
Now that we have a JSON file with all the information we need to know about to replace import paths and tagnames inside templates, we can start transforming the `LionTabs` documentation to `LeaTabs` documentation.
-For this, we created a `babel-plugin` called [babel-plugin-extend-docs](https://lion-web-components.netlify.app/?path=/docs/tools-babelpluginextenddocs--page).
+For this, we created a `babel-plugin` called [babel-plugin-extend-docs](https://lion-web.netlify.app/?path=/docs/tools-babelpluginextenddocs--page).
This will analyse the JavaScript script and story content inside the markdown files, which uses [MDJS](https://open-wc.org/mdjs/) syntax, and transform it on the fly in `es-dev-server`, as well as on rollup build for production.
So all you need to do is to install this plugin:
-```sh
+```bash
npm i babel-plugin-extend-docs --save-dev
```
@@ -164,7 +165,7 @@ In some cases you don't want to show all examples of how to use a component. Som
In our example, we will show you have to remove the `Rationale` section that you would normally inherit from the `Lion` documentation.
-For this step we make use of a remark plugin for the MD content, similar to how you would use a babel plugin for JS content. It is called [Remark extend](https://lion-web-components.netlify.app/?path=/docs/tools-remark-extend--page).
+For this step we make use of a remark plugin for the MD content, similar to how you would use a babel plugin for JS content. It is called [Remark extend](https://lion-web.netlify.app/?path=/docs/tools-remark-extend--page).
It will let you add, remove or replace sections or specific words.
First of all we need to add the plugin to the `.storybook/main.js`:
@@ -256,7 +257,7 @@ Or you can add an extra paragraph below the content. Create a fenced codeblock:
::removeMdAfter(':scope:last-child')
```
-> See [Remark extend](https://lion-web-components.netlify.app/?path=/docs/tools-remark-extend--page) for more information
+> See [Remark extend](https://lion-web.netlify.app/?path=/docs/tools-remark-extend--page) for more information
### Lea Tabs Special Feature
diff --git a/docs/blog/extending-lions-website.md b/docs/blog/extending-lions-website.md
new file mode 100644
index 000000000..00a770745
--- /dev/null
+++ b/docs/blog/extending-lions-website.md
@@ -0,0 +1,124 @@
+---
+title: Extending lions website
+published: false
+description: A static website with docs and demos for lion
+date: 2021-03-10
+tags: [javascript, rocket, documentation]
+cover_image: /blog/images/introducing-lions-website-cover-image.jpg
+---
+
+After a month of preparations we can finally present to you our new website. With it we are enabled to give more context to each of our components.
+Right now it's more or less a port for our existing demos from storybook. But we can organize it in a nicer way by splitting it into components, docs, guides and blog sections.
+
+## Importing Content into Markdown files
+
+We now use a system to import content from one markdown file into another.
+So let's say you find the documentation of `input-amount` useful and you want to present it on your page as well.
+Anywhere in your documentation you can now write.
+
+👉 `docs/input-amount.md`
+
+````md
+# Input Amount
+
+```js ::import('@lion/input-amount/docs/overview.md', 'heading[depth=1] ~ *')
+
+```
+````
+
+So when you now go to `https://domain.com/input-amount/` you will actually see the content from `@lion/input-amount/docs/overview.md`.
+
+Why is it a `js code block`?
+
+- You can define a start and end for what should be imported (using [unist-util-select](https://github.com/syntax-tree/unist-util-select#support))
+- You can add adjustments to the content as a function
+- As links like `[go there](@lion/input-amount/docs/overview.md)` would not work anyways, having one syntax that allows for additional features is enough
+
+### Importing Partial Content
+
+Quite often you probably don't want to get the full file, so there is a special helper for that.
+
+Let's assume you have the following source file
+
+```md
+# First Headline
+
+Content of first headline
+
+## Second Headline
+
+Content of second headline
+
+## Third Headline
+
+Content of Third headline
+```
+
+and you only want to get the 2nd "block" e.g. 2nd headline + text.
+
+With `importBlock` you can get exactly that.
+
+A block starts with a headline and ends when the next headline of an equal level starts.
+
+Note: importBlock is a faster way of writing imports for headlines
+
+```md
+::importBlock('./path/to/file.md', '## red')
+// is the same as
+::import('./path/to/file.md', 'heading[depth=2]:has([value=red])', 'heading[depth=2]:has([value=red]) ~heading[depth=2]')
+```
+
+If you want to know more please look at the documentation for [remark-extend](../docs/node-tools/remark-extend/overview.md).
+
+## Upgrading Documentation
+
+Unfortunately this is quite a different concept then what we used with storybook before. Luckily the content is very much the same.
+
+Let's convert an example page
+
+### FROM
+
+````md
+# Calendar
+
+`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](?path=/docs/calendar--default-story).
+
+```js script
+import { html, css } from '@lion/core';
+import './lion-calendar.js';
+
+export default {
+ title: 'Others/Calendar',
+};
+```
+
+...
+````
+
+### TO
+
+````md
+# Component >> Calendar ||20
+
+`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](../../path/to/calendar.md).
+
+```js script
+import { html, css } from '@lion/core';
+import '@lion/calendar/define';
+```
+
+...
+````
+
+So what you need to do:
+
+1. Remove the js default export and put the title / navigation into the headline
+2. Replace all imports with the Package Entry Points
+3. Adjust all link to be relative links to actual files (instead of storybook specific urls)
+
+## Upgrading Extending Documentation
+
+If you are currently extending our storybook documentation then this will no longer work.
+For now we will need to ask you to stay on the current version and NOT upgrade.
+
+We will release the necessary tools to extend our Lion Website in the upcoming weeks 🤗
diff --git a/docs/blog/images/controlling-exports-cover-image.jpg b/docs/blog/images/controlling-exports-cover-image.jpg
new file mode 100644
index 000000000..ed90497ba
Binary files /dev/null and b/docs/blog/images/controlling-exports-cover-image.jpg differ
diff --git a/demo/docs/assets/side-by-side.png b/docs/blog/images/ing-open-sources-lion-side-by-side.png
similarity index 100%
rename from demo/docs/assets/side-by-side.png
rename to docs/blog/images/ing-open-sources-lion-side-by-side.png
diff --git a/docs/blog/images/introducing-lions-website-cover-image.jpg b/docs/blog/images/introducing-lions-website-cover-image.jpg
new file mode 100644
index 000000000..82cadc685
Binary files /dev/null and b/docs/blog/images/introducing-lions-website-cover-image.jpg differ
diff --git a/docs/blog/index.md b/docs/blog/index.md
new file mode 100644
index 000000000..48d22b3e7
--- /dev/null
+++ b/docs/blog/index.md
@@ -0,0 +1,14 @@
+---
+title: Lion Blog
+layout: layout-blog-overview
+eleventyNavigation:
+ key: Blog
+ order: 40
+pagination:
+ data: collections.blog
+ size: 10
+ reverse: true
+ alias: posts
+---
+
+Discover articles from the core team and contributors about Lion, tips and tricks included!
diff --git a/demo/README.md b/docs/blog/ing-open-sources-lion.md
similarity index 92%
rename from demo/README.md
rename to docs/blog/ing-open-sources-lion.md
index fb6f52b71..fe9197ee0 100644
--- a/demo/README.md
+++ b/docs/blog/ing-open-sources-lion.md
@@ -1,10 +1,11 @@
-# ING open sources Lion: A library of performant, accessible & flexible Web Components
-
-```js script
-export default {
- title: 'Intro/Announcement',
-};
-```
+---
+title: ING open sources Lion
+published: true
+description: A library of performant, accessible & flexible Web Components
+date: 2020-08-10
+tags: [javascript, tests, demos]
+cover_image: https://miro.medium.com/max/1400/1*FT8CF6G-1IVa88G04nkf5w.png
+---
**TL;DR:** Web development can be hard, whether you're making your own components, implementing Design Systems, support many different browsers, incorporating accessibility, or adding third party dependencies. Lion aims to make your life a little bit easier, by taking the groundwork of feature complete, accessible, performant, and framework agnostic components out of your hands! Check out the repo at [ing-bank/lion](https://github.com/ing-bank/lion).
@@ -79,9 +80,9 @@ Lion is a core package of white label Web Components. What this means is that th
And this is exactly what we at ING do as well. Our very own ing-web components extend the Lion components and apply our own ING visual identity which mostly is a thin layer on top of Lion.
-Check out the [Lion demo's](http://lion-web-components.netlify.com/). Looks plain, doesn't it? And now compare Lion to ing-web:
+Check out the [Lion demo's](http://lion-web.netlify.app/). Looks plain, doesn't it? And now compare Lion to ing-web:
-
+
### 🎯 Focus
@@ -149,7 +150,7 @@ npm i lit-element @lion/tabs
Create a `lea-tabs` component by reusing the functionality of Lion. This gives Lea all the functionality and accessible core that she needs for his custom tabs component.
```js
-import { css } from '@lion/core';
+import { css } from 'lit-element';
import { LionTabs } from '@lion/tabs';
export class LeaTabs extends LionTabs {
@@ -178,7 +179,7 @@ customElements.define('lea-tabs', LeaTabs);
Lea also wants to be able to style the tab and tab-panel according to Betatechs visual identity. In order to do so, she creates a `lea-tab-panel` component and a `lea-tab` component, which she can then fully style however she desires, and eventually place inside the `lea-tabs` component. You can see how Lea achieved this in the example down below.
```js
-import { LitElement, html, css } from '@lion/core';
+import { LitElement, html, css } from 'lit-element';
export class LeaTab extends LitElement {
static get styles() {
@@ -209,7 +210,7 @@ Excellent! Lea can now use the tabs component like so:
```
-There we go, Lea's component is already done, so let's write some documentation. See the [live Lea tabs documentation page](?path=/docs/intro-tabs-example--default-story). You can see the full code of `lea-tabs` [on github](https://github.com/ing-bank/lion/tree/master/demo/).
+There we go, Lea's component is already done, so let's write some documentation. See the [live Lea tabs documentation page](../components/content/tabs/examples.md). You can see the full code of `lea-tabs` [on github](https://github.com/ing-bank/lion/tree/master/demo/).
P.S.: Do note that Lea is now responsible for keeping the documentation of `lea-tabs` up to date herself, and improvements on Lion's documentation will not automatically be reflected on Lea's documentation.
@@ -223,7 +224,7 @@ Component libraries are in huge demand. By open-sourcing our extendable componen
Building applications is hard, and sometimes, you need a little bit more than just the right component alone, but also things like: Validation, Forms, Overlays, Localization, etc. But fear not; Lion has got you covered!
-You can check them out in our [documentation](http://lion-web-components.netlify.com/), and we'll go into more depth about Lion's additional systems in future blog posts.
+You can check them out in our [documentation](http://lion-web.netlify.app/), and we'll go into more depth about Lion's additional systems in future blog posts.
## Thanks
diff --git a/docs/blog/introducing-lions-website.md b/docs/blog/introducing-lions-website.md
new file mode 100644
index 000000000..b78bd3865
--- /dev/null
+++ b/docs/blog/introducing-lions-website.md
@@ -0,0 +1,110 @@
+---
+title: Introducing lions website
+published: true
+description: A static website with docs and demos for lion
+date: 2021-03-10
+tags: [javascript, rocket, documentation]
+cover_image: /blog/images/introducing-lions-website-cover-image.jpg
+---
+
+After a month of preparations, we can finally present to you our new website. With it, we are enabled to give more context to each of our components.
+Right now it's more or less a port for our existing demos from storybook. But we can organize it in a nicer way by splitting it into components, docs, guides and blog sections.
+
+## Meet the new sections
+
+1. [Guides](../guides/index.md)
+ A dedicated section where we will teach you about how to get started with lion. This section is completely new and will grow over time.
+2. [Components](../components/index.md)
+ Here you will find our documentation for each of our components. Each is split into two pages namely Overview and Features. We also plan to add an API page soonish.
+3. [Docs](../docs/index.md)
+ This is the home for general documentation which includes the fundamental systems all our components are build upon and various tools we use in the frontend or backend.
+4. [Blog](./index.md)
+ We now have a dedicated section about all our blog posts. Take a peek and follow our story.
+
+## Upgrading Our Documentation
+
+As this page is now a static website and no longer storybook we needed to convert all our documentation.
+Luckily the format is very similar.
+
+Let's convert an example page
+
+### FROM
+
+````md
+# Calendar
+
+`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](?path=/docs/calendar--default-story).
+
+```js script
+import { html, css } from '@lion/core';
+import './lion-calendar.js';
+
+export default {
+ title: 'Others/Calendar',
+};
+```
+
+...
+````
+
+### TO
+
+````md
+# Component >> Calendar ||20
+
+`lion-calendar` is a reusable and accessible calendar view. It depends on [calendar](../../path/to/calendar.md).
+
+```js script
+import { html, css } from '@lion/core';
+import '@lion/calendar/define';
+```
+
+...
+````
+
+So what we needed to do:
+
+1. Remove the js default export and put the title / navigation into the headline
+2. Replace all imports with the Package Entry Points
+3. Adjust all link to be relative links to actual files (instead of storybook specific urls)
+
+## Handling Mono Repo Documentation
+
+All of our documentation now resides in the root `docs` folder. To publish them with our components we added a tool that can copy content and files into our published package.
+
+Let's say you have this file in your documentation.
+
+👉 `docs/components/accordion/overview.md`
+
+```
+# Accordion
+
+Is a very useful...
+```
+
+Now you want to make sure it gets published with your accordion package so you replace you Readme file with this
+
+👉 `packages/accordion/README.md`
+
+```
+# Accordion
+
+[=> See Source <=](../../docs/components/content/accordion/overview.md)
+```
+
+Now by calling `publish-docs` within the `prepublishOnly` step your Readme file will contain the content of its source.
+
+The benefits of this approach are
+
+- Readme still makes sense on GitHub (e.g. you can navigate to the package and with one click you are at the docs)
+- The published readme contains all the documentation you need
+- All links are version safe (e.g. if you look at v0.5 it will link to the GitHub Pages at this time)
+
+If you want to know more please look at the documentation for [publish-docs](../docs/node-tools/publish-docs/overview.md).
+
+## Upgrading Extending Documentation
+
+If you are currently extending our storybook documentation then this will no longer work.
+For now, we will need to ask you to stay on the current version and NOT upgrade.
+
+We will release the necessary tools to extend our Lion Website in the upcoming weeks 🤗
diff --git a/docs/browserconfig.xml b/docs/browserconfig.xml
new file mode 100644
index 000000000..549601dcd
--- /dev/null
+++ b/docs/browserconfig.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ #1d3557
+
+
+
diff --git a/docs/components/content/accordion/features.md b/docs/components/content/accordion/features.md
new file mode 100644
index 000000000..a4d7535e2
--- /dev/null
+++ b/docs/components/content/accordion/features.md
@@ -0,0 +1,214 @@
+# Content >> Accordion >> Features ||20
+
+```js script
+import { LitElement, html } from '@lion/core';
+
+import '@lion/accordion/define';
+```
+
+## 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.
+
+```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 how you could dynamically add a 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}
+ `)}
+
+```
diff --git a/docs/components/content/accordion/index.md b/docs/components/content/accordion/index.md
new file mode 100644
index 000000000..cf0241b22
--- /dev/null
+++ b/docs/components/content/accordion/index.md
@@ -0,0 +1,3 @@
+# Content >> Accordion
+
+-> go to Overview
diff --git a/docs/components/content/accordion/overview.md b/docs/components/content/accordion/overview.md
new file mode 100644
index 000000000..e280f4a99
--- /dev/null
+++ b/docs/components/content/accordion/overview.md
@@ -0,0 +1,52 @@
+# Content >> Accordion >> Overview ||10
+
+`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 { html } from '@lion/core';
+import '@lion/accordion/define';
+```
+
+```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.
+
+
+`;
+```
+
+## Features
+
+- content gets provided by users (slotted in)
+- handles accessibility
+- support navigation via keyboard
+
+## Installation
+
+```bash
+npm i --save @lion/accordion
+```
+
+```js
+import { LionAccordion } from '@lion/accordion';
+// or
+import '@lion/accordion/define';
+```
+
+## Rationale
+
+### Contents are not focusable
+
+Focusable elements should be interactive. Contents themselves do not offer any interactivity.
+If there is a button or a form inside the tab panel then these elements get focused directly.
diff --git a/packages/collapsible/demo/CustomCollapsible.js b/docs/components/content/collapsible/assets/CustomCollapsible.js
similarity index 88%
rename from packages/collapsible/demo/CustomCollapsible.js
rename to docs/components/content/collapsible/assets/CustomCollapsible.js
index dbfcde7d7..09df418b6 100644
--- a/packages/collapsible/demo/CustomCollapsible.js
+++ b/docs/components/content/collapsible/assets/CustomCollapsible.js
@@ -1,4 +1,4 @@
-import { LionCollapsible } from '../index.js';
+import { LionCollapsible } from '@lion/collapsible';
const EVENT = {
TRANSITION_END: 'transitionend',
@@ -8,8 +8,8 @@ const EVENT = {
* `CustomCollapsible` is a class for custom collapsible element (`` web component).
* @customElement custom-collapsible
*/
+// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
export class CustomCollapsible extends LionCollapsible {
- /** @type {any} */
static get properties() {
return {
transitioning: {
@@ -56,9 +56,7 @@ export class CustomCollapsible extends LionCollapsible {
contentNode.style.setProperty('opacity', '1');
contentNode.style.setProperty('padding', '12px 0');
contentNode.style.setProperty('max-height', '0px');
- await /** @type {Promise} */ (new Promise(resolve =>
- requestAnimationFrame(() => resolve()),
- ));
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
contentNode.style.setProperty('max-height', expectedHeight);
await this._waitForTransition({ contentNode });
}
@@ -107,9 +105,9 @@ export class CustomCollapsible extends LionCollapsible {
*/
async __calculateHeight(contentNode) {
contentNode.style.setProperty('max-height', '');
- await /** @type {Promise} */ (new Promise(resolve =>
- requestAnimationFrame(() => resolve()),
- ));
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
return this._contentHeight; // Expected height i.e. actual size once collapsed after animation
}
}
+
+customElements.define('custom-collapsible', CustomCollapsible);
diff --git a/packages/collapsible/demo/applyDemoCollapsibleStyles.js b/docs/components/content/collapsible/assets/applyDemoCollapsibleStyles.js
similarity index 100%
rename from packages/collapsible/demo/applyDemoCollapsibleStyles.js
rename to docs/components/content/collapsible/assets/applyDemoCollapsibleStyles.js
diff --git a/docs/components/content/collapsible/examples.md b/docs/components/content/collapsible/examples.md
new file mode 100644
index 000000000..ef5708c9d
--- /dev/null
+++ b/docs/components/content/collapsible/examples.md
@@ -0,0 +1,83 @@
+# Content >> Collapsible >> Examples ||30
+
+```js script
+import { html } from '@lion/core';
+import '@lion/collapsible/define';
+import '@lion/button/define';
+import './assets/CustomCollapsible.js';
+import './assets/applyDemoCollapsibleStyles.js';
+```
+
+## Custom Invoker Template
+
+A custom template can be specified to the `invoker` slot. It can be any button or custom component which mimics the button behavior for better accessibility support. In the below example, `lion-button` and native `button` with styling is used as a collapsible invoker.
+
+```js preview-story
+export const customInvokerTemplate = () => html`
+
+ More about cars
+
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have
+ four tires, and mainly transport people rather than goods.
+
+
+`;
+```
+
+## Extended collapsible with animation
+
+`LionCollapsible` can easily be extended to add more features in the component, like animation for example.
+
+```js preview-story
+export const customAnimation = () => html`
+
+
+ A motorcycle, often called a motorbike, bike, or cycle, is a two- or three-wheeled motor
+ vehicle.
+
+
+
+
+ Motorcycle design varies greatly to suit a range of different purposes: long distance
+ travel, commuting, cruising, sport including racing, and off-road riding. Motorcycling is
+ riding a motorcycle and related social activity such as joining a motorcycle club and
+ attending motorcycle rallies.
+
+
+
+
+
+ A car (or automobile) is a wheeled motor vehicle used for transportation.
+
+
+
+
+ Most definitions of cars say that they run primarily on roads, seat one to eight people,
+ have four tires, and mainly transport people rather than goods.
+
+
+
+`;
+```
+
+Use `_showAnimation()` and `_hideAnimation()` methods to customize open and close behavior. Check the code for a full example of a `custom-collapsible`.
+
+```js
+_showAnimation({ contentNode }) {
+ const expectedHeight = await this.__calculateHeight(contentNode);
+ contentNode.style.setProperty('opacity', '1');
+ contentNode.style.setProperty('padding', '12px 0');
+ contentNode.style.setProperty('max-height', '0px');
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ contentNode.style.setProperty('max-height', expectedHeight);
+ await this._waitForTransition({ contentNode });
+}
+
+_hideAnimation({ contentNode }) {
+ if (this._contentHeight === '0px') {
+ return;
+ }
+ ['opacity', 'padding', 'max-height'].map(prop => contentNode.style.setProperty(prop, 0));
+ await this._waitForTransition({ contentNode });
+}
+```
diff --git a/docs/components/content/collapsible/features.md b/docs/components/content/collapsible/features.md
new file mode 100644
index 000000000..b8f569000
--- /dev/null
+++ b/docs/components/content/collapsible/features.md
@@ -0,0 +1,79 @@
+# Content >> Collapsible >> Features ||20
+
+```js script
+import { html } from '@lion/core';
+import '@lion/collapsible/define';
+```
+
+## Default open
+
+Add the `opened` attribute to keep the component default open.
+
+```js preview-story
+export const defaultOpen = () => html`
+
+
+
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have
+ four tires, and mainly transport people rather than goods.
+
+
+`;
+```
+
+## Methods
+
+There are the following methods available to control the extra content for the collapsible.
+
+- `toggle()`: toggle the extra content
+- `show()`: show the extra content
+- `hide()`: hide the extra content
+
+```js preview-story
+export const methods = ({ shadowRoot }) => html`
+
+
+
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have
+ four tires, and mainly transport people rather than goods.
+
+
+
+
+
+
+
+`;
+```
+
+## Events
+
+`lion-collapsible` fires an event on `invoker` click to notify the component's current state. It is useful for analytics purposes or to perform some actions while expanding and collapsing the component.
+
+- `@opened-changed`: triggers when collapsible either gets opened or closed
+
+```js preview-story
+export const events = ({ shadowRoot }) => html`
+
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have
+ four tires, and mainly transport people rather than goods.
+
+
+`;
+```
diff --git a/docs/components/content/collapsible/index.md b/docs/components/content/collapsible/index.md
new file mode 100644
index 000000000..f15486238
--- /dev/null
+++ b/docs/components/content/collapsible/index.md
@@ -0,0 +1,3 @@
+# Content >> Collapsible
+
+-> go to Overview
diff --git a/docs/components/content/collapsible/overview.md b/docs/components/content/collapsible/overview.md
new file mode 100644
index 000000000..802359176
--- /dev/null
+++ b/docs/components/content/collapsible/overview.md
@@ -0,0 +1,39 @@
+# Content >> Collapsible >> Overview ||10
+
+`lion-collapsible` is a combination of a button (the invoker), a chunk of 'extra content', and can be extended with an animation to disclose the extra content. There are two slots available respectively; `invoker` to specify the collapsible's invoker and `content` for the extra content of the collapsible.
+
+```js script
+import { html } from '@lion/core';
+import '@lion/collapsible/define';
+```
+
+```js preview-story
+export const main = () => html`
+
+
+
+ Most definitions of cars say that they run primarily on roads, seat one to eight people, have
+ four tires, and mainly transport people rather than goods.
+
+
+`;
+```
+
+## Features
+
+- Use `opened` attribute or `toggle()` method to render default open
+- `invoker` slot can be custom template e.g. `lion-button` or native `button` with custom styling
+- Observe the state with the help of `@opened-changed` event
+- `show()` and `hide()` are helper methods to hide or show the content from outside
+
+## Installation
+
+```bash
+npm i --save @lion/collapsible
+```
+
+```js
+import { LionCollapsible } from '@lion/collapsible';
+// or
+import '@lion/collapsible/define';
+```
diff --git a/docs/components/content/index.md b/docs/components/content/index.md
new file mode 100644
index 000000000..deae6f052
--- /dev/null
+++ b/docs/components/content/index.md
@@ -0,0 +1 @@
+# Content ||10
diff --git a/packages/progress-indicator/demo/CustomProgressIndicator.js b/docs/components/content/progress-indicator/assets/custom-progress-indicator.js
similarity index 65%
rename from packages/progress-indicator/demo/CustomProgressIndicator.js
rename to docs/components/content/progress-indicator/assets/custom-progress-indicator.js
index 85fc4d1c5..da27c83ec 100644
--- a/packages/progress-indicator/demo/CustomProgressIndicator.js
+++ b/docs/components/content/progress-indicator/assets/custom-progress-indicator.js
@@ -1,26 +1,26 @@
-import { svg, css } from '@lion/core';
-import { LionProgressIndicator } from '../index.js';
+import { html, css } from '@lion/core';
+import { LionProgressIndicator } from '@lion/progress-indicator';
export class CustomProgressIndicator extends LionProgressIndicator {
static get styles() {
return [
css`
:host {
- display: inline-block;
+ display: block;
}
- svg {
- animation: spinner-rotate 2s linear infinite;
+ .progress--icon {
display: inline-block;
- height: 48px;
width: 48px;
+ height: 48px;
+ animation: spinner-rotate 2s linear infinite;
}
- circle {
+ .progress--icon--circle {
animation: spinner-dash 1.35s ease-in-out infinite;
fill: none;
- stroke-width: 3.6;
- stroke: firebrick;
+ stroke-width: 6px;
+ stroke: var(--primary-color);
stroke-dasharray: 100, 28; /* This is a fallback for IE11 */
}
@@ -49,8 +49,12 @@ export class CustomProgressIndicator extends LionProgressIndicator {
}
_graphicTemplate() {
- return svg``;
+ return html`
+
+ `;
}
}
+
+customElements.define('custom-progress-indicator', CustomProgressIndicator);
diff --git a/docs/components/content/progress-indicator/examples.md b/docs/components/content/progress-indicator/examples.md
new file mode 100644
index 000000000..3baa49193
--- /dev/null
+++ b/docs/components/content/progress-indicator/examples.md
@@ -0,0 +1,76 @@
+# Content >> Progress Indicator >> Examples ||30
+
+```js script
+import { html } from '@lion/core';
+import './assets/custom-progress-indicator.js';
+```
+
+## Extended indicator with a custom visual
+
+`LionProgressIndicator` is designed to be extended to add visuals. Implement the `_graphicTemplate()` method to set the rendered content and apply styles normally.
+
+### Example extension
+
+```js
+class CustomProgressIndicator extends LionProgressIndicator {
+ static get styles() {
+ return [
+ css`
+ :host {
+ display: block;
+ }
+
+ .progress--icon {
+ display: inline-block;
+ width: 48px;
+ height: 48px;
+ animation: spinner-rotate 2s linear infinite;
+ }
+
+ .progress--icon--circle {
+ animation: spinner-dash 1.35s ease-in-out infinite;
+ fill: none;
+ stroke-width: 6px;
+ stroke: var(--primary-color);
+ stroke-dasharray: 100, 28; /* This is a fallback for IE11 */
+ }
+
+ @keyframes spinner-rotate {
+ to {
+ transform: rotate(360deg);
+ }
+ }
+
+ @keyframes spinner-dash {
+ 0% {
+ stroke-dasharray: 6, 122;
+ stroke-dashoffset: 0;
+ }
+ 50% {
+ stroke-dasharray: 100, 28;
+ stroke-dashoffset: -16;
+ }
+ 100% {
+ stroke-dasharray: 6, 122;
+ stroke-dashoffset: -127;
+ }
+ }
+ `,
+ ];
+ }
+
+ _graphicTemplate() {
+ return html`
+
+ `;
+ }
+}
+```
+
+### Result
+
+```js preview-story
+export const main = () => html` `;
+```
diff --git a/docs/components/content/progress-indicator/index.md b/docs/components/content/progress-indicator/index.md
new file mode 100644
index 000000000..3180953cb
--- /dev/null
+++ b/docs/components/content/progress-indicator/index.md
@@ -0,0 +1,3 @@
+# Content >> Progress Indicator
+
+-> go to Overview
diff --git a/docs/components/content/progress-indicator/overview.md b/docs/components/content/progress-indicator/overview.md
new file mode 100644
index 000000000..7ad6689ad
--- /dev/null
+++ b/docs/components/content/progress-indicator/overview.md
@@ -0,0 +1,29 @@
+# Content >> Progress Indicator >> Overview ||10
+
+`lion-progress-indicator` implements accessibility requirements for progress indicators.
+
+```html
+
+```
+
+Note: You don't see a live demo here as it would be empty, since there is no styling. Check out the [examples](./examples.md) if you want to see a possible implementation.
+
+## Features
+
+`LionProgressIndicator` is designed to be extended to add visuals.
+
+- Accessibility compliant
+- Localized "Loading" label
+- Implementation independent of visuals
+
+## Installation
+
+```bash
+npm i --save @lion/progress-indicator
+```
+
+```js
+import { LionProgressIndicator } from '@lion/progress-indicator';
+// or
+import '@lion/progress-indicator/define';
+```
diff --git a/docs/components/content/tabs/examples.md b/docs/components/content/tabs/examples.md
new file mode 100644
index 000000000..f9c46d068
--- /dev/null
+++ b/docs/components/content/tabs/examples.md
@@ -0,0 +1,23 @@
+# Content >> Tabs >> Examples ||30
+
+```js script
+import { LitElement, html } from '@lion/core';
+import './src/lea-tabs.js';
+import './src/lea-tab.js';
+import './src/lea-tab-panel.js';
+```
+
+## Lea Tabs
+
+Learn how to create the `lea tabs` in our ["introducing lion" blogpost](../../../blog/ing-open-sources-lion.md).
+
+```js preview-story
+export const main = () => html`
+
+ Info
+ Info page with lots of information about us.
+ Work
+ Work page that showcases our work.
+
+`;
+```
diff --git a/docs/components/content/tabs/features.md b/docs/components/content/tabs/features.md
new file mode 100644
index 000000000..79b42ea5d
--- /dev/null
+++ b/docs/components/content/tabs/features.md
@@ -0,0 +1,194 @@
+# Content >> Tabs >> Features ||20
+
+```js script
+import { LitElement, html } from '@lion/core';
+
+import '@lion/tabs/define';
+```
+
+## Selected Index
+
+You can set the `selectedIndex` to select a certain tab.
+
+```js preview-story
+export const selectedIndex = () => html`
+
+
+
Info page with lots of information about us.
+
+
Work page that showcases our work.
+
+`;
+```
+
+## Slots Order
+
+The tab and panel slots are ordered by DOM order.
+
+This means you can switch the grouping in your `lion-tabs` from tab + panel to all tabs first or all panels first.
+
+```js preview-story
+export const slotsOrder = () => html`
+
+
+
+
Info page with lots of information about us.
+
Work page that showcases our work.
+
+`;
+```
+
+## Nesting tabs
+
+You can include tabs within tabs
+
+```js preview-story
+export const nestedTabs = () => html`
+
+
+
+
+
Find some more info about our favorite movies:
+
+
+
+
+ Cars is a 2006 American computer-animated comedy film produced by Pixar Animation Studios
+ and released by Walt Disney Pictures.
+
+
+ The feature film directorial debut of John Lasseter, it was the first entirely
+ computer-animated feature film, as well as the first feature film from Pixar.
+
+
+
+
Work page that showcases our work.
+
+`;
+```
+
+## Distribute New Elements
+
+Below, we demonstrate on how you could dynamically add new tab + panels.
+
+```js preview-story
+export const distributeNewElement = () => {
+ const tagName = 'demo-tabs-add-dynamically';
+ if (!customElements.get(tagName)) {
+ customElements.define(
+ tagName,
+ class extends LitElement {
+ static get properties() {
+ return {
+ __collection: { type: Array },
+ };
+ }
+ render() {
+ return html`
+
Append
+
+
+
panel 1
+
+
panel 2
+
+
+
+
Push
+
+
+
panel 1
+
+
panel 2
+ ${this.__collection.map(
+ item => html`
+
+
${item.panel}
+ `,
+ )}
+
+
+ `;
+ }
+ constructor() {
+ super();
+ this.__collection = [];
+ }
+ __handleAppendClick() {
+ const tabsElement = this.shadowRoot.querySelector('#appendTabs');
+ const c = 2;
+ const n = Math.floor(tabsElement.children.length / 2);
+ for (let i = n + 1; i < n + c; i += 1) {
+ const tab = document.createElement('button');
+ tab.setAttribute('slot', 'tab');
+ tab.innerText = `tab ${i}`;
+ const panel = document.createElement('p');
+ panel.setAttribute('slot', 'panel');
+ panel.innerText = `panel ${i}`;
+ tabsElement.append(tab);
+ tabsElement.append(panel);
+ }
+ }
+ __handlePushClick() {
+ const tabsElement = this.shadowRoot.querySelector('#pushTabs');
+ const i = Math.floor(tabsElement.children.length / 2) + 1;
+ this.__collection = [
+ ...this.__collection,
+ {
+ button: `tab ${i}`,
+ panel: `panel ${i}`,
+ },
+ ];
+ }
+ },
+ );
+ }
+ return html` `;
+};
+```
+
+One way is by creating the DOM elements and appending them as needed.
+
+Inside your `lion-tabs` extension, an example for appending nodes on a certain button click:
+
+```js
+__handleAppendClick() {
+ const tabsAmount = this.children.length / 2;
+ const tab = document.createElement('button');
+ tab.setAttribute('slot', 'tab');
+ tab.innerText = `tab ${tabsAmount + 1}`;
+ const panel = document.createElement('p');
+ panel.setAttribute('slot', 'panel');
+ panel.innerText = `panel ${tabsAmount + 1}`;
+ this.append(tab);
+ this.append(panel);
+}
+```
+
+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 tabsAmount = this.children.length;
+ myCollection = [
+ ...myCollection,
+ {
+ button: `tab ${tabsAmount + 1}`,
+ panel: `panel ${tabsAmount + 1}`,
+ },
+ ];
+ renderMyCollection();
+}
+```
+
+Make sure your template re-renders when myCollection is updated.
+
+```html
+
+ ${myCollection.map(item => html`
+
+
${item.panel}
+ `)}
+
+```
diff --git a/docs/components/content/tabs/index.md b/docs/components/content/tabs/index.md
new file mode 100644
index 000000000..3018ec15e
--- /dev/null
+++ b/docs/components/content/tabs/index.md
@@ -0,0 +1,3 @@
+# Content >> Tabs
+
+-> go to Overview
diff --git a/docs/components/content/tabs/overview.md b/docs/components/content/tabs/overview.md
new file mode 100644
index 000000000..747013b01
--- /dev/null
+++ b/docs/components/content/tabs/overview.md
@@ -0,0 +1,48 @@
+# Content >> Tabs >> Overview ||10
+
+A component to allow users to quickly move between a small number of equally important views.
+
+```js script
+import { LitElement, html } from '@lion/core';
+import '@lion/tabs/define';
+```
+
+```js preview-story
+export const main = () => html`
+
+
+
Info page with lots of information about us.
+
+
Work page that showcases our work.
+
+`;
+```
+
+## Installation
+
+```bash
+npm i --save @lion/tabs;
+```
+
+```js
+import { LionTabs } from '@lion/tabs';
+// or
+import '@lion/tabs/define';
+```
+
+## Rationale
+
+### No separate active/focus state when using keyboard
+
+We will immediately switch content as all our content comes from light dom (e.g. no latency)
+
+See Note at
+
+> It is recommended that tabs activate automatically when they receive focus as long as their
+> associated tab panels are displayed without noticeable latency. This typically requires tab
+> panel content to be preloaded.
+
+### Panels are not focusable
+
+Focusable elements should have a means to interact with them. Tab panels themselves do not offer any interactivity.
+If there is a button or a form inside the tab panel then these elements get focused directly.
diff --git a/demo/src/LeaTabPanel.js b/docs/components/content/tabs/src/lea-tab-panel.js
similarity index 89%
rename from demo/src/LeaTabPanel.js
rename to docs/components/content/tabs/src/lea-tab-panel.js
index 993461a19..3584d24a5 100644
--- a/demo/src/LeaTabPanel.js
+++ b/docs/components/content/tabs/src/lea-tab-panel.js
@@ -20,3 +20,5 @@ export class LeaTabPanel extends LitElement {
`;
}
}
+
+customElements.define('lea-tab-panel', LeaTabPanel);
diff --git a/demo/src/LeaTab.js b/docs/components/content/tabs/src/lea-tab.js
similarity index 96%
rename from demo/src/LeaTab.js
rename to docs/components/content/tabs/src/lea-tab.js
index b43d5944f..64b6940c7 100644
--- a/demo/src/LeaTab.js
+++ b/docs/components/content/tabs/src/lea-tab.js
@@ -41,3 +41,5 @@ export class LeaTab extends LitElement {
return html``;
}
}
+
+customElements.define('lea-tab', LeaTab);
diff --git a/demo/src/LeaTabs.js b/docs/components/content/tabs/src/lea-tabs.js
similarity index 90%
rename from demo/src/LeaTabs.js
rename to docs/components/content/tabs/src/lea-tabs.js
index aa81386a7..3c3f385a0 100644
--- a/demo/src/LeaTabs.js
+++ b/docs/components/content/tabs/src/lea-tabs.js
@@ -24,3 +24,5 @@ export class LeaTabs extends LionTabs {
// being awesome
}
}
+
+customElements.define('lea-tabs', LeaTabs);
diff --git a/packages/icon/docs/icons/bugs/bug01.svg.js b/docs/components/icons/icon/assets/bugs/bug01.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug01.svg.js
rename to docs/components/icons/icon/assets/bugs/bug01.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug02.svg.js b/docs/components/icons/icon/assets/bugs/bug02.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug02.svg.js
rename to docs/components/icons/icon/assets/bugs/bug02.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug05.svg.js b/docs/components/icons/icon/assets/bugs/bug05.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug05.svg.js
rename to docs/components/icons/icon/assets/bugs/bug05.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug06.svg.js b/docs/components/icons/icon/assets/bugs/bug06.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug06.svg.js
rename to docs/components/icons/icon/assets/bugs/bug06.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug08.svg.js b/docs/components/icons/icon/assets/bugs/bug08.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug08.svg.js
rename to docs/components/icons/icon/assets/bugs/bug08.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug12.svg.js b/docs/components/icons/icon/assets/bugs/bug12.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug12.svg.js
rename to docs/components/icons/icon/assets/bugs/bug12.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug19.svg.js b/docs/components/icons/icon/assets/bugs/bug19.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug19.svg.js
rename to docs/components/icons/icon/assets/bugs/bug19.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug23.svg.js b/docs/components/icons/icon/assets/bugs/bug23.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug23.svg.js
rename to docs/components/icons/icon/assets/bugs/bug23.svg.js
diff --git a/packages/icon/docs/icons/bugs/bug24.svg.js b/docs/components/icons/icon/assets/bugs/bug24.svg.js
similarity index 100%
rename from packages/icon/docs/icons/bugs/bug24.svg.js
rename to docs/components/icons/icon/assets/bugs/bug24.svg.js
diff --git a/packages/icon/docs/icons/iconset-bugs.js b/docs/components/icons/icon/assets/iconset-bugs.js
similarity index 100%
rename from packages/icon/docs/icons/iconset-bugs.js
rename to docs/components/icons/icon/assets/iconset-bugs.js
diff --git a/packages/icon/docs/icons/iconset-misc.js b/docs/components/icons/icon/assets/iconset-misc.js
similarity index 100%
rename from packages/icon/docs/icons/iconset-misc.js
rename to docs/components/icons/icon/assets/iconset-misc.js
diff --git a/packages/icon/docs/icons/iconset-space.js b/docs/components/icons/icon/assets/iconset-space.js
similarity index 100%
rename from packages/icon/docs/icons/iconset-space.js
rename to docs/components/icons/icon/assets/iconset-space.js
diff --git a/packages/icon/docs/icons/misc/arrowLeft.svg.js b/docs/components/icons/icon/assets/misc/arrowLeft.svg.js
similarity index 100%
rename from packages/icon/docs/icons/misc/arrowLeft.svg.js
rename to docs/components/icons/icon/assets/misc/arrowLeft.svg.js
diff --git a/packages/icon/docs/icons/space/aliens-spaceship.svg.js b/docs/components/icons/icon/assets/space/aliens-spaceship.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/aliens-spaceship.svg.js
rename to docs/components/icons/icon/assets/space/aliens-spaceship.svg.js
diff --git a/packages/icon/docs/icons/space/meteor.svg.js b/docs/components/icons/icon/assets/space/meteor.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/meteor.svg.js
rename to docs/components/icons/icon/assets/space/meteor.svg.js
diff --git a/packages/icon/docs/icons/space/moon-flag.svg.js b/docs/components/icons/icon/assets/space/moon-flag.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/moon-flag.svg.js
rename to docs/components/icons/icon/assets/space/moon-flag.svg.js
diff --git a/packages/icon/docs/icons/space/moon.svg.js b/docs/components/icons/icon/assets/space/moon.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/moon.svg.js
rename to docs/components/icons/icon/assets/space/moon.svg.js
diff --git a/packages/icon/docs/icons/space/night.svg.js b/docs/components/icons/icon/assets/space/night.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/night.svg.js
rename to docs/components/icons/icon/assets/space/night.svg.js
diff --git a/packages/icon/docs/icons/space/orbit.svg.js b/docs/components/icons/icon/assets/space/orbit.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/orbit.svg.js
rename to docs/components/icons/icon/assets/space/orbit.svg.js
diff --git a/packages/icon/docs/icons/space/planet.svg.js b/docs/components/icons/icon/assets/space/planet.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/planet.svg.js
rename to docs/components/icons/icon/assets/space/planet.svg.js
diff --git a/packages/icon/docs/icons/space/robot.svg.js b/docs/components/icons/icon/assets/space/robot.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/robot.svg.js
rename to docs/components/icons/icon/assets/space/robot.svg.js
diff --git a/packages/icon/docs/icons/space/rocket.svg.js b/docs/components/icons/icon/assets/space/rocket.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/rocket.svg.js
rename to docs/components/icons/icon/assets/space/rocket.svg.js
diff --git a/packages/icon/docs/icons/space/satellite.svg.js b/docs/components/icons/icon/assets/space/satellite.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/satellite.svg.js
rename to docs/components/icons/icon/assets/space/satellite.svg.js
diff --git a/packages/icon/docs/icons/space/signal.svg.js b/docs/components/icons/icon/assets/space/signal.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/signal.svg.js
rename to docs/components/icons/icon/assets/space/signal.svg.js
diff --git a/packages/icon/docs/icons/space/space-helmet.svg.js b/docs/components/icons/icon/assets/space/space-helmet.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/space-helmet.svg.js
rename to docs/components/icons/icon/assets/space/space-helmet.svg.js
diff --git a/packages/icon/docs/icons/space/sun.svg.js b/docs/components/icons/icon/assets/space/sun.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/sun.svg.js
rename to docs/components/icons/icon/assets/space/sun.svg.js
diff --git a/packages/icon/docs/icons/space/telescope.svg.js b/docs/components/icons/icon/assets/space/telescope.svg.js
similarity index 100%
rename from packages/icon/docs/icons/space/telescope.svg.js
rename to docs/components/icons/icon/assets/space/telescope.svg.js
diff --git a/docs/components/icons/icon/features.md b/docs/components/icons/icon/features.md
new file mode 100644
index 000000000..1bf59957d
--- /dev/null
+++ b/docs/components/icons/icon/features.md
@@ -0,0 +1,93 @@
+# Icons >> Icon >> Features ||20
+
+```js script
+import { html } from '@lion/core';
+import { icons } from '@lion/icon';
+import './assets/iconset-bugs.js';
+import './assets/iconset-misc.js';
+import * as spaceSet from './assets/iconset-space.js';
+
+import '@lion/icon/define';
+
+icons.addIconResolver('lion', (iconset, name) => {
+ switch (iconset) {
+ case 'bugs':
+ return import('./assets/iconset-bugs.js').then(module => module[name]);
+ case 'space':
+ return import('./assets/iconset-space.js').then(module => module[name]);
+ case 'misc':
+ return import('./assets/iconset-misc.js').then(module => module[name]);
+ default:
+ throw new Error(`Unknown iconset ${iconset}`);
+ }
+});
+```
+
+## Icon sets
+
+Icons are displayed using icon sets. These are collections of icons, lazily loaded on demand for performance.
+See the [system documentation](../../../docs/systems/icon/overview.md) to learn more about icon sets.
+
+```js preview-story
+export const iconSets = () => html`
+ ${Object.keys(spaceSet).map(
+ name => html`
+
+
+
+ ${name}
+
+ `,
+ )}
+`;
+```
+
+If for some reason you don't want to lazy load icons, you can still import and use them
+synchronously.
+
+## Accessibility
+
+It is recommended to add an `aria-label` to provide information to visually impaired users:
+
+A `lion-icon` without an `aria-label` attribute will be automatically given an `aria-hidden` attribute.
+
+```js preview-story
+export const accessibleLabel = () => html`
+
+`;
+```
+
+## Styling
+
+By default, a `lion-icon` will be `1em` × `1em` (the current line-height).
+
+`lion-icon` uses SVGs and may be styled with CSS, including using CSS properties such as `fill`:
+
+```js preview-story
+export const Styling = () => html`
+
+
+`;
+```
+
+See [SVG and CSS](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS) on MDN web docs for more information.
diff --git a/docs/components/icons/icon/index.md b/docs/components/icons/icon/index.md
new file mode 100644
index 000000000..0642be966
--- /dev/null
+++ b/docs/components/icons/icon/index.md
@@ -0,0 +1,3 @@
+# Icons >> Icon
+
+-> go to Overview
diff --git a/docs/components/icons/icon/overview.md b/docs/components/icons/icon/overview.md
new file mode 100644
index 000000000..48a4902ab
--- /dev/null
+++ b/docs/components/icons/icon/overview.md
@@ -0,0 +1,40 @@
+# Icons >> Icon >> Overview ||10
+
+A web component for displaying icons.
+
+```js script
+import { html } from '@lion/core';
+import { icons } from '@lion/icon';
+import '@lion/icon/define';
+
+icons.addIconResolver('lion', (iconset, name) => {
+ switch (iconset) {
+ case 'bugs':
+ return import('./assets/iconset-bugs.js').then(module => module[name]);
+ case 'space':
+ return import('./assets/iconset-space.js').then(module => module[name]);
+ case 'misc':
+ return import('./assets/iconset-misc.js').then(module => module[name]);
+ default:
+ throw new Error(`Unknown iconset ${iconset}`);
+ }
+});
+```
+
+```js preview-story
+export const main = () => html`
+
+`;
+```
+
+## Installation
+
+```bash
+npm i --save @lion/icon
+```
+
+```js
+import { LionIcon } from '@lion/icon';
+// or
+import '@lion/icon/define';
+```
diff --git a/docs/components/icons/index.md b/docs/components/icons/index.md
new file mode 100644
index 000000000..71654445e
--- /dev/null
+++ b/docs/components/icons/index.md
@@ -0,0 +1 @@
+# Icons ||30
diff --git a/docs/components/index.md b/docs/components/index.md
new file mode 100644
index 000000000..a2a9287a0
--- /dev/null
+++ b/docs/components/index.md
@@ -0,0 +1,16 @@
+# Components ||20
+
+We offer many web components for your application needs. For each, you will find an overview page to highlight its capabilities and how to start using it. Additionally, there will be a features page that showcases multiple use cases in action. Some even have a dedicated examples page so you can see a possible styled implementation.
+
+For organizational purposes, we split them into the following groups.
+
+1. [Content](./content/accordion/overview.md)
+ Everything to help you organize your content.
+2. [Icons](./icons/icon/overview.md)
+ Loading and displaying icons.
+3. [Inputs](./inputs/overview.md)
+ Input components that lets you make complex forms with ease, including validation.
+4. [Interaction](./interaction/button/overview.md)
+ A set of interactive components.
+5. [Navigation](./navigation/pagination/overview.md)
+ The reasoning behind some of our decisions.
diff --git a/docs/components/inputs/calendar/features.md b/docs/components/inputs/calendar/features.md
new file mode 100644
index 000000000..470c2edf4
--- /dev/null
+++ b/docs/components/inputs/calendar/features.md
@@ -0,0 +1,192 @@
+# Inputs >> Calendar >> Features ||20
+
+```js script
+import { html, css } from '@lion/core';
+import '@lion/calendar/define';
+```
+
+## Selected date
+
+The `selectedDate` is the date which is currently marked as selected.
+You usually select a date by clicking on it with the mouse or hitting Enter on the keyboard.
+
+The `selectedDate` might not be within the dates in the current month view.
+
+```js preview-story
+export const selectedDate = () => html`
+
+
+`;
+```
+
+## Central Date
+
+The `centralDate` defines which day will be focused when keyboard moves the focus to the current month grid.
+By default it is set to today, or the enabled day of the current month view that is closest to today's date.
+
+The next and previous months' buttons work by changing the `centralDate` with plus or minus one month.
+Changing the `centralDate` may mean a different view will be displayed to your users if it is in a different month.
+Usually if you change only the day, "nothing" happens as it's already currently in view.
+
+The `centralDate` can be different from `selectedDate` as you can have today as actively selected but still look at date that is years ago.
+When the `selectedDate` changes, it will sync its value to the `centralDate`.
+
+```js preview-story
+export const centralDate = () => {
+ const today = new Date();
+ const centralDate = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
+ return html`
+
+
+ `;
+};
+```
+
+## Controlling focus
+
+You can control the focus by calling the following methods
+
+- `focusCentralDate()`
+- `focusSelectedDate()`
+- `focusDate(dateInstanceToFocus)`
+
+> Be aware that the central date changes when a new date is focused.
+
+```js preview-story
+export const controllingFocus = () => {
+ const today = new Date();
+ const selectedDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
+ const centralDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 5);
+ return html`
+
+
+
+
+
+ `;
+};
+```
+
+## Limiting selectable values
+
+### Providing a lower limit
+
+To give a lower limit you can bind a date to the `minDate` property.
+
+```js preview-story
+export const providingLowerLimit = () => {
+ const minDate = new Date();
+ return html`
+
+
+ `;
+};
+```
+
+### Provide a higher limit
+
+To give a higher limit you can bind a date to the `maxDate` property. In this example, we show how to create an offset of + 2 days.
+
+```js preview-story
+export const providingHigherLimit = () => {
+ const today = new Date();
+ const maxDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2);
+ return html`
+
+
+ `;
+};
+```
+
+### Provide a list of disabled dates
+
+In some cases a specific date or day of the week needs to be disabled, supply those days to the `disableDates` property.
+
+```js preview-story
+export const disabledDates = () => html`
+
+ day.getDay() === 6 || day.getDay() === 0}
+ >
+`;
+```
+
+### Combined disable dates
+
+To limit the scope of possible dates further, combine the methods mentioned above.
+
+```js preview-story
+export const combinedDisabledDates = () => {
+ const today = new Date();
+ const maxDate = new Date(today.getFullYear(), today.getMonth() + 2, today.getDate());
+ return html`
+
+ day.getDay() === 6 || day.getDay() === 0}
+ .minDate="${new Date()}"
+ .maxDate="${maxDate}"
+ >
+ `;
+};
+```
diff --git a/docs/components/inputs/calendar/index.md b/docs/components/inputs/calendar/index.md
new file mode 100644
index 000000000..1e2b998ea
--- /dev/null
+++ b/docs/components/inputs/calendar/index.md
@@ -0,0 +1,3 @@
+# Inputs >> Calendar ||20
+
+-> go to Overview
diff --git a/docs/components/inputs/calendar/overview.md b/docs/components/inputs/calendar/overview.md
new file mode 100644
index 000000000..cdec4e2ba
--- /dev/null
+++ b/docs/components/inputs/calendar/overview.md
@@ -0,0 +1,49 @@
+# Inputs >> Calendar >> Overview ||10
+
+`lion-calendar` is a reusable and accessible calendar view.
+
+```js script
+import { html, css } from '@lion/core';
+import '@lion/calendar/define';
+```
+
+```js story
+export const main = () => {
+ return html`
+
+
+ `;
+};
+```
+
+## Features
+
+- fully accessible keyboard navigation (Arrow Keys, PgUp, PgDn, ALT+PgUp, ALT+PgDn)
+- **minDate**: disables all dates before a given date
+- **maxDate**: disables all dates after a given date
+- **disableDates**: disables some dates within an available range
+- **selectedDate**: currently selected date
+- **centralDate**: date that determines the currently visible month and that will be focused when keyboard moves the focus to the month grid
+- **focusedDate**: (getter only) currently focused date (if there is any with real focus)
+- **focusDate(date)**: focus on a certain date
+- **focusSelectedDate()**: focus on the current selected date
+- **focusCentralDate()**: focus on the current central date
+- **firstDayOfWeek**: typically Sunday (default) or Monday
+- **weekdayHeaderNotation**: long/short/narrow for the current locale (e.g. Thursday/Thu/T)
+- **locale**: different locale for the current component only
+
+## Installation
+
+```bash
+npm i --save @lion/calendar
+```
+
+```js
+import '@lion/calendar/define';
+```
diff --git a/docs/components/inputs/checkbox-group/features.md b/docs/components/inputs/checkbox-group/features.md
new file mode 100644
index 000000000..6274421a2
--- /dev/null
+++ b/docs/components/inputs/checkbox-group/features.md
@@ -0,0 +1,205 @@
+# Inputs >> Checkbox Group >> Features ||20
+
+```js script
+import { html } from '@lion/core';
+import '@lion/checkbox-group/define';
+import '@lion/checkbox-group/define';
+import '@lion/checkbox-group/define';
+```
+
+## Model value
+
+The `modelValue` of a `lion-checkbox-group` is an array containing the `choiceValues` of the `lion-checkbox` elements that have been checked.
+
+Given the scientists example above, say that we were to select the first and last options
+(Archimedes & Marie Curie).
+
+Then the `modelValue` of the `lion-checkbox-group` will look as follows:
+
+```js
+const groupElement = [parent].querySelector('lion-checkbox-group');
+groupElement.modelValue;
+ => ["Archimedes", "Marie Curie"];
+```
+
+## The `name` attribute
+
+The `name` attribute of a `lion-checkbox-group` automatically gets assigned to its `lion-checkbox` children. You can also specify names for the `lion-checkbox` elements, but if this name is different from the name assigned to `lion-checkbox-group`, then an exception will be thrown.
+
+Our recommendation would be to set the `name` attribute only on the `lion-checkbox-group` and not on the `lion-checkbox` elements.
+
+## Example
+
+```html
+
+
+
+
+
+```
+
+## Pre-select
+
+You can pre-select options by targeting the `modelValue` object of the option and setting the `checked` property to `true`.
+
+```js preview-story
+export const preselect = () => html`
+
+
+
+
+
+`;
+```
+
+## Disabled
+
+You can disable the entire group by setting the `disabled` attribute on the ``.
+
+```js preview-story
+export const disabled = () => html`
+
+
+
+
+
+`;
+```
+
+## Help text
+
+You can add help text on each checkbox with `help-text` attribute on the ``.
+
+```js preview-story
+export const helpText = () => html`
+
+
+
+
+
+`;
+```
+
+## Event
+
+You can listen to the `model-value-changed` event whenever the value of the checkbox group is changed.
+
+```js preview-story
+export const event = ({ shadowRoot }) => html`
+
+ (shadowRoot.getElementById('selectedDinosaur').innerText = JSON.stringify(
+ ev.target.modelValue,
+ null,
+ 4,
+ ))}
+ >
+
+
+
+
+
+ Selected scientists: N/A
+`;
+```
+
+## Indeterminate
+
+```js preview-story
+export const indeterminate = () => html`
+
+
+
+
+
+
+
+`;
+```
+
+```js preview-story
+export const indeterminateSiblings = () => html`
+
+
+
+
+
+
+
+
+
+
+
+`;
+```
+
+```js preview-story
+export const indeterminateChildren = () => html`
+
+
+
+
+
+
+
+
+
+
+
+`;
+```
diff --git a/docs/components/inputs/checkbox-group/index.md b/docs/components/inputs/checkbox-group/index.md
new file mode 100644
index 000000000..a5862f14b
--- /dev/null
+++ b/docs/components/inputs/checkbox-group/index.md
@@ -0,0 +1,3 @@
+# Inputs >> Checkbox Group ||20
+
+-> go to Overview
diff --git a/docs/components/inputs/checkbox-group/overview.md b/docs/components/inputs/checkbox-group/overview.md
new file mode 100644
index 000000000..2f3c3d8df
--- /dev/null
+++ b/docs/components/inputs/checkbox-group/overview.md
@@ -0,0 +1,38 @@
+# Inputs >> Checkbox Group >> Overview ||10
+
+`lion-checkbox-group` component enhances the functionality of the native `` element.
+Its purpose is to provide a way for users to check **multiple** options amongst a set of choices, or to function as a single toggle.
+
+> You should use `` elements as the children of the ``.
+
+```js script
+import { html } from '@lion/core';
+import '@lion/checkbox-group/define';
+```
+
+```js story
+export const main = () => html`
+
+
+
+
+
+`;
+```
+
+> Make sure that the checkbox-group also has a name attribute, this is necessary for the [lion-form](../form/overview.md)'s serialization result.
+
+## Features
+
+Since it extends from [lion-fieldset](../fieldset/overview.md),
+it has all the features a fieldset has.
+
+## Installation
+
+```bash
+npm i --save @lion/checkbox-group
+```
+
+```js
+import '@lion/checkbox-group/define';
+```
diff --git a/docs/components/inputs/combobox/examples.md b/docs/components/inputs/combobox/examples.md
new file mode 100644
index 000000000..85d119706
--- /dev/null
+++ b/docs/components/inputs/combobox/examples.md
@@ -0,0 +1,77 @@
+# Inputs >> Combobox >> Examples ||30
+
+```js script
+import { html } from '@lion/core';
+import './src/md-combobox/md-combobox.js';
+import './src/gh-combobox/gh-combobox.js';
+import './src/wa-combobox/wa-combobox.js';
+```
+
+```js preview-story
+export const MaterialDesign = () => html`
+
+ Apple
+ Artichoke
+ Asparagus
+ Banana
+ Beets
+
+`;
+```
+
+```js preview-story
+export const Github = () => html`
+
+ master
+ develop
+ release
+ feat/abc
+ feat/xyz123
+
+`;
+```
+
+```js preview-story
+export const Whatsapp = () => html`
+
+
+
+
+
+
+
+`;
+```
diff --git a/docs/components/inputs/combobox/features.md b/docs/components/inputs/combobox/features.md
new file mode 100644
index 000000000..801d06bef
--- /dev/null
+++ b/docs/components/inputs/combobox/features.md
@@ -0,0 +1,253 @@
+# Inputs >> Combobox >> Features ||20
+
+A combobox is a widget made up of the combination of two distinct elements:
+
+- a single-line textbox
+- an associated listbox overlay
+
+Based on the combobox configuration and entered textbox value, options in the listbox will be
+filtered, checked, focused and the textbox value may be autocompleted.
+Optionally the combobox contains a graphical button adjacent to the textbox, indicating the
+availability of the popup.
+
+> Fore more information, consult [Combobox wai-aria design pattern](https://www.w3.org/TR/wai-aria-practices/#combobox)
+
+```js script
+import { html } from '@lion/core';
+import { listboxData } from '../../../../packages/listbox/docs/listboxData.js';
+import '@lion/listbox/define';
+import '@lion/combobox/define';
+import './src/demo-selection-display.js';
+import { lazyRender } from './src/lazyRender.js';
+import levenshtein from './src/levenshtein.js';
+```
+
+## Autocomplete
+
+Below you will find an overview of all possible `autocomplete` behaviors and how they correspond
+to the configurable values `none`, `list`, `inline` and `both`.
+
+| | list | filter | focus | check | complete |
+| -----: | :--: | :----: | :---: | :---: | :------: |
+| none | ✓ | | | | |
+| list | ✓ | ✓ | ✓ | ✓ | |
+| inline | ✓ | | ✓ | ✓ | ✓ |
+| both | ✓ | ✓ | ✓ | ✓ | ✓ |
+
+- **list** shows a list on keydown character press
+- **filter** filters list of potential matches according to `matchmode` or provided `matchCondition`
+- **focus** automatically focuses closest match (makes it the activedescendant)
+- **check** automatically checks/selects closest match when `selection-follows-focus` is enabled (this is the default configuration)
+- **complete** completes the textbox value inline (the 'missing characters' will be added as selected text)
+
+When `autocomplete="none"` is configured, the suggested options in the overlay are not filtered
+based on the characters typed in the textbox.
+Selection will happen manually by the user.
+
+```js preview-story
+export const autocompleteNone = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+When `autocomplete="list"` is configured, it will filter listbox suggestions based on textbox value.
+
+```js preview-story
+export const autocompleteList = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+When `autocomplete="inline"` is configured, it will present a value completion prediction inside the text input itself.
+It does NOT filter list of potential matches.
+
+```js preview-story
+export const autocompleteInline = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+When `autocomplete="both"` is configured, it combines the filtered list from `'list'` with the text input value completion prediction from `'inline'`.
+This is the default value for `autocomplete`.
+
+```js preview-story
+export const autocompleteBoth = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+## Match Mode
+
+When `match-mode="begin"` is applied, the entered text in the textbox only filters
+options whose values begin with the entered text. For instance, the entered text 'ch' will match
+with value 'Chard', but not with 'Artichoke'.
+By default `match-mode="all"` is applied. This will also match parts of a word.
+So 'ch' will both match 'Chard' and 'Artichoke'.
+
+```js preview-story
+export const matchModeBegin = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+```js preview-story
+export const matchModeAll = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+When the preconfigurable `match-mode` conditions are not sufficient,
+one can define a custom matching function.
+The example below matches when the Levenshtein distance is below 3 (including some other conditions).
+
+```js preview-story
+export const customMatchCondition = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+## Options
+
+```js preview-story
+export const showAllOnEmpty = () => html`
+
+ ${lazyRender(
+ listboxData.map(entry => html` ${entry} `),
+ )}
+
+`;
+```
+
+### Changing defaults
+
+By default `selection-follows-focus` will be true (aligned with the
+wai-aria examples and the native `