From bc01c494afa9a8711cebfe7f87b3ef612eed19ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BCske?= <33117553+fabianhueske@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:40:20 +0200 Subject: [PATCH 1/5] chore: retire in-Storybook docs, wire Storybook <-> docs links Storybook becomes a component playground + autodocs only; meteor.shopware.com (apps/docs) is the single documentation site. They cross-link per component. Storybook (packages/component-library): - Remove all custom docs MDX (components, foundations, getting-started), the _components React helpers, preview-head.html styling, remark-gfm, @storybook/blocks, and STORYBOOK_DOCS_STANDARD.md - Rely on global autodocs; simplify main.ts/preview.ts; fix stale storySort - Add a dynamic toolbar 'Documentation' button linking each component to its meteor.shopware.com page (declare @storybook/components + @storybook/icons) - Use the shared shopware-meteor-logo.svg; drop old vercel logo URLs Docs (apps/docs): - Add a per-component 'Storybook' header button (custom i-custom:storybook icon) - Move inset to /utilities/components (layout utility, like theme-provider) - Replace hard-coded section redirects with section-redirect.global.ts, which resolves the first sidebar entry from the live navigation tree --- apps/docs/app/app.config.ts | 5 + apps/docs/app/assets/icons/storybook.svg | 3 + .../app/components/DocsPageHeaderLinks.vue | 28 + .../app/middleware/section-redirect.global.ts | 59 ++ apps/docs/content/2.components/card.md | 4 +- .../components}/inset.md | 0 apps/docs/nuxt.config.ts | 59 +- packages/component-library/.storybook/main.ts | 18 +- .../component-library/.storybook/manager.js | 51 +- .../.storybook/preview-head.html | 199 ------ .../component-library/.storybook/preview.ts | 43 +- .../.storybook/shopwareTheme.js | 4 +- packages/component-library/README.md | 2 +- .../STORYBOOK_DOCS_STANDARD.md | 123 ---- packages/component-library/package.json | 4 +- .../public/shopware-meteor-logo.svg | 34 + .../public/shopware_docs_horizontal_dark.svg | 18 - .../public/shopware_docs_horizontal_white.svg | 85 --- .../mt-action-menu/mt-action-menu.mdx | 133 ---- .../src/components/mt-avatar/mt-avatar.mdx | 74 --- .../src/components/mt-badge/mt-badge.mdx | 82 --- .../src/components/mt-banner/mt-banner.mdx | 73 --- .../src/components/mt-button/mt-button.mdx | 88 --- .../src/components/mt-card/mt-card.mdx | 115 ---- .../src/components/mt-chart/mt-chart.mdx | 56 -- .../components/mt-checkbox/mt-checkbox.mdx | 74 --- .../mt-collapsible/mt-collapsible.mdx | 57 -- .../mt-colorpicker/mt-colorpicker.mdx | 60 -- .../mt-data-table/mt-data-table.mdx | 571 ----------------- .../mt-datepicker/mt-datepicker.mdx | 69 -- .../mt-email-field/mt-email-field.mdx | 63 -- .../mt-empty-state/mt-empty-state.mdx | 60 -- .../mt-entity-data-table.mdx | 65 -- .../mt-entity-select/mt-entity-select.mdx | 67 -- .../mt-floating-ui/mt-floating-ui.mdx | 74 --- .../components/mt-help-text/mt-helptext.mdx | 63 -- .../src/components/mt-icon/mt-icon.mdx | 67 -- .../src/components/mt-link/mt-link.mdx | 62 -- .../src/components/mt-loader/mt-loader.mdx | 72 --- .../src/components/mt-modal/mt-modal.mdx | 162 ----- .../mt-number-field/mt-number-field.mdx | 63 -- .../mt-pagination/mt-pagination.mdx | 56 -- .../mt-password-field/mt-password-field.mdx | 63 -- .../src/components/mt-popover/mt-popover.mdx | 30 - .../mt-progress-bar/mt-progress-bar.mdx | 74 --- .../mt-promo-badge/mt-promo-badge.mdx | 74 --- .../mt-radio-group/mt-radio-group.mdx | 99 --- .../src/components/mt-search/mt-search.mdx | 63 -- .../src/components/mt-select/mt-select.mdx | 85 --- .../mt-skeleton-bar/mt-skeleton-bar.mdx | 70 -- .../src/components/mt-slider/mt-slider.mdx | 67 -- .../components/mt-snackbar/mt-snackbar.mdx | 128 ---- .../src/components/mt-switch/mt-switch.mdx | 66 -- .../src/components/mt-tabs/mt-tabs.mdx | 73 --- .../mt-text-editor/mt-text-editor.mdx | 598 ------------------ .../mt-text-field/mt-text-field.mdx | 87 --- .../src/components/mt-text/mt-text.mdx | 60 -- .../components/mt-textarea/mt-textarea.mdx | 62 -- .../src/components/mt-toast/mt-toast.mdx | 55 -- .../src/components/mt-tooltip/mt-tooltip.mdx | 77 --- .../mt-unit-field/mt-unit-field.mdx | 62 -- .../components/mt-url-field/mt-url-field.mdx | 62 -- .../src/docs/_components/color-palette.js | 187 ------ .../src/docs/_components/do-dont.js | 67 -- .../docs/_components/elevation-surfaces.js | 158 ----- .../docs/_components/interaction-states.js | 144 ----- .../docs/_components/storybook-page-header.js | 216 ------- .../src/docs/_components/token-browser.js | 523 --------------- .../src/docs/_components/typography-scale.js | 182 ------ .../src/docs/foundations/accessibility.mdx | 88 --- .../src/docs/foundations/components.mdx | 174 ----- .../foundations/content/ai-interaction.mdx | 53 -- .../src/docs/foundations/content/glossary.mdx | 101 --- .../docs/foundations/content/messaging.mdx | 114 ---- .../src/docs/foundations/content/wording.mdx | 126 ---- .../docs/foundations/design-principles.mdx | 52 -- .../src/docs/foundations/icons.mdx | 111 ---- .../src/docs/foundations/interactions.mdx | 70 -- .../docs/foundations/tokens/border-radius.mdx | 51 -- .../docs/foundations/tokens/color-palette.mdx | 21 - .../src/docs/foundations/tokens/elevation.mdx | 39 -- .../src/docs/foundations/tokens/spacing.mdx | 80 --- .../foundations/tokens/tokens-overview.mdx | 87 --- .../docs/foundations/tokens/typography.mdx | 72 --- .../src/docs/getting-started/contributing.mdx | 9 - .../src/docs/getting-started/designers.mdx | 69 -- .../src/docs/getting-started/developers.mdx | 129 ---- .../src/docs/getting-started/introduction.mdx | 45 -- .../src/docs/getting-started/migration.mdx | 70 -- pnpm-lock.yaml | 25 +- 90 files changed, 202 insertions(+), 7881 deletions(-) create mode 100644 apps/docs/app/assets/icons/storybook.svg create mode 100644 apps/docs/app/middleware/section-redirect.global.ts rename apps/docs/content/{2.components => 3.utilities/components}/inset.md (100%) delete mode 100644 packages/component-library/.storybook/preview-head.html delete mode 100644 packages/component-library/STORYBOOK_DOCS_STANDARD.md create mode 100644 packages/component-library/public/shopware-meteor-logo.svg delete mode 100644 packages/component-library/public/shopware_docs_horizontal_dark.svg delete mode 100644 packages/component-library/public/shopware_docs_horizontal_white.svg delete mode 100644 packages/component-library/src/components/mt-action-menu/mt-action-menu.mdx delete mode 100644 packages/component-library/src/components/mt-avatar/mt-avatar.mdx delete mode 100644 packages/component-library/src/components/mt-badge/mt-badge.mdx delete mode 100644 packages/component-library/src/components/mt-banner/mt-banner.mdx delete mode 100644 packages/component-library/src/components/mt-button/mt-button.mdx delete mode 100644 packages/component-library/src/components/mt-card/mt-card.mdx delete mode 100644 packages/component-library/src/components/mt-chart/mt-chart.mdx delete mode 100644 packages/component-library/src/components/mt-checkbox/mt-checkbox.mdx delete mode 100644 packages/component-library/src/components/mt-collapsible/mt-collapsible.mdx delete mode 100644 packages/component-library/src/components/mt-colorpicker/mt-colorpicker.mdx delete mode 100644 packages/component-library/src/components/mt-data-table/mt-data-table.mdx delete mode 100644 packages/component-library/src/components/mt-datepicker/mt-datepicker.mdx delete mode 100644 packages/component-library/src/components/mt-email-field/mt-email-field.mdx delete mode 100644 packages/component-library/src/components/mt-empty-state/mt-empty-state.mdx delete mode 100644 packages/component-library/src/components/mt-entity-data-table/mt-entity-data-table.mdx delete mode 100644 packages/component-library/src/components/mt-entity-select/mt-entity-select.mdx delete mode 100644 packages/component-library/src/components/mt-floating-ui/mt-floating-ui.mdx delete mode 100644 packages/component-library/src/components/mt-help-text/mt-helptext.mdx delete mode 100644 packages/component-library/src/components/mt-icon/mt-icon.mdx delete mode 100644 packages/component-library/src/components/mt-link/mt-link.mdx delete mode 100644 packages/component-library/src/components/mt-loader/mt-loader.mdx delete mode 100644 packages/component-library/src/components/mt-modal/mt-modal.mdx delete mode 100644 packages/component-library/src/components/mt-number-field/mt-number-field.mdx delete mode 100644 packages/component-library/src/components/mt-pagination/mt-pagination.mdx delete mode 100644 packages/component-library/src/components/mt-password-field/mt-password-field.mdx delete mode 100644 packages/component-library/src/components/mt-popover/mt-popover.mdx delete mode 100644 packages/component-library/src/components/mt-progress-bar/mt-progress-bar.mdx delete mode 100644 packages/component-library/src/components/mt-promo-badge/mt-promo-badge.mdx delete mode 100644 packages/component-library/src/components/mt-radio-group/mt-radio-group.mdx delete mode 100644 packages/component-library/src/components/mt-search/mt-search.mdx delete mode 100644 packages/component-library/src/components/mt-select/mt-select.mdx delete mode 100644 packages/component-library/src/components/mt-skeleton-bar/mt-skeleton-bar.mdx delete mode 100644 packages/component-library/src/components/mt-slider/mt-slider.mdx delete mode 100644 packages/component-library/src/components/mt-snackbar/mt-snackbar.mdx delete mode 100644 packages/component-library/src/components/mt-switch/mt-switch.mdx delete mode 100644 packages/component-library/src/components/mt-tabs/mt-tabs.mdx delete mode 100644 packages/component-library/src/components/mt-text-editor/mt-text-editor.mdx delete mode 100644 packages/component-library/src/components/mt-text-field/mt-text-field.mdx delete mode 100644 packages/component-library/src/components/mt-text/mt-text.mdx delete mode 100644 packages/component-library/src/components/mt-textarea/mt-textarea.mdx delete mode 100644 packages/component-library/src/components/mt-toast/mt-toast.mdx delete mode 100644 packages/component-library/src/components/mt-tooltip/mt-tooltip.mdx delete mode 100644 packages/component-library/src/components/mt-unit-field/mt-unit-field.mdx delete mode 100644 packages/component-library/src/components/mt-url-field/mt-url-field.mdx delete mode 100644 packages/component-library/src/docs/_components/color-palette.js delete mode 100644 packages/component-library/src/docs/_components/do-dont.js delete mode 100644 packages/component-library/src/docs/_components/elevation-surfaces.js delete mode 100644 packages/component-library/src/docs/_components/interaction-states.js delete mode 100644 packages/component-library/src/docs/_components/storybook-page-header.js delete mode 100644 packages/component-library/src/docs/_components/token-browser.js delete mode 100644 packages/component-library/src/docs/_components/typography-scale.js delete mode 100644 packages/component-library/src/docs/foundations/accessibility.mdx delete mode 100644 packages/component-library/src/docs/foundations/components.mdx delete mode 100644 packages/component-library/src/docs/foundations/content/ai-interaction.mdx delete mode 100644 packages/component-library/src/docs/foundations/content/glossary.mdx delete mode 100644 packages/component-library/src/docs/foundations/content/messaging.mdx delete mode 100644 packages/component-library/src/docs/foundations/content/wording.mdx delete mode 100644 packages/component-library/src/docs/foundations/design-principles.mdx delete mode 100644 packages/component-library/src/docs/foundations/icons.mdx delete mode 100644 packages/component-library/src/docs/foundations/interactions.mdx delete mode 100644 packages/component-library/src/docs/foundations/tokens/border-radius.mdx delete mode 100644 packages/component-library/src/docs/foundations/tokens/color-palette.mdx delete mode 100644 packages/component-library/src/docs/foundations/tokens/elevation.mdx delete mode 100644 packages/component-library/src/docs/foundations/tokens/spacing.mdx delete mode 100644 packages/component-library/src/docs/foundations/tokens/tokens-overview.mdx delete mode 100644 packages/component-library/src/docs/foundations/tokens/typography.mdx delete mode 100644 packages/component-library/src/docs/getting-started/contributing.mdx delete mode 100644 packages/component-library/src/docs/getting-started/designers.mdx delete mode 100644 packages/component-library/src/docs/getting-started/developers.mdx delete mode 100644 packages/component-library/src/docs/getting-started/introduction.mdx delete mode 100644 packages/component-library/src/docs/getting-started/migration.mdx diff --git a/apps/docs/app/app.config.ts b/apps/docs/app/app.config.ts index 70ef46a3e..b574001d5 100644 --- a/apps/docs/app/app.config.ts +++ b/apps/docs/app/app.config.ts @@ -50,6 +50,11 @@ export default defineAppConfig({ branch: "main", rootDir: "apps/docs", }, + storybook: { + // Base URL of the deployed component-library Storybook. Component pages link + // to their autodocs page here (see DocsPageHeaderLinks.vue). + url: "https://storybook.meteor.shopware.com", + }, ui: { colors: { primary: "brand", diff --git a/apps/docs/app/assets/icons/storybook.svg b/apps/docs/app/assets/icons/storybook.svg new file mode 100644 index 000000000..d2b25ff38 --- /dev/null +++ b/apps/docs/app/assets/icons/storybook.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/app/components/DocsPageHeaderLinks.vue b/apps/docs/app/components/DocsPageHeaderLinks.vue index 84b9c6fdd..f9cd7a6e5 100644 --- a/apps/docs/app/components/DocsPageHeaderLinks.vue +++ b/apps/docs/app/components/DocsPageHeaderLinks.vue @@ -39,6 +39,22 @@ const componentSourceUrl = computed(() => { return `${github.url}/tree/${github.branch || "main"}/${path}`; }); +// Link a component page to its Storybook entry. The docs slug matches the +// Storybook id prefix (e.g. "data-table" -> components-data-table); Storybook +// resolves that to the component's autodocs (or first story) automatically. +// Gated on the same source map as the GitHub link so the two buttons pair up. +const componentStorybookUrl = computed(() => { + if (!route.path.startsWith("/components/")) return undefined; + const slug = route.path.split("/").filter(Boolean).pop() ?? ""; + const sources = (runtimeConfig.public.componentSourcePaths ?? {}) as Record< + string, + string + >; + const storybook = appConfig.storybook as { url?: string } | undefined; + if (!sources[slug] || !storybook?.url) return undefined; + return `${storybook.url}/?path=/docs/components-${slug}`; +}); + const items = computed(() => [ [ { @@ -90,6 +106,18 @@ async function copyPage() { :ui="{ leadingIcon: 'text-neutral size-3.5' }" /> + + { + // Only the docs sections use this pattern; skip the landing page and the rest. + if (!/^\/(documentation|components|utilities)(\/|$)/.test(to.path)) return; + + const path = to.path.replace(/\/+$/, "") || "/"; + + // Own cache key (not Docus' "navigation_docs") so we don't clash with its + // useAsyncData options; the query result is cached per request either way. + // Mirror Docus' transform: strip a `/docs` wrapper level if one exists so the + // tree (and its order) matches the sidebar exactly. + const { data: navigation } = await useAsyncData( + "section-redirect:navigation", + () => queryCollectionNavigation("docs"), + { + transform: (data: ContentNavigationItem[]) => + data.find((item) => item.path === "/docs")?.children ?? data, + }, + ); + + const nav = navigation.value; + if (!nav) return; + + const node = findNode(nav, path); + // Leaf pages have no children; only section nodes are redirected. + if (!node?.children?.length) return; + + const target = firstLeafPath(node); + if (target && target !== path) { + // 302 (temporary): the target is derived from navigation order and can + // change when pages are added or reordered, so it must not be hard-cached. + return navigateTo(target, { redirectCode: 302, replace: true }); + } +}); + +function findNode( + items: ContentNavigationItem[], + path: string, +): ContentNavigationItem | undefined { + for (const item of items) { + if (item.path === path) return item; + const found = item.children && findNode(item.children, path); + if (found) return found; + } + return undefined; +} + +function firstLeafPath(item: ContentNavigationItem): string { + let current = item; + while (current.children?.length) current = current.children[0]!; + return current.path; +} diff --git a/apps/docs/content/2.components/card.md b/apps/docs/content/2.components/card.md index 6df9f13d7..453dcf636 100644 --- a/apps/docs/content/2.components/card.md +++ b/apps/docs/content/2.components/card.md @@ -58,7 +58,7 @@ Show the link or unlink toggle when a card represents values that can be inherit - The tabs area sits below the header and can be used for closely related views of the same section. - The content area holds the main information, form fields, or other section content. -**Card** also exposes [**Inset**](/components/inset) as a companion layout utility for cases where content inside the card should visually break out to the card edges without hard-coding spacing values. +**Card** also exposes [**Inset**](/utilities/components/inset) as a companion layout utility for cases where content inside the card should visually break out to the card edges without hard-coding spacing values. - Use **Inset** inside the default card content when an inner block should align to the card's outer padding instead of the current content flow. - Use **Inset** in the `footer` slot when the footer needs its own full-width background or custom padding treatment while still staying aligned to the card spacing tokens. @@ -109,4 +109,4 @@ Show the link or unlink toggle when a card represents values that can be inherit ## Related components -- [**Inset**](/components/inset): when you only need spacing or padded grouping inside an existing surface. +- [**Inset**](/utilities/components/inset): when you only need spacing or padded grouping inside an existing surface. diff --git a/apps/docs/content/2.components/inset.md b/apps/docs/content/3.utilities/components/inset.md similarity index 100% rename from apps/docs/content/2.components/inset.md rename to apps/docs/content/3.utilities/components/inset.md diff --git a/apps/docs/nuxt.config.ts b/apps/docs/nuxt.config.ts index 5fbe07f76..ac9a1fc3c 100644 --- a/apps/docs/nuxt.config.ts +++ b/apps/docs/nuxt.config.ts @@ -80,61 +80,10 @@ export default defineNuxtConfig({ // per environment with NUXT_SITE_URL. url: "https://meteor.shopware.com", }, - // Section roots have no index page, so permanently redirect each to its first - // child instead of 404ing. - routeRules: { - "/documentation": { - redirect: { - to: "/documentation/getting-started/installation", - statusCode: 301, - }, - }, - "/documentation/getting-started": { - redirect: { - to: "/documentation/getting-started/installation", - statusCode: 301, - }, - }, - "/documentation/guidelines": { - redirect: { - to: "/documentation/guidelines/design-principles", - statusCode: 301, - }, - }, - "/documentation/design": { - redirect: { to: "/documentation/design/tokens", statusCode: 301 }, - }, - "/documentation/content": { - redirect: { to: "/documentation/content/wording", statusCode: 301 }, - }, - "/components": { - redirect: { to: "/components/action-menu", statusCode: 301 }, - }, - "/utilities": { - redirect: { - to: "/utilities/components/theme-provider", - statusCode: 301, - }, - }, - "/utilities/composables": { - redirect: { - to: "/utilities/composables/use-snackbar", - statusCode: 301, - }, - }, - "/utilities/components": { - redirect: { - to: "/utilities/components/theme-provider", - statusCode: 301, - }, - }, - "/utilities/directives": { - redirect: { to: "/utilities/directives/tooltip", statusCode: 301 }, - }, - "/utilities/plugins": { - redirect: { to: "/utilities/plugins/device-helper", statusCode: 301 }, - }, - }, + // Section roots (/components, /utilities, /documentation/*) have no index page. + // Instead of hard-coding a redirect target per section (which goes stale when + // the sidebar is reordered), app/middleware/section-redirect.global.ts resolves + // the first entry from the live navigation tree and redirects there. // modules/ is auto-scanned, so meteor-components and component-examples load // automatically. meteor-components registers its work via the // `component-meta:extend` hook (fired during the build), so module order diff --git a/packages/component-library/.storybook/main.ts b/packages/component-library/.storybook/main.ts index 1d3843340..858499da6 100644 --- a/packages/component-library/.storybook/main.ts +++ b/packages/component-library/.storybook/main.ts @@ -1,32 +1,16 @@ -import remarkGfmModule from "remark-gfm"; import type { StorybookConfig } from "@storybook/vue3-vite"; import { mergeConfig } from "vite"; import path from "path"; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const remarkGfm = (remarkGfmModule as any).default ?? remarkGfmModule; - const config: StorybookConfig = { - stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: [ "@storybook/addon-links", - { - // eslint-disable-next-line storybook/no-uninstalled-addons - name: "@storybook/addon-docs", - options: { - mdxPluginOptions: { - mdxCompileOptions: { - remarkPlugins: [remarkGfm], - }, - }, - }, - }, { name: "@storybook/addon-essentials", options: { backgrounds: false, outline: false, - docs: false, }, }, "@storybook/addon-interactions", diff --git a/packages/component-library/.storybook/manager.js b/packages/component-library/.storybook/manager.js index 4a7e4a42a..8557f31a3 100644 --- a/packages/component-library/.storybook/manager.js +++ b/packages/component-library/.storybook/manager.js @@ -1,9 +1,58 @@ -import { addons } from "@storybook/manager-api"; +import React from "react"; +import { addons, types, useStorybookApi, useStorybookState } from "@storybook/manager-api"; +import { IconButton } from "@storybook/components"; +import { ShareAltIcon } from "@storybook/icons"; import { shopwareTheme } from "./shopwareTheme"; +const DOCS_BASE_URL = "https://meteor.shopware.com"; + +// Map the active Storybook entry to its page on the docs site. Component titles follow +// "Components/" and the docs URL uses the kebab-cased name, e.g. +// "Components/Data Table" -> https://meteor.shopware.com/components/data-table. +// Anything outside the Components section (directives, composables) links to the docs home. +function getDocsUrl(title) { + const segments = (title ?? "").split("/"); + if (segments[0] !== "Components" || !segments[1]) { + return DOCS_BASE_URL; + } + const slug = segments[1].trim().toLowerCase().replace(/\s+/g, "-"); + return `${DOCS_BASE_URL}/components/${slug}`; +} + +function DocsLink() { + const api = useStorybookApi(); + // Subscribe to state so the link updates as the user navigates between stories. + const { storyId } = useStorybookState(); + const data = api.getCurrentStoryData(); + const href = getDocsUrl(data && data.title); + + return React.createElement( + IconButton, + { + key: storyId, + as: "a", + href, + target: "_blank", + rel: "noopener noreferrer", + title: "Open in the Meteor documentation", + }, + React.createElement("span", { style: { marginRight: 6 } }, "Documentation"), + React.createElement(ShareAltIcon, null), + ); +} + addons.setConfig({ theme: shopwareTheme, sidebar: { collapsedRoots: ["composables", "directives"], }, }); + +addons.register("meteor/docs-link", () => { + addons.add("meteor/docs-link", { + type: types.TOOL, + title: "Meteor documentation", + match: () => true, + render: () => React.createElement(DocsLink), + }); +}); diff --git a/packages/component-library/.storybook/preview-head.html b/packages/component-library/.storybook/preview-head.html deleted file mode 100644 index 423635643..000000000 --- a/packages/component-library/.storybook/preview-head.html +++ /dev/null @@ -1,199 +0,0 @@ - diff --git a/packages/component-library/.storybook/preview.ts b/packages/component-library/.storybook/preview.ts index 967fad46a..81137dfa6 100644 --- a/packages/component-library/.storybook/preview.ts +++ b/packages/component-library/.storybook/preview.ts @@ -2,12 +2,9 @@ import type { Preview } from "@storybook/vue3"; import "~/src/assets/scss/all.scss"; import "~/src/assets/css/fonts/inter.font.css"; import { setup } from "@storybook/vue3"; -import { createApp } from "vue"; import { createI18n } from "vue-i18n"; import DeviceHelperPlugin from "../src/plugin/device-helper.plugin"; import MtThemeProvider from "../src/components/mt-theme-provider/mt-theme-provider.vue"; -import MtSnackbar from "../src/components/mt-snackbar/mt-snackbar.vue"; -import { useSnackbar } from "../src/components/mt-snackbar/composables/use-snackbar"; import { ThemeProvider } from "./ThemeProvider"; @@ -34,24 +31,6 @@ setup((app) => { app.use(DeviceHelperPlugin); }); -// Mount MtSnackbar lazily on the first meteor-docs:snackbar event so it is available on -// docs-only MDX pages. Deferring the mount means story tests (which never fire this event) -// never get a second MtSnackbar instance alongside the one rendered by the story itself, -// avoiding the double-render that would otherwise cause snackbar counts to be doubled. -document.addEventListener("meteor-docs:snackbar", (e: Event) => { - if (!document.getElementById("mt-snackbar-root")) { - const snackbarEl = document.createElement("div"); - snackbarEl.id = "mt-snackbar-root"; - document.body.appendChild(snackbarEl); - createApp({ components: { MtSnackbar }, template: "" }).mount(snackbarEl); - } - const { addSnackbar } = useSnackbar(); - addSnackbar({ - message: (e as CustomEvent<{ message: string }>).detail.message, - variant: "success", - }); -}); - const preview: Preview = { globalTypes: { theme: { @@ -70,27 +49,7 @@ const preview: Preview = { parameters: { options: { storySort: { - order: [ - "Introduction", - "Get Started", - ["Designers", "Developers", "Migration", "Contributing"], - "Foundations", - [ - "Design Principles", - "Accessibility", - "Interactions", - "Tokens", - ["Overview", "Color Palette", "Elevation", "Spacing", "Border Radius", "Typography"], - "Content", - ["Wording", "Messaging", "Glossary", "AI Interaction"], - "Icons", - "Components", - ], - "Components", - ["Overview"], - "Directives", - "Composables", - ], + order: ["Components", "Directives"], method: "alphabetical", }, }, diff --git a/packages/component-library/.storybook/shopwareTheme.js b/packages/component-library/.storybook/shopwareTheme.js index ec0e74b15..1496a4c91 100644 --- a/packages/component-library/.storybook/shopwareTheme.js +++ b/packages/component-library/.storybook/shopwareTheme.js @@ -4,8 +4,8 @@ export const shopwareTheme = create({ base: "light", brandTitle: "Shopware", - brandUrl: "https://github.com/shopware/meteor", - brandImage: "https://meteor-component-library.vercel.app/shopware_docs_horizontal_dark.svg", + brandUrl: "https://meteor.shopware.com", + brandImage: "/shopware-meteor-logo.svg", brandTarget: "_blank", colorPrimary: "#0870ff", diff --git a/packages/component-library/README.md b/packages/component-library/README.md index 59cfa16a6..ecacf7090 100644 --- a/packages/component-library/README.md +++ b/packages/component-library/README.md @@ -5,7 +5,7 @@ The meteor component library is a Vue component library developed by Shopware. I - Perfect suitable for Shopware Apps - Matches the Shopware administration look and feel - Small bundle sizes with tree-shaking -- Completely tested and [documented](https://meteor-component-library.vercel.app/) with Storybook +- Tested and previewed with Storybook, [documented](https://meteor.shopware.com) on meteor.shopware.com ## Requirements diff --git a/packages/component-library/STORYBOOK_DOCS_STANDARD.md b/packages/component-library/STORYBOOK_DOCS_STANDARD.md deleted file mode 100644 index ff7e03541..000000000 --- a/packages/component-library/STORYBOOK_DOCS_STANDARD.md +++ /dev/null @@ -1,123 +0,0 @@ -# Storybook docs standard - -Every public component should have a docs page that helps developers choose the right component, implement it without reading source code, and understand important behavior and accessibility constraints. - -## Page template - -Use this section order for component docs pages: - -```mdx - - Short description - - -## When to use - -## Examples - -### Basic - -Main example - -### Additional example - -## Anatomy - -## API reference - -## Do - -## Don't - -## Behavior notes - -## Accessibility notes - -## Comparisons -``` - -Notes: - -- Start every docs page with the shared `StorybookPageHeader` block. -- The shared header owns the H1. It must render the title in the format `Component name (mt-component-name)`. -- Pass `sourcePath` as the repo-relative path to the component folder so the docs header can link to the GitHub source location. -- Put the short component description inside `StorybookPageHeader` as children so rich MDX content such as links, bold text, multiple paragraphs, and inline code still works. -- Use `Available` as the default status. Use `Experimental` or `Deprecated` when a stronger lifecycle signal is needed. -- `Examples` is required. Start with the main example directly under the `Examples` heading. -- Additional examples should follow as `H3` subsections under `Examples`, using labels that describe what the example shows. -- `Anatomy`, `Behavior notes`, and `Comparisons` are optional. Only include them when they add real value. -- Keep the order stable. If a section is not needed, omit it instead of moving sections around. When used together, place `Behavior notes` below `API reference` and above `Accessibility notes`. - -## How to choose optional sections - -- Use `Anatomy` when structure, composition, or internal parts need explanation. -- Use `Behavior notes` when behavior is non-obvious, stateful, or easy to misuse. -- Use `Comparisons` when users are likely to choose between this component and another public component. - -Simpler components should stay close to the core template. More complex components can add the optional sections when needed, but the page should still stay concise. - -## Storybook authoring rules - -- Keep docs in Storybook by using `.mdx` pages colocated with the component stories. -- Use the component stories as the source of truth for live examples whenever possible. -- Use the shared `StorybookPageHeader` block for the page title, component lifecycle state, package import snippet, and short description instead of hand-writing those parts in each page. -- Every component should have one `Default` story that shows the component in its most common state. -- Additional stories should use human-readable user-facing names that describe what the story shows, with the first word capitalized and the remaining words lowercase, for example `Variants`, `Sizes`, `Inline edit`, or `Multiple selection`. -- This naming rule applies to the story name shown in Storybook. The export identifier in code may still use the project's normal code naming conventions. -- Include at least one `Canvas` example that matches the recommended usage. -- Prefer static, copy-friendly story code for docs examples and overview stories. Avoid generating example markup through loops when users are likely to copy the code from Storybook. -- The code shown in Storybook docs should be clean, focused, and easy to copy. Only show the relevant component markup and the minimum surrounding code a developer needs. -- Do not repeat the same example as an inline MDX code block when the rendered story already exposes the same copyable code in Storybook docs. -- In prose, use capitalized display names such as **Button**, **Badge**, or **Promo Badge**, wrapped in bold formatting rather than inline code. Avoid `mt-*` tag names in body copy unless the tag name itself is the thing being explained. -- Mention other public components as capitalized display names in bold formatting rather than inline code. Do not link to other component docs pages by default. - -## API reference guidance - -- Use Storybook API tables for props, slots, and events where possible. -- Place the Storybook API table directly under `API reference` unless another subsection is genuinely needed. -- Only add a separate exposed methods section when the component actually exposes public methods. - -## Companion exports - -Some components expose related parts that do not need their own standalone page. Document those exports on the parent page when they are primarily used together. - -Use the parent page to explain: - -- what each companion export does -- when it should be used -- how the pieces fit together -- any ordering or nesting requirements - -Examples: - -- `mt-action-menu` documents `mt-action-menu-item` and `mt-action-menu-group` -- `mt-modal` documents companion exports such as `mt-modal-root`, `mt-modal-trigger`, `mt-modal-action`, and `mt-modal-close` - -If a companion export becomes complex enough that it has its own decision-making surface, it can graduate to its own page later. - -## Comparison guidance - -Comparisons should explain when to choose one component over another, not just list differences. - -Important comparisons for this rollout include: - -- tooltip vs help text -- floating ui vs action menu -- select vs radio group -- checkbox vs radio group vs select - -## Accessibility guidance - -Accessibility notes should explicitly call out: - -- keyboard behavior -- screen reader behavior when relevant -- whether the component is appropriate for critical information -- any content limitations that affect usability or accessibility - -Do not assume that accessibility behavior is obvious from the example alone. diff --git a/packages/component-library/package.json b/packages/component-library/package.json index e947a38f6..b13a4e77d 100644 --- a/packages/component-library/package.json +++ b/packages/component-library/package.json @@ -118,7 +118,8 @@ "@storybook/addon-essentials": "^8.6.15", "@storybook/addon-interactions": "^8.6.15", "@storybook/addon-links": "^8.6.15", - "@storybook/blocks": "^8.6.15", + "@storybook/components": "^8.6.15", + "@storybook/icons": "^1.2.12", "@storybook/manager-api": "^8.6.15", "@storybook/preview-api": "^8.6.15", "@storybook/test": "^8.6.15", @@ -152,7 +153,6 @@ "npm-run-all2": "^6.1.1", "postcss-html": "^1.7.0", "prettier": "3.2.5", - "remark-gfm": "^4.0.1", "sass": "^1.69.5", "size-limit": "^11.2.0", "storybook": "^8.6.15", diff --git a/packages/component-library/public/shopware-meteor-logo.svg b/packages/component-library/public/shopware-meteor-logo.svg new file mode 100644 index 000000000..e980768f8 --- /dev/null +++ b/packages/component-library/public/shopware-meteor-logo.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/component-library/public/shopware_docs_horizontal_dark.svg b/packages/component-library/public/shopware_docs_horizontal_dark.svg deleted file mode 100644 index ad2da2f1b..000000000 --- a/packages/component-library/public/shopware_docs_horizontal_dark.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/packages/component-library/public/shopware_docs_horizontal_white.svg b/packages/component-library/public/shopware_docs_horizontal_white.svg deleted file mode 100644 index 98d5136ee..000000000 --- a/packages/component-library/public/shopware_docs_horizontal_white.svg +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/component-library/src/components/mt-action-menu/mt-action-menu.mdx b/packages/component-library/src/components/mt-action-menu/mt-action-menu.mdx deleted file mode 100644 index 94cbb65ab..000000000 --- a/packages/component-library/src/components/mt-action-menu/mt-action-menu.mdx +++ /dev/null @@ -1,133 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as ActionMenuStories from "./mt-action-menu.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Action Menu** reveals a short list of contextual actions from a trigger. Use - it to keep secondary actions close to the relevant object without adding too - many always-visible buttons to the interface. - - -## When to use - -- Use **Action Menu** for secondary or contextual actions tied to a specific record, card, row, or view. -- Use it when the user should choose from a short list of related actions such as `Duplicate`, `Move`, `Export`, or `Delete`. -- Use it when the actions should stay available but do not deserve permanent visual priority. - -## Examples - -### Basic - - - -### Without icons - - - -### Grouped items - - - -### Keyboard shortcuts - - - -### States - - - -### Nested submenu - - - -### External links - - - -### Match trigger width - - - -## Anatomy - -**Action Menu** is built from a small set of companion exports that work together: - -- `mt-dropdown-menu-root` manages the open and closed state for the menu. -- `mt-dropdown-menu-trigger` defines the interactive element that opens the menu, usually a **Button**. -- `mt-dropdown-menu-portal` renders the menu content in an overlay layer outside the surrounding layout flow. -- `mt-action-menu` renders the menu surface itself. -- `mt-action-menu-item` renders individual actions, links, shortcuts, and submenu triggers. -- `mt-action-menu-group` separates related actions and keeps mixed icon alignment consistent inside a group. -- `mt-dropdown-menu-sub` creates nested submenu flows when a second level is truly needed. - -These parts are exported together so the parent page can document the full pattern in one place. - -## API reference - - - -## Do - -- Keep labels short, specific, and action-oriented. -- Prioritize frequent actions and place destructive actions later in the menu. -- Keep action ordering consistent across similar contexts. -- Keep the menu focused on related actions for the same object or context. -- Use `mt-action-menu-group` to separate action sets when grouping improves scanning. -- Separate destructive actions into their own group when they appear alongside non-destructive actions. -- Use the `critical` variant for destructive actions such as delete or remove. -- Keep icon usage consistent within a visual group whenever possible. -- Use `shortcut` only when the same action is also available from the keyboard elsewhere in the product. - -## Don't - -- Do not use **Action Menu** for the main action on a screen or card. -- Do not hide critical task-completion steps only inside an **Action Menu**. -- Do not group unrelated actions or create groups without a clear purpose. -- Do not create deep or complex hierarchies when a flatter structure would be easier to scan. -- Do not overuse groups or create many single-item groups when a flatter list would scan better. -- Do not use icons, groups, or separators unless they add clarity. -- Do not rely on color, icons, or shortcut labels alone to explain what an action does. - -## Behavior notes - -- **Action Menu** is a compound pattern, not a standalone trigger. You always compose `mt-action-menu` with `mt-dropdown-menu-root`, `mt-dropdown-menu-trigger`, and `mt-dropdown-menu-portal`. -- `shortcut` accepts a structured object with `modifiers` and `key`. -- Supported modifier values are `mod`, `ctrl`, `alt`, `shift`, and `meta`. -- Use `mod` for cross-platform shortcuts because it maps to `Command` on Mac and `Control` on Windows and Linux. Use `meta` only when you need the platform-specific meta key explicitly. -- Supported special keys are `enter`, `esc`, `tab`, `space`, `backspace`, `delete`, `up`, `down`, `left`, and `right`. -- Shortcut labels are formatted automatically for Mac and PC, and `aria-keyshortcuts` is added for assistive technology. -- `mt-action-menu-item` with a `link` prop renders as an external anchor and opens in a new tab. -- `is-sub-menu` on `mt-action-menu` and `is-sub-trigger` on `mt-action-menu-item` are used together for nested submenu patterns. -- `match-trigger-width` is useful when the menu should align visually with a wider trigger such as a row action or account switcher. -- Keep nesting shallow. One submenu level is usually enough, and more than two levels should be avoided. - -## Accessibility notes - -- The trigger should have a clear accessible name so users understand what actions the menu contains. -- Menu item labels should stay understandable without depending only on icons, color, or shortcut labels. -- If you provide `shortcut`, it supplements the action label rather than replacing it. -- Destructive actions should remain clearly labeled in text, not only visually differentiated through the `critical` variant. -- Use submenu patterns carefully, because deep menu hierarchies are harder to navigate with keyboard and assistive technology. - -## Comparisons - -### Action Menu vs Floating UI - -- Use **Action Menu** for a short, scannable list of actions or links. -- Use **Floating UI** when you need to build a custom popover surface with richer content such as text, filters, form fields, or mixed layout content instead of a menu of actions. diff --git a/packages/component-library/src/components/mt-avatar/mt-avatar.mdx b/packages/component-library/src/components/mt-avatar/mt-avatar.mdx deleted file mode 100644 index 5944bca73..000000000 --- a/packages/component-library/src/components/mt-avatar/mt-avatar.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as AvatarStories from "./mt-avatar.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Avatar** represents a person or entity in a compact visual form, it can show - a profile image or initials derived from a name. **Avatar** helps users - recognize people quickly in lists, comments, messages, and navigation areas. - - -## When to use - -- Use **Avatar** to identify users or entities in lists, headers, menus, and chat threads. -- Use an image when recognition matters most and a profile image is available. -- Use initials when no image is available or when a lightweight fallback is enough. - -## Examples - -### Basic - - - -### Sizes - - - -### With image - - - -### Square - - - -### All background colors - - - -## API reference - - - -## Do - -- Provide an `imageUrl` when possible, photos improve recognition. -- Rely on initials when no image is available, they are concise and readable. -- Pick a size that fits the layout, for example `s` for dense lists or `l` for profile headers. -- Use the `square` variant when the surrounding UI uses sharper corners. - -## Don't - -- Don’t treat **Avatar** as a clickable element, wrap it in a button or link if interaction is required. -- Don’t place long text inside **Avatar**, it should only contain initials or an image. -- Don’t oversize **Avatar** in dense layouts, this harms scanability. - -## Behavior notes - -- If no `imageUrl` is provided, **Avatar** falls back to initials derived from `firstName` and `lastName`. -- The background color is derived from the provided name, which gives repeated names a stable visual treatment. -- **Avatar** is presentational. If the avatar should trigger an action, place it inside an interactive wrapper instead of making the avatar itself responsible for interaction. - -## Accessibility notes - -- If the avatar is only decorative, keep it out of the interaction flow. -- If the avatar represents an important person or entity, make sure the surrounding UI still exposes that identity in text. -- When wrapping the avatar in an interactive control, ensure the control has a clear accessible name. diff --git a/packages/component-library/src/components/mt-badge/mt-badge.mdx b/packages/component-library/src/components/mt-badge/mt-badge.mdx deleted file mode 100644 index 7ced6c84f..000000000 --- a/packages/component-library/src/components/mt-badge/mt-badge.mdx +++ /dev/null @@ -1,82 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as BadgeStories from "./mt-badge.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Badge** is a compact, non-interactive status label. Use it to show short, - glanceable states such as `Active`, `Draft`, or `Archived` without adding a - full message component to the layout. - - -## When to use - -- Use **Badge** for short labels that help users scan status, category, or state quickly. -- Use it inline in tables, cards, lists, or detail headers where space is limited. -- Use the variant, size, icon, or status indicator to reinforce meaning when that extra visual cue helps. - -## Examples - -### Basic - - - -### Variants - - - -### Sizes - - - -### Status indicator - - - -### Icon - - - -## API reference - - - -## Do - -- Keep badge labels short and easy to scan. -- Choose the `variant` that matches the semantic meaning of the label. -- Use the smallest size that still fits the surrounding layout comfortably. -- Add an icon or status indicator only when it meaningfully improves recognition. - -## Don't - -- Do not use **Badge** for long messages or explanatory text. -- Do not make **Badge** behave like buttons or links. -- Do not overload a surface with too many badges, because they quickly lose emphasis. -- Do not rely on color alone to communicate meaning. - -## Behavior notes - -- **Badge** is presentational and non-interactive. If users need to click, open, or trigger something, use a more appropriate interactive component around it. -- The component stays intentionally compact, so it works best with a short label and a single supporting visual treatment. -- The status indicator and icon are optional enhancements, not a replacement for a clear text label. - -## Accessibility notes - -- Keep the label understandable on its own so meaning does not depend only on color, the status dot, or the icon. -- Because badges are non-interactive, avoid using them as the only way to expose important information or actions. -- Use concise text so the badge remains readable in dense interfaces and small sizes. - -## Comparisons - -### Badge vs Promo Badge - -- Use **Badge** for general product states such as status, health, or workflow labels. -- Use **Promo Badge** for predefined promotional labels such as `New`, `Beta`, or `Shopware AI`. diff --git a/packages/component-library/src/components/mt-banner/mt-banner.mdx b/packages/component-library/src/components/mt-banner/mt-banner.mdx deleted file mode 100644 index f4cf40e01..000000000 --- a/packages/component-library/src/components/mt-banner/mt-banner.mdx +++ /dev/null @@ -1,73 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as BannerStories from "./mt-banner.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Banner** displays persistent, inline messaging inside the page layout. Use - it when people should notice a status, warning, or informational message while - continuing their work in the surrounding interface. - - -## When to use - -- Use **Banner** for important contextual information that should remain visible until the user has read or dismissed it. -- Use it for warnings, success confirmations, inherited-state hints, or inline guidance near the affected content. -- Use it when the message belongs in the page flow instead of as a temporary overlay notification. - -## Examples - -### Basic - - - -### Closable - - - -### Variants - - - -## API reference - - - -## Do - -- Keep the message concise and directly tied to the surrounding context. -- Choose the `variant` that matches the meaning of the message. -- Prefer **Banner** with the default icon enabled, using the icon that comes with the selected `variant`. -- Use the `title` to help people scan the message quickly when the content is more than a short sentence. -- Use `closable` only when dismissing the banner is a safe choice for the user. -- If you add actions inside the banner content, choose components and variants with sufficient color contrast. **Button** in `primary` or `secondary` is a good starting point. - -## Don't - -- Do not use **Banner** for transient confirmations that can disappear after a few seconds. -- Do not stack many **Banner** components together, because they quickly compete with the main page content. -- Do not hide blocking or decision-heavy flows in **Banner** when a modal or dedicated screen is more appropriate. -- Do not replace the built-in variant icon unless you have a strong semantic reason to do so. -- Do not rely on icon color alone to communicate meaning. The text should still be clear on its own. - -## Accessibility notes - -- **Banner** content should remain understandable without depending on the icon alone. -- Use clear text so the message still works for people using screen readers or high-contrast modes. -- Only make a banner dismissible when removing it will not hide information the user still needs. -- If the banner includes actions in the slot content, verify their contrast against the banner background instead of assuming every button style will remain readable. -- Keep the message brief and structured so it can be scanned quickly inside a busy page. - -## Comparisons - -### Banner vs Snackbar - -- Use **Banner** for persistent, inline messaging that should stay visible in the page until the user moves on or dismisses it. -- Use **Snackbar** for lightweight temporary feedback that appears separately from the page content. diff --git a/packages/component-library/src/components/mt-button/mt-button.mdx b/packages/component-library/src/components/mt-button/mt-button.mdx deleted file mode 100644 index 26b5a6014..000000000 --- a/packages/component-library/src/components/mt-button/mt-button.mdx +++ /dev/null @@ -1,88 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as ButtonStories from "./mt-button.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Button** is the standard action trigger for Meteor interfaces. Use it when - people need to submit, confirm, continue, or trigger an action directly from - the UI. - - -## When to use - -- Use **Button** for clear user actions such as saving, creating, confirming, or navigating to the next step. -- Use the `variant` to communicate action hierarchy and intent. -- Use button sizes to fit the surrounding layout without changing the meaning of the action. - -## Examples - -### Basic - - - -### Variants - - - -### Sizes - - - -### With icon - - - -### Icon only - - - -### States - - - -## API reference - - - -## Do - -- Keep button labels short, specific, and action-oriented. -- Use `primary` for the main action in a local context. -- Use `secondary` or `tertiary` for supporting actions. -- Use `critical` only for destructive or high-risk actions. -- Use `square` when you render an icon-only button so the control stays visually balanced. - -## Don't - -- Do not use multiple primary buttons in the same local action group. -- Do not use vague labels such as `Submit` or `Continue` when a clearer verb would help. -- Do not rely on color alone to explain what a button will do. -- Do not use **Button** for purely navigational inline text links. - -## Behavior notes - -- **Button** supports visual hierarchy through variants, but the label still carries the main meaning of the action. -- Loading and disabled states should be used to prevent duplicate actions or indicate temporary unavailability. -- The icon slots are optional enhancements. For icon-only buttons, use `square` and provide an accessible name. - -## Accessibility notes - -- **Button** text should clearly describe the action without depending on surrounding context alone. -- If you use only an icon visually, ensure the button still has an accessible name. -- Icon-only buttons are valid, but they should use the `square` prop and a clear accessible label such as `aria-label`. -- Disabled and loading buttons should be used intentionally so people understand why the action is unavailable. - -## Comparisons - -### Button vs Link - -- Use **Button** for actions such as saving, submitting, confirming, or opening an interface change inside the current flow. -- Use **Link** for navigation when the user is moving to another page, route, or destination. diff --git a/packages/component-library/src/components/mt-card/mt-card.mdx b/packages/component-library/src/components/mt-card/mt-card.mdx deleted file mode 100644 index 3e0b3f9f3..000000000 --- a/packages/component-library/src/components/mt-card/mt-card.mdx +++ /dev/null @@ -1,115 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as CardStories from "./mt-card.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Card** groups related content, metadata, and local actions inside a bordered - surface. Use it to break a page into clearly separated sections without losing - the relationship between the content and the actions around it. - - -## When to use - -- Use **Card** for settings sections, dashboards, detail views, or other grouped content that should read as one surface. -- Use it when a section needs a local header and content that should stay visually connected. -- Use it with tabs when users need to switch between closely related views inside the same section. -- Use it when related content should stay visually connected instead of being spread across loose page elements. - -## Examples - -### Basic - - - -### Header content - - - -### Tabs - - - -### Loading - - - -### Inheritance - - - -### Inset content - - - -### Inset footer - - - -## Companion export - -**Card** exposes **Inset** as a companion layout utility for cases where content inside the card should visually break out to the card edges without hard-coding spacing values. - -- Use **Inset** inside the default card content when an inner block should align to the card's outer padding instead of the current content flow. -- Use **Inset** in the `footer` slot when the footer needs its own full-width background or custom padding treatment while still staying aligned to the card spacing tokens. -- **Inset** works by consuming the card's inset spacing variables, so it is most useful inside components such as **Card** that define those values for you. - -## Anatomy - -**Card** is composed from a few structural regions and optional slots: - -- The header can contain an avatar, title, subtitle, and related header content on the right. -- The tabs area sits below the header and can be used for closely related views of the same section. -- The content area holds the main information, form fields, or other section content. - -## API reference - - - -## Do - -- Keep each **Card** focused on one topic, entity, or task. -- Use `title` and `subtitle` when they help users scan the section quickly. -- Use the header to add context, identity, or one closely related action for the section. -- Use tabs only when the content stays part of the same overall section. -- Keep the content inside a **Card** concise enough that the surface still feels like one grouped area. - -## Don't - -- Do not overload one **Card** with unrelated content blocks or too many competing actions. -- Do not nest many **Card** components inside each other when a simpler layout would be easier to scan. -- Do not use **Card** as a generic wrapper for unrelated page content. -- Do not add decorative header content if it does not help users understand the section. -- Do not use tabs for unrelated destinations or views that should be separate sections. -- Do not rely on a decorative card title alone when the contained actions need their own clear labels. - -## Behavior notes - -- The card header is only rendered when `title`, `subtitle`, `avatar`, or their replacement slots are provided. -- The `tabs` slot only renders the tab bar. The surrounding view is responsible for handling tab state and swapping the content. -- `title` and `subtitle` slots can replace the matching props when you need custom header content. -- `isLoading` overlays the content area with a loader while the card keeps its layout. -- **Inset** pairs naturally with **Card** because the card content and footer define the inset spacing variables that **Inset** consumes. -- `inheritance` shows the link or unlink toggle and emits `update:inheritance` when the toggle is clicked. -- `before-card` and `after-card` are private slots and should not be used in application code. - -## Accessibility notes - -- Prefer using the `title` prop when the card needs a programmatic label, because it is also used as the card's `aria-label`. -- If you render custom controls in `headerRight`, make sure each control has a clear accessible name. -- Keep card titles descriptive so users can understand the section before interacting with controls inside it. -- If you add tabs inside a **Card**, those tabs still need their own keyboard support and clear labels. - -## Comparisons - -### Card vs Inset - -- Use **Card** when you need a surfaced section with its own border, header, content, and optional actions. -- Use **Inset** when you only need spacing or padded grouping inside an existing surface. diff --git a/packages/component-library/src/components/mt-chart/mt-chart.mdx b/packages/component-library/src/components/mt-chart/mt-chart.mdx deleted file mode 100644 index 84add0cd1..000000000 --- a/packages/component-library/src/components/mt-chart/mt-chart.mdx +++ /dev/null @@ -1,56 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as ChartStories from "./mt-chart.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Chart** renders data visualizations inside the Meteor design system. For - now, this page documents the `area` chart type. - - -## When to use - -- Use **Chart** with `type="area"` to show how values change over time. -- Use it when users should compare trends, peaks, and drops across one or more series. -- Use it when a visual trend is easier to understand than a table alone. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Keep the time scale or sequence clear by providing meaningful x-axis labels. -- Use short series names so the legend and tooltip stay easy to scan. -- Pair the chart with nearby summary text when the data drives a decision. - -## Don't - -- Do not use an area chart when exact individual values are the primary goal. -- Do not rely on color alone to explain the data series. -- Do not use the chart as the only presentation of critical information. - -## Behavior notes - -- **Chart** passes `series` and `options` through to ApexCharts and merges your `options` with Meteor's default chart options. -- Changing `series`, `options`, `type`, `width`, or `height` updates the rendered chart. -- This page currently documents the `area` chart type only. - -## Accessibility notes - -- Provide the chart with nearby text that explains the main takeaway, because charts are not equally accessible for every user. -- Do not use the chart alone for critical information without an accessible text or table alternative. -- Keep labels and series names concise so tooltip and axis content stays readable. diff --git a/packages/component-library/src/components/mt-checkbox/mt-checkbox.mdx b/packages/component-library/src/components/mt-checkbox/mt-checkbox.mdx deleted file mode 100644 index 158a3f7cb..000000000 --- a/packages/component-library/src/components/mt-checkbox/mt-checkbox.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as CheckboxStories from "./mt-checkbox.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Checkbox** lets users select an independent yes or no value, or choose - multiple items from a list. - - -## When to use - -- Use **Checkbox** when users can turn an option on or off without affecting other options. -- Use it when multiple options in a group can be selected at the same time. -- Use it for form fields that should submit a boolean value. - -## Examples - -### Basic - - - -### States - - - -## API reference - - - -## Do - -- Use short, clear labels that describe the checked state in plain language. -- Use help text when the effect of the option is not obvious from the label alone. -- Use the indeterminate state only when the checkbox represents a mixed selection. - -## Don't - -- Do not use **Checkbox** for mutually exclusive options. -- Do not use the indeterminate state as a permanent third value. -- Do not rely on the checkmark alone without a visible label. - -## Behavior notes - -- Prefer `v-model` with `modelValue` and `update:modelValue` for new code. The `checked` prop and `change` event are legacy APIs. -- `partial` shows an indeterminate visual state. It is useful for parent selections with mixed child states. -- `inheritedValue` and `isInherited` support inheritance fields. An inherited **Checkbox** is disabled until inheritance is removed. -- `helpText`, `error`, `required`, and `bordered` integrate with the shared field wrapper. - -## Accessibility notes - -- Always provide a visible label, either through the `label` prop or the `label` slot. -- **Checkbox** uses a native checkbox input, so it supports standard keyboard interaction such as toggling with the Space key. -- If the indeterminate state needs explanation, describe its meaning in nearby text instead of relying on the visual state alone. -- Keep help text and error messages specific so users understand what the option controls. - -## Comparisons - -### Checkbox vs Radio Group - -- Use **Checkbox** when users can turn an option on or off independently, or when multiple options can be selected. -- Use **Radio Group** when users must choose exactly one option from a small set of visible choices. - -### Checkbox vs Select - -- Use **Checkbox** for a single boolean setting or when a small set of options should stay visible on the page. -- Use **Select** when users need to choose from a longer list or when space is limited. diff --git a/packages/component-library/src/components/mt-collapsible/mt-collapsible.mdx b/packages/component-library/src/components/mt-collapsible/mt-collapsible.mdx deleted file mode 100644 index 8b08fbcc8..000000000 --- a/packages/component-library/src/components/mt-collapsible/mt-collapsible.mdx +++ /dev/null @@ -1,57 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as CollapsibleStories from "./mt-collapsible.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Collapsible** is an interactive component that expands and collapses related - content behind a trigger. Use it to keep secondary information available - without crowding the surrounding layout. - - -## Examples - -### Basic - - - -### Disabled - - - -## Anatomy - -**Collapsible** is built from three companion exports that work together: - -- `mt-collapsible` is the root container that owns the open and closed state. -- `mt-collapsible-trigger` is the interactive element that toggles the open state. It renders as a `button` by default. -- `mt-collapsible-content` is the region that is shown or hidden when the state changes. - -## API reference - - - -## Behavior notes - -- **Collapsible** is a compound pattern. You always compose `mt-collapsible` with `mt-collapsible-trigger` and `mt-collapsible-content`. -- The open state can be controlled externally with `v-model:open` or left uncontrolled with `default-open`. -- When `disabled`, the trigger no longer toggles the content and the data attributes reflect the disabled state for styling. -- The content slides open and closed over 300 ms by default, using the `--reka-collapsible-content-height` CSS variable that the component exposes on its content element. The animation respects `prefers-reduced-motion`. -- `keep-mounted` defaults to `true` so the closed content stays in the DOM (it's hidden with `hidden="until-found"`, which keeps it discoverable to the browser's find-in-page feature) and the slide animation can play. Set `keep-mounted="false"` when the closed subtree is too expensive to leave mounted; the open/close animation will not play in that case. - -## Accessibility notes - -- The trigger should always have a clear accessible name so users understand what content the toggle reveals. -- The trigger and content are linked through `aria-controls` and `aria-expanded`, so assistive technology announces the state change automatically. -- When using a custom element via `as-child`, make sure the rendered element is still focusable and supports keyboard activation. diff --git a/packages/component-library/src/components/mt-colorpicker/mt-colorpicker.mdx b/packages/component-library/src/components/mt-colorpicker/mt-colorpicker.mdx deleted file mode 100644 index 3bdd50575..000000000 --- a/packages/component-library/src/components/mt-colorpicker/mt-colorpicker.mdx +++ /dev/null @@ -1,60 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as ColorpickerStories from "./mt-colorpicker.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Colorpicker** lets users choose and edit a color value directly in a form - field. - - -## When to use - -- Use **Colorpicker** when users need to define a custom brand, accent, or interface color. -- Use it when a text input alone would make color editing harder or more error-prone. -- Use the alpha option when transparency is part of the value users need to control. - -## Examples - -### Basic - - - -### Without alpha - - - -## API reference - - - -## Do - -- Use a clear label so users understand what surface or token the color affects. -- Keep the emitted format consistent with the value your feature expects. -- Enable alpha only when transparency is actually supported by the consuming feature. - -## Don't - -- Do not use **Colorpicker** when users only need to choose from a small fixed palette. -- Do not enable alpha if the saved value ignores transparency. -- Do not rely on color alone to explain what a setting changes. - -## Behavior notes - -- **Colorpicker** supports different emitted output formats through `colorOutput`, such as `auto`, `hex`, `hsl`, and `rgb`. -- When `alpha` is enabled, the picker also exposes transparency controls and can emit values with opacity. -- `helpText`, `required`, `error`, and inheritance-related props integrate with the shared field wrapper. - -## Accessibility notes - -- Always provide a visible label so users understand what the chosen color controls. -- Do not rely only on the visual preview. The text value should remain understandable and editable. -- If alpha is enabled, make sure nearby context explains how transparency affects the result. diff --git a/packages/component-library/src/components/mt-data-table/mt-data-table.mdx b/packages/component-library/src/components/mt-data-table/mt-data-table.mdx deleted file mode 100644 index 2584bd349..000000000 --- a/packages/component-library/src/components/mt-data-table/mt-data-table.mdx +++ /dev/null @@ -1,571 +0,0 @@ -import { Canvas, Meta, Markdown } from "@storybook/blocks"; -import * as DataTableStories from "./mt-data-table.stories"; -import * as DataTableInteractiveStories from "./mt-data-table.interactive.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - The **Data Table** is a powerful and flexible data table component designed to - handle large datasets with features like pagination, sorting, filtering, and - more. It's implemented as a "dumb" component, which means it focuses solely on - rendering and user interactions, while delegating all data management and - business logic to the parent component. - - -## Understanding the Dumb Component Pattern - -As a dumb component, **Data Table** follows these principles: - -1. **No Internal State Management**: The component doesn't maintain its own data state. All data must be provided through props and updated by the parent component. -2. **Event-Based Communication**: When user interactions occur (sorting, filtering, pagination, etc.), the component emits events. The parent component must listen to these events and handle the data updates accordingly. -3. **Prop-Driven Updates**: Any changes to the data or UI state must be driven by prop changes from the parent component. - -This means that as a developer implementing this component, you need to: - -1. **Handle Data Loading**: Implement the logic to fetch and manage data, including: - - - Initial data loading - - Pagination - - Sorting - - Filtering - - Search functionality - -2. **Manage State**: Maintain the state of: - - - Current page - - Items per page - - Sort column and direction - - Applied filters - - Search term - - Selected rows - - Loading states - -3. **Respond to Events**: Listen to and handle all relevant events to update your data and state. - -Here's a basic example of implementing the component with proper data management: - -```html - - - -``` - -This implementation pattern ensures: - -- Clear separation of concerns -- Predictable data flow -- Flexibility in data source and business logic -- Reusability across different data scenarios - -## Creating a Wrapper Component - -Since the data management logic can be repetitive across different tables in your application, it often makes sense to create a wrapper component that encapsulates this logic. This approach can: - -- Reduce boilerplate code -- Ensure consistent data handling -- Centralize API integration -- Make table implementations more maintainable - -Here's an example of creating a reusable wrapper component: - -```html - - - - -``` - -Now you can use this wrapper component with much less boilerplate: - -```html - - - - -``` - -This wrapper approach is particularly useful when you: - -- Have multiple tables in your application -- Need consistent data handling across tables -- Want to share common functionality (error handling, loading states, etc.) -- Need to maintain the same API integration pattern - -You can extend the wrapper component to include additional features like: - -- Error handling and retry logic -- Data caching -- Custom filtering logic -- Export functionality -- Bulk action handling -- Persistent table state - -## Key Features - -- **Data Rendering**: Displays data in a tabular format with customizable columns -- **Pagination**: Built-in pagination support with customizable page sizes -- **Sorting**: Column-based sorting with customizable sort directions -- **Filtering**: Flexible filtering system with various filter types -- **Search**: Global search functionality (can be disabled) -- **Row Selection**: Single and multiple row selection with bulk actions -- **Column Management**: Drag-and-drop column reordering and resizing -- **Loading States**: Built-in loading state handling with skeletons -- **Empty States**: Customizable empty state display -- **Responsive**: Adapts to different screen sizes with horizontal/vertical scrolling - -## Basic Usage - -Here's a simple example of how to use the **Data Table** component: - -```html - - - -``` - - - -## Column Configuration - -Columns are defined through a configuration object that specifies how each column should be rendered and behave: - -```typescript -interface ColumnDefinition { - label: string; // Column header label - property: string; // Property path in data source object - renderer?: string; // How to render the cell ('text'|'number'|'price') - position: number; // Column order (use increments of 100) - sortable?: boolean; // Enable/disable sorting (default: true) - width?: number; // Fixed column width - allowResize?: boolean; // Allow column resizing - visible?: boolean; // Show/hide column -} -``` - -## Props - - - {` -| Prop Name | Type | Default | Description | -|-----------|------|---------|-------------| -| dataSource | Array | [] | Array of objects containing the data to display | -| columns | Array | [] | Array of column definitions | -| paginationLimit | Number | 10 | Number of items per page | -| paginationCurrentPage | Number | 1 | Current active page | -| paginationTotalItems | Number | 0 | Total number of items | -| isLoading | Boolean | false | Shows loading state when true | -| disableSearch | Boolean | false | Disable search functionality | -| title | String | undefined | Table title | -| subtitle | String | undefined | Table subtitle | -| caption | String | undefined | Table caption for accessibility | -| showOutlines | Boolean | true | Show cell outlines | -| showStripes | Boolean | false | Show alternating row stripes | -| enableOutlineFraming | Boolean | false | Enable outline framing | -| enableRowNumbering | Boolean | false | Show row numbers | -| allowRowSelection | Boolean | false | Enable row selection | -| selectedRows | Array | [] | Array of selected row IDs | -| disableRowSelect | Array | [] | Array of row IDs which is disabled selection | -| filters | Array | [] | Array of filter definitions | -| appliedFilters | Array | [] | Array of currently applied filters | -| layout | String | 'default' | Table layout ('default' or 'full') | -| enableReload | Boolean | false | Show reload button | -| additionalContextButtons | Array | [] | Additional context buttons to show in the context menu | -`} - - -## Events - - - {` -| Event Name | Payload | Description | -|------------|---------|-------------| -| paginationLimitChange | number | Emitted when page size changes | -| paginationCurrentPageChange | number | Emitted when current page changes | -| searchValueChange | string | Emitted when search value changes | -| sortChange | { property: string, direction: string } | Emitted when sort changes | -| selectionChange | { id: string, value: boolean } | Emitted when single row selection changes | -| multipleSelectionChange | { selections: string[], value: boolean } | Emitted when multiple selection changes | -| bulkEdit | string[] | Emitted when bulk edit is triggered | -| bulkDelete | string[] | Emitted when bulk delete is triggered | -| reload | void | Emitted when reload button is clicked | -| changeShowOutlines | boolean | Emitted when outline visibility changes | -| changeShowStripes | boolean | Emitted when stripe visibility changes | -| changeOutlineFraming | boolean | Emitted when outline framing changes | -| changeEnableRowNumbering | boolean | Emitted when row numbering changes | -| contextSelect | { key: string, data: any } | Emitted when select a context menu item | -`} - - -## Slots - - - {` -| Slot Name | Props | Description | -|-----------|-------|-------------| -| toolbar | - | Additional content for the toolbar area | -| empty-state | - | Custom empty state content when no data is available | -`} - - -## Filtering - -The data table supports a flexible filtering system. Filters can be defined using the following structure: - -```typescript -interface Filter { - id: string; - label: string; - type: { - id: string; - options: Array<{ - id: string; - label: string; - }>; - }; -} -``` - -Example of setting up filters: - -```html - - - -``` - -## Loading States - -The data table provides built-in loading state handling with skeleton loading: - -```html - - - -``` - -## Best Practices - -1. **Column Positioning**: Use increments of 100 for column positions to leave room for inserting new columns later. -2. **Data Source**: Always provide a unique `id` field for each row in your data source. -3. **Performance**: Keep the data source size reasonable for the current page. Use server-side pagination for large datasets. -4. **Sorting**: Enable sorting only for columns where it makes sense. -5. **Filters**: Keep filter options concise and relevant to improve user experience. -6. **Loading States**: Always show loading state during data fetching to provide good UX. -7. **Accessibility**: Use the `caption` prop to provide a descriptive table summary for screen readers. - -## Examples - -### Basic Table - - - -### Full Width Table - - - -### Empty State - - - -### With Sticky Header - - diff --git a/packages/component-library/src/components/mt-datepicker/mt-datepicker.mdx b/packages/component-library/src/components/mt-datepicker/mt-datepicker.mdx deleted file mode 100644 index 8629603e4..000000000 --- a/packages/component-library/src/components/mt-datepicker/mt-datepicker.mdx +++ /dev/null @@ -1,69 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as DatepickerStories from "./mt-datepicker.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Datepicker** lets users choose a date, time, or date range with a - calendar-style input. - - -## When to use - -- Use **Datepicker** when users need to choose a calendar date, a time, or both together. -- Use it when a structured picker is safer and faster than freeform date input. -- Use range mode when users need to define a start and end date in one field. - -## Examples - -### Basic - - - -### Date only - - - -### Time only - - - -### Range - - - -## API reference - - - -## Do - -- Match the `dateType` to the value users actually need, such as `date`, `time`, or `datetime`. -- Use a clear label so users know what event, deadline, or schedule the value belongs to. -- Set the timezone intentionally when the chosen value is shared across regions or systems. - -## Don't - -- Do not use **Datepicker** when a plain text field is enough for a non-date value. -- Do not expose time selection if the feature only needs a date. -- Do not leave timezone behavior implicit when it affects saved values. - -## Behavior notes - -- `dateType` controls whether the picker behaves as a date, time, or datetime input. -- `range` lets one field hold a start and end value. -- `timeZone` affects how values are interpreted and displayed. -- `helpText`, `required`, `disabled`, `error`, and `size` integrate with the shared field styling. - -## Accessibility notes - -- Always provide a visible label so users understand what date or time they are setting. -- Use the simplest picker mode that fits the task, because extra controls increase interaction cost. -- If timezone matters for the saved value, explain that nearby instead of relying only on the hint. diff --git a/packages/component-library/src/components/mt-email-field/mt-email-field.mdx b/packages/component-library/src/components/mt-email-field/mt-email-field.mdx deleted file mode 100644 index 10bf5ec07..000000000 --- a/packages/component-library/src/components/mt-email-field/mt-email-field.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as EmailFieldStories from "./mt-email-field.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Email Field** helps users enter and validate a single email address in a - form. - - -## When to use - -- Use **Email Field** when the value should be an email address. -- Use it when browser email keyboards, autofill, or native validation improve the input flow. -- Use it for account settings, contact forms, invitations, or other flows that store one address. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear label such as `Email address` or `Work email`. -- Add help text when users need context about how the address will be used. -- Use the shared field features such as `error`, `hint`, or inheritance only when they add real value. - -## Don't - -- Do not use **Email Field** for general text input. -- Do not rely on the placeholder as the only label. -- Do not use it for multiple addresses in one field. - -## Behavior notes - -- **Email Field** uses a native `type="email"` input and checks browser validity on blur. -- `copyable` adds a copy button for read-only or reference-heavy flows where copying the address is useful. -- The component supports shared field patterns such as `prefix`, `suffix`, `hint`, `helpText`, `error`, and inheritance handling. - -## Accessibility notes - -- Always provide a visible label so users understand what address is expected. -- Native email input behavior can improve keyboard and autofill support, especially on mobile devices. -- Keep error and help text specific so users understand what needs to be corrected. - -## Comparisons - -### Email Field vs Text Field - -- Use **Email Field** when the value must be an email address and email-specific input behavior is helpful. -- Use **Text Field** when the value is general text and should not be constrained to an email format. diff --git a/packages/component-library/src/components/mt-empty-state/mt-empty-state.mdx b/packages/component-library/src/components/mt-empty-state/mt-empty-state.mdx deleted file mode 100644 index 514776700..000000000 --- a/packages/component-library/src/components/mt-empty-state/mt-empty-state.mdx +++ /dev/null @@ -1,60 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as EmptyStateStories from "./mt-empty-state.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Empty State** explains why content is missing and helps users take the next - useful step. - - -## When to use - -- Use **Empty State** when a list, page section, or feature has no content to show yet. -- Use it when users need context about why the area is empty and what they can do next. -- Use it for first-time setup, filtered no-results moments, or empty collections. - -## Examples - -### Basic - - - -### With action - - - -## API reference - - - -## Do - -- Use a headline and description that explain the empty situation clearly. -- Add one relevant next step, such as a **Link** or primary button, when users can recover from the empty state. -- Choose an icon that supports the message without becoming the main focus. - -## Don't - -- Do not use **Empty State** for critical warnings or error-heavy states. -- Do not overload the component with multiple competing actions. -- Do not leave users without context about why the area is empty. - -## Behavior notes - -- **Empty State** combines an icon, headline, description, and optional recovery action in one surface. -- A text link and a button can be shown when users should navigate or create something from the empty state. -- The `button` slot can replace the default action button when a custom button is needed. - -## Accessibility notes - -- Keep the headline and description clear enough to explain the empty situation without relying on the icon. -- Use action text that describes the next step clearly, especially when the state blocks progress. -- Make sure linked or button-based recovery actions are keyboard reachable and specific. diff --git a/packages/component-library/src/components/mt-entity-data-table/mt-entity-data-table.mdx b/packages/component-library/src/components/mt-entity-data-table/mt-entity-data-table.mdx deleted file mode 100644 index 8dd4dbb6d..000000000 --- a/packages/component-library/src/components/mt-entity-data-table/mt-entity-data-table.mdx +++ /dev/null @@ -1,65 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as EntityDataTableStories from "./mt-entity-data-table.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Entity Data Table** is a repository-backed version of **Data Table**. It - owns loading and table state for Admin SDK entity data so the parent can focus - on columns and actions. - - -## When to use - -- Use **Entity Data Table** when rows should come from an Admin SDK entity repository. -- Use it when you want searching, sorting, pagination, and filtering to refetch automatically. -- Use it when you want built-in row selection and bulk-action wiring on top of **Data Table**. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Let the wrapper own repository-backed table state. -- Define `availableFilters` only for filters users actually need. -- Handle `open-details`, `bulk-delete`, and `bulk-edit` in the parent component. - -## Don't - -- Do not use it when the parent must fully own fetching and table state. -- Do not duplicate repository search, sort, pagination, or filter handling in the parent component. -- Do not use it for local-only datasets where plain **Data Table** is enough. - -## Behavior notes - -- Data is fetched on mount. -- The table refetches when page, limit, sort, search, or filters change. -- `availableFilters` generates the filter UI for repository-backed filters. -- Bulk actions emit the currently selected row ids. - -## Accessibility notes - -- Give the table a meaningful `title` or surrounding heading. -- Keep column labels and filter labels clear and specific. - -## Comparisons - -### Entity Data Table vs Data Table - -- Use **Entity Data Table** for Admin SDK repository data with built-in loading and table state. -- Use **Data Table** when the parent component should own fetching and state management. diff --git a/packages/component-library/src/components/mt-entity-select/mt-entity-select.mdx b/packages/component-library/src/components/mt-entity-select/mt-entity-select.mdx deleted file mode 100644 index 1ae7fac7f..000000000 --- a/packages/component-library/src/components/mt-entity-select/mt-entity-select.mdx +++ /dev/null @@ -1,67 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as EntitySelectStories from "./mt-entity-select.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Entity Select** is a repository-backed version of **Select**. It loads - options from an Admin SDK entity repository so the parent component only needs - to manage the selected value. - - -## When to use - -- Use **Entity Select** when options should come from an Admin SDK entity repository. -- Use it when you want built-in search and pagination for repository results. -- Use it when an existing `modelValue` should be hydrated to a visible selected label. - -## Examples - -### Basic - - - -### With initial value - - - -## API reference - - - -## Do - -- Pass `entity` for the default repository lookup. -- Use `repository` only when you need custom fetching behavior. -- Keep `labelProperty` and `valueProperty` aligned with the entity shape you want to show and store. - -## Don't - -- Do not use it when options are already available locally. -- Do not duplicate search or pagination in the parent component. -- Do not assume `modelValue` stores the whole entity by default. - -## Behavior notes - -- The first page loads on mount. -- Search is handled through repository term lookups. -- `modelValue` stores the selected `valueProperty`, which defaults to `id`. - -## Accessibility notes - -- Use a clear visible field label so users understand what entity they are selecting. -- Keep result labels distinct so search results stay easy to understand. - -## Comparisons - -### Entity Select vs Select - -- Use **Entity Select** for Admin SDK repository data. -- Use **Select** for local or externally managed option lists. diff --git a/packages/component-library/src/components/mt-floating-ui/mt-floating-ui.mdx b/packages/component-library/src/components/mt-floating-ui/mt-floating-ui.mdx deleted file mode 100644 index 8570e9f37..000000000 --- a/packages/component-library/src/components/mt-floating-ui/mt-floating-ui.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as FloatingUiStories from "./mt-floating-ui.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Floating UI** is the low-level primitive for building custom popover-style - elements with automatic positioning. Use it when you need floating content - that should anchor to a trigger, flip when space is limited, and close when - users click outside. - - -## When to use - -- Use **Floating UI** for custom popover-style surfaces such as rich pickers, menus, inspectors, or inline tools. -- Use it when the floating content needs automatic positioning relative to a trigger element. -- Use it when higher-level components such as **Action Menu** do not fit the custom floating interaction you need. - -## Examples - -### Basic - - - -### Multiple anchors - -Use `detached` mode together with `anchorElement` to share one floating surface across multiple triggers. The floating panel repositions itself to each anchor as the user hovers, and a short close delay keeps it reachable when the cursor travels from trigger to panel. - - - -## API reference - - - -## Do - -- Manage the open state outside the component and update it through `isOpened` and the `close` event. -- Provide a clear trigger element so users understand what opens the floating surface. -- Style the slotted content as a complete surface, including background, border, spacing, and shadow. -- Use `matchReferenceWidth` when the floating surface should align to the trigger width. - -## Don't - -- Do not use **Floating UI** when a higher-level component such as **Action Menu** already fits the use case. -- Do not rely on it to provide a finished surface design. It only handles positioning and behavior. -- Do not forget to handle keyboard and focus behavior for custom interactive content. - -## Behavior notes - -- **Floating UI** teleports its content to `body`, so the floating surface can escape clipping and stacking issues in the local layout. -- The component uses Floating UI middleware to offset, flip, and measure the floating surface automatically. -- `showArrow` adds an arrow element, but the slotted content still needs to provide the actual surface styling. -- The default slot receives `referenceElementWidth` and `referenceElementHeight`, which can help when the floating content needs to size itself relative to the trigger. -- `matchReferenceWidth` sets the floating container width to the trigger width automatically. - -## Accessibility notes - -- Make sure the trigger clearly communicates what will open. -- If the floating content contains interactive controls, manage focus and keyboard behavior intentionally. -- Provide visible structure and labels inside the floating surface so the custom content remains understandable once opened. - -## Comparisons - -### Floating UI vs Popover - -- Use **Floating UI** when you need a custom floating surface with your own structure and behavior. -- Use **Popover** when the standard popover styling and interaction model already fit. diff --git a/packages/component-library/src/components/mt-help-text/mt-helptext.mdx b/packages/component-library/src/components/mt-help-text/mt-helptext.mdx deleted file mode 100644 index 56bf9a775..000000000 --- a/packages/component-library/src/components/mt-help-text/mt-helptext.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as HelpTextStories from "./mt-helptext.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Help Text** shows brief supporting guidance in a tooltip without adding - persistent text to the layout. - - -## When to use - -- Use **Help Text** for short explanatory details that are helpful but not essential at first glance. -- Use it next to a label or control when users may occasionally need extra context. -- Use it to keep interfaces compact while still offering lightweight guidance. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use **Help Text** for brief, supplementary explanations. -- Place it near the element it describes, such as next to a label. -- Keep the tooltip text short and specific so users can understand it quickly. - -## Don't - -- Do not use **Help Text** for critical information users must always see. -- Do not put lengthy content in the tooltip. -- Do not use it as a substitute for a clear label or necessary inline guidance. - -## Behavior notes - -- **Help Text** uses **Tooltip** internally and shows the provided text on hover or keyboard focus. -- `placement`, `width`, `showDelay`, and `hideDelay` can adjust how the tooltip behaves. -- The trigger is a small icon button, so the content should stay concise and contextual. - -## Accessibility notes - -- Only use **Help Text** for optional supporting guidance, not for instructions users must read to complete a task. -- Keep the tooltip text short enough to be understood quickly when focus moves to the trigger. -- Make sure the nearby label or surrounding UI still makes sense without opening the tooltip. - -## Comparisons - -### Help Text vs Tooltip - -- Use **Help Text** when you need a standard small help trigger for brief contextual guidance near a field or control. -- Use **Tooltip** when you need more flexible tooltip behavior or a different trigger element. diff --git a/packages/component-library/src/components/mt-icon/mt-icon.mdx b/packages/component-library/src/components/mt-icon/mt-icon.mdx deleted file mode 100644 index 4530ff59a..000000000 --- a/packages/component-library/src/components/mt-icon/mt-icon.mdx +++ /dev/null @@ -1,67 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as IconStories from "./mt-icon.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Icon** renders an SVG from the Meteor icon kit. Use it for interface - affordances, status cues, and supporting visuals inside other components. For - icon philosophy, sizing, color guidance, and the full common usages reference, - see the [Icons](?path=/docs/foundations-icons--docs) foundation page. - - -## All icons - - - -## When to use - -- Use **Icon** for compact visual cues that support a label, action, or state. -- Use it when an icon from the Meteor icon kit should be rendered consistently through the component library. -- Use it when color and sizing should follow design tokens instead of ad-hoc inline SVG usage. - -For icon philosophy, sizing guidelines, color guidance, and the canonical common usages reference, see the [Icons](?path=/docs/foundations-icons--docs) foundation page. - -## Examples - -### Basic - - - -### Mode - - - -### Sizes - - - -### Colors - - - -## API reference - - - -## Behavior notes - -- `name` can include the icon mode directly, such as `regular-products` or `solid-pencil-s`. -- `mode` is available when the name itself does not already include the `regular-` or `solid-` prefix, for example `name="calendar" mode="solid"`. -- Prefer the explicit `mode` prop in docs and app code when it makes the chosen variant easier to read at a glance. -- `solid` icons usually work better for very small sizes and for larger decorative icon use, while `regular` icons are often a better default for standard interface actions and supporting UI. -- `size` accepts pixel strings and plain numeric values, which are normalized to pixel sizes. -- Icons inherit `currentColor`, so using icon color tokens keeps them aligned with the theme and state tokens used elsewhere in the library. - -## Accessibility notes - -- Use `decorative` for icons that are purely visual and already explained by surrounding text. -- If an icon communicates meaning on its own, make sure that meaning is still available through visible text or accessible labeling on the parent control. -- Do not depend on icon color alone to convey success, warning, or error states. diff --git a/packages/component-library/src/components/mt-link/mt-link.mdx b/packages/component-library/src/components/mt-link/mt-link.mdx deleted file mode 100644 index f68f92d7b..000000000 --- a/packages/component-library/src/components/mt-link/mt-link.mdx +++ /dev/null @@ -1,62 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as LinkStories from "./mt-link.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Link** helps users navigate to another page, route, or destination. - - -## When to use - -- Use **Link** when the primary intent is navigation. -- Use it for inline links, secondary navigation, and text-level actions that move users somewhere else. -- Use it when the destination should feel lightweight and text-like instead of button-like. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use link text that clearly describes the destination or result. -- Use the `type` prop when an internal or external destination should be signaled visually. -- Keep **Link** close to the content or context it belongs to. - -## Don't - -- Do not use **Link** for actions that stay on the current page. -- Do not use vague text such as `Click here` when the destination matters. -- Do not disable a link unless removing navigation is genuinely necessary. - -## Behavior notes - -- **Link** can render different elements through the `as` prop and defaults to a `router-link`. -- `variant` changes the visual emphasis, while `type` can add an internal or external direction cue. -- `disabled` removes the active destination and click behavior. - -## Accessibility notes - -- Link text should make sense out of context so screen reader users can understand the destination. -- Use **Link** only when the control actually navigates somewhere. -- Make sure disabled links are used sparingly, since unavailable navigation can be confusing. - -## Comparisons - -### Link vs Button - -- Use **Link** when the user should navigate to another page, route, or destination. -- Use **Button** when the user should trigger an action on the current page. diff --git a/packages/component-library/src/components/mt-loader/mt-loader.mdx b/packages/component-library/src/components/mt-loader/mt-loader.mdx deleted file mode 100644 index fdd7fedcc..000000000 --- a/packages/component-library/src/components/mt-loader/mt-loader.mdx +++ /dev/null @@ -1,72 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as LoaderStories from "./mt-loader.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Loader** shows an indeterminate loading state as a centered spinner overlay. - Use it when a surface is busy for a short time and the exact completion - progress is not known. - - -## When to use - -- Use **Loader** while content, a card, or a control is waiting for async work to finish. -- Use it when the loading duration is uncertain and a simple busy indicator is enough. -- Use it in places where users should stay in context instead of being redirected to a separate loading screen. - -## Examples - -### Basic - - - -### With text - - - -## API reference - - - -## Do - -- Keep the loading state brief and replace it with real content as soon as possible. -- Use a size that fits the surrounding surface, such as a compact loader inside controls and a larger loader in content areas. -- Use `title` and `description` when users need short context for what is loading. -- Pair **Loader** with disabled or blocked interactions when the underlying surface should not be used while loading. -- Place it inside a bounded surface that provides the context for what is loading. - -## Don't - -- Do not use **Loader** when you can show real progress with a percentage or completed amount. -- Do not leave **Loader** visible for long-running tasks without additional context or feedback. -- Do not use it as the only way to explain what is happening when the wait may take noticeable time. - -## Behavior notes - -- **Loader** is an indeterminate indicator. It does not expose a value or completion state. -- The component centers itself and renders as an absolute overlay with a semi-transparent background, so it works best inside the surface that is being loaded. -- `size` accepts pixel values such as `16px`, `32px`, or `48px`, and the ring thickness adapts to the provided size. -- `backdrop` controls whether **Loader** renders its backdrop background and defaults to `true`. -- `title` and `description` render below the spinner as centered body text and can be used independently. - -## Accessibility notes - -- **Loader** is visual only and does not announce loading state to assistive technologies on its own. -- `title` and `description` provide visible context, but the surrounding region should still expose an appropriate busy state when the loading state matters for screen-reader users. -- If loading blocks interaction, also disable or otherwise guard the affected controls so keyboard users are not left guessing what is available. - -## Comparisons - -### Loader vs Progress Bar - -- Use **Loader** when work is in progress but the exact completion amount is unknown. -- Use **Progress Bar** when you can communicate meaningful progress such as processed items, uploaded bytes, or percentage complete. diff --git a/packages/component-library/src/components/mt-modal/mt-modal.mdx b/packages/component-library/src/components/mt-modal/mt-modal.mdx deleted file mode 100644 index 2e70ba1d0..000000000 --- a/packages/component-library/src/components/mt-modal/mt-modal.mdx +++ /dev/null @@ -1,162 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as ModalStories from "./mt-modal.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Modal** displays a dialog on top of the current page and temporarily focuses - attention on one task or decision. - - -## When to use - -- Use **Modal** when users need to confirm, review, or complete a focused task without leaving the current page. -- Use it for flows that need clear interruption, such as confirmations, forms, or important details. -- Use it when the content should sit above the rest of the interface instead of becoming part of the page layout. - -## Examples - -### Basic - - - -## How to use - -In most cases, you will open a modal through a button. The component set is designed to make that pattern straightforward. - -> **Note:** Wrap related modal components in `mt-modal-root`. - -```html - -``` - -If you need to do something before you close the modal, you can use `mt-modal-action` like this: - -```html - -``` - -## Controlled state - -Sometimes you want to open a modal through something other than a button. In that case, use the `isOpen` prop to control the state directly. - -```html - - - -``` - -## Modal parts - -### `mt-modal-root` - -The root component that wraps all related modal components. It controls whether the modal is visible. - -### `mt-modal` - -The actual modal surface and content container. - -### `mt-modal-trigger` - -An element that opens the modal when clicked. - -### `mt-modal-action` - -Allows you to perform an action before closing the modal, most often for confirm flows. - -### `mt-modal-close` - -Closes the modal when clicked. - -## API reference - - - -## Do - -- Keep modal content focused on a single task or decision. -- Use a clear title and footer actions so users understand how to continue or cancel. -- Choose a modal width that matches the amount of content. - -## Don't - -- Do not overload a modal with several unrelated tasks. -- Do not use a modal when inline content or a dedicated page would be easier to understand. -- Do not make closing behavior ambiguous. - -## Behavior notes - -- `mt-modal-root` controls whether the modal is mounted and visible. -- `mt-modal-trigger`, `mt-modal-action`, and `mt-modal-close` support common opening and closing flows. -- Footer actions are usually the clearest place for confirmation and cancellation controls. - -## Accessibility notes - -- Use a clear title so users immediately understand the purpose of the dialog. -- Keep focus behavior predictable and ensure footer actions are reachable by keyboard. -- Only use **Modal** when interrupting the current flow is justified by the task. diff --git a/packages/component-library/src/components/mt-number-field/mt-number-field.mdx b/packages/component-library/src/components/mt-number-field/mt-number-field.mdx deleted file mode 100644 index 6bbb2282f..000000000 --- a/packages/component-library/src/components/mt-number-field/mt-number-field.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as NumberFieldStories from "./mt-number-field.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Number Field** lets users enter numeric values with optional limits, steps, - and controls. - - -## When to use - -- Use **Number Field** when the value should be numeric. -- Use it for quantities, counts, percentages, or other values that benefit from step-based input. -- Use it when min and max limits should guide valid input. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear label that explains what the number represents. -- Configure `step`, `min`, and `max` when the range is known. -- Choose the right `numberType` for integer or decimal input. - -## Don't - -- Do not use **Number Field** for values that are identifiers but look numeric. -- Do not use it when users need to pick both a number and a unit together. -- Do not hide important limits from users if the field enforces them. - -## Behavior notes - -- **Number Field** builds on the shared field base and adds numeric parsing, stepping, and optional arrow controls. -- `numberType`, `digits`, `fillDigits`, and `numberAlignEnd` help tune how numeric values are entered and displayed. -- `showControls` can show increase and decrease buttons for step-based adjustment. - -## Accessibility notes - -- Always provide a visible label that explains the meaning of the number. -- Use help text when users need context about valid ranges, units, or rounding. -- Make sure any min, max, or step behavior matches the expectations shown in the UI. - -## Comparisons - -### Number Field vs Text Field - -- Use **Number Field** when the value should behave like a number with numeric constraints or stepping. -- Use **Text Field** when numeric-looking content should stay as plain text. diff --git a/packages/component-library/src/components/mt-pagination/mt-pagination.mdx b/packages/component-library/src/components/mt-pagination/mt-pagination.mdx deleted file mode 100644 index 4a5998cec..000000000 --- a/packages/component-library/src/components/mt-pagination/mt-pagination.mdx +++ /dev/null @@ -1,56 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as PaginationStories from "./mt-pagination.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Pagination** helps users move through larger result sets one page at a time. - - -## When to use - -- Use **Pagination** when a list or table is split into pages. -- Use it when users need to move forward, backward, or jump to a specific page. -- Use it alongside data tables, result lists, or overview pages with many items. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Keep `limit` and `totalItems` in sync with the actual dataset. -- Reset back to the first page when the page size or result set changes significantly. -- Place **Pagination** close to the content it controls. - -## Don't - -- Do not use **Pagination** when all items comfortably fit in one view. -- Do not use **Pagination** for exploratory browsing patterns where users are expected to keep discovering content continuously. Infinite scrolling may fit better there. -- Do not leave the current page disconnected from the visible result range. -- Do not mix pagination with infinite scrolling in the same content area. - -## Behavior notes - -- **Pagination** displays the current visible range, for example `1-25 of 100`. -- It emits `change-current-page` when users navigate with the buttons or by typing a page number. -- Changing `limit` resets the current page back to `1`. - -## Accessibility notes - -- Navigation buttons include accessible labels for first, previous, next, and last page. -- Keep **Pagination** near the content it updates so the relationship stays clear. -- Make sure page changes also update the visible content in a predictable way for keyboard and screen-reader users. diff --git a/packages/component-library/src/components/mt-password-field/mt-password-field.mdx b/packages/component-library/src/components/mt-password-field/mt-password-field.mdx deleted file mode 100644 index a62098918..000000000 --- a/packages/component-library/src/components/mt-password-field/mt-password-field.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as PasswordFieldStories from "./mt-password-field.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Password Field** lets users enter sensitive text and optionally reveal it - temporarily. - - -## When to use - -- Use **Password Field** for passwords, secrets, or other masked credential input. -- Use it when users may need to reveal the current value briefly to confirm what they typed. -- Use it in sign-in, account setup, or credential update flows. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear label such as `Password` or `New password`. -- Add help text or hint content when users need password rules. -- Keep the visibility toggle enabled when it helps users avoid typing mistakes. - -## Don't - -- Do not use **Password Field** for non-sensitive text. -- Do not rely on placeholder text as the only instruction. -- Do not hide password requirements if the field enforces them. - -## Behavior notes - -- **Password Field** masks input by default and can reveal the value with the visibility toggle when `toggable` is enabled. -- The component supports shared field patterns such as `hint`, `helpText`, `error`, and inheritance handling. -- It emits `submit` on Enter, which can help in credential flows. - -## Accessibility notes - -- Always provide a visible label so users know what credential is expected. -- Keep password rules, errors, and hints specific so users understand how to fix issues. -- Make sure the show or hide password control has a clear accessible name, especially if the field appears more than once on a page. - -## Comparisons - -### Password Field vs Text Field - -- Use **Password Field** when the value is sensitive and should be masked by default. -- Use **Text Field** when the value is not sensitive and does not need reveal behavior. diff --git a/packages/component-library/src/components/mt-popover/mt-popover.mdx b/packages/component-library/src/components/mt-popover/mt-popover.mdx deleted file mode 100644 index 7c9f91e65..000000000 --- a/packages/component-library/src/components/mt-popover/mt-popover.mdx +++ /dev/null @@ -1,30 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as PopoverStories from "./mt-popover.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Popover** is experimental. Prefer **Action Menu** or **Floating UI** when - those components already fit the use case. - - -## Replacement guidance - -- Use **Action Menu** when you need a standard menu of actions or options. -- Use **Floating UI** when you need a custom floating surface with your own layout, content, or interaction behavior. - -## Example - - - -## API reference - - diff --git a/packages/component-library/src/components/mt-progress-bar/mt-progress-bar.mdx b/packages/component-library/src/components/mt-progress-bar/mt-progress-bar.mdx deleted file mode 100644 index c938a3ae6..000000000 --- a/packages/component-library/src/components/mt-progress-bar/mt-progress-bar.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as ProgressBarStories from "./mt-progress-bar.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Progress Bar** visualizes determinate progress within a known range. Use it - when users benefit from seeing how much work is completed and how much is - still left. - - -## When to use - -- Use **Progress Bar** for uploads, imports, processing steps, or other tasks where completion can be measured. -- Use it when you can communicate a meaningful current value and maximum value. -- Use it in context, close to the content or action the progress belongs to. - -## Examples - -### Basic - - - -### Custom units and error - - - -## API reference - - - -## Do - -- Provide a clear label so users understand what the progress refers to. -- Keep the value and maximum meaningful, stable, and easy to reason about. -- Use the default percentage label when percent is the clearest way to communicate progress. -- Use `progressLabelType` when a concrete unit such as `kb` or `items` is more useful than a percentage. -- Show the `error` state when progress is blocked or the related process fails. - -## Don't - -- Do not use **Progress Bar** when the task has no measurable completion state. -- Do not rely on progress color alone to communicate success or failure. -- Do not feed invalid values on purpose and rely on clamping as the primary behavior. -- Do not hide the surrounding context for long-running tasks that may need more explanation. - -## Behavior notes - -- **Progress Bar** uses the current value and `maxValue` to calculate the filled width. -- The fill width is clamped between `0%` and `100%`, so values below `0` or above the maximum do not overflow the track. -- By default the progress label is shown as a percentage. When `progressLabelType` is set to another value, the label is rendered as `current unit / max unit`. -- The visible progress label is decorative for assistive technologies, while the actual progress semantics come from the `progressbar` role and ARIA values. -- When `error` is present, the fill changes to the critical color and the error message is rendered below the track. - -## Accessibility notes - -- **Progress Bar** exposes `role="progressbar"` with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax`. -- Provide a visible `label`, because the progressbar is associated with that label via `aria-labelledby`. -- The percentage or unit label is marked `aria-hidden`, so important status information should not depend on that text alone. -- If the task is especially important or long-running, pair **Progress Bar** with additional explanatory text so screen-reader and keyboard users have enough context. - -## Comparisons - -### Progress Bar vs Loader - -- Use **Progress Bar** when you know the current progress and can communicate it meaningfully. -- Use **Loader** when work is in progress but the exact completion amount is unknown. diff --git a/packages/component-library/src/components/mt-promo-badge/mt-promo-badge.mdx b/packages/component-library/src/components/mt-promo-badge/mt-promo-badge.mdx deleted file mode 100644 index 16527ea15..000000000 --- a/packages/component-library/src/components/mt-promo-badge/mt-promo-badge.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as PromoBadgeStories from "./mt-promo-badge.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Promo Badge** is a specialized version of **Badge** used to highlight - promotional states or feature labels such as `New`, `Beta`, or `Shopware AI`. - Unlike the general **Badge**, **Promo Badge** is limited to predefined - promotional variants so the visual treatment stays consistent across the - product. - - -## When to use - -- Use **Promo Badge** to highlight promotional elements across our products. -- Use it for predefined feature labels such as `New`, `Beta`, or `Shopware AI`. -- Use it when a promotional label should stand out consistently without inventing a new visual treatment. - -## Examples - -### Basic - - - -### Variants - - - -### Sizes - - - -## API reference - - - -## Do - -- Use **Promo Badge** to highlight promotional elements across our products. -- Keep the meaning consistent with the defined variants such as `New`, `Beta`, and `Shopware AI`. -- Use **Promo Badge** sparingly so it draws attention without cluttering the interface. - -## Don't - -- Do not use **Promo Badge** for status indicators such as error or success. Use **Badge** instead. -- Do not create custom promotional variants. Stick to the predefined set for consistency. -- Do not make **Promo Badge** clickable, because it is not a **Button** or **Link**. Showing a **Tooltip** on hover is fine. - -## Behavior notes - -- **Promo Badge** wraps **Badge** and maps each promotional variant to a predefined label, icon, and badge style. -- The visible text is controlled by the selected `variant`, so it stays consistent across products and languages. -- The component is intentionally limited to a small set of promotional meanings rather than being a general status label. - -## Accessibility notes - -- Keep the promotional meaning understandable from the text label and not from the icon alone. -- Because **Promo Badge** is non-interactive, avoid using it as the only way to expose an important action. -- Use it sparingly so promotional emphasis remains meaningful in dense interfaces. - -## Comparisons - -### Promo Badge vs Badge - -- Use **Promo Badge** for predefined promotional labels such as `New`, `Beta`, or `Shopware AI`. -- Use **Badge** for general product states such as status, health, or workflow labels. diff --git a/packages/component-library/src/components/mt-radio-group/mt-radio-group.mdx b/packages/component-library/src/components/mt-radio-group/mt-radio-group.mdx deleted file mode 100644 index 88966fc62..000000000 --- a/packages/component-library/src/components/mt-radio-group/mt-radio-group.mdx +++ /dev/null @@ -1,99 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as RadioGroupStories from "./mt-radio-group.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Radio Group** lets users select exactly one option from a small set of - mutually exclusive choices. It works best when the available options should - stay visible so users can compare them before deciding. - - -## When to use - -- Use **Radio Group** for mutually exclusive options where only one selection is allowed. -- Use it when the available choices should stay visible on the page instead of being hidden in a collapsed control. -- Use it when users benefit from scanning or comparing a small number of options before choosing one. - -## Examples - -### Basic - - - -### Custom item - - - -## Companion exports - -**Radio Group** is a compound component made up of several public exports: - -- `MtRadioGroupRoot` provides the shared label, help text, hint, error handling, and selected-value state. -- `MtRadioGroupList` lays out a standard vertical list of radio items with the expected spacing. -- `MtRadioGroupItem` renders the default radio option with a label. -- `MtRadioGroupCustomItem` lets you build fully custom option cards while still participating in the shared radio-group state. -- `MtRadioGroupIndicator` renders the actual radio input and control, and is mainly useful inside custom-item compositions. - -## API reference - - - -## Do - -- Use clear, concise labels for each option. -- Use a descriptive group label that explains what the user is selecting. -- Include help text or a hint when the selection needs additional context. -- Use the `error` prop to display validation errors when needed. -- Ensure each radio item has a unique `id` and `value`. -- Use `MtRadioGroupList` to wrap multiple `MtRadioGroupItem` components for proper spacing. -- Use `MtRadioGroupCustomItem` when you need custom-styled radio options such as pricing plans or feature cards. -- Keep the number of options manageable. A small visible set is the sweet spot. - -## Don't - -- Do not use **Radio Group** for multiple selections. Use **Checkbox** instead. -- Do not use a single radio button. Radio groups should present a real choice. -- Do not omit labels. Always provide clear labels for accessibility and usability. -- Do not use radio groups for long lists of options where a **Select** would be easier to scan. -- Do not use the same `value` for multiple items within the same group. -- Do not forget to handle the `v-model` binding so the selected value stays in sync. - -## Behavior notes - -- **Radio Group** uses `v-model` on `MtRadioGroupRoot` to manage the selected value across all items in the group. -- `MtRadioGroupItem` should always be used inside `MtRadioGroupRoot`, because it depends on the shared radio-group context. -- `MtRadioGroupCustomItem` gives you layout freedom, but you still need to render `MtRadioGroupIndicator` so the custom option remains a real radio control. -- `helpText`, `hint`, and `error` follow the shared field patterns used by other form components. - -## Accessibility notes - -- Provide a clear group label or other nearby visible context so users understand what the choice controls. -- `MtRadioGroupIndicator` uses native radio inputs, so the group benefits from standard browser keyboard behavior. -- Keep option labels explicit so users do not need surrounding context to understand each choice. -- If you build custom items, make sure the visible content still makes the selected state and choice meaning clear. - -## Comparisons - -### Radio Group vs Checkbox - -- Use **Radio Group** when users must choose exactly one option from a small set. -- Use **Checkbox** when users can turn an option on or off independently or choose multiple items. - -### Radio Group vs Select - -- Use **Radio Group** when a small number of options should stay visible and easy to compare. -- Use **Select** when there are more options or when space is limited. diff --git a/packages/component-library/src/components/mt-search/mt-search.mdx b/packages/component-library/src/components/mt-search/mt-search.mdx deleted file mode 100644 index c039c2bf1..000000000 --- a/packages/component-library/src/components/mt-search/mt-search.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as SearchStories from "./mt-search.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Search** lets users filter or find content in a list, table, or page - section. - - -## When to use - -- Use **Search** to narrow down visible content as users type or confirm a search term. -- Use it for list views, data tables, or page-level finding tools. -- Use it when a lightweight search input is enough and a full form field wrapper is not needed. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Place **Search** close to the content it affects. -- Use a placeholder or surrounding heading that makes the search scope clear. -- Keep search behavior responsive so users quickly see what changed. - -## Don't - -- Do not use **Search** for values that should be stored as form data. -- Do not use it when a labeled form field is the better semantic choice. -- Do not leave the search scope ambiguous. - -## Behavior notes - -- **Search** uses a native `type="search"` input and emits both `update:modelValue` and `change`. -- The component is intentionally lightweight and does not include the shared field wrapper used by the form field family. -- `size` can make the control more compact in dense toolbars or filter areas. - -## Accessibility notes - -- Make sure the purpose of **Search** is clear from nearby visible context if the placeholder alone is not enough. -- Keep the filtering result predictable so users understand what content the search affects. -- Ensure keyboard users can reach the input in the same order as the content it controls. - -## Comparisons - -### Search vs Text Field - -- Use **Search** when the value is meant to filter or find content. -- Use **Text Field** when the value should be entered and stored as regular form data. diff --git a/packages/component-library/src/components/mt-select/mt-select.mdx b/packages/component-library/src/components/mt-select/mt-select.mdx deleted file mode 100644 index 348e408f2..000000000 --- a/packages/component-library/src/components/mt-select/mt-select.mdx +++ /dev/null @@ -1,85 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as SelectStories from "./mt-select.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Select** lets users choose one or more values from a list of predefined - options. It works well when the available values should come from a controlled - dataset and the field can help users search within that list. - - -## When to use - -- Use **Select** when users should pick from predefined options instead of entering free-form text. -- Use it when the list may be longer than a simple inline choice group would comfortably show. -- Use it when searching, custom option labels, or multi selection would improve the picking experience. - -## Examples - -### Basic - - - -### Multiple selection - - - -## API reference - - - -## Do - -- Use a clear field label so users understand what they are choosing. -- Keep option labels concise and easy to scan. -- Use `enableMultiSelection` only when selecting multiple values is a real user need. -- Set `labelProperty` and `valueProperty` explicitly when your option shape differs from the defaults. -- Add hint or help text when the selection rules need extra explanation. - -## Don't - -- Do not use **Select** when users should enter arbitrary text instead of choosing from known values. -- Do not use a long select list when a smaller visible choice group would be easier to compare directly. -- Do not rely on placeholder text as the only instruction for the field. -- Do not mix unclear or duplicated option labels, especially in searchable lists. -- Do not enable multi selection when the form should store exactly one value. - -## Behavior notes - -- **Select** supports both single and multiple selection. `modelValue` can be a single value, an option object, or an array depending on the selection mode and `valueProperty`. -- The component searches within the provided options by default and supports a custom `searchFunction` when filtering needs to follow business-specific rules. -- `labelProperty` can be a string or an array of property paths. When an array is used, the first non-empty value is used as the visible label. -- Shared field features such as `hint`, `error`, prefix and suffix content, and inheritance handling work the same way as in other form fields. -- `small` renders a compact variant without the inline search input, which is better suited to tighter layouts. - -## Customization notes - -- Use `selection-label-property` to customize how selected items are rendered in the closed field. -- Use `result-item` or `result-label-property` when result rows need richer formatting than a plain text label. -- Use `before-item-list` and `after-item-list` for supporting content around the result list, such as guidance or actions. - -## Accessibility notes - -- Provide a clear field label or other nearby visible context so users understand what the selection controls. -- Keep option labels distinct so users can tell similar choices apart while searching or navigating with the keyboard. -- Use help text when multiple selection, custom option rendering, or inherited values might otherwise be unclear. - -## Comparisons - -### Select vs Radio Group - -- Use **Select** when the option list is longer, space is limited, or search helps users find the right value. -- Use **Radio Group** when a small set of options should stay visible so users can compare them directly. - -### Select vs Checkbox - -- Use **Select** when users should choose one value, or a constrained subset of values, from a predefined list. -- Use **Checkbox** when users are toggling independent options on and off rather than choosing from a shared option set. diff --git a/packages/component-library/src/components/mt-skeleton-bar/mt-skeleton-bar.mdx b/packages/component-library/src/components/mt-skeleton-bar/mt-skeleton-bar.mdx deleted file mode 100644 index 47feb71e6..000000000 --- a/packages/component-library/src/components/mt-skeleton-bar/mt-skeleton-bar.mdx +++ /dev/null @@ -1,70 +0,0 @@ -import { Canvas, Meta } from "@storybook/blocks"; - -import * as SkeletonBarStories from "./mt-skeleton-bar.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Skeleton Bar** shows a shimmering placeholder block while content is - loading. Use it when you want to preserve layout and signal that content will - appear in place shortly. - - -## When to use - -- Use **Skeleton Bar** when the final content structure is known but the real content is still loading. -- Use it in cards, lists, tables, or detail views where preserving the final layout helps reduce visual jump. -- Use multiple bars together when the loading state should suggest lines of text or grouped placeholder content. - -## Examples - -### Basic - - - -### Form layout - - - -## API reference - -**Skeleton Bar** does not expose public props, slots, or events. Size and layout are controlled by the surrounding container. - -## Do - -- Match the placeholder size and grouping roughly to the final content shape. -- Use **Skeleton Bar** close to the surface where the real content will appear. -- Replace the skeleton as soon as the actual content is ready. -- Combine several bars with different widths when that helps communicate the expected structure. - -## Don't - -- Do not use **Skeleton Bar** when you can show real progress or completion state. -- Do not leave skeleton placeholders visible for long-running tasks without additional context. -- Do not use skeleton bars as a generic decoration when nothing is actually loading. -- Do not make users interact with placeholder content that is not ready yet. - -## Behavior notes - -- **Skeleton Bar** is purely visual. It renders a shimmer animation over a neutral placeholder surface. -- The component takes the full available width of its container, so surrounding layout controls its final size. -- Consecutive skeleton bars add vertical spacing automatically, which makes it easy to stack them into text-like placeholder groups. - -## Accessibility notes - -- **Skeleton Bar** does not announce loading state or placeholder semantics on its own. -- When the loading state matters for assistive technologies, expose that state on the surrounding region or pair it with additional loading text. -- Keep the placeholder structure close to the final content so all users can understand what area is being loaded. - -## Comparisons - -### Skeleton Bar vs Loader - -- Use **Skeleton Bar** when the content layout is known and you want to preserve that layout while data loads. -- Use **Loader** when a simple indeterminate loading indicator is enough and a structural placeholder would not add value. diff --git a/packages/component-library/src/components/mt-slider/mt-slider.mdx b/packages/component-library/src/components/mt-slider/mt-slider.mdx deleted file mode 100644 index a2182ec9e..000000000 --- a/packages/component-library/src/components/mt-slider/mt-slider.mdx +++ /dev/null @@ -1,67 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as SliderStories from "./mt-slider.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Slider** lets users choose a numeric value or range by dragging a handle - along a scale. - - -## When to use - -- Use **Slider** when users should adjust a value within a known numeric range. -- Use it when seeing the relative position of a value is more helpful than typing alone. -- Use it for settings such as limits, thresholds, percentages, or price ranges. - -## Examples - -### Basic - - - -### Range - - - -## API reference - - - -## Do - -- Use a clear label that explains what the scale controls. -- Set sensible `min`, `max`, and `step` values for the use case. -- Use a range slider when users need to set both a lower and upper bound together. - -## Don't - -- Do not use **Slider** when precise text or number entry is the primary need. -- Do not use a very large range with tiny steps if dragging would become frustrating. -- Do not rely only on the visual bar to explain the meaning of the selected value. - -## Behavior notes - -- **Slider** can represent either a single numeric value or a two-value range through `isRange`. -- The component includes linked number inputs, so users can adjust values by dragging or by typing. -- `markCount`, `step`, and `minDistance` help shape how the scale behaves and how dense the value guidance feels. - -## Accessibility notes - -- Use a visible label that makes the meaning of the scale clear. -- Make sure the chosen range and step size still allow keyboard users to reach useful values efficiently. -- Avoid using **Slider** for critical settings if users need extremely precise control without an alternate input path. - -## Comparisons - -### Slider vs Number Field - -- Use **Slider** when users benefit from adjusting a value along a visible range. -- Use **Number Field** when precise numeric entry matters more than direct manipulation. diff --git a/packages/component-library/src/components/mt-snackbar/mt-snackbar.mdx b/packages/component-library/src/components/mt-snackbar/mt-snackbar.mdx deleted file mode 100644 index 4672bd6f5..000000000 --- a/packages/component-library/src/components/mt-snackbar/mt-snackbar.mdx +++ /dev/null @@ -1,128 +0,0 @@ -import { Canvas, Markdown, Meta } from "@storybook/blocks"; - -import * as SnackbarStories from "./mt-snackbar.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Snackbar** displays temporary overlay feedback near the bottom-right corner - of the interface. Use it to confirm an action, report a non-critical outcome, - or show short-lived progress without interrupting the current workflow. - - -## When to use - -- Use **Snackbar** to confirm that an action completed, such as saving, deleting, or updating something. -- Use it for non-blocking errors or warnings that people should notice without being pulled out of their current flow. -- Use it for background progress that resolves into success or error feedback. -- Use it when you may need one non-critical follow-up action, such as `Undo`. - -## Examples - -### Basic - - - -## Anatomy - -**Snackbar** consists of two parts: - -- `MtSnackbar` renders the notification stack and should typically be mounted once near the app shell. -- `useSnackbar()` lets feature code trigger a snackbar by calling `addSnackbar()`. - -```vue - - - -``` - -## API reference - -### `useSnackbar()` - - - {` -| Name | Description | -| --- | --- | -| \`snackbars\` (\`Readonly>\`) | Reactive list of currently visible snackbars. | -| \`addSnackbar(snackbar: Omit)\` | Adds a snackbar and returns the reactive snackbar instance. Useful for updating progress or completion state later. | -| \`removeSnackbar(id: string)\` | Removes a snackbar by id. | -| \`clearSnackbars()\` | Removes all currently visible snackbars. | -`} - - -### Snackbar - -The exported **Snackbar** type describes the notification object managed by `useSnackbar()`. The `id` is created automatically by `addSnackbar()`. - - - {` -| Field | Value | Default | Description | -| --- | --- | --- | --- | -| \`message\` | \`string\` | Required | Main snackbar text shown to the user. | -| \`variant\` | \`"success" \| "error" \| "warning" \| "progress"\` | Default appearance | Controls semantic styling and live-region behavior. | -| \`icon\` | \`string\` | Auto by variant | Optional icon name. Usually only needed when you want to override the default icon behavior. | -| \`link\` | \`{ text: string; url: string }\` | None | Optional single follow-up action rendered as a link button. | -| \`duration\` | \`number\` | \`5000\` | Auto-dismiss timeout in milliseconds. Use \`0\` to disable automatic dismissal. | -| \`progressPercentage\` | \`number\` | \`0\` | Current progress value for \`progress\` snackbars. | -| \`uploadState\` | \`"success" \| "error"\` | None | Final state for a \`progress\` snackbar. Triggers the completion message and short follow-up timeout. | -| \`successMessage\` | \`string\` | \`"Upload completed"\` | Optional final message shown when a progress snackbar resolves successfully. | -| \`errorMessage\` | \`string\` | \`"Upload failed"\` | Optional final message shown when a progress snackbar resolves with an error. | -`} - - -## Do - -- Keep the message short, specific, and easy to scan at a glance. -- Mount `MtSnackbar` once and trigger messages through `useSnackbar()` where the action happens. -- Use `progress` snackbars for background work that will resolve into a success or error state. -- Keep optional actions short and directly related to the message. - -## Don't - -- Do not use **Snackbar** for critical information that must stay visible until the user explicitly dismisses it. -- Do not overload a snackbar with long text or multiple actions. -- Do not rely on a snackbar as the only place a user can recover from a destructive action. -- Do not fire many snackbars in rapid succession, because they quickly become noisy and easy to miss. - -## Behavior notes - -- **Snackbar** uses a global store, so one mounted `MtSnackbar` host is usually the right setup for an application. -- Standard snackbars close automatically after `5000ms` by default. -- Setting `duration` to `0` disables the auto-dismiss timer, which means you should remove that snackbar through `removeSnackbar` or `clearSnackbars`. -- Hovering the snackbar stack pauses dismissal timers until the pointer leaves. -- `progress` snackbars stay visible until `uploadState` changes to `success` or `error`, then they close shortly after showing the final message. -- The optional `link` supports one non-critical follow-up action, not a full decision flow. - -## Accessibility notes - -- Error and warning snackbars use assertive live announcements, while default, success, and progress snackbars use polite announcements. -- Snackbar notifications receive focus when they appear, so messages should remain concise and understandable out of context. -- Do not use **Snackbar** for blocking confirmations, legal notices, or other information that must remain available until explicitly dismissed. -- If you provide an action like `Undo`, make sure that recovery path also exists elsewhere in the interface and not only in the temporary snackbar. - -## Comparisons - -### Snackbar vs Banner - -- Use **Snackbar** for temporary overlay feedback that fades out on its own and does not belong in the page layout. -- Use **Banner** for persistent inline messaging that should remain visible while the user continues working. diff --git a/packages/component-library/src/components/mt-switch/mt-switch.mdx b/packages/component-library/src/components/mt-switch/mt-switch.mdx deleted file mode 100644 index bf7bc13e5..000000000 --- a/packages/component-library/src/components/mt-switch/mt-switch.mdx +++ /dev/null @@ -1,66 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as SwitchStories from "./mt-switch.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Switch** lets users turn a setting on or off directly from the interface. - - -## When to use - -- Use **Switch** for a single binary setting that takes effect immediately. -- Use it when the on or off state is easy to understand without extra confirmation. -- Use it in forms or settings screens where users expect a quick toggle interaction. - -## Examples - -### Basic - - - -### States - - - -## API reference - - - -## Do - -- Use a clear label that describes the setting, not just the current state. -- Use **Switch** for immediate preferences or feature toggles. -- Add help text when users need more context about the effect of changing the setting. - -## Don't - -- Do not use **Switch** for multi-select choices. -- Do not use it when turning something on or off requires additional explanation or confirmation. -- Do not rely on the visual position of the thumb alone to communicate state. - -## Behavior notes - -- **Switch** supports a binary on or off value through `modelValue`. -- The component also supports shared field patterns such as `error`, `helpText`, inheritance handling, and bordered presentation. -- Because **Switch** is meant for direct toggling, it works best when users can understand the outcome immediately. - -## Accessibility notes - -- Use a visible label that clearly explains what the toggle controls. -- Make sure the setting makes sense when announced with its checked or unchecked state by assistive technology. -- Avoid using **Switch** for critical actions where accidental toggling would be risky. - -## Comparisons - -### Switch vs Checkbox - -- Use **Switch** for a single setting that turns something on or off immediately. -- Use **Checkbox** when users are selecting items or confirming a value as part of a broader form submission flow. diff --git a/packages/component-library/src/components/mt-tabs/mt-tabs.mdx b/packages/component-library/src/components/mt-tabs/mt-tabs.mdx deleted file mode 100644 index ef19b4a5d..000000000 --- a/packages/component-library/src/components/mt-tabs/mt-tabs.mdx +++ /dev/null @@ -1,73 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as TabsStories from "./mt-tabs.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Tabs** help users switch between related views, sections, or content areas - without leaving the current context. - - -## When to use - -- Use **Tabs** when users need to move between peer sections of content. -- Use them when only one panel or view should be active at a time. -- Use them for compact navigation inside a page, card, or workspace area. - -## Examples - -### Basic - - - -### Vertical - - - -## API reference - - - -## Do - -- Use short, clear labels so users can scan the available sections quickly. -- Keep the set of tabs related and at the same hierarchy level. -- Use badges or error indicators only when the extra status helps users prioritize where to look. - -## Don't - -- Do not use **Tabs** for unrelated actions or mixed controls. -- Do not overload tabs with long labels that make scanning difficult. -- Do not use tabs when a vertical navigation, segmented control, or button group would express the structure more clearly. - -## Behavior notes - -- **Tabs** take their items from the `items` prop and emit `new-item-active` when the selection changes. -- If there are too many horizontal tabs to fit, extra items move into a `More` menu automatically. -- `vertical` changes the layout direction, and `small` provides a more compact presentation. -- `small` is marked as deprecated in the component and should be treated as a legacy option. - -## Accessibility notes - -- Use labels that make sense as standalone tab names. -- Keep the order stable so keyboard and screen-reader users can build a reliable mental model. -- Make sure the currently active tab corresponds to clearly updated content nearby. - -## Comparisons - -### Tabs vs Segmented Control - -- Use **Tabs** when the user is switching between sections or views. -- Use `Segmented Control` only for its current experimental grouped-action behavior, not as a general replacement for tab navigation. - -### Tabs vs Button - -- Use **Tabs** when one selection changes the active content area. -- Use **Button** when the user should trigger an action instead of navigating between peer sections. diff --git a/packages/component-library/src/components/mt-text-editor/mt-text-editor.mdx b/packages/component-library/src/components/mt-text-editor/mt-text-editor.mdx deleted file mode 100644 index 6cc1214af..000000000 --- a/packages/component-library/src/components/mt-text-editor/mt-text-editor.mdx +++ /dev/null @@ -1,598 +0,0 @@ -import { Canvas, Meta, Markdown } from "@storybook/blocks"; - -import * as TextEditorStories from "./mt-text-editor.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - The **Text Editor** component is a powerful and flexible rich text editor - built with [Tiptap](https://tiptap.dev/). It is designed to handle a variety - of use cases, such as adding rich text editing to your application. This - component is highly customizable and can be extended with additional buttons, - features, or configurations to suit your needs. - - -## Features - -- **Rich Text Editing**: Includes common formatting options like bold, italic, underline, and more, with support for text alignment, bullet lists, ordered lists, and more. -- **Customizable Toolbar**: Add or exclude toolbar buttons as per your requirements, or hide the toolbar completely. -- **Code Editor Mode**: Toggle between WYSIWYG editing and raw HTML editing with CodeMirror integration. Can be controlled programmatically or set to start in code mode by default. -- **Character Count**: Displays a live character count at the bottom. -- **Inline Editing**: Option to enable inline editing with a floating toolbar. -- **Contextual Buttons**: Provides custom buttons for the footer of the editor that change contextually based on the current cursor position. -- **Security Gate on Initial Load**: Blocks WYSIWYG editing if the initial HTML would change; review a diff first. -- **Diff Review on Switch**: When switching from Code → WYSIWYG, shows a diff and requires acceptance if changes are detected. - ---- - -## Usage - -To use the **Text Editor** component in your project, import it and provide the required props. - -### Minimal Example - -```html - - - -``` - - - -### Inline Editing - -```html - - - -``` - - - -### Hide Toolbar - -```html - - - -``` - - - -### Start in Code Mode - -```html - - - -``` - - - -### Two-way Code Mode Binding - -```html - - - -``` - - - -### Custom Toolbar Buttons - -```html - - - -``` - - - -### Props - - - {` -| Prop Name | Type | Default | Description | -| --------------- | ------- | ------- | ----------------------------------------------------------------------- | -| modelValue | String | '' | The HTML content of the editor. Use 'v-model' to bind it to a variable. | -| isInlineEdit | Boolean | 'false' | Enables inline editing with a floating toolbar. | -| tipTapConfig | Object | {} | Custom configuration for the Tiptap editor. | -| customButtons | Array | [] | Array of custom buttons to add to the toolbar. | -| excludedButtons | Array | [] | Array of button names to exclude from the toolbar. | -| disabled | Boolean | 'false' | Disables the editor and all toolbar buttons. | -| placeholder | String | '' | Placeholder text shown when the editor is empty. | -| error | Object | null | Error object to display validation errors. | -| label | String | null | Label text displayed above the editor. | -| showToolbar | Boolean | 'true' | Controls toolbar visibility. Set to false to hide the toolbar completely. | -| codeMode | Boolean | 'false' | Controls code editor mode. Set to true to show code editor instead of WYSIWYG. Supports v-model:codeMode for two-way binding. | -`} - - ---- - -## Events - - - {` -| Event Name | Payload | Description | -| ------------------ | ------- | ------------------------------------------------------------------------------- | -| update:modelValue | String | Emitted when the editor content changes. Contains the HTML string. | -| update:codeMode | Boolean | Emitted when the editor mode changes between WYSIWYG and code editor. | -`} - - ---- - -## Exposed Methods - - - {` -| Method Name | Return Type | Description | -| ----------- | ---------------- | --------------------------------------------------------------------------------------------------- | -| validate() | Promise | Validates code editor content against Tiptap's parsed output. Shows diff modal if changes detected. Returns true if valid, false if diff modal was shown. | -`} - - ---- - -## Slots - -The **Text Editor** component provides several slots for customization: - -### `button_` - -Allows you to override or customize specific buttons in the toolbar. For example, to customize the `text-color` button: - -```html - - - -``` - -### `contextual-buttons` - -Provides custom buttons for the footer of the editor. These buttons can change contextually based on the editor's state. - -### `footer-left` and `footer-right` - -Customize the left or right sections of the editor's footer. - ---- - -## Toolbar Buttons - -The **Text Editor** includes the following built-in buttons by default: - - - {` -| Button Name | Description | Alignment | Position | -| -------------- | -------------------------------------------------- | --------- | -------- | -| format | Opens a popover with formatting options. | 'left' | 1000 | -| text-color | Allows the user to pick a text color. | 'left' | 2000 | -| bold | Toggles bold text. | 'left' | 3000 | -| italic | Toggles italic text. | 'left' | 4000 | -| underline | Toggles underlined text. | 'left' | 5000 | -| strikethrough | Toggles strikethrough text. | 'left' | 6000 | -| superscript | Toggles superscript text. | 'left' | 7000 | -| subscript | Toggles subscript text. | 'left' | 8000 | -| text-alignment | Opens a popover to set text alignment. | 'left' | 9000 | -| unordered-list | Toggles an unordered list. | 'left' | 10000 | -| numbered-list | Toggles an numbered list. | 'left' | 11000 | -| link | Opens a modal to insert or edit links. | 'left' | 12000 | -| table | Opens a modal to insert or modify tables. | 'left' | 13000 | -| undo | Undoes the last action. | 'right' | 1000 | -| redo | Redoes the last undone action. | 'right' | 2000 | -| toggle-code | Toggles between WYSIWYG mode and raw HTML editing. | 'right' | 3000 | -`} - - -You can exclude or add custom buttons using the `excludedButtons` and `customButtons` props. - ---- - -## Customizing with Tiptap Extensions - -The editor uses [Tiptap extensions](https://tiptap.dev/docs/editor/core-concepts/extensions) for its features. -You can include custom extensions by passing them through the `tipTapConfig` prop except the hardcoded properties `content`, `editorProps` and `onUpdate`. -For example: - -```html - - - -``` - ---- - -## Customizing with Custom Buttons - -The **Text Editor** supports adding custom buttons to the toolbar by passing an array of `CustomButton` objects to the `customButtons` prop. - -### Key Properties of `CustomButton` - -- **`name`** (required): A unique identifier for the button. -- **`label`** (required): The visible label for the button, can be the direct text or a translation key. -- **`icon`**: An optional icon to display instead of the label. You can use an icon name from the Meteor icon set. -- **`isActive`**: A function that determines whether the button is currently active (e.g., for toggling bold or italic formatting). -- **`action`**: A function that executes when the button is clicked. This is where you can apply an editor command. -- **`children`**: An array of child buttons to create a dropdown or multi-level menu. -- **`alignment`**: Specifies whether the button should appear on the left or right side of the toolbar. -- **`position`**: Defines the order of the button in the toolbar. Buttons with lower `position` values appear first. See the table with existing buttons to see their positions. -- **`disabled`**: A function that determines whether the button should be disabled. -- **`contextualButtons`**: A function that returns additional buttons to display in the footer based on the editor's state. - ---- - -### Example: Adding a Simple Custom Button - -Here’s how you can add a simple custom button to toggle bold formatting: - -```html - - - -``` - -### Example: Adding a Dropdown Menu - -You can create a dropdown menu by using the `children` property. Each child button is defined as another `CustomButton` object. - -```html - - - -``` - ---- - -### Example: Adding Contextual Buttons in the Footer - -Contextual buttons are buttons that appear in the editor's footer and can change depending on the editor's current state. Use the `contextualButtons` property to define these. - -```html - - - -``` - ---- - -### Example: Disabling a Button Based on Editor State - -You can disable a button dynamically by providing a `disabled` function. For example, disabling the "Bold" button if the editor is empty: - -```html - - - -``` - ---- - -### Example: Using a complete custom button with the dynamic slot - -For each button a dynamic slot is rendered: `button_${name}`. You can use this slot to replace the automatic rendered button defined in the button object with your own component: - -```html - - - -``` - ---- - -### Positioning Custom Buttons in the Toolbar - -By default, buttons are sorted in the toolbar based on their `position` value. Buttons with lower `position` values appear earlier. The default buttons have predefined positions in increments of `1000`, leaving space for you to insert custom buttons at specific positions. - -For example: - -- Default buttons (like `bold`, `italic`, etc.) have `position` values starting at `1000`. -- You can insert your custom buttons between or after them by specifying values like `1500`, `3500`, or `8500`. - -```js -const customButtons = [ - { - name: "custom-highlight", - label: "Highlight", - icon: "circle-xs", - position: 3500, // Places this button between "Bold" (3000) and "Italic" (4000) - action: (editor) => editor.chain().focus().toggleHighlight().run(), - }, -]; -``` - ---- - -## Security & Integrity Checks - -### Gate on Initial Unsupported HTML - -When the editor mounts in WYSIWYG, it dry-runs a Tiptap parse and compares the HTML. If it differs (for example, unsupported attributes are removed), an overlay blocks editing. You can: - -- Show Diff: open a modal with side-by-side diff -- Stay in Code: switch to code view - -Accepting the diff applies the parsed HTML and enables WYSIWYG editing. - - - -#### Behavior - -- No additional props required; enabled by default. -- `update:modelValue` emits are suppressed while the gate is active. -- UI texts are localized via `mt-text-editor.gate.*` and `mt-text-editor.diff.*`. - -## Code Editor Mode - -The **Text Editor** includes a code editor mode for editing raw HTML. This mode is powered by [CodeMirror](https://codemirror.net/). Use the `toggle-code` button to switch between WYSIWYG mode and code mode. - -### Diff Review on Switch - -When switching from Code → WYSIWYG, the editor compares the code with Tiptap’s parsed output. If changes are detected, a modal shows a side-by-side diff. You can: - -- Accept Changes & Switch: apply the parsed HTML and switch to WYSIWYG -- Stay in Code Editor: keep editing in code view - -### Programmatic Validation - -The component exposes a `validate()` method via template ref. It checks the current code editor content against what Tiptap can represent. If the HTML differs, the diff modal is shown for the user to review. - -- Returns `true` if valid (no diff or not in code mode). -- Returns `false` if the diff modal was shown. - -This is useful for parent components that need to validate content before closing a modal or saving. - -```html - - - -``` diff --git a/packages/component-library/src/components/mt-text-field/mt-text-field.mdx b/packages/component-library/src/components/mt-text-field/mt-text-field.mdx deleted file mode 100644 index 83f20155e..000000000 --- a/packages/component-library/src/components/mt-text-field/mt-text-field.mdx +++ /dev/null @@ -1,87 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as TextFieldStories from "./mt-text-field.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Text Field** lets users enter short free-form text values in a form. - - -## When to use - -- Use **Text Field** for names, titles, identifiers, and other short text values. -- Use it when users should type one value in a single-line input. -- Use it when shared field features such as `hint`, `helpText`, `error`, or inheritance are helpful. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear visible label that describes the expected value. -- Keep the content short enough for a single-line input. -- Add help text or hint content when users need more context. - -## Don't - -- Do not use **Text Field** for longer multi-line content. -- Do not rely on the placeholder as the only instruction. -- Do not use it when a more specific field type would guide input better. - -## Behavior notes - -- **Text Field** supports shared field features such as `prefix`, `suffix`, `hint`, `helpText`, `error`, and inheritance handling. -- `copyable` adds a copy button for reference-heavy values that users may need to copy out of the field. -- `maxLength` can show a live character counter when the value length matters. - -## Accessibility notes - -- Always provide a visible label so users understand what value is expected. -- Keep help text and error text specific so users can correct problems quickly. -- Use a more specific field type when browser input behavior or validation would improve accessibility. - -## Comparisons - -### Text Field vs Email Field - -- Use **Text Field** for general text that should not be constrained to an email format. -- Use **Email Field** when the value must be an email address. - -### Text Field vs URL Field - -- Use **Text Field** when the value is general text and should not be normalized as a web address. -- Use **URL Field** when the value should be a link and URL-specific behavior is helpful. - -### Text Field vs Number Field - -- Use **Text Field** when the value should stay as plain text, even if it contains digits. -- Use **Number Field** when the value should behave like a number with numeric constraints or stepping. - -### Text Field vs Unit Field - -- Use **Text Field** when the value is free-form text and does not need measurement logic. -- Use **Unit Field** when users should enter a number together with a selectable unit. - -### Text Field vs Password Field - -- Use **Text Field** when the value is not sensitive and does not need to be masked. -- Use **Password Field** when the value is sensitive and should be hidden by default. - -### Text Field vs Search - -- Use **Text Field** when the value should be entered and stored as regular form data. -- Use **Search** when the value is meant to filter or find content in the interface. diff --git a/packages/component-library/src/components/mt-text/mt-text.mdx b/packages/component-library/src/components/mt-text/mt-text.mdx deleted file mode 100644 index e1bc613c4..000000000 --- a/packages/component-library/src/components/mt-text/mt-text.mdx +++ /dev/null @@ -1,60 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as TextStories from "./mt-text.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Text** is the base typography component for body copy, headings, and - supporting text. - - -## When to use - -- Use **Text** when you need consistent typography across content and interface text. -- Use it when semantic HTML and visual text styles should be chosen separately. -- Use it for body copy, labels, headings, and supporting descriptions. - -## Examples - -### Basic - - - -### Sizes - - - -## API reference - - - -## Do - -- Choose the `as` prop based on the semantic meaning of the content. -- Choose `size` and `weight` based on visual hierarchy. -- Keep color choices aligned with the text token system. - -## Don't - -- Do not use **Text** only for spacing or layout. -- Do not use heading-sized text when the content is not actually a heading in the page structure. -- Do not override the semantics of important content without a clear reason. - -## Behavior notes - -- **Text** renders as a semantic element through the `as` prop and defaults to a paragraph. -- `size`, `weight`, and `color` control the visual style while the content stays in the default slot. -- Larger sizes use the heading font tokens, while smaller sizes use the body font tokens. - -## Accessibility notes - -- Use the `as` prop to preserve the correct document structure, especially for headings. -- Do not rely on visual styling alone to communicate hierarchy if semantic headings are required. -- Keep color choices readable against the background and aligned with contrast requirements. diff --git a/packages/component-library/src/components/mt-textarea/mt-textarea.mdx b/packages/component-library/src/components/mt-textarea/mt-textarea.mdx deleted file mode 100644 index 280972a30..000000000 --- a/packages/component-library/src/components/mt-textarea/mt-textarea.mdx +++ /dev/null @@ -1,62 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as TextareaStories from "./mt-textarea.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Textarea** lets users enter longer free-form text in a form. - - -## When to use - -- Use **Textarea** for notes, descriptions, comments, or other multi-line content. -- Use it when users need more space than a single-line field should provide. -- Use it when line breaks and longer written input are part of the value. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear visible label that explains what kind of content belongs in the field. -- Add help text or hint content when users need guidance about tone, length, or formatting. -- Use `maxLength` when the content length matters and should stay visible. - -## Don't - -- Do not use **Textarea** for short single-line values. -- Do not rely on the placeholder as the only instruction. -- Do not use it when a more structured input would guide users better. - -## Behavior notes - -- **Textarea** supports shared field patterns such as `hint`, `helpText`, `error`, and inheritance handling. -- The field can grow vertically because resizing is enabled for the text area itself. -- `maxLength` shows a live character counter when the content length matters. - -## Accessibility notes - -- Always provide a visible label so users understand what longer text is expected. -- Keep help text and error text specific so users know how to improve the content. -- Use clear length guidance when the field has a maximum character limit. - -## Comparisons - -### Textarea vs Text Field - -- Use **Textarea** when users need to enter longer multi-line content. -- Use **Text Field** when the value should stay short and single-line. diff --git a/packages/component-library/src/components/mt-toast/mt-toast.mdx b/packages/component-library/src/components/mt-toast/mt-toast.mdx deleted file mode 100644 index f606ba8da..000000000 --- a/packages/component-library/src/components/mt-toast/mt-toast.mdx +++ /dev/null @@ -1,55 +0,0 @@ -import { Canvas, Meta, Markdown } from "@storybook/blocks"; - -import * as ToastStories from "./mt-toast.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - -A toast is a form of notifaction, directly bound to user actions, to provide additional feedback. -Unsimilar to notifications, toasts are not persistent, they are context specific. - -In `meteor` toasts are placed into one of two categories automatically. These categories are `quick toasts` and `regular toasts`. - - - -## Quick toasts - -A `quick toast` has a show time of 3 seconds and is displayed in the `bottom center` position. - -Quick toasts are either of type `informal` or `positive`. In addition to that, quick toasts can neither be manually dismissed, nor contain an action. - -## Regular toasts - -A regular toast has a show time of 10 seconds or until it's manually dismissed. - -A regular toast can be of any type: `positive`, `informal` and `critical`. It follows the exact oposites of a quick toast. - -A regular toast either has a `action`, is manually dismissable or of type `critical`. - -## Overview - -These rules generate the following toast matrix. - - - {` -| Type | Dismissable | Action | Toast category | Auto close | Show time | -|----------|-------------|--------|----------------|------------|-----------| -| critical | no | none | regular | yes | 10s | -| critical | yes | none | regular | yes | 10s | -| critical | yes | yes | regular | no | none | -| positive | no | no | quick | yes | 3s | -| positive | yes | no | regular | yes | 10s | -| positive | yes | yes | regular | yes | 10s | -| informal | no | no | quick | yes | 3s | -| informal | yes | no | regular | yes | 10s | -| informal | yes | yes | regular | yes | 10s | -`} - - - diff --git a/packages/component-library/src/components/mt-tooltip/mt-tooltip.mdx b/packages/component-library/src/components/mt-tooltip/mt-tooltip.mdx deleted file mode 100644 index a4013e9ca..000000000 --- a/packages/component-library/src/components/mt-tooltip/mt-tooltip.mdx +++ /dev/null @@ -1,77 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as TooltipStories from "./mt-tooltip.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Tooltip** shows brief supporting information when users hover or focus a - trigger element. - - -## When to use - -- Use **Tooltip** for short contextual guidance tied to a specific trigger. -- Use it when extra information is helpful but should stay hidden until requested. -- Use it when the trigger is already part of the interface, such as a button, icon, or inline control. - -## Examples - -### Basic - - - -### Placement - - - -### Rich content - - - -## Anatomy - -- **Tooltip** uses the default slot as the trigger. -- The slot provides trigger bindings such as focus, hover, and accessibility attributes that should be spread onto the trigger element. -- The tooltip body itself is rendered in a floating layer and positioned relative to the trigger. - -## API reference - - - -## Do - -- Keep tooltip content brief and easy to scan. -- Attach the tooltip to the element it explains. -- Use `placement` and `maxWidth` when the surrounding layout needs a more predictable presentation. - -## Don't - -- Do not use **Tooltip** for critical information users must always see. -- Do not put long-form instructions or complex content in the tooltip. -- Do not rely on **Tooltip** as the only way to explain a control that would otherwise be unclear. - -## Behavior notes - -- **Tooltip** opens on hover and keyboard focus, and closes on blur, mouse leave, or common dismiss keys such as `Escape`. -- The `content` prop accepts sanitized HTML, so simple formatting can be shown when needed. -- `delayDurationInMs`, `hideDelayDurationInMs`, `placement`, and `maxWidth` help tune the tooltip behavior for the surrounding UI. - -## Accessibility notes - -- Only use **Tooltip** for supporting information, not for instructions users must read to complete a task. -- Make sure the trigger control still has a clear accessible name and purpose without opening the tooltip. -- Keep the content short enough that it can be understood quickly when focus moves to the trigger. - -## Comparisons - -### Tooltip vs Help Text - -- Use **Tooltip** when you need flexible tooltip behavior on an existing trigger element. -- Use **Help Text** when you need the standard small help icon pattern next to a field or label. diff --git a/packages/component-library/src/components/mt-unit-field/mt-unit-field.mdx b/packages/component-library/src/components/mt-unit-field/mt-unit-field.mdx deleted file mode 100644 index da3ff67db..000000000 --- a/packages/component-library/src/components/mt-unit-field/mt-unit-field.mdx +++ /dev/null @@ -1,62 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as UnitFieldStories from "./mt-unit-field.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **Unit Field** combines a numeric input with a unit selector for measurements. - - -## When to use - -- Use **Unit Field** when users should enter a value together with a measurement unit. -- Use it for lengths, weights, or other values that may switch between compatible units. -- Use it when unit conversion should happen as part of the input flow. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear label that explains what is being measured. -- Set a sensible `defaultUnit` for the most common case. -- Choose the right `measurementType` so the available units match the use case. - -## Don't - -- Do not use **Unit Field** when the unit is fixed and never changes. -- Do not use it for non-measurement values such as counts or identifiers. -- Do not hide the meaning of the measurement behind a vague label. - -## Behavior notes - -- **Unit Field** combines **Number Field** behavior with a unit selector. -- Changing the selected unit can convert the current value to keep the measurement consistent. -- `measurementType` controls which units are available, and `defaultUnit` controls the current selection. - -## Accessibility notes - -- Always provide a visible label so users understand what the measurement refers to. -- Make sure unit changes remain understandable, especially when the displayed numeric value updates after conversion. -- Use help text when users need guidance about accepted ranges or preferred units. - -## Comparisons - -### Unit Field vs Number Field - -- Use **Unit Field** when users need to enter both a numeric value and a selectable measurement unit. -- Use **Number Field** when the value is numeric and the unit is fixed or communicated elsewhere. diff --git a/packages/component-library/src/components/mt-url-field/mt-url-field.mdx b/packages/component-library/src/components/mt-url-field/mt-url-field.mdx deleted file mode 100644 index 9ff2fd1d2..000000000 --- a/packages/component-library/src/components/mt-url-field/mt-url-field.mdx +++ /dev/null @@ -1,62 +0,0 @@ -import { ArgTypes, Canvas, Meta } from "@storybook/blocks"; - -import * as UrlFieldStories from "./mt-url-field.stories"; -import StorybookPageHeader from "../../docs/_components/storybook-page-header.js"; - - - - - **URL Field** helps users enter and normalize a single web address. - - -## When to use - -- Use **URL Field** when the value should point to a website or route. -- Use it when protocol handling like `https://` and `http://` should stay visible and easy to change. -- Use it for external links, landing pages, or reference URLs stored in forms. - -## Examples - -### Basic - - - -## API reference - - - -## Do - -- Use a clear label such as `Website` or `Target URL`. -- Add help text when users need rules about valid links or allowed domains. -- Enable `copyable` when users often reuse the saved URL elsewhere. - -## Don't - -- Do not use **URL Field** for general text input. -- Do not ask users to type the full protocol if the field already helps manage it. -- Do not use it for multiple links in one field. - -## Behavior notes - -- **URL Field** uses a native `type="url"` input and keeps the protocol toggle visible beside the input. -- The component normalizes entered URLs and can omit search parameters or hashes when configured. -- `copyable` adds a quick way to copy the current URL from the field. - -## Accessibility notes - -- Always provide a visible label so users know what kind of link is expected. -- Keep help text specific when only certain URLs or domains are allowed. -- Make sure the final saved URL is understandable if the protocol toggle changes the output value. - -## Comparisons - -### URL Field vs Text Field - -- Use **URL Field** when the value should be a web address and URL-specific behavior is helpful. -- Use **Text Field** when the value is general text and should not be normalized as a URL. diff --git a/packages/component-library/src/docs/_components/color-palette.js b/packages/component-library/src/docs/_components/color-palette.js deleted file mode 100644 index 09bbe9925..000000000 --- a/packages/component-library/src/docs/_components/color-palette.js +++ /dev/null @@ -1,187 +0,0 @@ -import React, { useCallback, useEffect, useState } from "react"; -import primitives from "../../../../tokens/dictionaries/foundation/primitives.tokens.json"; - -// Derived from primitives.tokens.json — updates automatically when the token file changes. -const PALETTE_DEFINITIONS = Object.entries(primitives.color).map(([name, steps]) => ({ - name: name.charAt(0).toUpperCase() + name.slice(1), - prefix: `--color-${name}`, - steps: Object.keys(steps).map(Number), -})); - -function resolveTokens() { - const style = getComputedStyle(document.documentElement); - return PALETTE_DEFINITIONS.map(({ name, prefix, steps }) => ({ - name, - swatches: steps.map((step) => { - const token = `${prefix}-${step}`; - const hex = style.getPropertyValue(token).trim(); - return { token, hex }; - }), - })); -} - -function luminance(r, g, b) { - return r * 0.299 + g * 0.587 + b * 0.114; -} - -function isLight(color) { - if (!color) return true; - const hex = color.trim(); - if (hex.startsWith("#") && hex.length >= 7) { - const r = parseInt(hex.slice(1, 3), 16); - const g = parseInt(hex.slice(3, 5), 16); - const b = parseInt(hex.slice(5, 7), 16); - return luminance(r, g, b) > 160; - } - const rgb = hex.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/); - if (rgb) return luminance(Number(rgb[1]), Number(rgb[2]), Number(rgb[3])) > 160; - return true; -} - -function getStep(token) { - const match = token.match(/(\d+)$/); - return match ? match[1] : token; -} - -function notify(message) { - document.dispatchEvent(new CustomEvent("meteor-docs:snackbar", { detail: { message } })); -} - -function Swatch({ token, hex, onCopy }) { - const light = isLight(hex); - const textColor = light ? "rgba(0,0,0,0.75)" : "rgba(255,255,255,0.9)"; - const mutedColor = light ? "rgba(0,0,0,0.45)" : "rgba(255,255,255,0.55)"; - const step = getStep(token); - - return React.createElement( - "div", - { - style: { - backgroundColor: hex, - padding: "9px 14px", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - gap: 8, - }, - }, - React.createElement( - "button", - { - onClick: () => onCopy(`var(${token})`), - title: `Copy var(${token})`, - onMouseEnter: (e) => { - e.currentTarget.style.textDecoration = "underline"; - }, - onMouseLeave: (e) => { - e.currentTarget.style.textDecoration = "none"; - }, - style: { - background: "none", - border: "none", - cursor: "pointer", - padding: 0, - fontFamily: "var(--font-family-body, monospace)", - fontSize: 12, - fontWeight: 500, - color: textColor, - letterSpacing: "0.01em", - textDecoration: "none", - }, - }, - step, - ), - React.createElement( - "button", - { - onClick: () => onCopy(hex), - title: `Copy ${hex}`, - onMouseEnter: (e) => { - e.currentTarget.style.textDecoration = "underline"; - }, - onMouseLeave: (e) => { - e.currentTarget.style.textDecoration = "none"; - }, - style: { - background: "none", - border: "none", - cursor: "pointer", - padding: 0, - fontFamily: "var(--font-family-body, monospace)", - fontSize: 11, - color: mutedColor, - textDecoration: "none", - }, - }, - hex, - ), - ); -} - -function Palette({ name, swatches, onCopy }) { - return React.createElement( - "div", - { style: { minWidth: 0 } }, - React.createElement( - "div", - { - style: { - fontFamily: "var(--font-family-body, sans-serif)", - fontSize: 13, - fontWeight: 600, - color: "var(--color-text-primary-default)", - marginBottom: 8, - }, - }, - name, - ), - React.createElement( - "div", - { - style: { - borderRadius: 8, - overflow: "hidden", - border: "1px solid var(--color-border-secondary-default)", - }, - }, - swatches.map((s) => - React.createElement(Swatch, { key: s.token, token: s.token, hex: s.hex, onCopy }), - ), - ), - ); -} - -export default function ColorPalette() { - const [palettes, setPalettes] = useState([]); - - useEffect(() => { - setPalettes(resolveTokens()); - }, []); - - const handleCopy = useCallback((value) => { - navigator.clipboard - .writeText(value) - .then(() => notify(`Copied ${value}`)) - .catch(() => {}); - }, []); - - return React.createElement( - "div", - { - style: { - display: "grid", - gridTemplateColumns: "repeat(2, 1fr)", - gap: 24, - margin: "24px 0", - }, - }, - palettes.map((p) => - React.createElement(Palette, { - key: p.name, - name: p.name, - swatches: p.swatches, - onCopy: handleCopy, - }), - ), - ); -} diff --git a/packages/component-library/src/docs/_components/do-dont.js b/packages/component-library/src/docs/_components/do-dont.js deleted file mode 100644 index d20bdeb7f..000000000 --- a/packages/component-library/src/docs/_components/do-dont.js +++ /dev/null @@ -1,67 +0,0 @@ -import React from "react"; - -const gridStyle = { - display: "grid", - gridTemplateColumns: "1fr 1fr", - gap: "16px", - margin: "24px 0", -}; - -const cardStyle = (isdo) => ({ - borderRadius: "4px", - padding: "10px 16px", - backgroundColor: isdo ? "#e1ffe0" : "#fff2f0", -}); - -const labelStyle = (isdo) => ({ - display: "flex", - alignItems: "center", - gap: "6px", - fontSize: "14px", - fontWeight: 700, - lineHeight: "22px", - color: isdo ? "#00470a" : "#e2262a", - marginBottom: "8px", - textTransform: "uppercase", - letterSpacing: "0.05em", -}); - -const textStyle = { - fontSize: "16px", - lineHeight: "26px", - color: "#1e1e24", - margin: 0, -}; - -const checkIcon = React.createElement( - "svg", - { width: 14, height: 14, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, - React.createElement("path", { - fillRule: "evenodd", - clipRule: "evenodd", - d: "M12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24ZM7.56066 10.9393L10.5 13.8787L16.4393 7.93934C17.0251 7.35355 17.9749 7.35355 18.5607 7.93934C19.1464 8.52513 19.1464 9.47487 18.5607 10.0607L11.5607 17.0607C10.9749 17.6464 10.0251 17.6464 9.43934 17.0607L5.43934 13.0607C4.85355 12.4749 4.85355 11.5251 5.43934 10.9393C6.02513 10.3536 6.97487 10.3536 7.56066 10.9393Z", - }), -); - -const timesIcon = React.createElement( - "svg", - { width: 14, height: 14, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, - React.createElement("path", { - fillRule: "evenodd", - clipRule: "evenodd", - d: "M24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12ZM12 9.87596L8.81394 6.6899C8.2274 6.10337 7.27644 6.10337 6.6899 6.6899C6.10337 7.27644 6.10337 8.2274 6.6899 8.81394L9.87596 12L6.6899 15.1861C6.10337 15.7726 6.10337 16.7236 6.6899 17.3101C7.27644 17.8966 8.2274 17.8966 8.81394 17.3101L12 14.124L15.1861 17.3101C15.7726 17.8966 16.7236 17.8966 17.3101 17.3101C17.8966 16.7236 17.8966 15.7726 17.3101 15.1861L14.124 12L17.3101 8.81394C17.8966 8.2274 17.8966 7.27644 17.3101 6.6899C16.7236 6.10337 15.7726 6.10337 15.1861 6.6899L12 9.87596Z", - }), -); - -export default function DoDont({ do: doText, dont }) { - return React.createElement("div", { style: gridStyle }, [ - React.createElement("div", { key: "do", style: cardStyle(true) }, [ - React.createElement("span", { key: "label", style: labelStyle(true) }, [checkIcon, "Do"]), - React.createElement("p", { key: "text", style: textStyle }, doText), - ]), - React.createElement("div", { key: "dont", style: cardStyle(false) }, [ - React.createElement("span", { key: "label", style: labelStyle(false) }, [timesIcon, "Don't"]), - React.createElement("p", { key: "text", style: textStyle }, dont), - ]), - ]); -} diff --git a/packages/component-library/src/docs/_components/elevation-surfaces.js b/packages/component-library/src/docs/_components/elevation-surfaces.js deleted file mode 100644 index c10f68414..000000000 --- a/packages/component-library/src/docs/_components/elevation-surfaces.js +++ /dev/null @@ -1,158 +0,0 @@ -import React from "react"; - -const CONTAINER_SIZE = 260; -const CARD_SIZE = 160; - -// centerY = CONTAINER_SIZE - (bottomPct * CONTAINER_SIZE) - CARD_SIZE / 2 -const SURFACE_TOKENS = [ - { token: "--color-elevation-surface-sunken", label: "Sunken", bottom: "18%", centerY: 133 }, - { token: "--color-elevation-surface-default", label: "Default", bottom: "30%", centerY: 102 }, - { token: "--color-elevation-surface-raised", label: "Raised", bottom: "42%", centerY: 71 }, -]; - -function ThemePanel({ themeLabel }) { - const labelColor = "var(--color-static-black)"; - const lineColor = "#e0e0e0"; - const captionColor = "var(--color-static-black)"; - - return React.createElement( - "div", - { - "data-theme": themeLabel, - style: { - flex: 1, - display: "flex", - flexDirection: "column", - alignItems: "center", - padding: "24px 16px", - }, - }, - React.createElement( - "div", - { - style: { - position: "relative", - width: 340, - height: CONTAINER_SIZE, - }, - }, - // 3D scene - React.createElement( - "div", - { - style: { - position: "absolute", - left: 0, - top: 0, - width: CONTAINER_SIZE, - height: CONTAINER_SIZE, - perspective: "800px", - display: "flex", - alignItems: "center", - justifyContent: "center", - }, - }, - SURFACE_TOKENS.map((t) => - React.createElement("div", { - key: t.token, - style: { - position: "absolute", - width: CARD_SIZE, - height: CARD_SIZE, - borderRadius: 12, - background: `var(${t.token})`, - border: "1px solid var(--color-border-secondary-default)", - bottom: t.bottom, - transform: "rotateX(45deg) rotateZ(-45deg)", - transformStyle: "preserve-3d", - }, - }), - ), - ), - // Caption - React.createElement( - "span", - { - style: { - position: "absolute", - bottom: 0, - left: 0, - width: CONTAINER_SIZE, - textAlign: "center", - fontSize: 12, - fontFamily: "var(--font-family-body, sans-serif)", - color: captionColor, - }, - }, - themeLabel === "dark" ? "Dark mode" : "Light mode", - ), - // Legend lines + labels - SURFACE_TOKENS.map((t) => - React.createElement( - "div", - { - key: t.token + "-legend", - style: { - position: "absolute", - top: t.centerY, - left: 256, - transform: "translateY(-50%)", - display: "flex", - alignItems: "center", - gap: 0, - }, - }, - // dot - React.createElement("div", { - style: { - width: 5, - height: 5, - borderRadius: "50%", - background: lineColor, - flexShrink: 0, - }, - }), - // line - React.createElement("div", { - style: { - width: 24, - height: 1, - background: lineColor, - flexShrink: 0, - }, - }), - // label - React.createElement( - "span", - { - style: { - paddingLeft: 6, - fontSize: 12, - fontWeight: 500, - fontFamily: "var(--font-family-body, sans-serif)", - color: labelColor, - whiteSpace: "nowrap", - }, - }, - t.label, - ), - ), - ), - ), - ); -} - -export default function ElevationSurfaces() { - return React.createElement( - "div", - { - style: { - display: "flex", - gap: 16, - margin: "24px 0", - }, - }, - React.createElement(ThemePanel, { themeLabel: "light" }), - React.createElement(ThemePanel, { themeLabel: "dark" }), - ); -} diff --git a/packages/component-library/src/docs/_components/interaction-states.js b/packages/component-library/src/docs/_components/interaction-states.js deleted file mode 100644 index 0f9073a30..000000000 --- a/packages/component-library/src/docs/_components/interaction-states.js +++ /dev/null @@ -1,144 +0,0 @@ -import React from "react"; - -function Label({ children }) { - return React.createElement( - "span", - { - style: { - display: "block", - fontSize: 11, - fontWeight: 600, - textTransform: "uppercase", - letterSpacing: "0.06em", - color: "var(--color-text-secondary-default)", - fontFamily: "var(--font-family-body, sans-serif)", - marginBottom: 10, - }, - }, - children, - ); -} - -function StateCard({ label, children }) { - return React.createElement( - "div", - { - style: { - display: "flex", - flexDirection: "column", - alignItems: "center", - gap: 10, - }, - }, - children, - React.createElement( - "span", - { - style: { - fontSize: 12, - color: "var(--color-text-secondary-default)", - fontFamily: "var(--font-family-body, sans-serif)", - }, - }, - label, - ), - ); -} - -const BUTTON_BASE = { - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - minHeight: 32, - padding: "0 15px", - borderRadius: "var(--border-radius-button)", - fontSize: "var(--font-size-2xs)", - fontWeight: "var(--font-weight-semibold)", - fontFamily: "var(--font-family-body)", - cursor: "pointer", - border: "1px solid transparent", - transition: "background 120ms ease, border-color 120ms ease", - textDecoration: "none", - userSelect: "none", -}; - -const buttonVariants = { - resting: { - background: "var(--color-interaction-primary-default)", - borderColor: "var(--color-interaction-primary-default)", - color: "#fff", - outline: "none", - }, - hover: { - background: "var(--color-interaction-primary-hover)", - borderColor: "var(--color-interaction-primary-hover)", - color: "#fff", - outline: "none", - }, - focus: { - background: "var(--color-interaction-primary-default)", - borderColor: "var(--color-interaction-primary-default)", - color: "#fff", - outline: "2px solid var(--color-border-brand-default)", - outlineOffset: "2px", - }, - pressed: { - background: "var(--color-interaction-primary-pressed)", - borderColor: "var(--color-interaction-primary-pressed)", - color: "#fff", - outline: "none", - }, - disabled: { - background: "var(--color-interaction-primary-disabled)", - borderColor: "var(--color-interaction-primary-disabled)", - color: "rgba(255,255,255,0.55)", - outline: "none", - cursor: "not-allowed", - }, -}; - -const STATES = ["resting", "hover", "focus", "pressed", "disabled"]; - -export default function InteractionStates() { - return React.createElement( - "div", - { - style: { - background: "var(--color-elevation-surface-raised)", - borderRadius: 8, - border: "1px solid var(--color-border-secondary-default)", - padding: "24px", - display: "flex", - flexDirection: "column", - gap: 16, - margin: "24px 0", - }, - }, - React.createElement( - "div", - { - style: { - display: "flex", - gap: 24, - flexWrap: "wrap", - alignItems: "flex-start", - }, - }, - STATES.map((state) => - React.createElement( - StateCard, - { key: state, label: state.charAt(0).toUpperCase() + state.slice(1) }, - React.createElement( - "button", - { - tabIndex: -1, - disabled: state === "disabled", - style: { ...BUTTON_BASE, ...buttonVariants[state] }, - }, - "Add product", - ), - ), - ), - ), - ); -} diff --git a/packages/component-library/src/docs/_components/storybook-page-header.js b/packages/component-library/src/docs/_components/storybook-page-header.js deleted file mode 100644 index 1b95dacea..000000000 --- a/packages/component-library/src/docs/_components/storybook-page-header.js +++ /dev/null @@ -1,216 +0,0 @@ -import { Markdown } from "@storybook/blocks"; -import React from "react"; - -const STATUS_VARIANTS = { - available: { - label: "Available", - backgroundColor: "#e1ffe0", - borderColor: "#36d046", - indicatorColor: "#36d046", - }, - experimental: { - label: "Experimental", - backgroundColor: "#fff3e3", - borderColor: "#fbaf18", - indicatorColor: "#fbaf18", - }, - deprecated: { - label: "Deprecated", - backgroundColor: "#fff2f0", - borderColor: "#e2262a", - indicatorColor: "#e2262a", - }, -}; - -const containerStyle = { - margin: "0.75rem 0", -}; - -const statusContainerStyle = { - marginTop: "1.25rem", - marginBottom: "1.25rem", -}; - -const statusHeaderStyle = { - display: "inline-flex", - alignItems: "center", - gap: "0.75rem", - flexWrap: "wrap", -}; - -const statusInfoStyle = { - display: "inline-flex", - alignItems: "center", - gap: "0.375rem", -}; - -const labelStyle = { - fontFamily: "inherit", - fontSize: 15, - lineHeight: "inherit", - fontWeight: 700, - color: "#1e1e24", -}; - -const titleTagStyle = { - color: "#6b7280", - fontWeight: 400, -}; - -const badgeStyle = (variant) => ({ - display: "inline-flex", - alignItems: "center", - gap: "0.25rem", - height: "1.25rem", - padding: "0 0.5rem", - borderRadius: "624.9375rem", - border: `1px solid ${variant.borderColor}`, - backgroundColor: variant.backgroundColor, - color: "#1e1e24", - fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, sans-serif", - fontSize: 13, - lineHeight: "1.125rem", - fontWeight: 500, -}); - -const sourceLinkStyle = { - borderLeft: "1px solid #e2e3e9", - color: "#0870ff", - paddingLeft: "1rem", - textDecoration: "underline", - fontSize: 15, -}; - -const indicatorStyle = (variant) => ({ - width: "0.5rem", - height: "0.5rem", - borderRadius: "0.75rem", - backgroundColor: variant.indicatorColor, - flexShrink: 0, -}); - -const codeContainerStyle = (hasDescription) => ({ - marginTop: hasDescription ? "0.75rem" : "0.5rem", -}); - -const descriptionStyle = { - marginTop: "0.75rem", -}; - -const DEFAULT_PACKAGE_NAME = "@shopware-ag/meteor-component-library"; -const COMPONENT_LIBRARY_GITHUB_TREE_URL = "https://github.com/shopware/meteor/tree/main"; - -function createImportCode(packageImports, packageName) { - if (!packageImports) { - return null; - } - - const imports = Array.isArray(packageImports) ? packageImports : [packageImports]; - - if (imports.length === 1) { - return `import { ${imports[0]} } from "${packageName}";`; - } - - return `import {\n ${imports.join(",\n ")},\n} from "${packageName}";`; -} - -const NPM_PACKAGE_URL = "https://www.npmjs.com/package/"; - -export default function StorybookPageHeader({ - title, - tagName, - status, - code, - language = "ts", - packageImports, - packageName = DEFAULT_PACKAGE_NAME, - sourcePath, - sourceUrl: sourceUrlProp, - npmPackage, - children, -}) { - const variant = status === "none" ? null : STATUS_VARIANTS[status] ?? STATUS_VARIANTS.available; - const sourceUrl = - sourceUrlProp ?? (sourcePath ? `${COMPONENT_LIBRARY_GITHUB_TREE_URL}/${sourcePath}` : null); - const npmUrl = npmPackage ? `${NPM_PACKAGE_URL}${npmPackage}` : null; - const displayedCode = code ?? createImportCode(packageImports, packageName); - const displayedLanguage = code ? language : "ts"; - const markdownCodeBlock = displayedCode - ? `\`\`\`${displayedLanguage}\n${displayedCode}\n\`\`\`` - : null; - const hasDescription = Boolean(children); - const showStatusRow = variant || sourceUrl || npmUrl; - - const links = [ - sourceUrl ? { key: "github", href: sourceUrl, label: "GitHub" } : null, - npmUrl ? { key: "npm", href: npmUrl, label: "npm" } : null, - ].filter(Boolean); - - return React.createElement("div", { style: containerStyle }, [ - React.createElement( - "h1", - { key: "title" }, - title, - tagName - ? React.createElement( - "span", - { className: "sb-unstyled", style: { ...titleTagStyle, marginLeft: "0.25em" } }, - `(${tagName})`, - ) - : null, - ), - showStatusRow - ? React.createElement( - "div", - { style: statusContainerStyle, key: "status" }, - React.createElement( - "div", - { style: statusHeaderStyle }, - variant - ? React.createElement( - "div", - { style: statusInfoStyle }, - React.createElement("span", { style: labelStyle }, "Status:"), - React.createElement( - "span", - { style: badgeStyle(variant) }, - React.createElement("span", { - "aria-hidden": "true", - style: indicatorStyle(variant), - }), - variant.label, - ), - ) - : null, - ...links.map((link, index) => { - const isFirstWithoutBadge = !variant && index === 0; - return React.createElement( - "a", - { - className: "sb-unstyled", - href: link.href, - key: link.key, - rel: "noopener noreferrer", - style: isFirstWithoutBadge - ? { ...sourceLinkStyle, borderLeft: "none", paddingLeft: 0 } - : sourceLinkStyle, - target: "_blank", - }, - link.label, - ); - }), - ), - ) - : null, - hasDescription - ? React.createElement("div", { key: "description", style: descriptionStyle }, children) - : null, - markdownCodeBlock - ? React.createElement( - "div", - { key: "code", style: codeContainerStyle(hasDescription) }, - React.createElement(Markdown, null, markdownCodeBlock), - ) - : null, - ]); -} diff --git a/packages/component-library/src/docs/_components/token-browser.js b/packages/component-library/src/docs/_components/token-browser.js deleted file mode 100644 index 0c725ec31..000000000 --- a/packages/component-library/src/docs/_components/token-browser.js +++ /dev/null @@ -1,523 +0,0 @@ -import React, { useCallback, useEffect, useState } from "react"; -import lightTokens from "../../../../tokens/dictionaries/administration/light.tokens.json"; -import darkTokens from "../../../../tokens/dictionaries/administration/dark.tokens.json"; -import primitives from "../../../../tokens/dictionaries/foundation/primitives.tokens.json"; - -function flattenTokenData(obj, prefix = "") { - const descriptions = {}; - const values = {}; - for (const [k, v] of Object.entries(obj)) { - const key = prefix ? `${prefix}.${k}` : k; - const cssVar = "--" + key.replace(/\./g, "-"); - if (v && typeof v === "object" && "$value" in v) { - descriptions[cssVar] = v.$description || ""; - const raw = v.$value; - if (typeof raw === "string" && raw.startsWith("{") && raw.endsWith("}")) { - values[cssVar] = "--" + raw.slice(1, -1).replace(/\./g, "-"); - } - } else if (v && typeof v === "object") { - const nested = flattenTokenData(v, key); - Object.assign(descriptions, nested.descriptions); - Object.assign(values, nested.values); - } - } - return { descriptions, values }; -} - -const { descriptions: TOKEN_DESCRIPTIONS, values: TOKEN_VALUES_LIGHT } = - flattenTokenData(lightTokens); -const { values: TOKEN_VALUES_DARK } = flattenTokenData(darkTokens); - -// Collect CSS var names for all leaf tokens under a given object. -function collectTokenKeys(obj, prefix) { - const result = []; - for (const [k, v] of Object.entries(obj)) { - const key = `${prefix}-${k}`; - if (v && typeof v === "object" && "$value" in v) { - result.push(key); - } else if (v && typeof v === "object") { - result.push(...collectTokenKeys(v, key)); - } - } - return result; -} - -function capitalize(str) { - return str.charAt(0).toUpperCase() + str.slice(1); -} - -// Derived from the token JSON files — updates automatically when tokens change. -function buildTokenGroups() { - const groups = []; - - for (const [topKey, topVal] of Object.entries(lightTokens)) { - const prefix = `--${topKey}`; - - if (topKey === "color") { - for (const [subKey, subVal] of Object.entries(topVal)) { - groups.push({ - name: capitalize(subKey).replace(/-/g, " "), - tokens: collectTokenKeys(subVal, `${prefix}-${subKey}`), - }); - } - } else if (topKey === "font") { - for (const [subKey, subVal] of Object.entries(topVal)) { - const name = subKey === "line-height" ? "Line Height" : `Font ${capitalize(subKey)}`; - groups.push({ - name, - tokens: collectTokenKeys(subVal, `${prefix}-${subKey}`), - }); - } - } else { - groups.push({ - name: topKey.split("-").map(capitalize).join(" "), - tokens: collectTokenKeys(topVal, prefix), - }); - } - } - - // Scale tokens live in primitives, not in the semantic token files. - if (primitives?.scale?.size) { - groups.push({ - name: "Scale", - tokens: Object.keys(primitives.scale.size).map((k) => `--scale-size-${k}`), - }); - } - - return groups; -} - -const TOKEN_GROUPS = buildTokenGroups(); - -function previewType(token) { - if (token.startsWith("--color-")) return "color"; - if (token.startsWith("--border-radius-")) return "radius"; - if (token.startsWith("--scale-size-")) return "scale-size"; - if (token.startsWith("--font-size-")) return "font-size"; - if (token.startsWith("--font-weight-")) return "font-weight"; - if (token.startsWith("--font-family-")) return "font-family"; - if (token.startsWith("--font-line-height-")) return "line-height"; - return "text"; -} - -function ColorSwatch({ token, value, label }) { - const isTransparent = - value && (value.includes("rgba") || value.endsWith("00") || value.includes("0 0 0 0")); - return React.createElement( - "div", - { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 3 } }, - React.createElement("div", { - "data-theme": label === "dark" ? "dark" : undefined, - style: { - width: 28, - height: 28, - borderRadius: 4, - backgroundColor: `var(${token})`, - border: "1px solid rgba(0, 0, 0, 0.1)", - backgroundImage: isTransparent - ? "repeating-conic-gradient(#ccc 0% 25%, white 0% 50%) 0 0 / 8px 8px" - : undefined, - }, - }), - React.createElement( - "span", - { - style: { - fontSize: 9, - color: "var(--color-text-secondary-default)", - fontFamily: "var(--font-family-body, sans-serif)", - letterSpacing: "0.02em", - }, - }, - label, - ), - ); -} - -function Preview({ token, lightValue, darkValue, primitiveLightToken, primitiveDarkToken }) { - const type = previewType(token); - const size = 32; - - if (type === "color") { - return React.createElement( - "div", - { - style: { display: "flex", gap: 6, flexShrink: 0 }, - }, - React.createElement(ColorSwatch, { - token, - value: lightValue, - label: "light", - primitiveToken: primitiveLightToken, - }), - React.createElement(ColorSwatch, { - token, - value: darkValue, - label: "dark", - primitiveToken: primitiveDarkToken, - }), - ); - } - - if (type === "radius") { - const isRound = token.includes("round"); - return React.createElement("div", { - style: { - width: size, - height: size, - borderRadius: isRound ? "50%" : `var(${token})`, - backgroundColor: "var(--color-background-brand-default)", - border: "2px solid var(--color-border-brand-default)", - flexShrink: 0, - }, - }); - } - - if (type === "scale-size") { - return React.createElement( - "div", - { - style: { - width: 64, - height: size, - display: "flex", - alignItems: "center", - flexShrink: 0, - }, - }, - React.createElement("div", { - style: { - height: 6, - width: `var(${token})`, - maxWidth: 64, - minWidth: lightValue === "0rem" ? 2 : undefined, - backgroundColor: "var(--color-interaction-primary-default)", - borderRadius: 3, - }, - }), - ); - } - - if (type === "font-size") { - return React.createElement( - "div", - { - style: { - width: size, - height: size, - display: "flex", - alignItems: "center", - justifyContent: "center", - overflow: "hidden", - flexShrink: 0, - }, - }, - React.createElement( - "span", - { - style: { - fontSize: `var(${token})`, - lineHeight: 1, - fontWeight: 500, - color: "var(--color-text-primary-default)", - }, - }, - "Aa", - ), - ); - } - - if (type === "font-weight") { - return React.createElement( - "div", - { - style: { - width: size, - height: size, - display: "flex", - alignItems: "center", - justifyContent: "center", - flexShrink: 0, - }, - }, - React.createElement( - "span", - { - style: { - fontSize: 14, - fontWeight: `var(${token})`, - color: "var(--color-text-primary-default)", - }, - }, - "Aa", - ), - ); - } - - if (type === "font-family") { - return React.createElement( - "div", - { - style: { - width: size, - height: size, - display: "flex", - alignItems: "center", - justifyContent: "center", - flexShrink: 0, - }, - }, - React.createElement( - "span", - { - style: { - fontSize: 13, - fontFamily: `var(${token})`, - color: "var(--color-text-primary-default)", - }, - }, - "Aa", - ), - ); - } - - if (type === "line-height") { - const barStyle = { - height: 2, - width: size, - backgroundColor: "var(--color-interaction-primary-default)", - borderRadius: 1, - }; - return React.createElement( - "div", - { style: { width: size, flexShrink: 0 } }, - React.createElement("div", { style: { ...barStyle, marginBottom: `var(${token})` } }), - React.createElement("div", { style: barStyle }), - ); - } - - return React.createElement("div", { style: { width: size, height: size, flexShrink: 0 } }); -} - -function ValueBadge({ value }) { - return React.createElement( - "span", - { - style: { - fontFamily: "var(--font-family-body, monospace)", - fontSize: 11, - color: "var(--color-text-secondary-default)", - background: "var(--color-background-secondary-default)", - border: "1px solid var(--color-border-secondary-default)", - borderRadius: 4, - padding: "1px 6px", - whiteSpace: "nowrap", - }, - }, - value, - ); -} - -function TokenRow({ - token, - description, - lightValue, - darkValue, - primitiveLightToken, - primitiveDarkToken, - onCopy, - isLast, -}) { - const isColor = previewType(token) === "color"; - const displayLight = isColor ? primitiveLightToken || lightValue : lightValue; - const displayDark = isColor - ? primitiveDarkToken || darkValue - : lightValue !== darkValue && darkValue - ? darkValue - : null; - - return React.createElement( - "div", - { - style: { - display: "grid", - gridTemplateColumns: "auto 1fr auto", - gap: "0 16px", - alignItems: "center", - padding: "8px 16px", - borderBottom: isLast ? "none" : "1px solid var(--color-border-secondary-default)", - }, - }, - React.createElement(Preview, { - token, - lightValue, - darkValue, - primitiveLightToken, - primitiveDarkToken, - }), - React.createElement( - "div", - { style: { minWidth: 0 } }, - React.createElement( - "button", - { - onClick: () => onCopy(token), - onMouseEnter: (e) => { - e.currentTarget.style.textDecoration = "underline"; - }, - onMouseLeave: (e) => { - e.currentTarget.style.textDecoration = "none"; - }, - title: `Copy var(${token})`, - style: { - background: "none", - border: "none", - cursor: "pointer", - padding: 0, - fontFamily: "var(--font-family-body, monospace)", - fontSize: 14, - fontWeight: 500, - color: "var(--color-text-primary-default)", - textDecoration: "none", - display: "block", - textAlign: "left", - marginBottom: 4, - }, - }, - token, - ), - description - ? React.createElement( - "span", - { - style: { - fontSize: 12, - color: "var(--color-text-secondary-default)", - display: "block", - marginTop: 1, - }, - }, - description, - ) - : null, - ), - !isColor - ? React.createElement( - "div", - { - style: { display: "flex", flexDirection: "column", gap: 3, alignItems: "flex-end" }, - }, - displayLight ? React.createElement(ValueBadge, { value: displayLight }) : null, - displayDark ? React.createElement(ValueBadge, { value: displayDark }) : null, - ) - : React.createElement("div", { style: { width: 0 } }), - ); -} - -function TokenGroup({ name, tokens, lightValues, darkValues, onCopy }) { - return React.createElement( - "div", - { - style: { marginBottom: 40 }, - }, - React.createElement( - "div", - { - style: { - border: "1px solid #e2e3e9", - borderRadius: 8, - overflow: "hidden", - }, - }, - React.createElement( - "div", - { - style: { - fontFamily: "var(--font-family-body, sans-serif)", - fontSize: 16, - fontWeight: 600, - color: "var(--color-text-primary-default)", - padding: "0.75rem 1rem", - backgroundColor: "#fafbfe", - borderBottom: "1px solid #e2e3e9", - margin: 0, - }, - }, - name, - ), - (() => { - const visible = tokens.filter((t) => !TOKEN_DESCRIPTIONS[t]?.startsWith("Deprecated:")); - return visible.map((t, i) => - React.createElement(TokenRow, { - key: t, - token: t, - description: TOKEN_DESCRIPTIONS[t] || "", - lightValue: lightValues[t] || "", - darkValue: darkValues[t] || "", - primitiveLightToken: TOKEN_VALUES_LIGHT[t] || "", - primitiveDarkToken: TOKEN_VALUES_DARK[t] || "", - onCopy, - isLast: i === visible.length - 1, - }), - ); - })(), - ), - ); -} - -function resolveValues() { - const lightStyle = getComputedStyle(document.documentElement); - - const darkEl = document.createElement("div"); - darkEl.setAttribute("data-theme", "dark"); - darkEl.style.cssText = "position:absolute;visibility:hidden;pointer-events:none"; - document.body.appendChild(darkEl); - const darkStyle = getComputedStyle(darkEl); - - const light = {}; - const dark = {}; - - TOKEN_GROUPS.forEach(({ tokens }) => { - tokens.forEach((token) => { - light[token] = lightStyle.getPropertyValue(token).trim(); - dark[token] = darkStyle.getPropertyValue(token).trim(); - }); - }); - - document.body.removeChild(darkEl); - return { light, dark }; -} - -function notify(message) { - document.dispatchEvent(new CustomEvent("meteor-docs:snackbar", { detail: { message } })); -} - -export default function TokenBrowser() { - const [values, setValues] = useState({ light: {}, dark: {} }); - - useEffect(() => { - setValues(resolveValues()); - }, []); - - const handleCopy = useCallback((token) => { - navigator.clipboard - .writeText(`var(${token})`) - .then(() => notify(`Copied var(${token})`)) - .catch(() => {}); - }, []); - - return React.createElement( - "div", - { style: { marginTop: 24 } }, - TOKEN_GROUPS.map((group) => - React.createElement(TokenGroup, { - key: group.name, - name: group.name, - tokens: group.tokens, - lightValues: values.light, - darkValues: values.dark, - onCopy: handleCopy, - }), - ), - ); -} diff --git a/packages/component-library/src/docs/_components/typography-scale.js b/packages/component-library/src/docs/_components/typography-scale.js deleted file mode 100644 index 780c618b3..000000000 --- a/packages/component-library/src/docs/_components/typography-scale.js +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useEffect, useState } from "react"; -import lightTokens from "../../../../tokens/dictionaries/administration/light.tokens.json"; - -// Derived from the token JSON — updates automatically when the token file changes. -const SIZES = Object.keys(lightTokens.font.size).map((label) => ({ - label, - token: `--font-size-${label}`, - lineHeight: `--font-line-height-${label}`, -})); - -const WEIGHTS = Object.keys(lightTokens.font.weight).map((label) => ({ - token: `--font-weight-${label}`, - label: label.charAt(0).toUpperCase() + label.slice(1), -})); - -function resolveToken(token) { - return getComputedStyle(document.documentElement).getPropertyValue(token).trim(); -} - -const wrapperStyle = { - margin: "var(--scale-size-24) 0", - border: "1px solid var(--color-border-secondary-default)", - borderRadius: 8, - overflow: "hidden", -}; - -const rowStyle = (last) => ({ - display: "grid", - gridTemplateColumns: "10rem 1fr", - alignItems: "baseline", - gap: "var(--scale-size-16)", - padding: "var(--scale-size-12) var(--scale-size-16)", - borderBottom: last ? "none" : "1px solid var(--color-border-secondary-default)", - backgroundColor: "var(--color-elevation-surface-default)", -}); - -const metaStyle = { - fontFamily: "var(--font-family-body)", - fontSize: "var(--font-size-xs)", - lineHeight: "var(--font-line-height-xs)", - color: "var(--color-text-secondary-default)", - display: "flex", - flexDirection: "column", - gap: "var(--scale-size-2)", -}; - -const tokenStyle = { - fontFamily: "monospace", - fontSize: "var(--font-size-2xs)", - color: "var(--color-text-primary-default)", -}; - -const headingStyle = { - fontFamily: "var(--font-family-body)", - fontSize: 16, - fontWeight: 600, - color: "var(--color-text-primary-default)", - padding: "0.75rem 1rem", - backgroundColor: "#fafbfe", - borderBottom: "1px solid #e2e3e9", - margin: 0, -}; - -function SectionHeader({ children }) { - return React.createElement("div", { style: headingStyle }, children); -} - -function SizeRow({ label, token, lineHeight, resolvedPx, last }) { - return React.createElement("div", { style: rowStyle(last) }, [ - React.createElement("div", { key: "meta", style: metaStyle }, [ - React.createElement( - "span", - { - key: "label", - style: { - ...tokenStyle, - fontFamily: "var(--font-family-body)", - fontWeight: "var(--font-weight-semibold)", - fontSize: "var(--font-size-s)", - color: "var(--color-text-primary-default)", - }, - }, - label, - ), - React.createElement("span", { key: "px" }, resolvedPx), - ]), - React.createElement( - "span", - { - key: "sample", - style: { - fontFamily: "var(--font-family-body)", - fontSize: `var(${token})`, - lineHeight: `var(${lineHeight})`, - fontWeight: "var(--font-weight-regular)", - color: "var(--color-text-primary-default)", - }, - }, - "The quick brown fox jumps over the lazy dog", - ), - ]); -} - -function WeightRow({ token, label, resolvedValue, last }) { - return React.createElement("div", { style: rowStyle(last) }, [ - React.createElement("div", { key: "meta", style: metaStyle }, [ - React.createElement( - "span", - { - key: "label", - style: { - ...tokenStyle, - fontFamily: "var(--font-family-body)", - fontWeight: "var(--font-weight-semibold)", - fontSize: "var(--font-size-s)", - color: "var(--color-text-primary-default)", - }, - }, - label, - ), - React.createElement("span", { key: "value" }, resolvedValue), - ]), - React.createElement( - "span", - { - key: "sample", - style: { - fontFamily: "var(--font-family-body)", - fontSize: "var(--font-size-m)", - lineHeight: "var(--font-line-height-m)", - fontWeight: `var(${token})`, - color: "var(--color-text-primary-default)", - }, - }, - "The quick brown fox jumps over the lazy dog", - ), - ]); -} - -export default function TypographyScale() { - const [resolved, setResolved] = useState({}); - - useEffect(() => { - const values = {}; - for (const { token } of SIZES) { - values[token] = resolveToken(token); - } - for (const { token } of WEIGHTS) { - values[token] = resolveToken(token); - } - setResolved(values); - }, []); - - return React.createElement("div", null, [ - React.createElement("div", { key: "sizes", style: wrapperStyle }, [ - React.createElement(SectionHeader, { key: "header" }, "Size scale"), - ...SIZES.map((s, i) => - React.createElement(SizeRow, { - key: s.token, - ...s, - resolvedPx: resolved[s.token] ?? "", - last: i === SIZES.length - 1, - }), - ), - ]), - React.createElement( - "div", - { key: "weights", style: { ...wrapperStyle, marginTop: "var(--scale-size-16)" } }, - [ - React.createElement(SectionHeader, { key: "header" }, "Weights"), - ...WEIGHTS.map((w, i) => - React.createElement(WeightRow, { - key: w.token, - ...w, - resolvedValue: resolved[w.token] ?? "", - last: i === WEIGHTS.length - 1, - }), - ), - ], - ), - ]); -} diff --git a/packages/component-library/src/docs/foundations/accessibility.mdx b/packages/component-library/src/docs/foundations/accessibility.mdx deleted file mode 100644 index 3ddf23492..000000000 --- a/packages/component-library/src/docs/foundations/accessibility.mdx +++ /dev/null @@ -1,88 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; - - - - - -An accessible app means people of all abilities can interact with, understand, and navigate it. At Shopware, we are committed to creating inclusive and barrier-free experiences for our merchants and their customers. - -Meteor components ship with built-in accessibility features: keyboard support, sensible ARIA usage, and WCAG 2.1 AA compliance. However, you still need to review your patterns, content, and interactions so your app is accessible end-to-end. - -## What Meteor provides out of the box - -- **Keyboard navigation**: All interactive components are fully operable via keyboard -- **ARIA attributes**: Components include appropriate roles, states, and properties -- **Focus management**: Focus is managed correctly in modals, dropdowns, and other overlays -- **Color contrast**: [Design tokens](?path=/docs/foundations-tokens-overview--docs) meet WCAG 2.1 AA contrast ratios for text and UI elements -- **Dark mode**: Both light and dark themes maintain accessible contrast levels -- **Screen reader support**: Components are tested with common assistive technologies - -## What you still need to consider - -### Keep experiences simple - -Respect people's time by making apps that are easy to use and navigate. - -- Avoid complex flows where simpler alternatives exist -- Use consistent patterns across pages so users don't have to relearn interactions -- Use concise, plain language. Aim for a reading level appropriate to your audience - -### Be inclusive - -- Use inclusive language throughout your interface -- Don't make assumptions about people's abilities -- Avoid jargon, metaphors, and non-literal phrases - -### Provide text alternatives - -Text can be seen, heard via text-to-speech, and touched with a braille reader. - -- Write clear and concise labels and alt text for all meaningful images and icons -- Use the `decorative` prop on [`mt-icon`](?path=/docs/components-icon--docs) for purely visual icons so they stay out of the accessibility tree -- Include transcripts or captions for any video content - -### Never rely on color alone - -Some people cannot perceive color, and others perceive it differently. - -- Always pair color with a secondary indicator such as an icon, label, or pattern -- Use semantic icon tokens (`critical`, `positive`, `attention`) to reinforce meaning alongside color - -### Use semantic HTML - -Semantic HTML describes the meaning of elements to browsers and assistive technologies. - -- Use landmark elements (`header`, `nav`, `main`, `footer`) to structure your pages -- Avoid using `div` or `span` as interactive elements -- Make sure headings follow a logical hierarchy - -### Give people control - -- Ensure your layouts reflow correctly across all screen sizes -- Respect the user's reduced motion settings with `prefers-reduced-motion` -- Allow sufficient time for time-sensitive interactions - -### Test broadly - -- Test with keyboard-only navigation -- Test with screen readers (VoiceOver, NVDA, or JAWS) -- Test with real users with disabilities when possible - -## Recommended tools - -- **[WAVE](https://wave.webaim.org/)**: Visual feedback tool that overlays accessibility information directly on the page. Useful for quickly spotting heading hierarchy and contrast issues. -- **[Lighthouse](https://developer.chrome.com/docs/lighthouse/accessibility/)**: Built into Chrome DevTools. Useful for a quick overall accessibility score as part of a broader performance audit. - -## Pre-ship checklist - -Run through these before shipping any new feature or page: - -- All interactive elements are reachable and operable by keyboard alone -- No keyboard focus traps exist outside of intended modal or overlay patterns -- All images and meaningful icons have descriptive labels or alt text -- Color is never the only indicator of state or meaning -- Text and UI elements meet WCAG 2.1 AA contrast ratios -- Heading levels follow a logical hierarchy without skipping levels -- Form fields have visible labels connected via `for`/`id` or `aria-labelledby` -- Animations and transitions respect `prefers-reduced-motion` diff --git a/packages/component-library/src/docs/foundations/components.mdx b/packages/component-library/src/docs/foundations/components.mdx deleted file mode 100644 index 7c068b953..000000000 --- a/packages/component-library/src/docs/foundations/components.mdx +++ /dev/null @@ -1,174 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; -import DoDont from "../_components/do-dont.js"; - - - - - -This page covers how Meteor components are structured, named, and built. Follow these conventions when contributing a new component or updating an existing one. - -## Naming and file structure - -Every component uses the `mt-` prefix and kebab-case. The component name, its folder, and its primary file all share the same name. - -``` -mt-button/ - mt-button.vue - mt-button.stories.ts - mt-button.interactive.stories.ts - mt-button.mdx - mt-button.spec.ts -``` - -Add `sub-components/` and `composables/` only when the component uses the compound pattern. Do not create them preemptively. - -## Props - -- Props use camelCase. Plural nouns for arrays (`items`), singular for objects (`item`), `num` prefix or `Count`/`Index` suffix for numbers (`numColumns`, `activeIndex`). -- Boolean props use plain adjectives with no prefix: `loading`, `open`, `disabled`. Avoid inverted booleans: `showIcon` not `hideIcon`. -- Avoid native HTML attribute names. Use `heading` instead of `title`. `disabled`, `required`, and `readonly` are intentional exceptions. -- Name props from the component's perspective, not the caller's: `hasSubmitButton` not `hasSubmitPermission`. -- Never use `"default"` as a named prop value. Set the default via `defineProps`'s `default` option. -- Prefer slots over props for content that may contain markup. - -Use the standard prop names below consistently. Never substitute alternatives like `type`, `kind`, or `style`. - -| Prop | Purpose | Values / Type | -| ------------- | ------------------------------------------- | ---------------------------------------------------------- | -| `variant` | Semantic intent / feedback tone | `neutral` `info` `attention` `critical` `positive` | -| `appearance` | Visual hierarchy for interactive components | `primary` `secondary` `tertiary` | -| `shape` | Geometric shape variant | `circle` `square` `pill` | -| `size` | Dimension | `2xs` `xs` `s` `m` `l` `xl` `2xl` `3xl` | -| `width` | Explicit width constraint | `2xs` `xs` `s` `m` `l` `xl` `2xl` `3xl` `full` | -| `height` | Explicit height constraint | `2xs` `xs` `s` `m` `l` `xl` `2xl` `3xl` `full` | -| `placement` | Floating element anchor position | `top` `bottom` `left` `right` + `-start` / `-end` variants | -| `modelValue` | Primary bound value, enables `v-model` | any | -| `label` | Visible label for a field or control | string | -| `placeholder` | Input placeholder text | string | -| `helpText` | Helper text below a field | string | -| `name` | Form field name attribute | string | -| `heading` | Heading of a card, modal, or section | string | -| `subtitle` | Secondary heading below `heading` | string | -| `error` | Validation error for a field | `{ code?: number; detail: string }` | -| `disabled` | Prevents interaction | boolean | -| `required` | Marks a field as required | boolean | -| `readonly` | Allows reading but not editing | boolean | -| `loading` | Shows a loading state | boolean | -| `open` | Controls open/expanded state | boolean | -| `closable` | Shows a dismiss / close control | boolean | -| `as` | Polymorphic root element override | `string \| Component` | - -## Slots - -All slot names use kebab-case without exception. Use logical direction names that are RTL-safe rather than physical ones (`start`/`end` over `left`/`right`). - -Standard positional slot names: - -| Slot | Purpose | -| -------------- | ------------------------------------------------------------------------------------------ | -| `default` | Primary content area. Always provide unless the component has none | -| `leading` | Icon or element flanking the component's primary content from the outside | -| `trailing` | Icon or element flanking the component's primary content from the outside, on the end side | -| `prefix` | Content rendered inside an input field boundary at the start (e.g. currency symbol) | -| `suffix` | Content rendered inside an input field boundary at the end (e.g. unit label) | -| `header-start` | Left region of a header row | -| `header-end` | Right region of a header row | -| `trigger` | Element that opens an overlay or compound component. Receives `{ open }` scoped state | -| `footer` | Bottom region of a card, modal, or panel | - -Use scoped slots to expose internal state that consumers may need to react to: - -```vue - -``` - -Mark internal-only slots with `@private`: - -```vue - - -``` - -## Events - -Events use kebab-case. Two-way binding follows Vue's `update:propName` pattern. Custom events are verbs or verb-object phrases. - -Always declare every event in `defineEmits`. Undeclared `$emit` calls are a maintenance hazard and break TypeScript consumers. - -```ts -const emit = defineEmits<{ - "update:modelValue": [value: string]; - "update:open": [value: boolean]; - "item-activate": [item: Item]; - focus: [event: FocusEvent]; - blur: [event: FocusEvent]; -}>(); -``` - -Pair every two-way prop with a matching `update:propName` event. Never use a `change` event as a substitute for `update:modelValue`. - -Never re-emit raw DOM events directly. The one permitted exception is `focus` and `blur` on form field components: consumers need the `FocusEvent` object for custom validation timing and there is no semantic equivalent. - -## CSS - -Classes follow BEM: `mt-{component}__element` for sub-parts and `mt-{component}--modifier` for variants and states. - -```css -.mt-button { -} -.mt-button__content { -} -.mt-button--primary { -} -.mt-button--disabled { -} -``` - -Always use design tokens for colors, spacing, typography, and border radii. Never hardcode values that a token covers. This ensures components respond correctly to theme changes. - -## Compound components - -Use the compound component pattern when a component requires coordinated sub-parts that consumers may want to compose independently. Sub-components are named `mt-{parent}-{role}`. Shared state lives in a composable under `composables/` and is distributed via `provide`/`inject`. - -``` -mt-modal/ - mt-modal.vue - sub-components/ - mt-modal-trigger.vue - mt-modal-close.vue - mt-modal-action.vue - composables/ - useModalContext.ts -``` - -Export all public sub-components from the package index alongside the parent. - - - -## Accessibility - -Every interactive component must be keyboard-navigable and work with a screen reader out of the box. Use semantic HTML elements as the base. Apply ARIA attributes only when native semantics are insufficient. - -Form controls must always render a visible, associated label. Loading and disabled states must be communicated via the appropriate HTML attributes (`aria-busy`, `disabled`) so assistive technology can announce them without relying on visual cues alone. - -For detailed accessibility requirements and patterns, see the [Accessibility](?path=/docs/foundations-accessibility--docs) page. - -## Documentation - -Every component ships with a `.mdx` file and a `.stories.ts` file. The MDX file describes what the component is for, when to use it, and any important constraints. Stories cover all significant variants and states, and at least one interactive story exercises the primary user flow. Follow the conventions in [STORYBOOK_DOCS_STANDARD.md](https://github.com/shopware/meteor/blob/main/packages/component-library/STORYBOOK_DOCS_STANDARD.md) when writing component documentation. - -## Adding new components - -Meteor does not cover every component every team will ever need. When you hit a gap, follow this process. - -**1. Build it locally first.** Build the component in your own app. Local implementations ship faster and let you validate the design and API in a real product context before committing to a shared interface. - -**2. Inform the Meteor team.** Once the component is stable and in use, let the Meteor team know via a GitHub issue or the dedicated Slack channel. Share what the component does, where it is used, and any design decisions you made along the way. - -**3. The Meteor team evaluates the fit.** The team will assess whether the component belongs in Meteor or should stay local. Not every component needs to be global. Some are too product-specific, too narrow in scope, or not yet stable enough to expose as a shared API. - -**4. Promotion from local to global.** If the same component surfaces independently across multiple products, that is a strong signal it belongs in Meteor. The team tracks these patterns and will take ownership of the migration when the time is right. Your local implementation becomes the foundation for the global version. diff --git a/packages/component-library/src/docs/foundations/content/ai-interaction.mdx b/packages/component-library/src/docs/foundations/content/ai-interaction.mdx deleted file mode 100644 index 72f238b80..000000000 --- a/packages/component-library/src/docs/foundations/content/ai-interaction.mdx +++ /dev/null @@ -1,53 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; - - - - - -Designing for human-AI interaction means prioritizing clear communication, appropriate transparency, and merchant control at every stage of the interaction. - -## Before the interaction - -Set expectations before a merchant engages. Explain what the feature does in terms of outcomes, not technology. Be upfront about what data it uses. - - - -## During the interaction - -Always acknowledge that input was received and processing is underway. Never leave a blank or frozen state. - -Merchants must be able to review output before it takes effect. Every suggestion should be previewable, editable, and dismissable. - - - -**AI-generated content should never replace human-generated content without review or approval.** - -## When something goes wrong - -Acknowledge errors directly. Give the merchant a clear path to correct, dismiss, or retry from a clean state. - - - -## Writing for AI - -Write in plain, conversational language. Avoid technical jargon about the underlying model or infrastructure. Speak to the merchant's goals, not the system's mechanics. - -Use active voice. It makes AI responses feel direct and accountable. - - - -Never oversell what a feature can do. Be honest about limitations upfront rather than letting the merchant discover them through failure. diff --git a/packages/component-library/src/docs/foundations/content/glossary.mdx b/packages/component-library/src/docs/foundations/content/glossary.mdx deleted file mode 100644 index 3972bf6d5..000000000 --- a/packages/component-library/src/docs/foundations/content/glossary.mdx +++ /dev/null @@ -1,101 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; - - - - - -Use these terms consistently across all English and German copy. When a term appears here, always use this spelling and capitalisation. Do not invent alternatives. - -## Products and features - -| English | German | -| ------------------------- | ------------------------- | -| 3D & AR Commerce | 3D & AR Commerce | -| AI | KI | -| AI Copilot | AI Copilot | -| B2B Components | B2B Components | -| Community Edition | Community Edition | -| Custom fields | Zusatzfelder | -| Customer groups | Kundengruppen | -| Digital Sales Rooms | Digital Sales Rooms | -| Dynamic product groups | Dynamische Produktgruppen | -| Email templates | E-Mail-Templates | -| Essential characteristics | Wesentliche Merkmale | -| Extensions | Erweiterungen | -| Flow Builder | Flow Builder | -| Import / Export | Import/Export | -| Integrations | Integrationen | -| Language pack | Sprachpaket | -| Measurement system | Maßeinheitensystem | -| Migration Assistant | Migrations-Assistent | -| Number ranges | Nummernkreise | -| Payment methods | Zahlungsarten | -| Promotion | Rabattaktion | -| Rule Builder | Rule Builder | -| Sales Channel | Verkaufskanal | -| Salutations | Anreden | -| Scale units | Produkteinheiten | -| Shipping methods | Versandarten | -| Shopping Experiences | Erlebniswelten | -| Shopware Account | Shopware Account | -| Shopware Admin | Shopware Admin | -| Shopware Administration | Shopware Administration | -| Shopware Analytics | Shopware Analytics | -| Shopware Beyond | Shopware Beyond | -| Shopware Evolve | Shopware Evolve | -| Shopware Intelligence | Shopware Intelligence | -| Shopware Nexus | Shopware Nexus | -| Shopware PaaS | Shopware PaaS | -| Shopware Payments | Shopware Payments | -| Shopware Plans | Shopware Pläne | -| Shopware Rise | Shopware Rise | -| Shopware Services | Shopware Services | -| Snippets | Textbausteine | -| Spatial Commerce | Spatial Commerce | -| Subscriptions | Abonnements | -| Tax | Steuern | -| Users & permissions | Benutzer & Rechte | - -## User interface - -| English | German | -| ---------- | ---------------- | -| Activate | Aktivieren | -| Add | Hinzufügen | -| Apply | Übernehmen | -| Archive | Archivieren | -| Back | Zurück | -| Cancel | Abbrechen | -| Close | Schließen | -| Confirm | Bestätigen | -| Continue | Fortfahren | -| Create | Erstellen | -| Deactivate | Deaktivieren | -| Delete | Löschen | -| Discard | Verwerfen | -| Download | Herunterladen | -| Duplicate | Duplizieren | -| Edit | Bearbeiten | -| Filter | Filtern | -| Filters | Filter | -| Learn more | Mehr erfahren | -| Log in | Anmelden | -| Login | Anmeldung | -| Next | Weiter | -| Open | Öffnen | -| Overview | Übersicht | -| Preview | Vorschau | -| Publish | Veröffentlichen | -| Remove | Entfernen | -| Required | Pflichtfeld | -| Reset | Zurücksetzen | -| Restore | Wiederherstellen | -| Save | Speichern | -| Select all | Alle auswählen | -| Settings | Einstellungen | -| Sign up | Registrieren | -| Signup | Registrierung | -| Sort | Sortieren | -| Upload | Hochladen | -| View | Ansehen | diff --git a/packages/component-library/src/docs/foundations/content/messaging.mdx b/packages/component-library/src/docs/foundations/content/messaging.mdx deleted file mode 100644 index f1413d07e..000000000 --- a/packages/component-library/src/docs/foundations/content/messaging.mdx +++ /dev/null @@ -1,114 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; - - - - - -Good messages tell users what happened, why it matters, and what to do next. Every message in products using Meteor should be clear, honest, and respectful of the user's time. - -## Error messages - -Error messages appear after something has gone wrong. Tell users what failed and how to recover, not to apologise or assign blame. Lead with the most likely fix. Avoid "sorry", "please", and softeners like "oops": they trivialise real problems and undermine trust. - - - - - -## Form validation - -Validation messages appear inline, next to the field that failed. They are the most-read copy in any form, so precision matters. - -Write validation messages that tell the user what went wrong and what to do about it. Avoid blaming the user or restating what the label already says. - - - - - -For `helpText` (the supporting text shown below a field before any error), explain the constraint upfront so users do not encounter a validation error in the first place. - - - -## Success messages - -Success messages confirm that an action completed. Confirm the outcome and get out of the way. Avoid exclamation marks and "Success!" as a label. Reserve stronger celebration for significant milestones, not routine actions. - - - - - -## Warning messages - -Warning messages appear before a risky or irreversible action. They prepare users, not punish them. Be specific about what will happen and whether it can be undone. Stay calm: avoid exclamation marks and "Warning:" prefixes. - - - - - -## Info messages - -Info messages provide context that helps users understand the current state. They do not require action and should not interrupt the workflow. Get to the point and stop: do not pad with filler phrases. - - - -## Empty states - -Use the [Empty State](?path=/docs/components-empty-state--docs) component wherever possible. - -Empty states appear when a list, table, or section has no content yet. There are two distinct situations: - -- **Blank slate:** the user has not yet added anything. Introduce the feature and prompt the first action. -- **Cleared state:** the user completed a task or filtered out all results. Acknowledge the outcome and suggest what to do next. - -Write the heading as a direct observation, not an apology. Tell users what they can do from here. - - - - - -## Loading states - -Loading messages should be brief. The user already knows the app is working. - -Use a label only when it adds context about what is happening. For generic operations, no label is better than a vague one. - - - - diff --git a/packages/component-library/src/docs/foundations/content/wording.mdx b/packages/component-library/src/docs/foundations/content/wording.mdx deleted file mode 100644 index 75d7b8593..000000000 --- a/packages/component-library/src/docs/foundations/content/wording.mdx +++ /dev/null @@ -1,126 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; - - - - - -At Shopware, tone of voice plays a central role in our brand identity. Through a consistent and authentic tone, we aim to resonate with our users, establish trust, and create memorable experiences that reflect our commitment to innovation, reliability, and customer-centricity. - -## Writing goals - -**Empower**: Help people understand Shopware by using language that informs them and encourages them to make the most of our products. - -**Respect**: Treat readers with the respect they deserve. Put yourself in their shoes and don't patronize them. Be considerate and inclusive. - -**Educate**: Tell readers what they need to know, not just what we want to say. Give them the exact information they need, along with opportunities to learn more. - -**Engage**: Write to engage the reader, capturing their interest with relevant content and a conversational tone. - -## Our approach - -**Clear**: Understand the topic you're writing about. Use simple words and sentences. - -**Useful**: Before you start writing, ask yourself: What purpose does this serve? Who is going to read it? What do they need to know? - -**Friendly**: Write like a human. All of our content, from website copy to system alerts, should be warm and human. - -**Appropriate**: Adapt your tone depending on who you're writing to and what you're writing about. - -## Active voice - -You should (almost) always write in the active voice. In active voice, the subject does the action. In passive voice, the action is done to the subject. - - - - - -> **Tip:** Words like "was" and "by" often indicate passive voice. Scan for these and rework sentences where they appear. - -When to use passive voice: - -- To avoid referring to yourself or Shopware, for example, "Invoices are created monthly and emailed to you" avoids an unnecessary Shopware subject -- To make it clear you didn't personally take an action -- When the object is more important than the subject - -## Inclusive language - -We champion **people-first language**: keep the individual as the most important part of the sentiment and don't concentrate on characteristics like gender, sexual orientation, religion, or ability unless it's relevant. - -### Race and ethnicity - - - - - -### Gender - - - - - - - - - -### Sexual orientation and gender identity - - - -## Abbreviations and acronyms - -**Write in plain language.** If there's a chance your reader won't recognize an abbreviation or acronym, spell it out the first time you mention it. Then use the short version for all other references. - -> **Tip:** If the abbreviation is well known (like API or HTML), use it directly without spelling it out. - - - -## Capitalisation - -Shopware feature names are capitalised in every context. They need to stand out whenever they are mentioned. - -Examples: Rule Builder, Sales Channel, Flow Builder, B2B Components. - -Capital letters are used at the beginning of every sentence. Subsequent words within a sentence are generally not capitalised unless they are proper nouns or feature names. - -## Buttons - -Button labels should be as short as possible. Use a verb that describes the action. - - - -If the context alone does not make the action clear, add a noun and, if necessary, a preposition. - - - - - -Do not use articles. - -## Addressing users - -In English, use "you" and "your" for all audiences. - -In German, use the informal address for users. In the Shopware Admin, Du, Dich, Dir, and Dein are capitalised as a sign of respect, even though standard German grammar does not require it. - -Example: Speichere das Produkt, bevor Du die Seite verlässt. - -## Glossary - -For a full reference of approved English and German terms, see the [Glossary](?path=/docs/foundations-content-glossary--docs) page. diff --git a/packages/component-library/src/docs/foundations/design-principles.mdx b/packages/component-library/src/docs/foundations/design-principles.mdx deleted file mode 100644 index dc5683afe..000000000 --- a/packages/component-library/src/docs/foundations/design-principles.mdx +++ /dev/null @@ -1,52 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; - - - - - -Our Product Design Principles reflect how we think about design: **Building the right thing; building the thing right.** - -These principles are the foundation of Shopware's products and services, whether you work at Shopware or develop apps for the Shopware ecosystem. They are not a checklist. They are a shared lens for making decisions when requirements are ambiguous, tradeoffs are real, and speed matters. - -## Accessibility and inclusivity - -Design experiences that are accessible to users of all abilities and backgrounds, fostering inclusivity and equal opportunities for engagement. - -Accessibility is not a post-launch concern. It shapes interaction patterns, color contrast, keyboard behavior, and copy from the start. We follow WCAG 2.1 AA as a baseline and consider a broad range of users: those navigating by keyboard or screen reader, those on low-bandwidth connections, and those in high-pressure, task-dense work environments like a merchant's back office. See the [Accessibility](?path=/docs/foundations-accessibility--docs) guidelines for practical implementation guidance. - -## Data-informed decision making - -Leverage data, user research, analytics, and feedback to drive informed design decisions. Embrace the iterative nature of design and be open to evolving quickly with new learnings. - -Good intent is not enough. We validate assumptions with real usage data, usability sessions, and qualitative feedback. Decisions that cannot be tested should at least be clearly reasoned. When new evidence conflicts with a prior decision, the evidence wins. - -## Sticky merchant and shopper experiences - -Create engaging experiences that foster long-term relationships with users. Focus on seamless onboarding, intuitive interfaces, and powerful tools that drive engagement and conversions. - -Shopware products are used under commercial pressure. Merchants need to act quickly and confidently; shoppers need a path to purchase that feels effortless. Design should reduce the gap between a user's goal and their ability to achieve it, and reward sustained use with increasing efficiency. - -## Reduce friction, aim for simplicity - -Strive for simplicity and clarity, eliminating unnecessary complexity and reducing friction to create intuitive and efficient interfaces. Minimize distractions and provide empowering UX writing. - -Simplicity is not the absence of features. It is the absence of unnecessary obstacles. Every field, label, modal, and confirmation step has a cost. We earn complexity only when it serves an explicit user need. When in doubt, cut. - -## Streamlined and consistent experiences - -Deliver streamlined and consistent experiences by utilizing our design system. Reduce cognitive load by maintaining consistency in layouts, design elements, typography, and interactions. - -Consistency lowers the cost of learning. When the same interaction pattern appears in different parts of the product, users only have to learn it once. When the same visual token is used across components, the interface feels unified without requiring explicit coordination. - -## Ethical design practices - -Uphold ethical design principles: respect user privacy, promote transparency, and avoid manipulative techniques that compromise user trust. Build products that put people and the planet first. - -Trust is slow to build and fast to lose. We do not use dark patterns, deceptive defaults, or urgency mechanisms that exploit users. We are transparent about what data is collected, how it is used, and what actions are irreversible. We design for the long-term relationship, not the short-term conversion. The [Wording](?path=/docs/foundations-content-wording--docs) guidelines cover inclusive language and tone of voice that support these principles in copy. - -## How these principles shape Meteor - -Meteor is not just a component library. It is a set of decisions encoded in code. Every API choice, every default value, every token name reflects at least one of these principles. When you use a Meteor component, you inherit those decisions. When you override them, you take on the responsibility of honoring the same principles in your own implementation. - -If a design decision ever feels hard to justify, returning to these principles is a reliable starting point. diff --git a/packages/component-library/src/docs/foundations/icons.mdx b/packages/component-library/src/docs/foundations/icons.mdx deleted file mode 100644 index bbb061e66..000000000 --- a/packages/component-library/src/docs/foundations/icons.mdx +++ /dev/null @@ -1,111 +0,0 @@ -import { Canvas, Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; -import DoDont from "../_components/do-dont.js"; -import * as IconStories from "../../components/mt-icon/mt-icon.stories"; - - - - - -Meteor icons follow a minimal, expressive style aligned with Shopware's product language. The set is built for consistency, clarity, and legibility across all sizes and themes. For technical usage, see the [Icon component](?path=/docs/components-icon--docs) documentation. - -## Philosophy - -Icons in Meteor are functional first. They exist to support actions, communicate state, and aid navigation. Not to decorate. Every icon in the kit serves a clear interface purpose. - -The style is intentionally restrained: consistent stroke weights, unified corner radii, and optical sizing that keeps icons readable at small sizes. This restraint makes the set cohesive and ensures icons never compete with the content they support. - -## All icons - - - -## Common usages - -These examples demonstrate typical icon usage in the Shopware Administration and other Shopware products that use Meteor, where each icon has a clearly defined meaning. - - - -## Regular and solid variants - -Most icons come in two variants. Use them consistently within similar contexts. Mixing variants arbitrarily inside the same control group creates visual inconsistency. - -**Regular** is the default for standard interface actions and supporting UI. Use it for navigation, actions, and inline indicators. - -**Solid** works better for very small sizes and for larger decorative or prominent icon placements where a heavier fill reads better. - - - -## Sizing - -Meteor icons are designed for a small set of sizes. Staying within this set keeps the UI consistent across screens. - -- **24px**: Standalone icons, empty states, prominent surfaces -- **20px**: Standard actions, headers, supporting icons in regular-density UI -- **16px**: Inline controls, field affordances, compact supporting UI -- **14px or smaller**: Dense UI only, where the icon is clearly secondary - - - - - -## Color - -Always use semantic icon tokens so they adapt correctly to light and dark themes and to interactive states. - -- `--color-icon-primary-default` for the standard icon color -- `--color-icon-secondary-default` when the icon should attract less attention than surrounding content -- Semantic variants such as `brand`, `attention`, `critical`, `positive`, or `accent` when the icon color carries a contextual meaning - -See the [Tokens](?path=/docs/foundations-tokens-overview--docs) page for the full list of available icon color tokens. - - - - - -## Icons and meaning - -Icons rarely stand alone. Pair them with a visible label or place them inside a control that already communicates its purpose. When an icon does carry meaning on its own (a close button, a status indicator), make sure that meaning is also available through visible text or an accessible label on the parent control. - - - -## Adding icons to the kit - -New icons require sign-off from both design and engineering before they are added. The process ensures every new icon is genuinely needed, fits the visual style, and arrives in the codebase ready to use. - -### Starting the work - -New icons always start in Figma. The designer draws the icon and adds it to the Meteor icon library file before any code is written. This keeps the source of truth in design and makes the review step natural: reviewers can see the icon in context alongside the rest of the set. - -Open the discussion early by sharing the Figma frame in the Meteor Slack channel, leaving a comment on the icon library file, or opening a GitHub issue. Early visibility prevents duplicate work and gives engineering a chance to flag any technical constraints before design is finalized. - -### What to cover in the proposal - -- What the icon represents and the interface contexts where it will appear -- Whether a regular variant, a solid variant, or both are needed -- Whether an existing icon could cover the need with a different usage convention - -### Naming - -Icons follow the `regular-name` and `solid-name` prefix convention. The name after the prefix should describe what the icon depicts, not where it is used. Keep names short, lowercase, and hyphenated. - -### Review and sign-off - -Both the design team and the Meteor engineering team review the proposal. The review checks that the new icon is consistent with the existing visual style, that it is not a duplicate of something already in the kit, and that the name follows the naming convention. - -### From Figma to code - -Once approved, the icon lives in the Figma library. From there a custom sync script exports the icon assets and packages them into the icon kit. The Meteor team owns the PR that lands the icon in the codebase and publishes it in the next release. diff --git a/packages/component-library/src/docs/foundations/interactions.mdx b/packages/component-library/src/docs/foundations/interactions.mdx deleted file mode 100644 index 49ad7f01e..000000000 --- a/packages/component-library/src/docs/foundations/interactions.mdx +++ /dev/null @@ -1,70 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; -import DoDont from "../_components/do-dont.js"; -import InteractionStates from "../_components/interaction-states.js"; - - - - - -Every interactive element should communicate clearly whether it can be used, what will happen if it is used, and whether the system has responded. Getting this right reduces hesitation and builds trust in the interface. - - - -## Input methods - -Design and test with all four input methods in mind. - -- **Mouse:** Hover states must appear immediately. Hit areas should be comfortably large. Use `cursor: pointer` to signal interactivity. -- **Keyboard:** Every element must be reachable by Tab and operable with Enter or Space. Focus order should follow reading order. Never suppress the focus ring. -- **Touch:** Targets must be at least 40x40px. Hover states do not exist; never gate actions or labels behind hover. -- **Voice:** Every control needs a meaningful visible label. Icon-only buttons must have an `aria-label` matching what a user would say to activate it. - -## Communicating affordance - -Before a user commits to an action, they need enough information to feel confident doing so. Labels, icons, and placement do most of this work. Interaction states reinforce it. - -An element at rest should look clickable if it is clickable. Rely on shape, labeling, and context (not just color) to establish this. Hover and focus states then confirm the expectation once the user moves toward the element. - -Unavailable actions should remain visible but clearly inactive. A disabled state that is invisible or easy to overlook leaves users stuck without knowing why. Where possible, accompany a disabled element with an explanation nearby: a tooltip, helper text, or contextual message. - -## Responding to input - -Once a user acts, the interface should respond immediately. Even if the operation takes time, the system should acknowledge the input right away. - -- For operations that take time, show a [Loader](?path=/docs/components-loader--docs) or [Progress Bar](?path=/docs/components-progress-bar--docs) so the user knows work is in progress. -- For completed actions that do not require a new page or view, use a [Snackbar](?path=/docs/components-snackbar--docs) to confirm the outcome without interrupting the flow. -- For failures, explain what went wrong and what the user can do to resolve it. Surface errors close to where the problem occurred rather than in a generic top-level message. - -## Critical actions - -Actions that are destructive or irreversible must be marked clearly so users can make an informed decision before confirming. - -Use the `critical` variant on components and the corresponding critical color tokens for any action that deletes, removes, or permanently changes data. This applies to the trigger element as well as any confirmation dialog that follows. - -- Use `--color-interaction-critical-default` and its state variants for interactive controls. -- Use `--color-text-critical-*` and `--color-icon-critical-*` tokens for supporting text, icons, and inline warnings. -- Always pair a critical action with a confirmation step. Never trigger a destructive operation on a single click. - -## Focus ring - -The focus ring is the primary visual signal for keyboard navigation. It must always be visible. - -Meteor's standard focus ring: - -```css -outline: 2px solid var(--color-border-brand-default); -outline-offset: 2px; -``` - -The 2px outline is heavy enough to read against both light and dark surfaces. The 2px offset keeps it visually separate from the element's own border. - -### Adjusting the offset - -The offset is the only thing that should change between contexts. The thickness and color stay the same. - -If the default 2px offset looks broken or visually wrong in a specific component, adjust it. Higher values give the ring more breathing room; lower values pull it in. Zero offset is acceptable when the element is inside an `overflow: hidden` container and there is no other way to show the ring. - -## Tokens - -For interaction-specific color tokens, see the [Tokens](?path=/docs/foundations-tokens-overview--docs) page. diff --git a/packages/component-library/src/docs/foundations/tokens/border-radius.mdx b/packages/component-library/src/docs/foundations/tokens/border-radius.mdx deleted file mode 100644 index ef804b702..000000000 --- a/packages/component-library/src/docs/foundations/tokens/border-radius.mdx +++ /dev/null @@ -1,51 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; - - - - - -Border radius decisions made ad hoc produce interfaces where similar elements have subtly different shapes, making the UI feel unpolished without an obvious cause. - -Meteor provides two layers of border radius tokens: semantic tokens for general use, and element-scoped tokens that encode the correct radius for specific component types. Always prefer an element-scoped token when one exists for your use case. Fall back to semantic tokens when no element-scoped token applies. - -## Element-scoped tokens - -Element-scoped tokens encode the right radius for specific element types. Use these before reaching for a semantic token. - -| Token | Value | Use for | -| -------------------------- | ----- | ------------------------------------ | -| `--border-radius-card` | 8px | Cards and container surfaces | -| `--border-radius-button` | 4px | Buttons and button-like controls | -| `--border-radius-checkbox` | 4px | Checkboxes and similar input toggles | - -## Semantic tokens - -Use semantic tokens when no element-scoped token fits your element. - -| Token | Value | -| ----------------------- | ------ | -| `--border-radius-none` | 0px | -| `--border-radius-2xs` | 2px | -| `--border-radius-xs` | 4px | -| `--border-radius-s` | 6px | -| `--border-radius-m` | 8px | -| `--border-radius-l` | 12px | -| `--border-radius-xl` | 16px | -| `--border-radius-2xl` | 20px | -| `--border-radius-3xl` | 24px | -| `--border-radius-4xl` | 32px | -| `--border-radius-round` | 9999px | - -## Usage - - - - diff --git a/packages/component-library/src/docs/foundations/tokens/color-palette.mdx b/packages/component-library/src/docs/foundations/tokens/color-palette.mdx deleted file mode 100644 index fe048214c..000000000 --- a/packages/component-library/src/docs/foundations/tokens/color-palette.mdx +++ /dev/null @@ -1,21 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import ColorPalette from "../../_components/color-palette.js"; - - - - - -The color palette defines a fixed set of shades per hue. These shades are referenced by [Tokens](/docs/foundations-tokens--docs), which apply usage and meaning in components and layouts. - -## Naming convention - -Each palette color is identified by its hue name and a numeric shade. The number represents luminosity on a scale from 50 (lightest) to 950 (darkest). For example, `color-blue-500` is the mid-range blue, while `color-blue-50` is a near-white tint and `color-blue-950` is near-black. - -This naming convention makes the scale predictable: a higher number is always darker, a lower number is always lighter, regardless of hue. - -## How to use - -Palette values have no semantic meaning on their own. They are the toolbox designers use to define semantic tokens and build themes. **In product code, always use tokens instead of palette values directly.** Tokens carry intent (such as "primary action" or "destructive state") and adapt correctly across different themes. Reaching for a raw palette value skips that layer of meaning and breaks theming. - - diff --git a/packages/component-library/src/docs/foundations/tokens/elevation.mdx b/packages/component-library/src/docs/foundations/tokens/elevation.mdx deleted file mode 100644 index c58621be6..000000000 --- a/packages/component-library/src/docs/foundations/tokens/elevation.mdx +++ /dev/null @@ -1,39 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; -import ElevationSurfaces from "../../_components/elevation-surfaces.js"; - - - - - -Elevation communicates depth and hierarchy by assigning surfaces to distinct layers. Rather than using arbitrary background colors, Meteor provides a small set of elevation tokens that map directly to layers in the visual stack. Using them consistently ensures surfaces relate to each other correctly across both light and dark mode without any extra work. - - - -## Surface tokens - -Three tokens cover the vast majority of UI surfaces: - -| Token | Use for | -| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--color-elevation-surface-sunken` | Inset areas sitting inside another surface: sidebar sections, striped table rows, code blocks. Also used as a large-scale page background when the primary application background uses default. | -| `--color-elevation-surface-default` | The main page or application background | -| `--color-elevation-surface-raised` | Cards, panels, and containers that appear lifted above the default background | - -## Special-purpose tokens - -| Token | Use for | -| ------------------------------------ | -------------------------------------------------------------------------------- | -| `--color-elevation-floating-default` | Tooltips and other floating elements that require high contrast against the page | -| `--color-elevation-backdrop-default` | The semi-transparent scrim behind modals, drawers, and full-screen overlays | -| `--color-elevation-shadow-default` | Box shadow color for elevated elements such as popovers | - -## Theming - -Values adjust according to theming. Token values swap automatically when `data-theme="dark"` is applied to a parent element. The token names and the component code stay identical across themes. - - diff --git a/packages/component-library/src/docs/foundations/tokens/spacing.mdx b/packages/component-library/src/docs/foundations/tokens/spacing.mdx deleted file mode 100644 index 62fd0cdd0..000000000 --- a/packages/component-library/src/docs/foundations/tokens/spacing.mdx +++ /dev/null @@ -1,80 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; - - - - - -Spacing decisions made ad hoc accumulate into visual inconsistency. When every component picks its own padding and gap values, the UI becomes uneven in ways that are hard to diagnose and fix systematically. - -A fixed scale solves this by constraining choices to a shared set of values. Components that use the same scale step naturally feel related. Layouts built from scale-aligned components align with each other without manual correction. And when spacing needs to change globally, updating a token propagates everywhere at once. - -Meteor's scale is numeric: every token name reflects its value in pixels, so `--scale-size-8` is 8px. There is no semantic indirection to memorize. - -## Scale reference - -| Token | Value | -| ------------------ | ----- | -| `--scale-size-0` | 0px | -| `--scale-size-1` | 1px | -| `--scale-size-2` | 2px | -| `--scale-size-4` | 4px | -| `--scale-size-6` | 6px | -| `--scale-size-8` | 8px | -| `--scale-size-10` | 10px | -| `--scale-size-12` | 12px | -| `--scale-size-14` | 14px | -| `--scale-size-16` | 16px | -| `--scale-size-18` | 18px | -| `--scale-size-20` | 20px | -| `--scale-size-22` | 22px | -| `--scale-size-24` | 24px | -| `--scale-size-26` | 26px | -| `--scale-size-28` | 28px | -| `--scale-size-30` | 30px | -| `--scale-size-32` | 32px | -| `--scale-size-36` | 36px | -| `--scale-size-40` | 40px | -| `--scale-size-48` | 48px | -| `--scale-size-56` | 56px | -| `--scale-size-64` | 64px | -| `--scale-size-72` | 72px | -| `--scale-size-80` | 80px | -| `--scale-size-96` | 96px | -| `--scale-size-128` | 128px | -| `--scale-size-160` | 160px | -| `--scale-size-192` | 192px | -| `--scale-size-224` | 224px | -| `--scale-size-256` | 256px | - -## Choosing a step - -As a starting point: - -- **4–8px** for tight internal spacing within a component, such as an icon-to-label gap or badge padding -- **12–16px** for standard component padding and item spacing -- **24–40px** for spacing between form elements and section separation within a view -- **48–64px** for separation between major layout regions - -Consistency matters more than the exact value. Prefer reusing an existing step over introducing a one-off intermediate value. - - - - - - - - diff --git a/packages/component-library/src/docs/foundations/tokens/tokens-overview.mdx b/packages/component-library/src/docs/foundations/tokens/tokens-overview.mdx deleted file mode 100644 index 20e923b07..000000000 --- a/packages/component-library/src/docs/foundations/tokens/tokens-overview.mdx +++ /dev/null @@ -1,87 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import TokenBrowser from "../../_components/token-browser.js"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; - - - - - -## What are tokens - -Design tokens are standardized name-value pairs that encapsulate design decisions such as colors, typography, spacing, and motion. They act as a bridge between design and development, ensuring consistency across different platforms and tools. By abstracting raw values into meaningful names, design tokens enable scalable and maintainable UI systems. - -## Token names explained - -A design token's name conveys its intended use, with each part specifying a distinct aspect of its function: - -- **Type**: the broadest classification: `color`, `font`, `scale`, or `border-radius` -- **Category**: a functional grouping within the type: `icon`, `text`, `background`, or `elevation` within `color` -- **Instance**: a distinct usage within the category: `primary`, `positive`, or `critical` -- **Variant**: the state or modification: `default`, `hover`, `pressed`, or `disabled` - -The four-part structure applies to semantic color tokens. Other tokens such as `--font-size-s` or `--scale-size-8` use fewer parts since they do not carry state. - -## Usage - -```sh -npm install @shopware-ag/meteor-tokens -``` - -```ts -import "@shopware-ag/meteor-tokens/administration/light.css"; -import "@shopware-ag/meteor-tokens/administration/dark.css"; -``` - -## All tokens - - - -## Adding a new token - -New tokens require sign-off from both design and engineering before they are added. The process is intentionally lightweight but ensures every new token has a clear purpose and fits the existing naming structure. - -### Starting a proposal - -A new token can be initiated from either side. A designer might identify a gap while building a new component in Figma; an engineer might notice a hardcoded value that should be abstracted. Either way, the proposal should be shared with the other discipline before work begins. Use a [GitHub issue](https://github.com/shopware/meteor/issues), a Figma comment on the library file, or the Meteor Slack channel to open the discussion. - -#### What a good proposal includes - -- What the token represents and where it will be used -- The proposed name following the `type-category-instance-variant` structure -- The intended value in all available themes -- Whether an existing token could cover the need instead - -### Review and sign-off - -Both the design team and the Meteor engineering team review the proposal. The review checks that the name is consistent with existing conventions, that the token is genuinely needed and not a duplicate, and that it works correctly across both themes. - -### From Figma to code - -Once approved, the token is added to the Figma Variables library first. From there it is synced to the token package using the Figma Variables sync workflow. The Meteor team owns the PR that lands the token in the codebase and publishes it in the next release. - -## Customizing tokens - -We recommend against overriding existing Meteor tokens. Overrides can cause unexpected visual divergence from the design system and break when token values change in future releases. If your project needs additional tokens, define new ones with a custom prefix instead. - -If you do need to override, do so after importing the Meteor token files and scope changes to `[data-theme="dark"]` for theme variants. - -```css -@import "@shopware-ag/meteor-tokens/administration/light.css"; -@import "@shopware-ag/meteor-tokens/administration/dark.css"; - -/* Add new tokens with a custom prefix */ -:root { - --myapp-color-brand-default: #7c3aed; - --myapp-color-brand-hover: #6d28d9; -} - -[data-theme="dark"] { - --myapp-color-brand-default: #8b5cf6; - --myapp-color-brand-hover: #7c3aed; -} -``` diff --git a/packages/component-library/src/docs/foundations/tokens/typography.mdx b/packages/component-library/src/docs/foundations/tokens/typography.mdx deleted file mode 100644 index 03c7bf4fa..000000000 --- a/packages/component-library/src/docs/foundations/tokens/typography.mdx +++ /dev/null @@ -1,72 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../../_components/storybook-page-header.js"; -import DoDont from "../../_components/do-dont.js"; -import TypographyScale from "../../_components/typography-scale.js"; - - - - - -Meteor uses Inter as its single typeface. All type sizes, weights, and line heights are available as CSS custom properties and via the [Text](?path=/docs/components-text--docs) component. - -## Font family - -```css ---font-family-headings: "Inter"; ---font-family-body: "Inter"; -``` - -Inter is loaded via `@shopware-ag/meteor-component-library/font.css`. Import this stylesheet to ensure the font is available. If you manage fonts independently, make sure Inter is available under the name `Inter`. - -## Scale - -Meteor uses a named scale from `2xs` to `3xl`. Always pair a `--font-size-*` token with its matching `--font-line-height-*` token. The line heights are tuned to each size step. Use `semibold` and `bold` sparingly and reserve them for headings, labels, and key emphasis. Body copy should be `regular`. - - - -## Hierarchy - -Create hierarchy through size and weight, not decoration. A clear hierarchy guides the reader's eye without requiring color or additional visual treatments. - -- Use one prominent size per section. Combining multiple large sizes on a single screen competes for attention rather than directing it. -- Increase weight before increasing size. Stepping up from `regular` to `semibold` at the same size creates sufficient contrast for labels, headings, and interactive elements. -- Reserve `3xl` and `2xl` for page-level titles and major section headers. Use `xl` and `l` for subsections. Use `m` and `s` for card and section headlines. Use `xs` for body copy, supporting labels, and metadata. -- Secondary information should use `--color-text-secondary-default`, not a smaller size. Reducing size too aggressively harms legibility. - -## Line length - -Keep body copy between 60 and 80 characters per line. Use `max-width: 65ch` on the container to enforce this. This does not apply to labels or UI strings. - -## Usage - -Use the [Text](?path=/docs/components-text--docs) component to render text. It accepts `size`, `weight`, `color`, and `as` props and applies the correct token combination automatically. - -```html -Page title -Body copy -Supporting label -Inline text -``` - -The `as` prop controls the rendered HTML element and defaults to `p`. Use it to render headings, spans, or any other element without losing the token-based styling. - - - - - -## Accessibility - -- Ensure sufficient contrast between text and background. Use `--color-text-primary-default` on `--color-elevation-surface-default` as your baseline. -- Do not set font sizes below `--font-size-2xs` (12px). Below this threshold legibility degrades significantly. -- Avoid setting `line-height` below 1.4 for body copy. The token line heights already meet this. -- All text content in components must meet WCAG 2.1 AA contrast requirements. - -For broader accessibility guidance, see the [Accessibility](?path=/docs/foundations-accessibility--docs) page. diff --git a/packages/component-library/src/docs/getting-started/contributing.mdx b/packages/component-library/src/docs/getting-started/contributing.mdx deleted file mode 100644 index 64cf69dcc..000000000 --- a/packages/component-library/src/docs/getting-started/contributing.mdx +++ /dev/null @@ -1,9 +0,0 @@ -import { Meta, Markdown } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; -import content from "../../../../../CONTRIBUTING.md?raw"; - - - - - -{content} diff --git a/packages/component-library/src/docs/getting-started/designers.mdx b/packages/component-library/src/docs/getting-started/designers.mdx deleted file mode 100644 index 153451b1d..000000000 --- a/packages/component-library/src/docs/getting-started/designers.mdx +++ /dev/null @@ -1,69 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; - - - - - -Meteor ships three Figma libraries that work together. Activate all three in your file to get the full system. - -## Design principles - -Everything in Meteor is grounded in Shopware's product design principles: **Building the right thing; building the thing right.** - -The principles cover accessibility and inclusivity, data-informed decision making, reducing friction, consistency, and ethical design. They apply whether you work at Shopware or build apps for the Shopware ecosystem. Read them in full on the [Design Principles](?path=/docs/foundations-design-principles--docs) page before starting work in the system. - -## Activate the libraries - -1. Open your Figma file. -2. Go to **Assets** and click the **Libraries** icon. -3. Search for and enable all three Meteor libraries: - - **Meteor Primitives**: the raw color palette and base values - - **Meteor Tokens**: semantic variables for color, typography, spacing, and border radius - - **Meteor Components**: the full component set - -Once enabled, variables and components are available immediately in the Assets panel. - -## Insert components - -With the libraries active, open the **Assets** panel and search for the component you need. Drag it onto the canvas or use **Figma Insert** to place it directly. - -Components match their code counterparts: same variants, same states, same props. Check the component's documentation page here for available options before building out your design. - -Avoid detaching components. Detached instances lose their variable bindings and will not update when the library changes. If a component does not cover your use case, open a [GitHub issue](https://github.com/shopware/meteor/issues) or start a discussion in the Meteor Slack channel so the change can be made in both the Figma library and the codebase at the same time. - -## Use tokens, not hardcoded values - -Always apply variables from Meteor Tokens rather than hardcoded colors, sizes, or radii. Token-bound designs adapt automatically when the theme changes. See the [Tokens](?path=/docs/foundations-tokens-overview--docs) page for the full list of available tokens. - -## Accessibility - -Accessible design starts in Figma, not in code. Meteor components meet WCAG 2.1 AA out of the box, but the patterns and decisions you make in your designs determine whether the final product is actually accessible. - -A few things to consider while designing: - -- Use color tokens for text and backgrounds so contrast ratios are maintained across light and dark themes. Never use palette values directly. -- Never use color as the only way to communicate state or meaning. Pair it with a label, icon, or pattern. -- Make sure interactive elements have a visible focus state. Meteor components include focus styles by default; avoid hiding or overriding them. -- Write alt text and accessible labels for any icons or images that carry meaning. - -For the full set of guidelines and a pre-ship checklist, see the [Accessibility](?path=/docs/foundations-accessibility--docs) page. - -## Keeping libraries up to date - -Figma will show an **Update available** banner in the Assets panel when a new version is published. Review the changelog before updating to understand what changed. - -## Handing off to developers - -When your design is ready, prepare it for a clean handoff so developers can implement it accurately without back-and-forth. - -Practical steps for a clean handoff: - -1. Verify all colors and sizes are bound to Figma variables rather than hardcoded values. Detached variables appear without a variable badge in the Inspect panel. -2. If you used tokens in custom spacing or a one-off layout, call out the token name in the annotation. Use the format `--scale-size-16` so developers can copy it directly. -3. Flag any component that does not exist in Meteor yet, or any existing component you modified locally. Check the component catalogue first to confirm whether a component is available. If it is not, follow the process described in [Adding new components](?path=/docs/foundations-components--docs) so developers know how to handle it. -4. Review your design against the [Accessibility](?path=/docs/foundations-accessibility--docs) checklist before handing off. Flag any areas where focus order, contrast, or labeling needs special attention from the developer. - -## Component guidelines - -If you are working alongside developers, the [components guidelines](?path=/docs/foundations-components--docs) page covers how Meteor components are structured: naming conventions, props, slots, events, and the compound component pattern. Reading it helps you make design decisions that map cleanly to the code. diff --git a/packages/component-library/src/docs/getting-started/developers.mdx b/packages/component-library/src/docs/getting-started/developers.mdx deleted file mode 100644 index f3ea59393..000000000 --- a/packages/component-library/src/docs/getting-started/developers.mdx +++ /dev/null @@ -1,129 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; - - - - - -Meteor is a monorepo that publishes each part of the design system as a standalone npm package. You can adopt the full stack or pick only what your project needs: the component library, the token set, and the icon kit are all independently installable and versioned. - -## Component library - -The component library builds upon the icon kit and design tokens, and requires Vue 3. - -### Install - -```sh -npm install @shopware-ag/meteor-component-library @shopware-ag/meteor-icon-kit -``` - -### Import styles - -Add both imports to your application entry point. - -```ts -import "@shopware-ag/meteor-component-library/styles.css"; -import "@shopware-ag/meteor-component-library/font.css"; -``` - -### TypeScript - -Components are fully built with TypeScript. No additional setup is required. Types are resolved automatically when you import from `@shopware-ag/meteor-component-library`. - -### Configure i18n - -English and German translations are bundled. Register `vue-i18n` before mounting your app. - -```ts -import { createApp } from "vue"; -import { createI18n } from "vue-i18n"; -import App from "./App.vue"; - -const i18n = createI18n({ legacy: false }); - -createApp(App).use(i18n).mount("#app"); -``` - -### Use components - -Components are tree-shakable. Import only what you use. - -```vue - - - -``` - -## Tokens - -The token package is framework-agnostic. Install it independently whenever you need Meteor's design decisions without the component library. - -### Install - -```sh -npm install @shopware-ag/meteor-tokens -``` - -### Import - -The light theme defines design tokens via CSS variables on `:root` and the dark theme replaces the variable values when `data-theme="dark"` is set on elements as an HTML attribute. - -```ts -import "@shopware-ag/meteor-tokens/administration/light.css"; -import "@shopware-ag/meteor-tokens/administration/dark.css"; -``` - -### Use - -```css -.my-component { - color: var(--color-text-primary-default); - background: var(--color-elevation-surface-default); - padding: var(--scale-size-16); -} -``` - -## Icons - -The icon kit can be used standalone in any framework as SVGs, or as Vue components via `mt-icon` in the component library. - -### Install - -```sh -npm install @shopware-ag/meteor-icon-kit -``` - -### Import as SVG - -```ts -import PlusIcon from "@shopware-ag/meteor-icon-kit/icons/regular/plus.svg"; -``` - -### Use with mt-icon (Vue) - -`mt-icon` is included in the component library and renders any icon by name. - -{/* prettier-ignore */} -```html - - -``` - -## Browser support - -Meteor targets all major evergreen browsers. Features used in the component library and token package are limited to those with [Baseline Widely Available](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility) status, a web platform interoperability signal meaning the feature is supported in the latest stable releases of Chrome, Edge, Firefox, and Safari with no polyfills required. - -Legacy browsers and non-evergreen environments are not supported. - -## Changelog - -The full release history for all Meteor packages is available in the [changelog on GitHub](https://github.com/shopware/meteor/releases). diff --git a/packages/component-library/src/docs/getting-started/introduction.mdx b/packages/component-library/src/docs/getting-started/introduction.mdx deleted file mode 100644 index 645713ac4..000000000 --- a/packages/component-library/src/docs/getting-started/introduction.mdx +++ /dev/null @@ -1,45 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; - - - - - -Meteor is Shopware's open-source design system for building administrative product experiences across the Shopware ecosystem. - -## What's in Meteor - -Meteor is published as three independent npm packages. Use all three together or install only what you need. - -| Package | Description | -| --------------------------------------- | ------------------------------------------------------------------- | -| `@shopware-ag/meteor-component-library` | Vue 3 component set with built-in tokens, icons, and the Inter font | -| `@shopware-ag/meteor-tokens` | Design tokens as CSS custom properties, works in any framework | -| `@shopware-ag/meteor-icon-kit` | SVG icon set, usable standalone or as Vue components via `mt-icon` | - -## Where to start - -- **Designers**: See Get Started > [Designers](?path=/docs/get-started-designers--docs) to activate the Figma libraries and start working with components. -- **Developers**: See Get Started > [Developers](?path=/docs/get-started-developers--docs) for installation, setup, and usage examples. -- **Migrating from `sw-` components**: See [Migration](?path=/docs/get-started-migration--docs) for a component mapping and upgrade guidance. - -## Why Meteor - -Without a shared system, every team makes its own decisions about color, spacing, interaction, and language. The result is products that feel inconsistent, and a development process that wastes time re-solving problems that have already been solved elsewhere. - -Meteor gives designers and developers a single source of truth. By aligning Figma components, implementation components, and design tokens, it closes the translation gap between design and engineering: decisions are made once, encoded in tokens and components, and applied consistently everywhere. This reduces misalignment, accelerates delivery, and lowers the risk of visual drift as products grow. - -The payoff is compounding: teams ship faster because they are building on a stable foundation, products feel more polished because the same decisions apply everywhere, and design and engineering stay aligned because they are working from the same artifacts. Being open source means Meteor benefits from contributions across the Shopware partner and developer community. It is the foundation Shopware and its ecosystem build on together. - -## Changelog - -See what has changed across all Meteor packages in the [changelog on GitHub](https://github.com/shopware/meteor/releases). - -## Contributing and feedback - -Meteor is open source and maintained by Shopware. Contributions, bug reports, and feature requests are welcome on [GitHub](https://github.com/shopware/meteor). See the [Contributing](?path=/docs/get-started-contributing--docs) page for how to set up the project locally, submit a pull request, and create a changeset. - -## Useful resources - -- For Shopware platform documentation visit the [Shopware Developer Documentation](https://developer.shopware.com/). -- For brand guidelines, logos and downloadable assets, visit the [Shopware brand guidelines](https://brand.shopware.com/). diff --git a/packages/component-library/src/docs/getting-started/migration.mdx b/packages/component-library/src/docs/getting-started/migration.mdx deleted file mode 100644 index 613095154..000000000 --- a/packages/component-library/src/docs/getting-started/migration.mdx +++ /dev/null @@ -1,70 +0,0 @@ -import { Meta } from "@storybook/blocks"; -import StorybookPageHeader from "../_components/storybook-page-header.js"; - - - - - -If you're using older `sw-` components, here's how to transition to Meteor. - -## Component mapping - -- `sw-button` → `mt-button` -- `sw-text-field` → `mt-text-field` -- `sw-card` → `mt-card` -- `sw-banner` → `mt-banner` -- `sw-checkbox` → `mt-checkbox` -- `sw-switch-field` → `mt-switch` -- `sw-select` → `mt-select` -- `sw-datepicker` → `mt-datepicker` - -## Design tokens - -Replace custom CSS variables or hard-coded colors with [Meteor tokens](?path=/docs/foundations-tokens-overview--docs): - -```css -/* Use Meteor tokens directly */ -color: var(--color-icon-primary-default); -``` - -## Icons - -The component name is the same. The tag just changes from `sw-icon` to `mt-icon`. See the [Icons](?path=/docs/foundations-icons--docs) page for the full icon set and usage guidance. - -```html - -``` - -## From admin-extension-sdk to meteor-admin-sdk - -The `@shopware-ag/admin-extension-sdk` package was archived in March 2024 and replaced by `@shopware-ag/meteor-admin-sdk`. - -```sh -npm uninstall @shopware-ag/admin-extension-sdk -npm install @shopware-ag/meteor-admin-sdk -``` - -Update your imports: - -```js -// Before -import { notification } from "@shopware-ag/admin-extension-sdk"; - -// After -import { notification } from "@shopware-ag/meteor-admin-sdk"; -``` - -## Gradual migration - -Meteor works alongside existing components. Migrate page by page rather than all at once: - -1. Install Meteor alongside your existing setup -2. Replace components one at a time -3. Update styling to use design tokens progressively -4. Test each step before continuing - -## Between Meteor versions - -When upgrading between major versions of Meteor packages, check the [GitHub releases page](https://github.com/shopware/meteor/releases) for breaking changes. Each release includes a changelog entry that lists renamed props, removed components, and token renames. - -For minor and patch releases, upgrades are safe without additional migration steps. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c3747672..60d981332 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -469,9 +469,12 @@ importers: '@storybook/addon-links': specifier: ^8.6.15 version: 8.6.15(react@17.0.2)(storybook@8.6.15(prettier@3.2.5)) - '@storybook/blocks': + '@storybook/components': specifier: ^8.6.15 - version: 8.6.15(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(storybook@8.6.15(prettier@3.2.5)) + version: 8.6.15(storybook@8.6.15(prettier@3.2.5)) + '@storybook/icons': + specifier: ^1.2.12 + version: 1.2.12(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@storybook/manager-api': specifier: ^8.6.15 version: 8.6.15(storybook@8.6.15(prettier@3.2.5)) @@ -571,9 +574,6 @@ importers: prettier: specifier: 3.2.5 version: 3.2.5 - remark-gfm: - specifier: ^4.0.1 - version: 4.0.1 sass: specifier: ^1.69.5 version: 1.77.6 @@ -15974,11 +15974,8 @@ packages: vue-component-type-helpers@2.2.12: resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} - vue-component-type-helpers@3.3.4: - resolution: {integrity: sha512-joip1uZTaQR0nD23N400gIdJ7xY+WiiiMA/BCKz842gvGBknqDQAzklUvDEhqFvvrhQY8S2ZANBMu4X70VMFGw==} - - vue-component-type-helpers@3.3.5: - resolution: {integrity: sha512-Fe1jyPJoUGpJOYKOri44jduR7My4yYINOMJISuMAbmrs+L5LbIDUc8NTWZYY3EJLK0yPLuCmcd5zoCsE4k2/KA==} + vue-component-type-helpers@3.3.6: + resolution: {integrity: sha512-FkljacAwJ9BUoSUdpFe3VDy0sGigNlTH9+2zcXUWmZOjN8swiCkl3t48wOJun0OsUd2cEIda1l04tsxMiKIIrQ==} vue-demi@0.14.10: resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} @@ -19938,7 +19935,7 @@ snapshots: unplugin-auto-import: 21.0.0(@nuxt/kit@4.4.8(magicast@0.5.3))(@vueuse/core@14.3.0(vue@3.5.13(typescript@5.7.3))) unplugin-vue-components: 32.1.0(@nuxt/kit@4.4.8(magicast@0.5.3))(vue@3.5.13(typescript@5.7.3)) vaul-vue: 0.4.1(reka-ui@2.9.9(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3)) - vue-component-type-helpers: 3.3.4 + vue-component-type-helpers: 3.3.6 optionalDependencies: '@internationalized/date': 3.10.0 '@internationalized/number': 3.6.5 @@ -21691,7 +21688,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.37(typescript@5.7.3) - vue-component-type-helpers: 3.3.5 + vue-component-type-helpers: 3.3.6 '@supercharge/promise-pool@2.4.0': {} @@ -35849,9 +35846,7 @@ snapshots: vue-component-type-helpers@2.2.12: {} - vue-component-type-helpers@3.3.4: {} - - vue-component-type-helpers@3.3.5: {} + vue-component-type-helpers@3.3.6: {} vue-demi@0.14.10(vue@3.5.13(typescript@5.7.3)): dependencies: From 04b8c72ae022145e8320d67fba49af3d87ac5302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BCske?= <33117553+fabianhueske@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:29:56 +0200 Subject: [PATCH 2/5] chore(docs): rename component header "Source" button to "GitHub" --- apps/docs/app/components/DocsPageHeaderLinks.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/app/components/DocsPageHeaderLinks.vue b/apps/docs/app/components/DocsPageHeaderLinks.vue index f9cd7a6e5..263345da5 100644 --- a/apps/docs/app/components/DocsPageHeaderLinks.vue +++ b/apps/docs/app/components/DocsPageHeaderLinks.vue @@ -99,7 +99,7 @@ async function copyPage() { :to="componentSourceUrl" target="_blank" icon="i-simple-icons:github" - label="Source" + label="GitHub" color="neutral" variant="outline" size="md" From 9d4aabe3841884fb37bc0d37a531a22e18804a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BCske?= <33117553+fabianhueske@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:18:34 +0200 Subject: [PATCH 3/5] chore(docs): reorder header buttons to Storybook, GitHub, Copy page --- apps/docs/app/components/DocsPageHeaderLinks.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/docs/app/components/DocsPageHeaderLinks.vue b/apps/docs/app/components/DocsPageHeaderLinks.vue index 263345da5..d1fd74ef5 100644 --- a/apps/docs/app/components/DocsPageHeaderLinks.vue +++ b/apps/docs/app/components/DocsPageHeaderLinks.vue @@ -95,11 +95,11 @@ async function copyPage() {