diff --git a/.gitignore b/.gitignore
index 1e8bd3831..ca7f35034 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,3 @@
-# Ignore generated docs files
-docs/components/
-
src/components/icon/icons
dist/
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index 27d4058e2..163593f8f 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -1,4 +1,4 @@
-- Concepts
+- Overview
- [Getting Started](/)
- [Installation](/installation.md)
- [Customizing](/customizing.md)
diff --git a/docs/assets/scripts/component-metadata-plugin.js b/docs/assets/scripts/component-metadata-plugin.js
new file mode 100644
index 000000000..f810a98de
--- /dev/null
+++ b/docs/assets/scripts/component-metadata-plugin.js
@@ -0,0 +1,248 @@
+(() => {
+ let componentMetadata;
+
+ function createPropsTable(props) {
+ const table = document.createElement('table');
+ table.innerHTML = `
+
+
+ | Property |
+ Attribute |
+ Description |
+ Type |
+ Default |
+
+
+
+ ${props
+ .map(
+ prop => `
+
+ ${escapeHtml(prop.name)} |
+ ${escapeHtml(prop.attr)} |
+ ${escapeHtml(prop.docs)} |
+ ${escapeHtml(prop.type)} |
+ ${escapeHtml(prop.default)} |
+
+ `
+ )
+ .join('')}
+
+ `;
+
+ return table.outerHTML;
+ }
+
+ function createEventsTable(events) {
+ const table = document.createElement('table');
+ table.innerHTML = `
+
+
+ | Event |
+ Description |
+ Type |
+
+
+
+ ${events
+ .map(
+ event => `
+
+ ${escapeHtml(event.event)} |
+ ${escapeHtml(event.docs)} |
+ CustomEvent<${escapeHtml(event.detail)}> |
+
+ `
+ )
+ .join('')}
+
+ `;
+
+ return table.outerHTML;
+ }
+
+ function createMethodsTable(methods) {
+ const table = document.createElement('table');
+ table.innerHTML = `
+
+
+ | Method |
+ Description |
+ Returns |
+
+
+
+ ${methods
+ .map(
+ method => `
+
+ ${escapeHtml(method.name)} |
+ ${escapeHtml(method.docs)} |
+ ${escapeHtml(method.returns.type)} |
+
+ `
+ )
+ .join('')}
+
+ `;
+
+ return table.outerHTML;
+ }
+
+ function createSlotsTable(slots) {
+ const table = document.createElement('table');
+ table.innerHTML = `
+
+
+ | Slot |
+ Description |
+
+
+
+ ${slots
+ .map(
+ slot => `
+
+ ${slot.name ? escapeHtml(slot.name) : '(default)'} |
+ ${escapeHtml(slot.docs)} |
+
+ `
+ )
+ .join('')}
+
+ `;
+
+ return table.outerHTML;
+ }
+
+ function createCustomPropertiesTable(styles) {
+ const table = document.createElement('table');
+ table.innerHTML = `
+
+
+ | Name |
+ Description |
+
+
+
+ ${styles
+ .map(
+ style => `
+
+ ${escapeHtml(style.name)} |
+ ${escapeHtml(style.docs)} |
+
+ `
+ )
+ .join('')}
+
+ `;
+
+ return table.outerHTML;
+ }
+
+ function createDependenciesList(dependencies) {
+ const ul = document.createElement('ul');
+ ul.innerHTML = `
+ ${dependencies
+ .map(
+ dependency => `
+ ${escapeHtml(dependency)}
+ `
+ )
+ .join('')}
+ `;
+
+ return ul.outerHTML;
+ }
+
+ function escapeHtml(html) {
+ return (html + '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function getMetadata() {
+ return new Promise((resolve, reject) => {
+ if (componentMetadata) {
+ return resolve(componentMetadata);
+ }
+
+ return fetch('/assets/dist/components.json')
+ .then(res => res.json())
+ .then(data => {
+ componentMetadata = data;
+ resolve(componentMetadata);
+ });
+ });
+ }
+
+ if (!window.$docsify) {
+ throw new Error('Docsify must be loaded before installing this plugin.');
+ }
+
+ window.$docsify.plugins.push((hook, vm) => {
+ hook.beforeEach(async function (content, next) {
+ const metadata = await getMetadata();
+
+ content = content.replace(/\[component-metadata:([a-z-]+)\]/g, (match, tag) => {
+ const data = metadata.components.filter(data => data.tag === tag)[0];
+ let result = '';
+
+ if (!data) {
+ throw new Error('Component not found in metadata: ' + tag);
+ }
+
+ if (data.props.length) {
+ result += `
+ ## Properties
+ ${createPropsTable(data.props)}
+ `;
+ }
+
+ if (data.events.length) {
+ result += `
+ ## Events
+ ${createEventsTable(data.events)}
+ `;
+ }
+
+ if (data.methods.length) {
+ result += `
+ ## Methods
+ ${createMethodsTable(data.methods)}
+ `;
+ }
+
+ if (data.slots.length) {
+ result += `
+ ## Slots
+ ${createSlotsTable(data.slots)}
+ `;
+ }
+
+ if (data.styles.length) {
+ result += `
+ ## CSS Custom Properties
+ ${createCustomPropertiesTable(data.styles)}
+ `;
+ }
+
+ if (data.dependencies.length) {
+ result += `
+ ## Dependencies
+ ${createDependenciesList(data.dependencies)}
+ `;
+ }
+
+ // Strip whitespace so markdown doesn't process things as code blocks
+ return result.replace(/^ +| +$/gm, '');
+ });
+
+ next(content);
+ });
+ });
+})();
diff --git a/docs/components/alert.md b/docs/components/alert.md
new file mode 100644
index 000000000..48fba8770
--- /dev/null
+++ b/docs/components/alert.md
@@ -0,0 +1,48 @@
+# Alert
+
+```html preview
+
+ Nothing fancy here, just a simple alert.
+
+
+
+
+ This one is a bit fancier because now it has an icon and is closable.
+
+```
+
+## Types
+
+```html preview
+
+
+ Your changes have been saved
+ You can continue working or safely leave the app now.
+
+
+
+
+ Your changes have been saved
+ You can continue working or safely leave the app now.
+
+
+
+
+ Your changes have been saved
+ You can continue working or safely leave the app now.
+
+
+
+
+ Your changes have been saved
+ You can continue working or safely leave the app now.
+
+
+
+
+ Your changes have been saved
+ You can continue working or safely leave the app now.
+
+```
+
+[component-metadata:sl-alert]
\ No newline at end of file
diff --git a/docs/components/button.md b/docs/components/button.md
new file mode 100644
index 000000000..a74be509f
--- /dev/null
+++ b/docs/components/button.md
@@ -0,0 +1,124 @@
+# Button
+
+Good ol' buttons. They're usually the first thing I look at when reviewing a component library. Shoelace offers a variation for every theme color.
+
+```html preview
+Default
+Primary
+Success
+Info
+Warning
+Danger
+```
+
+## Pill
+
+Use the `pill` prop to give buttons rounded edges.
+
+```html preview
+Default
+Primary
+Success
+Info
+Warning
+Danger
+```
+
+## Sizes
+
+Use the `size` prop to change a button's size.
+
+```html preview
+Small
+Medium
+Large
+```
+
+## Circle
+
+Use the `circle` prop to create circular icon buttons.
+
+```html preview
+
+
+
+```
+
+## Text
+
+Use `type="text"` to create text buttons, which share the same size as regular buttons but don't have backgrounds or borders.
+
+```html preview
+Text
+Text
+Text
+```
+
+## Block
+
+Block buttons can be created by setting the button's width to `100%`.
+
+```html preview
+Small
+Medium
+Large
+```
+
+## Icons
+
+Use the `prefix` and `suffix` slots to add icons.
+
+```html preview
+
+
+ Settings
+
+
+
+ Refresh
+
+
+
+
+ Open
+
+```
+
+## Caret
+
+Use the `caret` prop to add a dropdown indicator when a button will trigger a dropdown, menu, or popover.
+
+```html preview
+Small
+Medium
+Large
+```
+
+## Loading
+
+Use the `loading` prop to make a button busy. The width will remain the same as before, preventing adjacent elements from moving around.
+
+```html preview
+Default
+Primary
+Success
+Info
+Warning
+Danger
+```
+
+## Disabled
+
+Use the `disabled` prop to disable a button.
+
+```html preview
+Default
+Primary
+Success
+Info
+Warning
+Danger
+```
+
+[component-metadata:sl-button]
+
diff --git a/docs/components/checkbox.md b/docs/components/checkbox.md
new file mode 100644
index 000000000..98bde9170
--- /dev/null
+++ b/docs/components/checkbox.md
@@ -0,0 +1,10 @@
+# Checkbox
+
+```html preview
+Default
+Checked
+Indeterminate
+Disabled
+```
+
+[component-metadata:sl-checkbox]
\ No newline at end of file
diff --git a/docs/components/color-picker.md b/docs/components/color-picker.md
new file mode 100644
index 000000000..733d581bd
--- /dev/null
+++ b/docs/components/color-picker.md
@@ -0,0 +1,11 @@
+# Color Picker
+
+```html preview
+
+```
+
+```html preview
+
+```
+
+[component-metadata:sl-color-picker]
\ No newline at end of file
diff --git a/docs/components/dialog.md b/docs/components/dialog.md
new file mode 100644
index 000000000..91caf8962
--- /dev/null
+++ b/docs/components/dialog.md
@@ -0,0 +1,23 @@
+# Dialog
+
+```html preview
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ Close
+
+
+Open Dialog
+
+
+```
+
+[component-metadata:sl-dialog]
\ No newline at end of file
diff --git a/docs/components/dropdown-divider.md b/docs/components/dropdown-divider.md
new file mode 100644
index 000000000..1c3132baf
--- /dev/null
+++ b/docs/components/dropdown-divider.md
@@ -0,0 +1,7 @@
+# Dropdown Divider
+
+Dropdown dividers can be used to separate dropdown items and other content inside dropdowns.
+
+See the Dropdown component for more usage examples.
+
+[component-metadata:sl-dropdown-divider]
\ No newline at end of file
diff --git a/docs/components/dropdown-item.md b/docs/components/dropdown-item.md
new file mode 100644
index 000000000..43b8553c7
--- /dev/null
+++ b/docs/components/dropdown-item.md
@@ -0,0 +1,7 @@
+# Dropdown Item
+
+Dropdown items can be used inside dropdowns to add menu items that the user can select.
+
+See the Dropdown component for more usage examples.
+
+[component-metadata:sl-dropdown-item]
\ No newline at end of file
diff --git a/docs/components/dropdown.md b/docs/components/dropdown.md
new file mode 100644
index 000000000..53948f590
--- /dev/null
+++ b/docs/components/dropdown.md
@@ -0,0 +1,62 @@
+# Dropdown
+
+```html preview
+
+ Dropdown
+ Dropdown Item 1
+ Dropdown Item 2
+ Dropdown Item 3
+
+ Checked
+ Disabled
+
+
+ Prefix
+
+
+
+ Suffix Icon
+
+
+
+```
+
+```html preview
+
+ Edit
+ Cut
+ Copy
+ Paste
+
+ Find
+ Replace
+
+```
+
+```html preview
+
+ Scrolling Menu
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+ Item 8
+ Item 9
+ Item 10
+ Item 11
+ Item 12
+ Item 13
+ Item 14
+ Item 15
+ Item 16
+ Item 17
+ Item 18
+ Item 19
+ Item 20
+
+```
+
+[component-metadata:sl-dropdown]
\ No newline at end of file
diff --git a/docs/components/icon.md b/docs/components/icon.md
new file mode 100644
index 000000000..eb762d677
--- /dev/null
+++ b/docs/components/icon.md
@@ -0,0 +1,41 @@
+# Icon
+
+```html preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Icons are courtesy of [Feather Icons](https://feathericons.com/).
+
+[component-metadata:sl-icon]
\ No newline at end of file
diff --git a/docs/components/input.md b/docs/components/input.md
new file mode 100644
index 000000000..74e177cb1
--- /dev/null
+++ b/docs/components/input.md
@@ -0,0 +1,76 @@
+# Input
+
+```html preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+[component-metadata:sl-input]
\ No newline at end of file
diff --git a/docs/components/progress-bar.md b/docs/components/progress-bar.md
new file mode 100644
index 000000000..0060ee4c1
--- /dev/null
+++ b/docs/components/progress-bar.md
@@ -0,0 +1,19 @@
+# Progress Bar
+
+```html preview
+
+
+
+
+
+```
+
+```html preview
+0%
+25%
+50%
+75%
+100%
+```
+
+[component-metadata:sl-progress-bar]
\ No newline at end of file
diff --git a/docs/components/progress-ring.md b/docs/components/progress-ring.md
new file mode 100644
index 000000000..adc9fe852
--- /dev/null
+++ b/docs/components/progress-ring.md
@@ -0,0 +1,13 @@
+# Progress Ring
+
+```html preview
+
+ 0%
+ 25%
+ 50%
+ 75%
+ 100%
+
+```
+
+[component-metadata:sl-progress-bar]
\ No newline at end of file
diff --git a/docs/components/radio.md b/docs/components/radio.md
new file mode 100644
index 000000000..4803c5fe2
--- /dev/null
+++ b/docs/components/radio.md
@@ -0,0 +1,9 @@
+# Radio
+
+```html preview
+Default
+Checked
+Disabled
+```
+
+[component-metadata:sl-radio]
\ No newline at end of file
diff --git a/docs/components/range.md b/docs/components/range.md
new file mode 100644
index 000000000..a21084b5a
--- /dev/null
+++ b/docs/components/range.md
@@ -0,0 +1,32 @@
+# Range
+
+```html preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+[component-metadata:sl-range]
\ No newline at end of file
diff --git a/docs/components/spinner.md b/docs/components/spinner.md
new file mode 100644
index 000000000..5294b9245
--- /dev/null
+++ b/docs/components/spinner.md
@@ -0,0 +1,9 @@
+# Spinner
+
+```html preview
+
+
+
+```
+
+[component-metadata:sl-spinner]
\ No newline at end of file
diff --git a/docs/components/switch.md b/docs/components/switch.md
new file mode 100644
index 000000000..3bf5d51be
--- /dev/null
+++ b/docs/components/switch.md
@@ -0,0 +1,9 @@
+# Switch
+
+```html preview
+Switch
+Checked
+Disabled
+```
+
+[component-metadata:sl-switch]
\ No newline at end of file
diff --git a/docs/components/tab-group.md b/docs/components/tab-group.md
new file mode 100644
index 000000000..fea01edc1
--- /dev/null
+++ b/docs/components/tab-group.md
@@ -0,0 +1,123 @@
+# Tab Group
+
+```html preview
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+```
+
+```html preview
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+```
+
+```html preview
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+```
+
+```html preview
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+```
+
+```html preview
+
+ Tab 1
+ Tab 2
+ Tab 3
+ Tab 4
+ Tab 5
+ Tab 6
+ Tab 7
+ Tab 8
+ Tab 9
+ Tab 10
+ Tab 11
+ Tab 12
+ Tab 13
+ Tab 14
+ Tab 15
+ Tab 16
+ Tab 17
+ Tab 18
+ Tab 19
+ Tab 20
+
+ Tab panel 1
+ Tab panel 2
+ Tab panel 3
+ Tab panel 4
+ Tab panel 5
+ Tab panel 6
+ Tab panel 7
+ Tab panel 8
+ Tab panel 9
+ Tab panel 10
+ Tab panel 11
+ Tab panel 12
+ Tab panel 13
+ Tab panel 14
+ Tab panel 15
+ Tab panel 16
+ Tab panel 17
+ Tab panel 18
+ Tab panel 19
+ Tab panel 20
+
+```
+
+## Notes
+
+Serious consideration was given to simplifying the tab group API into two components with a structure similar to:
+
+```html
+
+
+ General
+ Custom
+ Advanced
+ Disabled
+
+```
+
+However, there are two caveats to this approach. The first is that labels must be generated and stored in the tab group's shadow DOM. For accessibility reasons, tabs and tab panels must be linked via `id` using the `aria-controls` and `aria-labeledby` attribute, respectively. When a tab is inside a shadow DOM, its `id` becomes inaccessible to the light DOM and accessibility is broken.
+
+The second thing is that labels would be limited to text only. If you wanted to put an icon, a badge, or another element inside the label, it wouldn't be possible with this approach.
+
+[component-metadata:sl-tab-group]
\ No newline at end of file
diff --git a/docs/components/tab-panel.md b/docs/components/tab-panel.md
new file mode 100644
index 000000000..bdba888b8
--- /dev/null
+++ b/docs/components/tab-panel.md
@@ -0,0 +1,7 @@
+# Tab Panel
+
+Tab Panels are used to create panels inside Tab Groups.
+
+See the Tab Group component for more usage examples.
+
+[component-metadata:sl-tab-panel]
\ No newline at end of file
diff --git a/docs/components/tab.md b/docs/components/tab.md
new file mode 100644
index 000000000..695bef411
--- /dev/null
+++ b/docs/components/tab.md
@@ -0,0 +1,7 @@
+# Tab
+
+Tabs are used to create tabs inside Tab Groups.
+
+See the Tab Group component for more usage examples.
+
+[component-metadata:sl-tab]
\ No newline at end of file
diff --git a/docs/components/textarea.md b/docs/components/textarea.md
new file mode 100644
index 000000000..d22f3353f
--- /dev/null
+++ b/docs/components/textarea.md
@@ -0,0 +1,13 @@
+# Textarea
+
+```html preview
+
+
+
+
+
+
+
+```
+
+[component-metadata:sl-textarea]
\ No newline at end of file
diff --git a/docs/components/tooltip.md b/docs/components/tooltip.md
new file mode 100644
index 000000000..5befdd540
--- /dev/null
+++ b/docs/components/tooltip.md
@@ -0,0 +1,28 @@
+# Tooltip
+
+```html preview
+Tooltip
+This is a tooltip
+
+Tooltip With Arrow
+This is a tooltip with arrow
+
+Tooltip On Link
+This is a tooltip on a link
+
+
+
+Top
+Tooltip
+
+Bottom
+Tooltip
+
+Left
+Tooltip
+
+Right
+Tooltip
+```
+
+[component-metadata:sl-tooltip]
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
index fb0e86da8..5fe7fc130 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -64,7 +64,8 @@
-
+
+