diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 000000000..47245f4ba
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,7 @@
+{
+ "useTabs": true,
+ "jsxSingleQuote": true,
+ "singleQuote": true,
+ "singleAttributePerLine": true,
+ "printWidth": 120
+}
diff --git a/website/blog/2022-03-03-adding-blocks-wpcli.md b/website/blog/2022-03-03-adding-blocks-wpcli.md
index 918b25c43..186881c98 100644
--- a/website/blog/2022-03-03-adding-blocks-wpcli.md
+++ b/website/blog/2022-03-03-adding-blocks-wpcli.md
@@ -7,7 +7,7 @@ date: 2022-03-14
tags: [eightshift, boilerplate, wpcli, components, blocks]
hide_table_of_contents: false
---
-Although there are a few basic blocks available after creating a project, there are a lot more blocks available in the dev kit. However, you have to add them to your project using WP-CLI (the simplest method). To see the complete list of available components and blocks, visit our [Storybook](/devkit-components).
+Although there are a few basic blocks available after creating a project, there are a lot more blocks available in the dev kit. However, you have to add them to your project using WP-CLI (the simplest method). To see the complete list of available components and blocks, visit our [Storybook](/components/legacy-component-docs).
These can be used out-of-the-box, but also as a good starting point if you need similar blocks in your projects. It will also speed up your development time since you don't have to build everything from scratch.
diff --git a/website/blog/2022-04-25-using-assets.md b/website/blog/2022-04-25-using-assets.md
index 5a09c04c7..a0e4605e6 100644
--- a/website/blog/2022-04-25-using-assets.md
+++ b/website/blog/2022-04-25-using-assets.md
@@ -99,7 +99,7 @@ Here are some examples of icons available out-of-the-box in our Icon component:
## Using icons for editor and block options
-When developing your blocks and adding new options, you may need to add icons to improve the user experience. We have many icons already available for use. You can see the full list in our [Storybook](/devkit-components) under `UI icons` and `Block icons` section. We already added the icon when adding a new Color Theme option for the Quote block. Here is the simplified version:
+When developing your blocks and adding new options, you may need to add icons to improve the user experience. We have many icons already available for use. You can see the full list in our [Storybook](/components/legacy-component-docs) under `UI icons` and `Block icons` section. We already added the icon when adding a new Color Theme option for the Quote block. Here is the simplified version:
```jsx
import { ColorPaletteCustom, IconLabel, icons } from '@eightshift/frontend-libs/scripts';
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index ee587f398..a441ed459 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -1,10 +1,11 @@
-const {themes} = require('prism-react-renderer');
+const { themes } = require('prism-react-renderer');
const darkTheme = themes.dracula;
module.exports = {
title: 'Eightshift Development kit',
- tagline: 'All the tools you need to start building a modern WordPress project, using all the latest front end development tools.',
+ tagline:
+ 'All the tools you need to start building a modern WordPress project, using all the latest front end development tools.',
url: 'https://eightshift.com',
baseUrl: '/',
favicon: '/img/favicon.png',
@@ -16,7 +17,7 @@ module.exports = {
src: 'https://buttons.github.io/buttons.js',
async: true,
defer: true,
- }
+ },
],
themeConfig: {
navbar: {
@@ -38,8 +39,8 @@ module.exports = {
position: 'right',
},
{
- to: '/devkit-components/',
- activeBasePath: 'devkit-components',
+ to: '/components/welcome',
+ activeBasePath: 'components',
label: 'Components',
position: 'right',
},
@@ -107,7 +108,11 @@ module.exports = {
appId: 'CWB1S6U3C4',
apiKey: 'cbae3fc769aee256328548eff1e91c1c',
indexName: 'infinum_eightshift',
- startUrls: ['https://eightshift.com', 'https://eightshift.com/docs', 'https://eightshift.com/forms'],
+ startUrls: [
+ 'https://eightshift.com',
+ 'https://eightshift.com/docs',
+ 'https://eightshift.com/forms',
+ ],
contextualSearch: false,
},
prism: {
@@ -122,9 +127,9 @@ module.exports = {
docs: {
sidebar: {
autoCollapseCategories: true,
- }
+ },
},
- trailingSlash: false
+ trailingSlash: false,
},
presets: [
[
@@ -146,7 +151,8 @@ module.exports = {
},
blog: {
blogTitle: 'Tutorials and articles about Eightshift development kit',
- blogDescription: 'Tutorials and articles about Eightshift development kit',
+ blogDescription:
+ 'Tutorials and articles about Eightshift development kit',
blogSidebarTitle: 'Latest posts',
showReadingTime: true,
postsPerPage: 9,
@@ -168,6 +174,15 @@ module.exports = {
sidebarPath: require.resolve('./sidebars-forms.js'),
},
],
+ [
+ '@docusaurus/plugin-content-docs',
+ {
+ id: 'ui-components',
+ path: 'ui-components',
+ routeBasePath: 'components',
+ sidebarPath: require.resolve('./sidebars-components.js'),
+ },
+ ],
'es-text-loader',
],
customFields: {
@@ -178,7 +193,7 @@ module.exports = {
'Gutenberg blocks',
'development kit',
'wordpress kit',
- 'devkit'
+ 'devkit',
],
image: 'img-why-boilerplate@2x.png',
},
diff --git a/website/package-lock.json b/website/package-lock.json
index 256764c41..4d5fd02c6 100644
--- a/website/package-lock.json
+++ b/website/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@docusaurus/core": "^3.4.0",
"@docusaurus/preset-classic": "^3.4.0",
+ "@eightshift/ui-components": "^1.2.0",
"@infinum/docusaurus-theme": "^0.5.0",
"@mdx-js/react": "^3.0.1",
"@wp-playground/client": "^0.7.20",
@@ -3180,6 +3181,21 @@
"node": ">=18.0"
}
},
+ "node_modules/@eightshift/ui-components": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@eightshift/ui-components/-/ui-components-1.2.0.tgz",
+ "integrity": "sha512-hLA6be1CuC+XVEMyRE22DMxHUqL3KkG2s2KxQUvZ75yFt9iR1a4uyAttKM7MfpGdicn3hH939XicxWs9UszVlw==",
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-jsx-parser": "^1.29.0",
+ "svg-to-jsx-string": "^0.1.1"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -4931,6 +4947,16 @@
"@types/istanbul-lib-report": "*"
}
},
+ "node_modules/@types/jsdom": {
+ "version": "16.2.15",
+ "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.15.tgz",
+ "integrity": "sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/parse5": "^6.0.3",
+ "@types/tough-cookie": "*"
+ }
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -4985,6 +5011,11 @@
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
},
+ "node_modules/@types/parse5": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
+ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
+ },
"node_modules/@types/prettier": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
@@ -5024,6 +5055,26 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/@types/react-dom": {
+ "version": "17.0.25",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.25.tgz",
+ "integrity": "sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==",
+ "optional": true,
+ "dependencies": {
+ "@types/react": "^17"
+ }
+ },
+ "node_modules/@types/react-dom/node_modules/@types/react": {
+ "version": "17.0.80",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz",
+ "integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==",
+ "optional": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "^0.16",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/@types/react-router": {
"version": "5.1.20",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
@@ -5074,6 +5125,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/scheduler": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
+ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
+ "optional": true
+ },
"node_modules/@types/semver": {
"version": "7.5.8",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
@@ -5119,6 +5176,11 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="
+ },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -18848,6 +18910,38 @@
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-jsx-parser": {
+ "version": "1.29.0",
+ "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-1.29.0.tgz",
+ "integrity": "sha512-u0svZd0UsPffRrIK0sTbox54jhEbTmg6ED9ob5FQahp1DeOpd2Rq+KiSTIFNYkZUL+WZi4Ntia/Oj5T4lDJafQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@types/jsdom": "^16.2.6",
+ "acorn": "^8.0.5",
+ "acorn-jsx": "^5.3.1",
+ "browserslist": "^4.14.5",
+ "core-js": "^3.8.3"
+ },
+ "optionalDependencies": {
+ "@types/react": "^17.0.1",
+ "@types/react-dom": "^17.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/react-jsx-parser/node_modules/@types/react": {
+ "version": "17.0.80",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz",
+ "integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==",
+ "optional": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "^0.16",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/react-loadable": {
"name": "@docusaurus/react-loadable",
"version": "6.0.0",
@@ -22305,6 +22399,11 @@
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="
},
+ "node_modules/svg-to-jsx-string": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/svg-to-jsx-string/-/svg-to-jsx-string-0.1.1.tgz",
+ "integrity": "sha512-iZ6vpKQkZxX2TPnlWmb/GuSV9Msjfp7YVgyUnmMOEl9IUw8ilNqNBny/T/2tt6ctV1EqaDvOs5f7X0LrTIkqtA=="
+ },
"node_modules/svgo": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
diff --git a/website/package.json b/website/package.json
index 148f0e913..723ce5f82 100644
--- a/website/package.json
+++ b/website/package.json
@@ -34,6 +34,7 @@
"dependencies": {
"@docusaurus/core": "^3.4.0",
"@docusaurus/preset-classic": "^3.4.0",
+ "@eightshift/ui-components": "^1.2.0",
"@infinum/docusaurus-theme": "^0.5.0",
"@mdx-js/react": "^3.0.1",
"@wp-playground/client": "^0.7.20",
diff --git a/website/sidebars-components.js b/website/sidebars-components.js
new file mode 100644
index 000000000..de9103f4b
--- /dev/null
+++ b/website/sidebars-components.js
@@ -0,0 +1,117 @@
+module.exports = {
+ components: [
+ 'welcome',
+ {
+ type: 'category',
+ label: 'Eightshift UI components',
+ items: [
+ 'es-ui-components/getting-started',
+ {
+ type: 'category',
+ label: 'Icons',
+ items: ['es-ui-components/ui-icons', 'es-ui-components/block-icons'],
+ },
+ 'es-ui-components/base-control',
+ {
+ type: 'category',
+ label: 'Simple components',
+ items: [
+ 'es-ui-components/rich-label',
+ 'es-ui-components/button',
+ 'es-ui-components/notice',
+ 'es-ui-components/menu',
+ 'es-ui-components/tooltip',
+ 'es-ui-components/number-picker',
+ 'es-ui-components/placeholders',
+ 'es-ui-components/link-input',
+ 'es-ui-components/input-field',
+ 'es-ui-components/slider',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Toggles and toggle-related',
+ items: [
+ 'es-ui-components/toggle-switch',
+ 'es-ui-components/checkbox',
+ 'es-ui-components/radio-button',
+ 'es-ui-components/toggle-button',
+ 'es-ui-components/component-toggle',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Layout components',
+ items: [
+ 'es-ui-components/spacer',
+ 'es-ui-components/stacks',
+ 'es-ui-components/tabs',
+ 'es-ui-components/repeater',
+ 'es-ui-components/draggable-list',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Reveal components',
+ items: [
+ 'es-ui-components/triggered-popover',
+ 'es-ui-components/animated-visibility',
+ 'es-ui-components/expandable',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Responsive components',
+ items: [
+ 'es-ui-components/responsive',
+ 'es-ui-components/breakpoint-preview',
+ 'es-ui-components/responsive-preview',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Selection',
+ items: [
+ 'es-ui-components/column-config-slider',
+ 'es-ui-components/matrix-align',
+ {
+ type: 'category',
+ label: 'Select',
+ items: [
+ 'es-ui-components/select',
+ 'es-ui-components/multi-select',
+ 'es-ui-components/async-select',
+ 'es-ui-components/async-multi-select',
+ 'es-ui-components/select-customization',
+ ],
+ },
+ ],
+ },
+
+ {
+ type: 'category',
+ label: 'Colors and gradients',
+ items: [
+ 'es-ui-components/color-picker',
+ 'es-ui-components/solid-color-picker',
+ 'es-ui-components/gradient-editor',
+ 'es-ui-components/color-swatch',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Panels',
+ items: ['es-ui-components/container-panel', 'es-ui-components/options-panel'],
+ },
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Frontend libs components',
+ items: [
+ 'fe-libs-components/introduction'
+ ],
+ },
+ 'legacy-component-docs',
+ ],
+};
diff --git a/website/src/theme/styles.css b/website/src/theme/styles.css
index a555d9201..ae7cdbb2f 100644
--- a/website/src/theme/styles.css
+++ b/website/src/theme/styles.css
@@ -1,35 +1,38 @@
@import url('https://use.typekit.net/baz1roy.css');
+@import '~@eightshift/ui-components/dist/assets/fonts.css';
+@import '~@eightshift/ui-components/dist/assets/style.css';
+
.esd-full-fixed {
position: fixed;
inset: 0;
}
-
-progress {
- appearance: none;
+.esd-hide-bg-on-hover.es-uic-bg-violet-50:hover,
+.esd-hide-bg-on-hover:hover .es-uic-bg-violet-50,
+.esd-hide-bg-on-hover.es-uic-bg-gray-50:hover,
+.esd-hide-bg-on-hover:hover .es-uic-bg-gray-50 {
+ --tw-bg-opacity: 0 !important;
}
-progress::-webkit-progress-bar {
- background-color: white;
- border-radius: 10rem;
- border: 1px solid var(--ifm-color-gray-400);
-
- height: 0.5rem;
+.esd-white-space-pre {
+ white-space: break-spaces;
}
-progress::-webkit-progress-value {
- background-color: #D8262C;
- border-radius: 10rem;
+.esd-icon-showcase-button {
+ inline-size: 3.25rem !important;
+ block-size: 3.25rem !important;
- height: 0.5rem;
-
- transition: 0.5s inline-size ease-out;
+ svg {
+ inline-size: 1.5rem !important;
+ block-size: 1.5rem !important;
+ }
}
-progress[value]::-moz-progress-bar {
- background-color: #D8262C;
- border-radius: 10rem;
- height: 0.5rem;
+.esd-legacy-docs-iframe {
+ min-inline-size: 64vw;
+
+ border: 1px solid #D1D5DB !important;
+ border-radius: 0.5rem !important;
}
diff --git a/website/ui-components/components/component-showcase.js b/website/ui-components/components/component-showcase.js
new file mode 100644
index 000000000..bd9d1e2a9
--- /dev/null
+++ b/website/ui-components/components/component-showcase.js
@@ -0,0 +1,43 @@
+import { useState, useRef } from 'react';
+import { Button } from '@eightshift/ui-components';
+import { clsx } from 'clsx';
+
+export const ComponentShowcase = ({
+ children,
+ defaultValue,
+ className,
+ fitWidth = false,
+ resettable = false,
+ preContent,
+}) => {
+ const [data, setData] = useState(defaultValue);
+ const ref = useRef();
+
+ return (
+
+
+ {typeof children === 'function' && children(data, setData)}
+ {typeof children !== 'function' && children}
+
+
+
+ {preContent && preContent(data, setData, ref)}
+ {resettable && (
+
+ )}
+
+
+ );
+};
diff --git a/website/src/pages/devkit-components.js b/website/ui-components/components/legacy-playground.js
similarity index 74%
rename from website/src/pages/devkit-components.js
rename to website/ui-components/components/legacy-playground.js
index 3a0f39fd9..88863f5f3 100644
--- a/website/src/pages/devkit-components.js
+++ b/website/ui-components/components/legacy-playground.js
@@ -1,13 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
-import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
-import useBaseUrl from '@docusaurus/useBaseUrl';
-import Layout from '@theme/Layout';
import { startPlaygroundWeb, installTheme, wpCLI } from '@wp-playground/client';
-export default function DevKitComponents() {
- const context = useDocusaurusContext();
- const { siteConfig = {} } = context;
-
+export const LegacyComponentShowcase = () => {
const [isLoading, setIsLoading] = useState(true);
const [loadingStep, setLoadingStep] = useState('Initializing');
const [loadingProgress, setLoadingProgress] = useState(null);
@@ -22,9 +16,7 @@ export default function DevKitComponents() {
php: '8.3',
wp: '6.4',
},
- phpExtensionBundles: [
- 'kitchen-sink'
- ],
+ phpExtensionBundles: ['kitchen-sink'],
features: {
networking: true,
},
@@ -33,9 +25,9 @@ export default function DevKitComponents() {
{
step: 'login',
username: 'admin',
- password: 'password'
+ password: 'password',
},
- ]
+ ],
},
sapiName: 'cli',
});
@@ -49,8 +41,8 @@ export default function DevKitComponents() {
headers: {
'Content-Type': 'application/octet-stream',
},
- credentials: 'include'
- })
+ credentials: 'include',
+ });
setLoadingProgress(33);
setLoadingStep('Unpacking theme');
@@ -86,7 +78,9 @@ export default function DevKitComponents() {
});
// WP playground currently has an issue that pollutes the WP-CLI output, so --porcelain isn't of much help here unfortunately.
- const addedPostId = wpCliOutput.substring(wpCliOutput.indexOf('Created post ') + 13, wpCliOutput.lastIndexOf('.')).trim();
+ const addedPostId = wpCliOutput
+ .substring(wpCliOutput.indexOf('Created post ') + 13, wpCliOutput.lastIndexOf('.'))
+ .trim();
setLoadingStep('Finalizing');
setLoadingProgress(92);
@@ -105,29 +99,26 @@ export default function DevKitComponents() {
const iframeRef = useRef(null);
return (
-
+
- {isLoading &&
-
-
+ {isLoading && (
+
+
Preparing component docs
{loadingStep}
- }
-
+ )}
+
);
};
diff --git a/website/ui-components/es-ui-components/animated-visibility.mdx b/website/ui-components/es-ui-components/animated-visibility.mdx
new file mode 100644
index 000000000..ce0df7417
--- /dev/null
+++ b/website/ui-components/es-ui-components/animated-visibility.mdx
@@ -0,0 +1,113 @@
+# Animated visibility
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { AnimatedVisibility, Checkbox, OptionSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+Allows showing/hiding content in an animated way. After hiding, the content is unmounted from the DOM.
+
+
(
+
+ )}
+>
+ {(data) => (
+
+
+ This is demo content
+
+
+ )}
+
+
+```jsx
+
+ Your content goes here
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Transitions
+
+
(
+ <>
+ setData({ ...data, transition: v })}
+ options={[
+ { value: 'slideFade', label: 'Slide and fade', subtitle: 'Default' },
+ { value: 'scaleFade', label: 'Scale and fade' },
+ { value: 'scaleRotateFade', label: 'Scale, rotate, and fade' },
+ ]}
+ type='menu'
+ wrapperProps={{
+ keepOpen: false,
+ }}
+ />
+
+ setData({ ...data, checked: v })}
+ label='Visible'
+ />
+ >
+ )}
+>
+ {(data) => (
+
+
+ This is demo content
+
+
+ )}
+
+
+```jsx
+
+ This is demo content
+
+```
+
+### Skipping initial transition
+
+If you want to skip the initial transition (when the component mounts), and just want to transition
+when the visibility changes afterwards, you can use the `noInitial` prop.
+
+```jsx
+
+ Your content goes here
+
+```
diff --git a/website/ui-components/es-ui-components/async-multi-select.mdx b/website/ui-components/es-ui-components/async-multi-select.mdx
new file mode 100644
index 000000000..c13df9745
--- /dev/null
+++ b/website/ui-components/es-ui-components/async-multi-select.mdx
@@ -0,0 +1,35 @@
+# Async multi-select
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { AsyncMultiSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { demoGetData } from './select-helpers';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+const getOptions = async () => {
+ ...
+};
+
+
setValue(value)}
+ loadOptions={getOptions}
+/>
+```
+
+:::note
+Props are mostly the same as for the [Single select](/components/es-ui-components/select),
+with the exceptions of `options` (which is replaced with `loadOptions`) and `simpleValue`, which is not supported.
+:::
+
+For the complete list of props, use your IDE's autocomplete functionality.
diff --git a/website/ui-components/es-ui-components/async-select.mdx b/website/ui-components/es-ui-components/async-select.mdx
new file mode 100644
index 000000000..c10de79f5
--- /dev/null
+++ b/website/ui-components/es-ui-components/async-select.mdx
@@ -0,0 +1,35 @@
+# Async single select
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { AsyncSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { demoGetData } from './select-helpers';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+const getOptions = async () => {
+ ...
+};
+
+ setValue(value)}
+ loadOptions={getOptions}
+/>
+```
+
+:::note
+Props are mostly the same as for the [Single select](/components/es-ui-components/select),
+with the exceptions of `options` (which is replaced with `loadOptions`) and `simpleValue`, which is not supported.
+:::
+
+For the complete list of props, use your IDE's autocomplete functionality.
diff --git a/website/ui-components/es-ui-components/base-control.mdx b/website/ui-components/es-ui-components/base-control.mdx
new file mode 100644
index 000000000..1718a02da
--- /dev/null
+++ b/website/ui-components/es-ui-components/base-control.mdx
@@ -0,0 +1,132 @@
+# Control base
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { BaseControl, Button } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { globalManifest, responsiveOptions, breakpointNames } from './responsive-helpers';
+
+The base for a lot of Eightshift UI components controls.
+
+Allows creating your controls that will fit in nicely with Eightshift UI components
+
+
+
+
+ This is demo content
+
+
+
+
+```jsx
+
+ This is demo content
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Help text
+
+
+
+
+ This is demo content
+
+
+
+
+```jsx
+
+ This is demo content
+
+```
+
+### Actions
+
+
+
+
+
+ >
+ }
+ >
+
+ This is demo content
+
+
+
+
+```jsx
+
+
+
+ >
+ }
+ // highlight-end
+>
+ This is demo content
+
+```
+
+### Inline label
+
+
+
+
+ This is demo content
+
+
+
+
+```jsx
+
+ This is demo content
+
+```
diff --git a/website/ui-components/es-ui-components/block-icons.mdx b/website/ui-components/es-ui-components/block-icons.mdx
new file mode 100644
index 000000000..a5d088351
--- /dev/null
+++ b/website/ui-components/es-ui-components/block-icons.mdx
@@ -0,0 +1,44 @@
+# Block icons
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Button, InputField } from '@eightshift/ui-components';
+import { icons, blockIcons, BlockIcon } from '@eightshift/ui-components/icons';
+
+ (
+
+ )}
+>
+ {(data) => {
+ const filteredIcons = Object.keys(blockIcons).filter((k) =>
+ data?.length > 0 ? k.toLowerCase().includes(data?.toLowerCase()) : true
+ );
+
+ if (filteredIcons.length < 1) {
+ return (
+ Nothing found
+ );
+ }
+
+ return filteredIcons.map((icon, index) => (
+ }
+ tooltip={{icon}}
+ aria-label={icon}
+ onPress={() => {
+ navigator.clipboard.writeText(icon);
+ }}
+ />
+ ));
+ }}
+
diff --git a/website/ui-components/es-ui-components/breakpoint-preview.mdx b/website/ui-components/es-ui-components/breakpoint-preview.mdx
new file mode 100644
index 000000000..d596646b1
--- /dev/null
+++ b/website/ui-components/es-ui-components/breakpoint-preview.mdx
@@ -0,0 +1,148 @@
+# Breakpoint preview
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { BreakpointPreview } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+
+
+
+```jsx
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Start dots
+
+If you don't want to reduce the width of the blocks within.
+
+
+
+
+
+```jsx
+
+```
+
+### End dots
+
+If you don't want to reduce the width of the blocks within.
+
+
+
+
+
+```jsx
+
+```
diff --git a/website/ui-components/es-ui-components/button.mdx b/website/ui-components/es-ui-components/button.mdx
new file mode 100644
index 000000000..d5bc1fedf
--- /dev/null
+++ b/website/ui-components/es-ui-components/button.mdx
@@ -0,0 +1,199 @@
+# Button
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Button, ButtonGroup, OptionSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+:::note
+The click event is set by the `onPress` prop.
+:::
+
+
+
+
+
+```jsx
+
+```
+
+
+
+
+
+```jsx
+
+```
+
+
+
+
+
+```jsx
+
+```
+
+## Button groups
+
+If buttons are related, they can be grouped with a `ButtonGroup`.
+
+
+
+
+
+
+
+
+
+```jsx
+// highlight-next-line
+
+
+
+
+// highlight-next-line
+
+```
+
+Button groups improve focus behavior - instead of having to `Tab` through each button,
+the group is treated as a single focusable element that can be navigated with left/right arrows.
+
+### Vertical group
+
+
+
+
+
+
+
+
+
+In this case, the focus navigation is handled with up/down arrows.
+
+```jsx
+
+
+
+
+
+```
+
+:::note
+By default, vertical button groups take up the full width of their container.
+:::
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Sizes
+
+ (
+
+ )}
+>
+ {(data) => (
+ <>
+
+
+
+ >
+ )}
+
+
+```jsx
+
+```
+
+### Types
+
+ (
+
+ )}
+>
+ {(data) => (
+ <>
+
+
+
+ >
+ )}
+
+
+```jsx
+
+```
+
+### Tooltip
+
+
+
+
+
+```jsx
+
+```
diff --git a/website/ui-components/es-ui-components/checkbox.mdx b/website/ui-components/es-ui-components/checkbox.mdx
new file mode 100644
index 000000000..f279639d8
--- /dev/null
+++ b/website/ui-components/es-ui-components/checkbox.mdx
@@ -0,0 +1,84 @@
+# Checkbox
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Checkbox } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+/>
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Align checkbox to the end
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+
+```
+
+### Indeterminate state
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ // highlight-next-line
+ indeterminate={data === null}
+/>
+```
diff --git a/website/ui-components/es-ui-components/color-picker-helpers.js b/website/ui-components/es-ui-components/color-picker-helpers.js
new file mode 100644
index 000000000..21852c22d
--- /dev/null
+++ b/website/ui-components/es-ui-components/color-picker-helpers.js
@@ -0,0 +1,80 @@
+export const defaultColors = [
+ {
+ name: 'Red',
+ slug: 'red',
+ color: '#FF0000',
+ },
+ {
+ name: 'Green',
+ slug: 'green',
+ color: '#00FF00',
+ },
+ {
+ name: 'Blue',
+ slug: 'blue',
+ color: '#0000FF',
+ },
+ {
+ name: 'Yellow',
+ slug: 'yellow',
+ color: '#FFFF00',
+ },
+ {
+ name: 'Black',
+ slug: 'black',
+ color: '#000000',
+ },
+ {
+ name: 'White',
+ slug: 'white',
+ color: '#FFFFFF',
+ },
+];
+
+export const groupedColors = [
+ {
+ name: 'Red 50',
+ slug: 'red-50',
+ color: '#fef2f2',
+ },
+ {
+ name: 'Red 500',
+ slug: 'red-500',
+ color: '#ef4444',
+ },
+ {
+ name: 'Red 800',
+ slug: 'red-800',
+ color: '#991b1b',
+ },
+ {
+ name: 'Blue 100',
+ slug: 'blue100',
+ color: '#dbeafe',
+ },
+ {
+ name: 'Blue 500',
+ slug: 'blue500',
+ color: '#3b82f6',
+ },
+ {
+ name: 'Blue 700',
+ slug: 'blue700',
+ color: '#1d4ed8',
+ },
+ {
+ name: 'Green 500',
+ slug: 'green-500',
+ color: '#22c55e',
+ },
+ {
+ name: 'Black',
+ slug: 'black',
+ color: '#000000',
+ },
+ {
+ name: 'White',
+ slug: 'white',
+ color: '#FFFFFF',
+ },
+];
diff --git a/website/ui-components/es-ui-components/color-picker.mdx b/website/ui-components/es-ui-components/color-picker.mdx
new file mode 100644
index 000000000..bc1d03b23
--- /dev/null
+++ b/website/ui-components/es-ui-components/color-picker.mdx
@@ -0,0 +1,185 @@
+# Color picker
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { ColorPicker, OptionSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { defaultColors, groupedColors } from './color-picker-helpers';
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+const defaultColors = [
+ {
+ name: 'Red',
+ slug: 'red',
+ color: '#FF0000',
+ },
+ {
+ name: 'Green',
+ slug: 'green',
+ color: '#00FF00',
+ },
+ {
+ name: 'Blue',
+ slug: 'blue',
+ color: '#0000FF',
+ },
+ ...
+];
+
+ setData(value)}
+ colors={myColors}
+/>
+```
+
+:::tip
+In Frontend libs and Frontend libs Tailwind you can use the `getOption` helper to fetch the color data based on the list in the manifes.
+:::
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Allow deselecting a color
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ colors={myColors}
+ // highlight-next-line
+ clearable
+/>
+```
+
+### Disable color grouping
+
+By default, colors are grouped by their names, e.g. `red-100`, `red-200`, `red-300`...
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ colors={myColors}
+ // highlight-next-line
+ noColorGroups
+/>
+```
+
+### Show color hex code in the dropdown
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ colors={myColors}
+ // highlight-next-line
+ showColorCode
+/>
+```
+
+### Picker type
+
+ (
+ setData({ ...data, type: value })}
+ options={[
+ { value: 'default', label: 'Default', subtitle: default },
+ { value: 'fillColor', label: 'Fill color', subtitle: fillColor },
+ { value: 'textColor', label: 'Text color', subtitle: textColor },
+ {
+ value: 'textHighlightColor',
+ label: 'Text highlight color',
+ subtitle: textHighlightColor,
+ },
+ {
+ value: 'listMarkerColor',
+ label: 'List marker color',
+ subtitle: listMarkerColor,
+ },
+ ]}
+ type='menu'
+ />
+ )}
+>
+ {(data, setData) => {
+ return (
+ setData({ ...data, color: value })}
+ aria-label='Color picker'
+ colors={groupedColors}
+ type={data.type}
+ />
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ colors={myColors}
+ // highlight-next-line
+ type='textColor'
+/>
+```
diff --git a/website/ui-components/es-ui-components/color-swatch.mdx b/website/ui-components/es-ui-components/color-swatch.mdx
new file mode 100644
index 000000000..433b0f22e
--- /dev/null
+++ b/website/ui-components/es-ui-components/color-swatch.mdx
@@ -0,0 +1,90 @@
+# Color swatch
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { ColorSwatch } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+
+
+
+```jsx
+
+```
+
+:::caution
+This components only accepts a valid CSS color value or gradient, **with the exception of named colors**, of which only `transparent` is supported.
+:::
+
+## Empty state
+
+
+
+
+
+```jsx
+
+```
+
+## Transparent color
+
+
+
+
+
+```jsx
+
+```
+
+## Gradients
+
+
+
+
+
+
+
+```jsx
+
+
+
+```
+
+## Providing a custom color name
+
+For colors, the color name will be automatically generated based on the color value.
+If you want to provide a custom color name, you can do so by passing the `colorName` prop.
+
+You **should** provide this for gradients.
+
+
+
+
+
+```jsx
+
+```
diff --git a/website/ui-components/es-ui-components/column-config-slider.mdx b/website/ui-components/es-ui-components/column-config-slider.mdx
new file mode 100644
index 000000000..c2c4a492c
--- /dev/null
+++ b/website/ui-components/es-ui-components/column-config-slider.mdx
@@ -0,0 +1,141 @@
+# Column config slider
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { ColumnConfigSlider } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+/>
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Change number of columns
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ // highlight-next-line
+ columns={14}
+/>
+```
+
+### Mark outer columns as "gutter"
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ columns={14}
+ // highlight-next-line
+ showOuterAsGutter
+/>
+```
+
+### No offset thumb
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ // highlight-next-line
+ disableOffset
+/>
+```
+
+### No width thumb
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ // highlight-next-line
+ disableWidth
+/>
+```
diff --git a/website/ui-components/es-ui-components/component-toggle.mdx b/website/ui-components/es-ui-components/component-toggle.mdx
new file mode 100644
index 000000000..f5d01b5ee
--- /dev/null
+++ b/website/ui-components/es-ui-components/component-toggle.mdx
@@ -0,0 +1,144 @@
+# Component toggle
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { ComponentToggle, OptionSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => (
+
+
+ Component options go here
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+>
+ Component options go here
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Hide the use toggle
+
+
+ {(data, setData) => (
+
+
+ Component options go here
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ noUseToggle
+>
+ Component options go here
+
+```
+
+### Disable expand button
+
+
+ {(data, setData) => (
+
+
+ Component options go here
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ expandButtonDisabled
+>
+ Component options go here
+
+```
+
+### Styles
+
+ (
+ setData({ ...data, design: value })}
+ options={[
+ { value: 'default', label: 'Default', subtitle: default },
+ { value: 'compact', label: 'Compact', subtitle: compact },
+ { value: 'compactIcon', label: 'Compact (icon only)', subtitle: compactIcon },
+ { value: 'compactLabel', label: 'Compact (label only)', subtitle: compactLabel },
+ ]}
+ type='menu'
+ />
+ )}
+>
+ {(data, setData) => (
+ setData({ ...data, use: value })}
+ design={data.design}
+ >
+
+ Component options go here
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ design='compact'
+>
+ Component options go here
+
+```
diff --git a/website/ui-components/es-ui-components/container-panel.mdx b/website/ui-components/es-ui-components/container-panel.mdx
new file mode 100644
index 000000000..cdad16cbe
--- /dev/null
+++ b/website/ui-components/es-ui-components/container-panel.mdx
@@ -0,0 +1,190 @@
+# Container panel
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { ContainerPanel, Checkbox, Button } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+A perfect shell for options within the Block editor sidebar. Replaces the default WordPress `` component.
+
+
+
+
+ This is demo content
+
+
+
+
+```jsx
+
+ This is demo content
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Closable
+
+
+
+
+ This is demo content
+
+
+
+
+```jsx
+
+ This is demo content
+
+```
+
+### Use toggle
+
+
+ {(data, setData) => (
+
+
+ This is demo content
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-end
+>
+ This is demo content
+
+```
+
+### Use toggle and closable
+
+
+ {(data, setData) => (
+
+
+ This is demo content
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ closable
+ // highlight-end
+>
+ This is demo content
+
+```
+
+### Externally toggle use
+
+ (
+
+ )}
+ defaultValue={false}
+>
+ {(data) => (
+
+
+ This is demo content
+
+
+ )}
+
+
+```jsx
+
+ This is demo content
+
+```
+
+### Actions
+
+
+ {(data, setData) => (
+
+
+
+ >
+ }
+ closable
+ >
+
+ This is demo content
+
+
+ )}
+
+
+```jsx
+
+
+
+ >
+ }
+ // highlight-end
+>
+ This is demo content
+
+```
diff --git a/website/ui-components/es-ui-components/draggable-list-helpers.js b/website/ui-components/es-ui-components/draggable-list-helpers.js
new file mode 100644
index 000000000..0591f8972
--- /dev/null
+++ b/website/ui-components/es-ui-components/draggable-list-helpers.js
@@ -0,0 +1,46 @@
+import { DraggableList, DraggableListItem, Switch } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+export const draggableListDefaultItems = [
+ {
+ toggle: false,
+ title: 'Item 1',
+ icon: icons.num1Circle,
+ },
+ {
+ toggle: true,
+ title: 'Item 2',
+ icon: icons.num2Circle,
+ },
+ {
+ toggle: true,
+ icon: icons.num3Circle,
+ },
+];
+
+export const DraggableListDemo = ({ data, setData, ...rest }) => {
+ return (
+
+ {(item) => {
+ const { toggle, title, icon, updateData } = item;
+
+ return (
+
+ updateData({ toggle: value })}
+ />
+
+ );
+ }}
+
+ );
+};
diff --git a/website/ui-components/es-ui-components/draggable-list.mdx b/website/ui-components/es-ui-components/draggable-list.mdx
new file mode 100644
index 000000000..4dd9b19f9
--- /dev/null
+++ b/website/ui-components/es-ui-components/draggable-list.mdx
@@ -0,0 +1,40 @@
+# Draggable list
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { icons } from '@eightshift/ui-components/icons';
+import { DraggableListDemo, draggableListDefaultItems } from './draggable-list-helpers';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+>
+ {(item) => {
+ const { toggle, title, icon, updateData } = item;
+
+ return (
+
+ updateData({ toggle: value })}
+ />
+
+ );
+ }}
+
+```
+
+For the complete list of props, use your IDE's autocomplete functionality.
diff --git a/website/ui-components/es-ui-components/expandable.mdx b/website/ui-components/es-ui-components/expandable.mdx
new file mode 100644
index 000000000..32e8dceff
--- /dev/null
+++ b/website/ui-components/es-ui-components/expandable.mdx
@@ -0,0 +1,118 @@
+# Expandable
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Expandable, Button } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+
+
+ This is demo content
+
+
+
+
+```jsx
+
+ This is demo content
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Actions
+
+Add extra controls to the right of the label.
+
+
+ }
+ >
+
+ This is demo content
+
+
+
+
+```jsx
+
+ }
+ // highlight-end
+>
+ This is demo content
+
+```
+
+### Keep actions on expand
+
+If you want to keep the actions visible even when the content is expanded, you can use the `keepActionsOnExpand` prop.
+
+
+ }
+ keepActionsOnExpand
+ >
+
+ This is demo content
+
+
+
+
+```jsx
+
+ }
+ // highlight-next-line
+ keepActionsOnExpand
+>
+ This is demo content
+
+```
+
+### Disable focus trapping
+
+By default, the component will trap focus when expanded.
+If you want to disable this behavior, you can use the `noFocusHandling` prop.
+
+```jsx
+
+ This is demo content
+
+```
diff --git a/website/ui-components/es-ui-components/getting-started.mdx b/website/ui-components/es-ui-components/getting-started.mdx
new file mode 100644
index 000000000..7e04cdf53
--- /dev/null
+++ b/website/ui-components/es-ui-components/getting-started.mdx
@@ -0,0 +1,47 @@
+# Getting started
+
+## Installation
+
+It's easy to get started!
+
+### Get the package
+Install the `@eightshift/ui-components` package with your package manager of choice.
+
+### Import the styles
+
+At the minimum you have to import
+```scss
+@import '~@eightshift/ui-components/dist/assets/style.css';
+```
+
+We strongly recommend importing the font as well. This will ensure consistency between platforms and improve the overall look and feel.
+```scss
+@import '~@eightshift/ui-components/dist/assets/fonts.css';
+```
+
+To expand the font coverage to the Block editor (even the Gutenberg components), import:
+```scss
+@import '~@eightshift/ui-components/dist/assets/wp-font-enhancements.css';
+```
+
+Optionally, import the WordPress UI enhancements, which correct some of the inconsistencies in the default components.
+
+```scss
+@import '~@eightshift/ui-components/dist/assets/wp-ui-enhancements.css';
+```
+
+:::caution
+If using Frontend libs version 12 or older, skip the WordPress UI enhancements, as they will conflict with the styles included there.
+:::
+
+#### Recommended setup
+```scss
+@import '~@eightshift/ui-components/dist/assets/fonts.css';
+@import '~@eightshift/ui-components/dist/assets/wp-font-enhancements.css';
+@import '~@eightshift/ui-components/dist/assets/wp-ui-enhancements.css';
+@import '~@eightshift/ui-components/dist/assets/style.css';
+```
+
+### Enjoy!
+
+That's all! You are ready to use Eightshift UI components in your project.
diff --git a/website/ui-components/es-ui-components/gradient-editor.mdx b/website/ui-components/es-ui-components/gradient-editor.mdx
new file mode 100644
index 000000000..9ef2ee824
--- /dev/null
+++ b/website/ui-components/es-ui-components/gradient-editor.mdx
@@ -0,0 +1,24 @@
+# Gradient editor
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { GradientEditor } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => {
+ return (
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+/>
+```
diff --git a/website/ui-components/es-ui-components/input-field.mdx b/website/ui-components/es-ui-components/input-field.mdx
new file mode 100644
index 000000000..2c2bad72d
--- /dev/null
+++ b/website/ui-components/es-ui-components/input-field.mdx
@@ -0,0 +1,65 @@
+# Input field
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { InputField, OptionSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ placeholder='Basic text input'
+/>
+```
+
+:::note
+If you're not setting a `label`, add an `aria-label` to help describe the field to users that use screen readers.
+:::
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Field type
+
+ (
+ setData({ ...data, type: value })}
+ options={[
+ { value: 'text', label: 'Text', subtitle: text },
+ { value: 'search', label: 'Search field', subtitle: search },
+ { value: 'url', label: 'URL', subtitle: url },
+ { value: 'tel', label: 'Phone number', subtitle: tel },
+ { value: 'email', label: 'E-mail', subtitle: email },
+ { value: 'password', label: 'Password', subtitle: password },
+ { value: 'multiline', label: 'Multi-line text', subtitle: multiline },
+ ]}
+ type='menu'
+ />
+ )}
+>
+ {(data, setData) => (
+ setData({ ...data, text: value })}
+ type={data.type}
+ aria-label='Demo field'
+ />
+ )}
+
diff --git a/website/ui-components/es-ui-components/link-input-helpers.js b/website/ui-components/es-ui-components/link-input-helpers.js
new file mode 100644
index 000000000..7e183d64b
--- /dev/null
+++ b/website/ui-components/es-ui-components/link-input-helpers.js
@@ -0,0 +1,72 @@
+import { icons } from '@eightshift/ui-components/icons';
+
+const linkData = [
+ { label: 'Eightshift', value: 'https://eightshift.com', metadata: { subtype: 'url' } },
+ {
+ label: 'This is a demo post',
+ value: 'https://your-website.com/blog/demo-post',
+ metadata: { subtype: 'post' },
+ },
+ {
+ label: 'This is a demo page',
+ value: 'https://your-website.com/demo-page',
+ metadata: { subtype: 'page' },
+ },
+ { label: 'Homepage', value: 'https://your-website.com/', metadata: { subtype: 'page' } },
+ {
+ label: '2023 top secret report',
+ value: 'https://your-website.com/2023-top-secret-report.pdf',
+ metadata: { subtype: 'attachment' },
+ },
+ {
+ label: 'Services archive',
+ value: 'https://your-website.com/services/',
+ metadata: { subtype: 'category' },
+ },
+ {
+ label: 'Sign up form',
+ value: 'https://your-website.com/forms/signup',
+ metadata: { subtype: 'eightshift-forms' },
+ },
+ {
+ label: 'How to lorem ipsum?',
+ value: 'https://your-website.com/help-articles/how-to-lorem-ipsum',
+ metadata: { subtype: 'help-article' },
+ },
+];
+
+export const getLinkData = async (searchTerm) => {
+ if (!searchTerm) {
+ return linkData;
+ }
+
+ const filtered = linkData.filter(
+ ({ label, value }) =>
+ label.toLowerCase().includes(searchTerm.toLowerCase().trim()) ||
+ value.toLowerCase().includes(searchTerm.toLowerCase().trim())
+ );
+
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ if (filtered.length > 0) {
+ return filtered;
+ }
+
+ return [];
+};
+
+export const getIconOverrides = (type) => {
+ if (type === 'page') {
+ return icons.magicAltFillTransparent;
+ }
+
+ if (type === 'post') {
+ return icons.newspaper;
+ }
+
+ if (type === 'help-article') {
+ return icons.troubleshootAlt;
+ }
+
+ return null;
+}
diff --git a/website/ui-components/es-ui-components/link-input.mdx b/website/ui-components/es-ui-components/link-input.mdx
new file mode 100644
index 000000000..cd9b25eb8
--- /dev/null
+++ b/website/ui-components/es-ui-components/link-input.mdx
@@ -0,0 +1,90 @@
+# Link input
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { LinkInput } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { getLinkData, getIconOverrides } from './link-input-helpers';
+
+
+ {(data, setData) => (
+ setData(url)}
+ fetchSuggestions={getLinkData}
+ />
+ )}
+
+
+```jsx
+const getSearchSuggestions = async (search) => {
+ // Do your thing, and return an array of suggestions.
+ // Each item should have a `label` and a `value`.
+ // Optionally, you can include a `subtype` within
+ // `metadata` to show a different icon in the suggestions.
+};
+
+ setAttributes({ myUrl: url })}
+ fetchSuggestions={getSearchSuggestions}
+/>;
+```
+
+## Frontend libs integration
+
+If using Frontend libs (v13+) or Frontend libs Tailwind, for the `fetchSuggestions` callback you can import the included `wpSearchRoute` function.
+
+```jsx
+// highlight-next-line
+import { wpSearchRoute } from '@eightshift/frontend-libs-tailwind/scripts';
+
+...
+
+ setAttributes({ myUrl: url })}
+ // highlight-next-line
+ fetchSuggestions={getSearchSuggestions}
+/>
+```
+
+## Providing custom metadata icons
+
+If you want to provide custom icons for the search suggestions, you can provide a mapping list with the `suggestionTypeIconOverride` prop to map the `subtype` to the icon.
+
+
+ {(data, setData) => (
+ setData(url)}
+ fetchSuggestions={getLinkData}
+ suggestionTypeIconOverride={getIconOverrides}
+ help='Try searching for "demo"'
+ />
+ )}
+
+
+```jsx
+const overrides = {
+ post: icons.newspaper,
+ page: icons.magicAltFillTransparent,
+ 'help-article': icons.troubleshootAlt,
+};
+
+ setAttributes({ myUrl: url })}
+ fetchSuggestions={getSearchSuggestions}
+ // highlight-next-line
+ suggestionTypeIconOverride={(type) => overrides[type]}
+/>;
+```
+
+```jsx
+
+```
diff --git a/website/ui-components/es-ui-components/matrix-align.mdx b/website/ui-components/es-ui-components/matrix-align.mdx
new file mode 100644
index 000000000..67738f6ab
--- /dev/null
+++ b/website/ui-components/es-ui-components/matrix-align.mdx
@@ -0,0 +1,70 @@
+# Matrix align
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { MatrixAlign } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setData(value)}
+ value={data}
+/>
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Matrix size
+
+`3x3` is the default size. If needed, `2x2` can be set with the `size` prop.
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setData(value)}
+ value={data}
+/>
+```
+
+### Label
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setData(value)}
+ value={data}
+ // highlight-start
+ label='Alignment'
+ icon={icons.emptyRect}
+ // highlight-end
+/>
+```
diff --git a/website/ui-components/es-ui-components/menu.mdx b/website/ui-components/es-ui-components/menu.mdx
new file mode 100644
index 000000000..71f48336a
--- /dev/null
+++ b/website/ui-components/es-ui-components/menu.mdx
@@ -0,0 +1,459 @@
+# Menu
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Menu, MenuItem, MenuSeparator, MenuSection, SubMenuItem } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+
+
+
+```jsx
+
+```
+
+## Menu separator
+
+
+
+
+
+```jsx
+
+```
+
+## Menu with sections
+
+:::caution
+If using sections, you **do not** mix menu sections and regular menu items.
+:::
+
+
+
+
+
+```jsx
+
+```
+
+If you need to have a top-level menu item outside of any sections you can either add a section without a label, or use `MenuSeparator` elements with a disabled `MenuItem` to act as the section label.
+
+## Menu with a sub-menu
+
+
+
+
+
+```jsx
+
+```
+
+## Menu item customization
+
+
+
+
+
+```jsx
+
+```
+
+:::note
+The `shortcut` prop is used to display additional text on the right side of the menu item.
+It will not handle keyboard shortcut management.
+:::
+
+## Single/multi-select menu items
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+
+```
+
+```jsx
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Trigger button customization
+
+
+
+
+
+```jsx
+
+```
+
+
+
+
+
+```jsx
+
+```
+
+#### Advanced customizations
+
+`triggerProps` accepts all [`Button`](/components/es-ui-components/button) props.
+
+
+
+
+
+```jsx
+
+```
+
+### Keep open after item selection
+
+By default, the menu will close after an item is selected. If you want to keep it open, use the `keepOpen` prop.
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+
+```
+
+### Open on long press
+
+If you want the menu to open on long press instead of click, use the `openOnLongPress` prop.
+
+
+
+
+
+```jsx
+
+```
+
+This allows implementing a button that can, for example, act as a toggle button by default, but open a menu on long press.
+
+**Be careful about UX and discoverability in those cases.**
+
+ (
+
+ Font weight
+
+ {data}
+
+ )}
+>
+ {(data, setData) => (
+
+ )}
+
+
+_Click to toggle 'bold', press and hold for more font weights._
+
+```jsx
+
+```
diff --git a/website/ui-components/es-ui-components/multi-select.mdx b/website/ui-components/es-ui-components/multi-select.mdx
new file mode 100644
index 000000000..659568926
--- /dev/null
+++ b/website/ui-components/es-ui-components/multi-select.mdx
@@ -0,0 +1,34 @@
+# Multi-select
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { MultiSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { demoOptions } from './select-helpers';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ options={[
+ { value: 'option-1', label: 'Option 1' },
+ { value: 'option-2', label: 'Option 2' },
+ { value: 'option-3', label: 'Option 3' },
+ ]}
+/>
+```
+
+:::note
+Props are the same as for the [Single select](/components/es-ui-components/select).
+:::
+
+For the complete list of props, use your IDE's autocomplete functionality.
diff --git a/website/ui-components/es-ui-components/notice.mdx b/website/ui-components/es-ui-components/notice.mdx
new file mode 100644
index 000000000..0b547545c
--- /dev/null
+++ b/website/ui-components/es-ui-components/notice.mdx
@@ -0,0 +1,125 @@
+# Notice
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Notice, OptionSelect, Checkbox } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+
+
+
+```jsx
+
+```
+
+
+
+
+
+```jsx
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Type
+
+ (
+ default },
+ { value: 'error', label: 'Error', subtitle: error },
+ { value: 'info', label: 'Info', subtitle: info },
+ { value: 'placeholder', label: 'Placeholder', subtitle: placeholder },
+ { value: 'success', label: 'Success', subtitle: success },
+ { value: 'warning', label: 'Warning', subtitle: warning },
+ ]}
+ type='menu'
+ />
+ )}
+>
+ {(data) => (
+
+ )}
+
+
+```jsx
+
+```
+
+### Icon override
+
+
+
+
+
+```jsx
+
+```
+
+### Align icon to title
+
+Aligns the icon vertically to the title, instead of the whole notice. Useful if the content of the notice is longer.
+
+ (
+
+ )}
+>
+ {(data) => (
+
+ )}
+
+
+```jsx
+
+```
diff --git a/website/ui-components/es-ui-components/number-picker.mdx b/website/ui-components/es-ui-components/number-picker.mdx
new file mode 100644
index 000000000..bb51c0494
--- /dev/null
+++ b/website/ui-components/es-ui-components/number-picker.mdx
@@ -0,0 +1,214 @@
+# Number picker
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { NumberPicker, Button } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+/>
+```
+
+:::tip
+When focused inside the input field, use the up/down arrows to change the value.
+:::
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Min, max, step
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-start
+ min={-40}
+ max={40}
+ step={2}
+ // highlight-end
+/>
+```
+
+### Fixed width
+
+By default, the picker will try to calculate maximum width based on the `max` value.
+If it's not set, the width defaults to 4 characters.
+
+In cases where `min` is less than `0`, the width is slightly increased to account for the `-` sign.
+
+If you set the width to a certain number of characters, you can use the `fixedWidth` prop.
+Decimal numbers are allowed.
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ max={999}
+ // highlight-next-line
+ fixedWidth={12}
+/>
+```
+
+### Prefix and suffix
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ prefix='€'
+/>
+```
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ suffix='item(s)'
+/>
+```
+
+### Additional controls
+
+
+ {(data, setData) => (
+
+
+ )}
+
+
+```jsx
+ setValue(value)}
+>
+ // highlight-start
+
+```
+
+### Label, icon, subtitle, help, inline
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-start
+ label='Pick a number'
+ icon={icons.emptyCircle}
+ help='Help text'
+ // highlight-end
+/>
+```
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-start
+ label='Pick a number'
+ icon={icons.emptyCircle}
+ subtitle='Subtitle text'
+ inline
+ // highlight-end
+/>
+```
diff --git a/website/ui-components/es-ui-components/options-panel.mdx b/website/ui-components/es-ui-components/options-panel.mdx
new file mode 100644
index 000000000..e63e51d34
--- /dev/null
+++ b/website/ui-components/es-ui-components/options-panel.mdx
@@ -0,0 +1,92 @@
+# Options panel
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { OptionsPanel, OptionsPanelSection } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+An elegant way to display options in an options page, with a contained width and options for label and help text.
+
+
+
+
+
+ This is demo content
+
+
+
+
+
+```jsx
+
+
+ This is demo content
+
+
+```
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+## Multiple sections
+
+
+
+
+
+ Section 1 content
+
+
+
+
+ Section 2 content
+
+
+
+
+
+```jsx
+
+
+ Section 1 content
+
+
+ Section 2 content
+
+
+```
+
+## Title and help text
+
+
+
+
+
+ Section content
+
+
+
+
+
+```jsx
+
+
+ Section content
+
+
+```
diff --git a/website/ui-components/es-ui-components/placeholders.mdx b/website/ui-components/es-ui-components/placeholders.mdx
new file mode 100644
index 000000000..2c3530a34
--- /dev/null
+++ b/website/ui-components/es-ui-components/placeholders.mdx
@@ -0,0 +1,233 @@
+# Placeholders
+
+import { ComponentShowcase } from '../components/component-showcase';
+import {
+ ImagePlaceholder,
+ FilePlaceholder,
+ MediaPlaceholder,
+ Checkbox,
+ OptionSelect,
+ RichLabel,
+} from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+## Image placeholder
+
+ (
+
+ )}
+>
+ {(data) => }
+
+
+```jsx
+
+```
+
+### Image display mode
+
+ (
+ contain },
+ { value: 'cover', label: 'Cover', subtitle: cover },
+ { value: 'fill', label: 'Fill', subtitle: fill },
+ ]}
+ type='menu'
+ />
+ )}
+>
+ {(data) => (
+
+ )}
+
+
+```jsx
+
+```
+
+### Sizes
+
+ (
+ default },
+ { value: 'auto', label: 'Auto', subtitle: auto },
+ { value: 'large', label: 'Large', subtitle: large },
+ { value: 'fullWidth', label: 'Full width', subtitle: fullWidth },
+ { value: 'fullHeight', label: 'Full height', subtitle: fullHeight },
+ { value: 'full', label: 'Full width and height', subtitle: full },
+ { value: 'video', label: 'Video (16:9)', subtitle: video },
+ ]}
+ type='menu'
+ help='Preview in docs may not be fully accurate for all sizes'
+ />
+ )}
+>
+ {(data) => (
+
+ )}
+
+
+```jsx
+
+```
+
+### Styles
+
+ (
+ default },
+ { value: 'simple', label: 'Simple', subtitle: simple },
+ ]}
+ type='menu'
+ />
+ )}
+>
+ {(data) => (
+
+ )}
+
+
+```jsx
+
+```
+
+### No image (placeholder only)
+
+This can also be achieved by not providing an `url`, but this should be
+a bit more performant as you don't have an `` without a source.
+
+
+
+
+
+```jsx
+
+```
+
+## File placeholder
+
+
+
+
+
+```jsx
+
+```
+
+## Media placeholder
+
+`style` and `size` works the same as in the `ImagePlaceholder`, so they won't be covered again.
+
+
+
+
+
+```jsx
+
+```
+
+### Help text
+
+
+
+
+
+```jsx
+
+```
+
+
+
+ }
+ />
+
+
+```jsx
+
+ }
+ // highlight-end
+/>
+```
diff --git a/website/ui-components/es-ui-components/radio-button.mdx b/website/ui-components/es-ui-components/radio-button.mdx
new file mode 100644
index 000000000..99f67f5ef
--- /dev/null
+++ b/website/ui-components/es-ui-components/radio-button.mdx
@@ -0,0 +1,186 @@
+# Radio button
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { RadioButton, RadioButtonGroup } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+
+ {(data, setData) => {
+ return (
+
+
+
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+>
+
+
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Horizontal layout
+
+
+ {(data, setData) => {
+ return (
+
+
+
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ // highlight-next-line
+ orientation='horizontal'
+>
+
+
+
+```
+
+### Segmented design
+
+
+ {(data, setData) => {
+ return (
+
+
+
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+ // highlight-next-line
+ design='segmented'
+>
+
+
+
+```
+
+### Disable certain items
+
+
+ {(data, setData) => {
+ return (
+
+
+
+
+
+ );
+ }}
+
+
+```jsx
+ setData(value)}
+>
+
+
+
+
+```
diff --git a/website/ui-components/es-ui-components/repeater-helpers.js b/website/ui-components/es-ui-components/repeater-helpers.js
new file mode 100644
index 000000000..d2bcb927c
--- /dev/null
+++ b/website/ui-components/es-ui-components/repeater-helpers.js
@@ -0,0 +1,41 @@
+import { Repeater, RepeaterItem, InputField, Toggle } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+
+export const RepeaterDemo = ({ data, setData, ...rest }) => {
+ return (
+
+ {(item) => {
+ const { title, toggledOption, updateData, itemIndex } = item;
+
+ return (
+
+ updateData({ toggledOption: value })}
+ />
+
+ updateData({ title: value })}
+ />
+
+ );
+ }}
+
+ );
+};
diff --git a/website/ui-components/es-ui-components/repeater.mdx b/website/ui-components/es-ui-components/repeater.mdx
new file mode 100644
index 000000000..d0add1cfe
--- /dev/null
+++ b/website/ui-components/es-ui-components/repeater.mdx
@@ -0,0 +1,179 @@
+# Repeater
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { RepeaterDemo } from './repeater-helpers';
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+>
+ {(item) => {
+ const { title, toggledOption, updateData } = item;
+
+ return (
+
+ updateData({ toggledOption: value })}
+ />
+
+ updateData({ title: value })}
+ />
+
+ );
+ }}
+
+```
+
+:::tip
+Besides the item attributes, every `item` in the render function will also include a function for updating the current item (`updateData`), the current item's index (`itemIndex`), and a function for deleting the current item (`deleteItem`).
+:::
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Set default value for newly added items
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-start
+ addDefaultItem={{
+ title: 'My new item',
+ }}
+ // highlight-end
+>
+ {...}
+
+```
+
+### Prevent item deletion when below a certain count
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ minItems={3}
+>
+ {...}
+
+```
+
+### Replace the default empty state
+
+
+ {(data, setData) => (
+ Nothing to see here...}
+ />
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ emptyState={Nothing to see here...}
+>
+ {...}
+
+```
+
+### Don't show the empty state
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ hideEmptyState
+>
+ {...}
+
+```
+
+### Prevent adding items
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ // highlight-next-line
+ addDisabled
+>
+ {...}
+
+```
diff --git a/website/ui-components/es-ui-components/responsive-helpers.js b/website/ui-components/es-ui-components/responsive-helpers.js
new file mode 100644
index 000000000..6f889b0a7
--- /dev/null
+++ b/website/ui-components/es-ui-components/responsive-helpers.js
@@ -0,0 +1,45 @@
+export const responsiveOptions = [
+ {
+ label: 'Sans',
+ value: 'sans',
+ },
+ {
+ label: 'Serif',
+ value: 'serif',
+ },
+ {
+ label: 'Monospace',
+ value: 'mono',
+ },
+];
+
+export const globalManifest = {
+ globalVariables: {
+ breakpoints: {
+ mobile: 480,
+ tablet: 960,
+ desktop: 1440,
+ large: 1920,
+ },
+ },
+};
+
+export const globalManifest2 = {
+ globalVariables: {
+ breakpoints: {
+ sm: 480,
+ md: 960,
+ lg: 1440,
+ xl: 1920,
+ },
+ },
+};
+
+export const breakpointNames = ['mobile', 'tablet', 'desktop', 'large'];
+
+export const responsiveLegacyAttr = {
+ large: 'myAttrLarge',
+ desktop: 'myAttrDesktop',
+ tablet: 'myAttrTablet',
+ mobile: 'myAttrMobile',
+};
diff --git a/website/ui-components/es-ui-components/responsive-preview.mdx b/website/ui-components/es-ui-components/responsive-preview.mdx
new file mode 100644
index 000000000..1b85a8e85
--- /dev/null
+++ b/website/ui-components/es-ui-components/responsive-preview.mdx
@@ -0,0 +1,35 @@
+# Responsive preview
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { ResponsivePreview } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { globalManifest, responsiveOptions, breakpointNames } from './responsive-helpers';
+
+
+
+
+
+```jsx
+
+ Toggle me
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
diff --git a/website/ui-components/es-ui-components/responsive.mdx b/website/ui-components/es-ui-components/responsive.mdx
new file mode 100644
index 000000000..27bba1ed8
--- /dev/null
+++ b/website/ui-components/es-ui-components/responsive.mdx
@@ -0,0 +1,437 @@
+# Responsive
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Responsive, ResponsiveLegacy, Button, OptionSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { globalManifest, responsiveOptions, breakpointNames, responsiveLegacyAttr } from './responsive-helpers';
+
+Easily manage values that can be set per-breakpoint.
+
+:::note
+If using Frontend libs (that has **one attribute per breakpoint**), use the [ResponsiveLegacy](#responsivelegacy) component.
+:::
+
+ (
+ {JSON.stringify(data, null, 2)}
+ )}
+>
+ {(data, setData) => (
+
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ options={[
+ { value: 'sans', label: 'Sans-serif' },
+ { value: 'serif', label: 'Serif' },
+ { value: 'mono', label: 'Monospace' },
+ ]}
+ breakpoints={['mobile', 'tablet', 'desktop', 'large']}
+ breakpointData={{
+ mobile: 480,
+ tablet: 960,
+ desktop: 1440,
+ large: 1920,
+ }}
+>
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+```
+
+:::tip
+In Frontend libs Tailwind you can use the `getResponsiveData` helper to fetch breakpoint data.
+:::
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Inline
+
+ (
+ {JSON.stringify(data, null, 2)}
+ )}
+>
+ {(data, setData) => (
+
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ options={[
+ { value: 'sans', label: 'Sans-serif' },
+ { value: 'serif', label: 'Serif' },
+ { value: 'mono', label: 'Monospace' },
+ ]}
+ breakpoints={['mobile', 'tablet', 'desktop', 'large']}
+ breakpointData={{
+ mobile: 480,
+ tablet: 960,
+ desktop: 1440,
+ large: 1920,
+ }}
+ // highlight-next-line
+ inline
+>
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+```
+
+### Different control when inline (collapsed view)
+
+:::note
+Only applies to the control for the default breakpoint.
+:::
+
+ (
+ {JSON.stringify(data, null, 2)}
+ )}
+>
+ {(data, setData) => (
+
+ {({ currentValue, handleChange, options, isInlineCollapsedView }) => (
+ handleChange(value)}
+ value={currentValue}
+ type={isInlineCollapsedView ? 'menu' : 'toggleButtons'}
+ />
+ )}
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ options={[
+ { value: 'sans', label: 'Sans-serif' },
+ { value: 'serif', label: 'Serif' },
+ { value: 'mono', label: 'Monospace' },
+ ]}
+ breakpoints={['mobile', 'tablet', 'desktop', 'large']}
+ breakpointData={{
+ mobile: 480,
+ tablet: 960,
+ desktop: 1440,
+ large: 1920,
+ }}
+ inline
+>
+ // highlight-next-line
+ {({ currentValue, handleChange, options, isInlineCollapsedView }) => (
+ handleChange(value)}
+ value={currentValue}
+ // highlight-next-line
+ type={isInlineCollapsedView ? 'menu' : 'toggleButtons'}
+ />
+ )}
+
+```
+
+### Different control when inline (expanded view)
+
+:::note
+Only applies to the control for the default breakpoint.
+:::
+
+ (
+ {JSON.stringify(data, null, 2)}
+ )}
+>
+ {(data, setData) => (
+
+ {({ currentValue, handleChange, options, isInlineExpandedView }) => (
+ handleChange(value)}
+ value={currentValue}
+ type={isInlineExpandedView ? 'radiosSegmented' : 'toggleButtons'}
+ />
+ )}
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ options={[
+ { value: 'sans', label: 'Sans-serif' },
+ { value: 'serif', label: 'Serif' },
+ { value: 'mono', label: 'Monospace' },
+ ]}
+ breakpoints={['mobile', 'tablet', 'desktop', 'large']}
+ breakpointData={{
+ mobile: 480,
+ tablet: 960,
+ desktop: 1440,
+ large: 1920,
+ }}
+ inline
+>
+ // highlight-next-line
+ {({ currentValue, handleChange, options, isInlineExpandedView }) => (
+ handleChange(value)}
+ value={currentValue}
+ // highlight-next-line
+ type={isInlineExpandedView ? 'radiosSegmented' : 'toggleButtons'}
+ />
+ )}
+
+```
+
+### Disable desktop-first/mobile-first picker
+
+ (
+ {JSON.stringify(data, null, 2)}
+ )}
+>
+ {(data, setData) => (
+
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+ )}
+
+
+```jsx
+ setValue(value)}
+ options={[
+ { value: 'sans', label: 'Sans-serif' },
+ { value: 'serif', label: 'Serif' },
+ { value: 'mono', label: 'Monospace' },
+ ]}
+ breakpoints={['mobile', 'tablet', 'desktop', 'large']}
+ breakpointData={{
+ mobile: 480,
+ tablet: 960,
+ desktop: 1440,
+ large: 1920,
+ }}
+ // highlight-next-line
+ noModeSelect
+>
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+```
+
+## `ResponsiveLegacy`
+
+:::caution
+This component is meant to be used in projects that have **one attribute per breakpoint**, e.g. regular Frontend libs. If using Frontend libs **Tailwind**, use the [Responsive](/components/es-ui-components/responsive) component.
+:::
+
+Props are mostly the same as in the `Responsive` component.
+
+Main differences:
+
+- there is not desktop-first/mobile-first mode switcher
+- there is no _Responsive preview_
+
+ (
+ {JSON.stringify(data, null, 2)}
+ )}
+>
+ {(data, setData) => (
+ {
+ setData({
+ ...data,
+ [attrName]: newVal,
+ });
+ }}
+ options={responsiveOptions}
+ breakpointData={globalManifest.globalVariables.breakpoints}
+
+ >
+ {({ currentValue, options, handleChange }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+ )}
+
+
+
+```jsx
+ {
+ setAttributes({
+ ...attributes,
+ [attributeName]: newValue,
+ });
+ }}
+ options={[
+ { value: 'sans', label: 'Sans-serif' },
+ { value: 'serif', label: 'Serif' },
+ { value: 'mono', label: 'Monospace' },
+ ]}
+ breakpointData={globalManifest.globalVariables.breakpoints}
+>
+ {({ currentValue, handleChange, options }) => (
+ handleChange(value)}
+ value={currentValue}
+ />
+ )}
+
+```
+
+### Inherited values
+
+The default value that marks an attribute as _inherited_ from the breakpoint above is an empty string.
+To set a custom one, use the `inheritValue` prop.
+
+If you need the inherited value to be `undefined`, set the `allowUndefined` prop instead.
diff --git a/website/ui-components/es-ui-components/rich-label.mdx b/website/ui-components/es-ui-components/rich-label.mdx
new file mode 100644
index 000000000..072eb51e7
--- /dev/null
+++ b/website/ui-components/es-ui-components/rich-label.mdx
@@ -0,0 +1,93 @@
+# Rich label
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { RichLabel } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import { globalManifest, responsiveOptions, breakpointNames } from './responsive-helpers';
+
+Upgrade your basic labels with an optional icon and subtitle.
+
+
+
+
+
+```jsx
+
+```
+
+## Highlighted props
+
+For the complete list of props, use your IDE's autocomplete functionality.
+
+### Expand to full width
+
+
+
+
+
+```jsx
+
+```
+
+### Monochrome
+
+If you have cases where the text might be a different color, or of dynamic color,
+use the `noColor` prop to use opacity to highlight hierarchy instead of multiple colors.
+
+
+
+
+
+```jsx
+// The container has a red text color applied.
+
+```
+
+
+
+
+
+
+```jsx
+// The container has a red text color applied.
+
+```
diff --git a/website/ui-components/es-ui-components/select-customization.mdx b/website/ui-components/es-ui-components/select-customization.mdx
new file mode 100644
index 000000000..b61019f87
--- /dev/null
+++ b/website/ui-components/es-ui-components/select-customization.mdx
@@ -0,0 +1,256 @@
+# Customization
+
+import { ComponentShowcase } from '../components/component-showcase';
+import { Select, AsyncSelect, MultiSelect, AsyncMultiSelect } from '@eightshift/ui-components';
+import { icons } from '@eightshift/ui-components/icons';
+import {
+ demoOptions,
+ demoGetData,
+ CustomDropdownIndicator,
+ CustomMenuOption,
+ CustomClearIndicator,
+ CustomValueDisplay,
+ CustomMultiValueRemoveButton,
+ CustomMultiValueDisplay,
+} from './select-helpers';
+
+Select components allow many customizations to adapt to various use cases.
+
+For the complete list of customizations, use your IDE's autocomplete functionality. Look for props starting with `custom`.
+
+Most common customizations are listed below.
+
+## Custom dropdown arrow
+
+_Applies to all select components_
+
+
+ {(data, setData) => (
+
+ )}
+
+
+```jsx
+const CustomDropdownIndicator = (props) => {
+ return (
+
+
+ {props.selectProps.menuIsOpen ? icons.arrowUpCircleAlt : icons.arrowDownCircleAlt}
+
+
+ );
+};
+
+