commit b6a0a6110352748bda6d715e715f3c964a944f4b Author: 김수현 Date: Thu Oct 8 16:58:09 2026 +0900 Initial commit: HMDesignSystem baseline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad49a10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +node_modules/ +dist/ +storybook-static/ +theme-dist/ +*.tsbuildinfo + +# Release/test packages and generated dashboard exports are distributed outside Git. +*.zip +**/dist-dashboard/ + +*.log +.env +.env.* +!.env.example + +_local/ +.claude/settings.local.json diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 0000000..c46ff1b --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,30 @@ +import type { StorybookConfig } from "@storybook/react-vite" +import { readFileSync } from "node:fs" + +const config: StorybookConfig = { + stories: ["../src/**/*.stories.@(ts|tsx)"], + addons: [], + // In dev, Vite serves both raw /themes JSON and JSON module imports (?import). + // Storybook's static middleware would intercept the imports with application/json. + staticDirs: (_, { configType }) => [ + { from: "../public/fonts", to: "/fonts" }, + ...(configType === "PRODUCTION" ? [{ from: "../themes", to: "/themes" }] : []), + ], + managerHead: (head) => `${head}`, + // changeDetection off: no "new and modified stories" filter or status badges in the sidebar. + features: { sidebarOnboardingChecklist: false, changeDetection: false }, + // Verification Stories (the same sections as a component's representative Story) stay out of the sidebar by default. + // They keep their Story IDs and stay reachable by direct URL and the sidebar tag filter. + tags: { verification: { defaultFilterSelection: "exclude" } }, + // Storybook replaces vite.config's `server`, so watcher excludes go here. + // _local/ (gitignored work copies, ~20k files) would otherwise make the Vite watcher stall the dev server. + viteFinal: (config) => { + config.server ??= {} + config.server.watch = { ...config.server.watch, ignored: ["**/_local/**", "**/storybook-static/**"] } + return config + }, + framework: "@storybook/react-vite", +} + +export default config + diff --git a/.storybook/manager-navigation.js b/.storybook/manager-navigation.js new file mode 100644 index 0000000..d32980a --- /dev/null +++ b/.storybook/manager-navigation.js @@ -0,0 +1,70 @@ +export function decorateHanmacNavigation(api) { + let frame; + const write = (node, key, value) => { + const next = String(value); + if (node.getAttribute(key) !== next) node.setAttribute(key, next); + }; + const ancestors = (entry, refId) => { + const result = []; + const seen = new Set(); + while (entry?.parent && !seen.has(entry.parent)) { + seen.add(entry.parent); + entry = api.resolveStory(entry.parent, refId); + if (entry) result.push(entry); + } + return result; + }; + const decorate = () => { + frame = undefined; + const tree = document.querySelector('#storybook-explorer-tree'); + if (!tree) return; + const selected = api.getCurrentStoryData(); + const currentParents = new Set(ancestors(selected, selected?.refId).map((item) => item.id)); + const rows = [...tree.querySelectorAll('[data-item-id]')]; + const rendered = new Set(rows.map((row) => `${row.dataset.refId}:${row.dataset.itemId}`)); + const groups = new Map(); + let section = 0; + for (const row of rows) { + const refId = row.dataset.refId === 'storybook_internal' ? undefined : row.dataset.refId; + const entry = api.resolveStory(row.dataset.itemId, refId); + if (!entry) continue; + // Visual depth = rendered ancestors. A component with a single story of its own name is hoisted by Storybook (the + // story replaces it at depth 1), while the API still reports the story at depth 2. + write(row, 'data-hm-depth', ancestors(entry, refId).filter((item) => rendered.has(`${row.dataset.refId}:${item.id}`)).length); + write(row, 'data-hm-current-parent', currentParents.has(entry.id) && refId === selected?.refId); + if (entry.type === 'root') { + write(row, 'data-hm-section', section++); + continue; + } + // A single overlay covers the whole expanded branch, including deeper rows. + const branch = [...ancestors(entry, refId).reverse(), entry].find((item) => item.type !== 'root'); + const owner = rows.find((candidate) => candidate.dataset.itemId === branch?.id && candidate.dataset.refId === row.dataset.refId); + const expanded = owner?.querySelector(':scope > button[aria-expanded="true"]'); + write(row, 'data-hm-group', !!expanded); + write(row, 'data-hm-group-start', false); + write(row, 'data-hm-group-end', false); + if (expanded) { + const key = `${row.dataset.refId}:${branch.id}`; + if (!groups.has(key)) groups.set(key, []); + groups.get(key).push(row); + } + } + for (const members of groups.values()) { + write(members[0], 'data-hm-group-start', true); + write(members.at(-1), 'data-hm-group-end', true); + } + }; + const schedule = () => { if (frame === undefined) frame = requestAnimationFrame(decorate); }; + const observer = new MutationObserver(schedule); + observer.observe(document.body, { + childList: true, subtree: true, attributes: true, + attributeFilter: ['data-selected', 'aria-expanded', 'data-item-id'], + }); + schedule(); + window.addEventListener('pagehide', () => { + observer.disconnect(); + if (frame !== undefined) cancelAnimationFrame(frame); + }, { once: true }); +} + + diff --git a/.storybook/manager.css b/.storybook/manager.css new file mode 100644 index 0000000..e0c1390 --- /dev/null +++ b/.storybook/manager.css @@ -0,0 +1,317 @@ +/* Initial Storybook manager mapping from ui_viz. Manager-only; not runtime theme tokens. */ +:root { + --font-family-base: 'Noto Sans KR', 'Malgun Gothic', sans-serif; + --hm-green-default: #123328; + --hm-green-light-1: #205241; + --hm-green-light-2: #266651; + --hm-green-light-3: #249473; + --hm-fg-on-green-secondary: #BDDFD4; + --color-fg-on-green-primary: #FFFFFF; + --color-action-primary: #123328; + --color-action-accent-on-green: #FFC600; + --color-bg-content: #F5F0E5; + --color-bg-surface: #FFFFFF; + --effect-text-border-color: rgba(0, 0, 0, .3); + --overlay-black-20: rgba(0, 0, 0, .2); + --overlay-black-30: rgba(0, 0, 0, .3); +} +/* Storybook 10.6 manager only. Source: docs/patterns/dashboard-composition.md §6. + The 18px join / inset geometry follows the current LNB specimen, not a new token. */ +@font-face { + font-family: 'Hanmac Manager Noto Sans KR'; + src: url('/fonts/NotoSansKR-variable.ttf') format('truetype'); + font-weight: 100 900; + font-display: swap; +} +/* Manager-only alias of the bundled Noto Sans KR asset, followed by the + Foundation fallback chain (typography.css) instead of a bare sans-serif. */ +:root { + --hm-manager-font: 'Hanmac Manager Noto Sans KR', var(--font-family-base); +} + +/* The Storybook layout panel, outside the sidebar landmark, has a separate + border. Removing only the landmark's border leaves a one-device-pixel seam. */ +#root div:has(> div > #storybook-sidebar-region) { + border-right: 0; +} + +#storybook-sidebar-region { + --hm-manager-join: 18px; + color: var(--color-fg-on-green-primary); + background: linear-gradient(90deg, + var(--hm-green-default) 0%, var(--hm-green-light-1) 30%, + var(--hm-green-light-2) 50%, var(--hm-green-light-3) 90%, + var(--hm-green-light-2) 100%); + border-right: 0; + font-family: var(--hm-manager-font); +} +#storybook-sidebar-region [data-radix-scroll-area-content] > div { + padding: 28px 0 28px 20px; +} +/* Keep the native draggable scrollbar in the unused left gutter. The selected + item must reach the content edge without a scroll thumb crossing its curves. + Radix supplies left/right inline, so only those two positions need priority. */ +#storybook-sidebar-region > [scrollbarsize] > [data-orientation='vertical'] { + left: 4px !important; + right: auto !important; + width: 6px; + padding: 0; +} +#storybook-sidebar-region > [scrollbarsize] > [data-orientation='vertical'] > div { + background: var(--hm-fg-on-green-secondary); +} +#storybook-sidebar-region .sidebar-header { + margin: 0 20px 24px 0; + padding: 0; + color: var(--color-fg-on-green-primary); +} +#storybook-sidebar-region .sidebar-header > div a { + color: var(--color-fg-on-green-primary); + font: 700 20px/30px var(--hm-manager-font); + white-space: normal; + /* §6 Text Border: brand title keeps the original 1px (no shadow). */ + -webkit-text-stroke: 1px var(--effect-text-border-color); + paint-order: stroke fill; +} +#storybook-sidebar-region .sidebar-header button { + color: inherit; +} +#storybook-sidebar-region [role='search'] { + margin-right: 20px; + margin-bottom: 24px; +} +#storybook-sidebar-region .search-field { + min-height: 44px; + border: 1px solid var(--hm-fg-on-green-secondary); + border-radius: 4px; + background: var(--color-bg-surface); + color: var(--color-action-primary); +} +#storybook-explorer-searchfield { + color: var(--color-action-primary); + font-family: inherit; + font-size: 14px; +} +#storybook-sidebar-region [role='search'] > button { + color: var(--color-fg-on-green-primary); + background: transparent; + border-color: var(--hm-fg-on-green-secondary); + min-width: 32px; + height: 44px; +} +#storybook-sidebar-region [role='search'] > button:hover { + background: rgba(0, 0, 0, .10); +} + +#storybook-explorer-tree .sidebar-subheading { + position: relative; + margin: 0; + padding: 0 20px 0 0; + min-height: 56px; + color: var(--hm-fg-on-green-secondary); +} +#storybook-explorer-tree .sidebar-subheading[data-hm-section]:not([data-hm-section='0']) { + margin-top: 40px; +} +#storybook-explorer-tree .sidebar-subheading[data-hm-section]:not([data-hm-section='0'])::before { + content: ''; + position: absolute; + top: -20px; + left: 0; + right: 20px; + height: 2px; + background: linear-gradient(var(--overlay-black-30) 0 50%, rgba(255,255,255,.4) 50% 100%); + mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent); +} +#storybook-explorer-tree .sidebar-subheading > button { + min-height: 56px; + color: inherit; + font: 700 16px/28px var(--hm-manager-font); + letter-spacing: 0; + text-transform: none; +} +#storybook-explorer-tree .sidebar-item { + position: relative; + margin: 0; + padding: 0; + min-height: 56px; + overflow: visible; + border: 0; + border-radius: 0; + background: transparent; + color: var(--color-fg-on-green-primary); +} +#storybook-explorer-tree .sidebar-item[data-hm-group='true'] { + background: var(--overlay-black-20); + box-shadow: inset 1px 0 var(--hm-green-light-2); +} +#storybook-explorer-tree .sidebar-item[data-hm-group-start='true'] { + border-radius: 8px 0 0 0; + box-shadow: inset 1px 1px var(--hm-green-light-2); +} +#storybook-explorer-tree .sidebar-item[data-hm-group-end='true'] { + border-radius: 0 0 0 8px; + padding-bottom: 8px; + box-shadow: inset 1px -1px var(--hm-green-light-2); +} +#storybook-explorer-tree .sidebar-item > :is(a[id], button[id]:not([data-testid])) { + position: relative; + display: flex; + align-items: center; + gap: 12px; + min-height: 56px; + height: auto; + width: 100%; + margin: 0; + padding: 14px 32px 14px 18px; + border: 0; + border-radius: 4px 0 0 4px; + background: transparent; + color: inherit; + font: 700 18px/28px var(--hm-manager-font); + text-align: left; + text-decoration: none; + white-space: normal; + overflow: visible; + text-overflow: clip; +} +#storybook-explorer-tree .sidebar-item[data-hm-depth='2'] > :is(a[id], button[id]:not([data-testid])) { + margin-left: 32px; + width: calc(100% - 32px); +} +#storybook-explorer-tree .sidebar-item[data-hm-depth='3'] > :is(a[id], button[id]:not([data-testid])), +#storybook-explorer-tree .sidebar-item[data-hm-depth='4'] > :is(a[id], button[id]:not([data-testid])) { + margin-left: 44px; + width: calc(100% - 44px); +} +#storybook-explorer-tree .hm-manager-label { + min-width: 0; + white-space: normal; + word-break: keep-all; + overflow-wrap: anywhere; + overflow: visible; + text-overflow: clip; +} +/* D-093 D-014 (§6 Text Border): non-selected labels, including hover and the + yellow current parent, use the specimen's 1.5px border plus lower shadow. + Thickness and offset are REVIEW VALUEs. Selected labels carry no effect. */ +#storybook-explorer-tree .sidebar-item[data-selected='false'] .hm-manager-label { + -webkit-text-stroke: 1.5px var(--effect-text-border-color); + paint-order: stroke fill; + text-shadow: 1px 2px 1px var(--effect-text-border-color); +} +/* The group chevron carries the same lower shadow (specimen .ref-sidebar-chevron). */ +#storybook-explorer-tree .sidebar-item[data-selected='false'] > button[id]:not([data-testid]) > div:first-child > div > svg { + filter: drop-shadow(1px 2px 1px var(--effect-text-border-color)); +} +#storybook-explorer-tree .sidebar-item > :is(a[id], button[id]:not([data-testid])) > div:first-child { + position: static; + flex: 0 0 auto; + margin: 0; + gap: 8px; +} +/* Storybook 10.6.1: the leading wrapper has a direct svg[type] for every + menu type (group/component/story/docs), plus a nested untyped chevron + only on expandable rows. Remove type glyphs at every depth. */ +#storybook-explorer-tree .sidebar-item > :is(a[id], button[id]:not([data-testid])) > div:first-child > svg[type] { + display: none; +} +/* Leaves have no chevron. Remove the now-empty wrapper as a flex item so + neither its width nor the row's flex gap reserves an icon slot. + The existing depth margins and row padding still supply hierarchy indent. */ +#storybook-explorer-tree .sidebar-item > :is(a[id], button[id]:not([data-testid])) > div:first-child:has(> svg[type]):not(:has(> div > svg)) { + display: none; +} +/* First-depth leaves still sit inside their root label after losing the icon + slot. Deeper rows retain their existing 32px / 44px depth margins. */ +#storybook-explorer-tree .sidebar-item[data-hm-depth='1'] > :is(a[id], button[id]:not([data-testid])):has(> div:first-child > svg[type]):not(:has(> div:first-child > div > svg)) { + padding-left: 32px; +} +#storybook-explorer-tree .sidebar-item[data-hm-current-parent='true'], +#storybook-explorer-tree .sidebar-subheading[data-hm-current-parent='true'] { + color: var(--color-action-accent-on-green); +} +#storybook-explorer-tree .sidebar-item[data-selected='false'] > :is(a[id], button[id]:not([data-testid])):hover { + background: rgba(0, 0, 0, .10); +} +#storybook-explorer-tree .sidebar-item[data-selected='true'] > a[id] { + z-index: 1; + background: var(--color-bg-content); + color: var(--color-action-primary); + border-radius: 8px 0 0 8px; +} +/* Keep the keyboard skip link without reserving a 1px flex column. + That column exposed the green branch background at the selected item's end. */ +#storybook-explorer-tree .sidebar-item > a[href='#storybook-preview-wrapper'] { + position: absolute; + right: 0; + z-index: 4; +} +#storybook-explorer-tree .sidebar-item[data-selected='true'] > a[id]::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + box-shadow: inset 6px 0 8px -5px var(--overlay-black-30), + inset 0 3px 5px -3px var(--overlay-black-20), + inset 0 -2px 4px -3px var(--overlay-black-20); + mask-image: linear-gradient(90deg, #000 75%, transparent 100%); +} +#storybook-explorer-tree .sidebar-item[data-selected='true']::before, +#storybook-explorer-tree .sidebar-item[data-selected='true']::after { + content: ''; + position: absolute; + right: 0; + width: var(--hm-manager-join); + height: var(--hm-manager-join); + z-index: 2; + pointer-events: none; +} +#storybook-explorer-tree .sidebar-item[data-selected='true']::before { + bottom: 100%; + background: radial-gradient(circle at top left, transparent calc(var(--hm-manager-join) - .5px), var(--color-bg-content) var(--hm-manager-join)); +} +#storybook-explorer-tree .sidebar-item[data-selected='true']::after { + top: 100%; + background: radial-gradient(circle at bottom left, transparent calc(var(--hm-manager-join) - .5px), var(--color-bg-content) var(--hm-manager-join)); +} +#storybook-explorer-tree .sidebar-item[data-selected='true'][data-hm-group-end='true']::after { + top: calc(100% - 8px); +} +/* Auxiliary status/create actions are separate from selected row geometry. + Hide only these controls; the Story link and native group toggles stay intact. */ +#storybook-explorer-tree .sidebar-item[data-selected='true'] > :is([data-testid='tree-change-status-button'], [data-testid='context-menu']) { + display: none; +} +#storybook-explorer-tree [data-testid='context-menu'] { + color: inherit; + right: 4px; +} +/* Storybook gives utility buttons an opaque background and a 5px halo. + Keep their native behavior, with the same transparent / black-10% LNB states. */ +#storybook-explorer-tree :is([data-testid='context-menu'], .sidebar-subheading-action) { + background: transparent; + box-shadow: none; +} +#storybook-explorer-tree :is([data-testid='context-menu'], .sidebar-subheading-action):is(:hover, [aria-expanded='true']) { + background: rgba(0, 0, 0, .10); + box-shadow: none; +} +#storybook-explorer-tree .sidebar-item[data-selected='true'] > [data-testid='context-menu'] { + z-index: 3; + color: var(--color-action-primary); +} +#storybook-explorer-tree .sidebar-item[data-selected='true'] > [data-testid='context-menu'] svg { + color: var(--color-action-primary); +} +/* Keep the manager preview frame flush without consuming iframe width. */ +main:has(#storybook-preview-wrapper) { + border-left: 0; +} + +@media (prefers-reduced-motion: reduce) { + #storybook-sidebar-region * { scroll-behavior: auto; } +} + + diff --git a/.storybook/manager.js b/.storybook/manager.js new file mode 100644 index 0000000..fef8d37 --- /dev/null +++ b/.storybook/manager.js @@ -0,0 +1,55 @@ +import { createElement } from 'react' +import { addons } from 'storybook/manager-api' +import { create } from 'storybook/theming' +import { decorateHanmacNavigation } from './manager-navigation.js' + +// Match the measured Manager content background via its existing CSS variable. +// Keep mobile navigation, addon tabs, +// portalled menus and the preview iframe outside this toolbar scope. +const toolbarStyle = document.createElement('style') +toolbarStyle.id = 'hm-manager-toolbar-style' +toolbarStyle.textContent = ` + .sb-bar[data-testid="sb-preview-toolbar"] { + background: var(--color-bg-content); + color: #000000; + } + .sb-bar[data-testid="sb-preview-toolbar"] :is(button, a, [role="tab"], label, span, p, svg) { + color: inherit; + } + .sb-bar[data-testid="sb-preview-toolbar"] :is(button, a, [role="tab"]):hover { + background: rgba(0, 0, 0, 0.10); + } + .sb-bar[data-testid="sb-preview-toolbar"] :is(button, a, [role="tab"]):is([aria-checked="true"], [aria-expanded="true"], [aria-selected="true"]) { + background: rgba(0, 0, 0, 0.20); + } + .sb-bar[data-testid="sb-preview-toolbar"] :is(button, a, [role="tab"]):focus-visible { + outline-color: currentColor; + } +` +document.head.appendChild(toolbarStyle) + +addons.setConfig({ + layout: { + showPanel: false, + }, + theme: create({ + base: 'light', + brandTitle: 'HM Design System', + brandUrl: './', + brandTarget: '_self', + fontBase: "'Hanmac Manager Noto Sans KR', 'Noto Sans KR', 'Malgun Gothic', sans-serif", + colorPrimary: '#123328', + colorSecondary: '#123328', + appBg: '#F5F0E5', + appContentBg: '#FFFFFF', + appPreviewBg: '#FFFFFF', + textColor: '#123328', + textInverseColor: '#FFFFFF', + barSelectedColor: '#123328', + }), + sidebar: { + renderLabel: (item) => createElement('span', { className: 'hm-manager-label' }, item.name), + }, +}) + +addons.register('hm/manager-navigation-style', decorateHanmacNavigation) diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 0000000..fb54e21 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,2 @@ + + diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 0000000..53d0a83 --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,67 @@ +import type { Preview } from "@storybook/react-vite" +import { MINIMAL_VIEWPORTS } from "storybook/viewport" +import { Toaster } from "@hm-design/ui" +import { ensurePseudoStates } from "./pseudo-states" +import { DesignerThemePreview } from "../src/theme/designer-preview" +import "../src/index.css" +import "../src/hm-theme.css" + +const preview: Preview = { + parameters: { + layout: "centered", + viewport: { + options: { + // HM QA presets come first; keep Storybook's built-in choices available. + hmDesktop1280: { name: "HM Desktop · 1280", styles: { width: "1280px", height: "900px" }, type: "desktop" }, + hmDesktop1024: { name: "HM Desktop Boundary · 1024", styles: { width: "1024px", height: "900px" }, type: "desktop" }, + hmMobile1023: { name: "HM Mobile Boundary · 1023", styles: { width: "1023px", height: "900px" }, type: "mobile" }, + hmMobile390: { name: "HM Mobile · 390", styles: { width: "390px", height: "844px" }, type: "mobile" }, + ...MINIMAL_VIEWPORTS, + }, + }, + options: { + storySort: { + order: [ + "Foundation", + ["Color", "Typography", "Layout"], + "Components", + [ + "Form", + ["Button", "Input", "Textarea", "Select", "Checkbox", "Radio Group", "Switch", "Slider", "Toggle", "Toggle Group", "Label"], + "Navigation", + ["Tabs", "Breadcrumb", "Pagination", "Navigation Menu", "Menubar"], + "Overlay", + ["Dialog", "Alert Dialog", "Sheet", "Drawer", "Dropdown Menu", "Context Menu", "Popover", "Tooltip", "Hover Card"], + "Data & Display", + ["Card", "Table", "Badge", "Avatar", "Progress", "Skeleton", "Separator", "Aspect Ratio", "Carousel", "Scroll Area"], + "Disclosure", + ["Accordion", "Collapsible", "Resizable"], + "Search & Date", + ["Command", "Combobox", "Calendar", "Date Picker"], + "Feedback", + ["Alert", "Sonner"], + ], + "Patterns", + ["Data", ["Data Table", "Filter Bar"], "Layout", ["GNB", "LNB", "Dashboard Layout", "Loading Screen"]], + "Center Graphic", + "*", + ], + }, + }, + }, + decorators: [ + (Story, context) => { + // Lets state Stories pin hover / pressed with data-sb-hover / data-sb-active. + ensurePseudoStates() + // One Toaster for every Story, the same way an app mounts it once at its root. + return ( + + + + + ) + }, + ], +} + +export default preview diff --git a/.storybook/pseudo-states.ts b/.storybook/pseudo-states.ts new file mode 100644 index 0000000..35f7d9f --- /dev/null +++ b/.storybook/pseudo-states.ts @@ -0,0 +1,34 @@ +// Pins :hover and :active visuals for state Stories (Storybook only, never loaded by the app). +// Every rule that uses :hover or :active is cloned right after itself with the pseudo-class swapped for a +// data attribute, so cascade order and specificity stay the same. No declaration or value is authored here. +const HOVER = "data-sb-hover" +const ACTIVE = "data-sb-active" +const mirrored = new WeakSet() + +function mirror(list: CSSStyleSheet | CSSGroupingRule) { + for (let i = list.cssRules.length - 1; i >= 0; i--) { + const rule = list.cssRules[i] + if (rule instanceof CSSStyleRule) { + if (/:hover\b|:active\b/.test(rule.selectorText)) { + list.insertRule( + rule.cssText.replace(/:hover\b/g, `[${HOVER}]`).replace(/:active\b/g, `[${ACTIVE}]`), + i + 1, + ) + } + } else if ("cssRules" in rule) { + mirror(rule as CSSGroupingRule) + } + } +} + +export function ensurePseudoStates() { + for (const sheet of document.styleSheets) { + if (mirrored.has(sheet)) continue + mirrored.add(sheet) + try { + mirror(sheet) + } catch { + // Unreadable (cross-origin) sheet: nothing to mirror. + } + } +} diff --git a/.storybook/qa/LAN-PREVIEW-REPORT.md b/.storybook/qa/LAN-PREVIEW-REPORT.md new file mode 100644 index 0000000..4ba8524 --- /dev/null +++ b/.storybook/qa/LAN-PREVIEW-REPORT.md @@ -0,0 +1,180 @@ +# HMDesignSystem Storybook LAN Preview + +Result: **PARTIAL** — same-host LAN Preview is READY. A second physical PC was unavailable, so cross-PC access and cross-PC HMR remain NOT_VERIFIED. + +TEMP_STORYBOOK_URL: http://10.13.37.164:6008/ + +## 1. Worktree + +- Path: `D:\디자인시스템\HMDesignSystem-storybook` +- Branch: `chore/storybook-cleanup` +- HEAD: `6b4a39e4e9fef7cd706630ffc72b5e97e8696823` +- Remote: `https://gitea.hmac.kr/b25027/HMDesign.git` +- Track A Modified: **NO** +- Track A remains clean on `main`, HEAD `4e2402e6cb3507c4bb5e58549004323db59b66a8`. + +## 2. Storybook Server + +- Installed binary: Storybook **10.6.1** from Track B node_modules; dependencies already existed, so npm ci was unnecessary. +- Port: **6008**; Bind: **0.0.0.0**; Server: **RUNNING** +- LAN listener PID at verification: **23804** +- Command: `node node_modules/storybook/dist/bin/dispatcher.js dev -p 6008 --host 0.0.0.0 --no-open --disable-telemetry --exact-port` +- Local: http://localhost:6008/ +- LAN: http://10.13.37.164:6008/ +- Verified current IPv4: **10.13.37.164** (Ethernet 3); this is a temporary LAN address, not a permanent URL. +- The old LAN process was started on 6007 and had fallen back to 6008. Only that confirmed Track B LAN process was replaced with explicit 6008/exact-port operation. The existing localhost-only Track B process (PID 33604) was preserved; both addresses use the same worktree and matched 289 Story IDs. +- package.json's official **6007** script was unchanged. Track A and other ports/processes were not stopped. +- Server stdout/stderr are in ignored `.storybook/qa/lan-preview.*.log` files. PIDs are session observations, not durable shutdown instructions. + +## 3. LAN Verification + +- localhost: **PASS**, HTTP 200 and Button render +- LAN IP Same Host: **PASS**, HTTP 200; manager, sidebar, preview, navigation and representative stories loaded +- SECOND_PHYSICAL_PC: **NOT_VERIFIED** +- HMR localhost: **PASS** +- HMR LAN Same Host: **PASS** +- HMR Second Physical PC: **NOT_VERIFIED** +- A temporary visible `HM LAN HMR QA` paragraph was inserted into GNB All. Both origins displayed it without reload. Removal was also applied by HMR; the Story file was restored to its exact pre-probe SHA-256. No temporary probe remains. +- Existing firewall policy was inspected: Public network profile, enabled firewall profiles and pre-existing inbound Node allow rules. Firewall rule additions: **NO**. Network profile changes: **NO**. + +## 4. Storybook Chrome + +- Top Bar: **#266651 PASS** (`rgb(38, 102, 81)`) +- Toolbar icon/text: **#FFFFFF PASS** (all sampled button/SVG computed colors were white) +- Bottom Panel: **DEFAULT_HIDDEN PASS** +- Alt+A: **PASS**, panel opened with Controls/Actions/Interactions; hidden state restored afterward +- Preview Rail: **0px PASS**, preview main border-left and padding-left measured 0px +- Final browser viewport overrides were reset, and a LAN Dashboard tab remains available in Codex. + +## 5. Viewports + +Measured in Chrome, browser zoom 100%, Storybook zoom 100%, devicePixelRatio 1: + +- 1280: **PASS** — iframe / innerWidth 1280; innerHeight 900; GNB nav visible +- 1024: **PASS** — iframe / innerWidth 1024; innerHeight 900; GNB nav visible; LNB column visible +- 1023: **PASS** — iframe / innerWidth 1023; innerHeight 900; GNB nav hidden; LNB column hidden and Local mobile bar visible +- 390: **PASS** — iframe / innerWidth 390; innerHeight 844; mobile UI visible +- GNB Mobile Menu Open automatically selected 390 × 844 and its Sheet was visible. +- Existing LNB `hmMobile` preset still selected 390 × 844. Its service-menu Sheet opened successfully, including the nested three-depth menu. +- **Browser limitation:** the Codex in-app browser had a fractional-width anomaly at the 1023 boundary: rounded innerWidth was 1023 but both max-width 1023 and min-width 1024 media queries returned false. Chrome passed both boundaries. Production CSS and breakpoints were not changed; exact boundary QA should use Chrome. + +## 6. Representative Stories + +LAN rendering and navigation checks passed for: + +- Button: **PASS** — representative variants/sizes/state board +- GNB: **PASS** — four viewport measurements and mobile menu +- LNB: **PASS** — representative board, both embedded standalone iframes, desktop/mobile boundary and mobile Sheet +- Dashboard: **PASS** — current heading, status filter and data table rendered +- Calendar: **PASS** — single/disabled/range/two-month board +- Date Picker: **PASS** — empty/selected/format/controlled/size/disabled board +- Theme Designer: **PASS** — editor/live preview and successful application of `/themes/default.json` and `/themes/service-example.json`; reset to HM Default afterward +- CENTER: **PASS** — Title compositions and Tunnel reference render + +These are targeted rendering/navigation/responsive checks, not a full interaction or accessibility audit of every Story. + +## 7. Story Cleanup + +- Story Files: **61**; Stories: **289** +- Hierarchy: **Foundation → Components → Patterns → Center Graphic → Theme** +- PRIMARY: **65** +- VERIFICATION: **211** +- UTILITY: **13** (executable verification, compatibility and playground tools) +- Proven LEGACY / DUPLICATE: **0** +- KEEP: **65**; HIDE: **224** through the existing verification filter +- MERGE_CANDIDATE: **31**, advisory possible overlap with representative boards; all retained +- DELETE_CANDIDATE: **0**; deleted stories: **0** +- Full source-defined meta.title, Story export/name and meta/story/custom effective tags: `STORY-INVENTORY.md` and `story-inventory.json`. +- Framework-injected dev/test/manifest tags are in compiled index.json rather than the source-tag inventory. +- Before / built / inventory Story IDs: **289 / 289 / 289**, with no missing, added or renamed IDs. +- LNB `iframe.html?id=patterns-layout-lnb--standalone-desktop` and `--standalone-mobile` references were found and both nested frames rendered. +- Own Story change: GNB Mobile and Mobile Menu Open now start on global `hmMobile390`. Existing global presets, sorting, verification filtering and LNB 390 configuration were retained. + +## 8. Errors + +- Console: **no new errors observed** in the representative checks or final LAN tab; no Story not found, manager/addon or HMR errors observed after verification. +- Network: **no asset 404 observed**. Direct LAN HTTP probes returned 200 for the font, two Theme JSON files, preview CSS, Vite client, manager JS and checked Story modules. LNB nested previews loaded successfully. +- Known browser warning: Storybook 11 will require PopoverProvider ariaLabel. It was already present in the initial observation. +- Known build warnings: documentation-example `bg-[var(...)]` CSS optimizer warning, chunks over 500kB, and Vite plugin timing notices. The repository's existing reports already document the CSS/chunk warnings. They did not fail the build. +- Git emitted LF/CRLF normalization notices; `git diff --check` still passed. + +## 9. Validation + +- `npm run typecheck`: **PASS**, exit 0; generated Theme defaults matched, both workspace typechecks passed +- `npm run build-storybook`: **PASS**, exit 0; local ignored `storybook-static/` output only +- `git diff --check`: **PASS**, exit 0 +- Final integration validation source stability: **PASS**, no TS/TSX/JS/CSS/HTML files changed during the final check window +- During an earlier validation window, another active task changed Dashboard Story/CSS. Those changes were preserved, and integration checks were rerun against the stable final source state, including the later concurrent changeDetection option in main.ts. + +## 10. Parallel Safety + +- packages/**: **UNCHANGED** (Production source; git clean for protected paths) +- themes/**: **UNCHANGED** +- package.json / package-lock.json: **UNCHANGED** +- Protected file SHA-256 comparison: **277 files, 0 changes** from this session's baseline +- Track A: **UNTOUCHED**, clean main and same HEAD +- Package source edits: **NO** +- package publish: **NO**; package version changes: **NO** +- Runtime Theme contract/loader, exports/CSS contract and Gitea CI/CD changes: **NO** +- Publishing Page QA / Publishing staticDirs / official hosting work: **NOT PERFORMED** + +## 11. Git + +Own changed/new files: + +- `src/patterns/gnb.stories.tsx` +- `.storybook/qa/generate-inventory.mjs` +- `.storybook/qa/story-inventory.json` +- `.storybook/qa/STORY-INVENTORY.md` +- `.storybook/qa/LAN-PREVIEW.md` +- `.storybook/qa/LAN-PREVIEW-REPORT.md` + +Pre-existing or concurrent changes were preserved: `.storybook/main.ts`, `.storybook/manager.css`, `.storybook/preview.tsx`, `.storybook/preview-head.html`, `src/patterns/dashboard-layout.stories.tsx`, its Story-only CSS, `src/theme/designer-theme.stories.tsx`, and the six files in `src/foundation/`. + +- Commit: **NOT CREATED** +- Merge: **NOT PERFORMED** +- Rebase / cherry-pick: **NOT PERFORMED** +- No automatic main integration or push was performed. + +## 12. Status + +- LAN TEMP PREVIEW: **READY** for Storybook cleanup and same-host LAN QA +- Official Static Hosting: **NOT CONFIGURED** by this batch +- This URL is an official deployment URL: **NO** +- Other physical PCs can be declared verified: **NO**, pending actual cross-PC access + +## Required answers + +1. Storybook Worktree execution: **YES**, Track B. +2. Execution on main: **NO**. +3. 6008 bound to 0.0.0.0: **YES**. +4. localhost:6008 opens: **YES**. +5. 10.13.37.164:6008 opens on the same PC: **YES**. +6. Opens from another physical PC: **NOT_VERIFIED**. +7. Missing cross-PC verification explicitly marked: **YES**. +8. Top Bar #266651 maintained: **YES**. +9. Toolbar icon/text white: **YES**. +10. Bottom panel default hidden: **YES**. +11. Preview rail 0px: **YES**. +12. 1280 preset available and measured: **YES**. +13. 1024 preset available and measured: **YES**. +14. 1023 preset available and measured: **YES**. +15. 390 preset available and measured: **YES**. +16. GNB/LNB 1024→1023 transition: **PASS in Chrome**, in-app-browser caveat above. +17. Story HMR works: **YES**. +18. Same-host LAN receives HMR changes: **YES**; cross-PC HMR **NOT_VERIFIED**. +19. New console errors observed: **NO**. +20. Asset 404 observed: **NO** in checked resources/screens. +21. Track A modified: **NO**. +22. Package source modified: **NO**. +23. Package publish repeated: **NO**. +24. Official deployment URL: **NO**. +25. Current preview usable for Storybook cleanup: **YES**; second physical PC validation remains outstanding. + +## Time tracking (Asia/Seoul) + +- Started At: 2026-10-07 15:31:24 KST +- Completed At: 2026-10-07 15:56:10 KST +- Actual Duration: 24분 46초 +- Estimated Duration: 35~50분 +- Variance: 예상보다 10분 14초 ~ 25분 14초 짧음 diff --git a/.storybook/qa/LAN-PREVIEW.md b/.storybook/qa/LAN-PREVIEW.md new file mode 100644 index 0000000..7c2d046 --- /dev/null +++ b/.storybook/qa/LAN-PREVIEW.md @@ -0,0 +1,78 @@ +# Track B temporary Storybook LAN preview + +Purpose: **TEMPORARY INTERNAL LAN PREVIEW / LAN TEMP PREVIEW** for Storybook cleanup. This is a development server with HMR, not official static hosting or a permanent URL. + +- Worktree: `D:\디자인시스템\HMDesignSystem-storybook` +- Branch: `chore/storybook-cleanup` +- Port: `6008`; bind: `0.0.0.0` +- TEMP_STORYBOOK_URL: http://10.13.37.164:6008/ +- Local: http://localhost:6008/ +- IPv4 `10.13.37.164` was verified on 2026-10-07. Recheck it before later sharing; do not assume a fixed address. + +## Start and inspect + +Run from Track B in PowerShell: + +```powershell +Set-Location -LiteralPath 'D:\디자인시스템\HMDesignSystem-storybook' +git status -sb +git branch --show-current +git rev-parse HEAD +# Only when node_modules is absent: +# npm ci +.\node_modules\.bin\storybook.cmd dev -p 6008 --host 0.0.0.0 --no-open --disable-telemetry --exact-port +``` + +The installed Storybook binary is version 10.6.1. `--exact-port` prevents silent fallback to another port. The official package.json script stays on 6007. Do not use that script without overriding the port for Track B. If 6008 is occupied, identify its process before stopping or replacing it. + +```powershell +Get-NetIPAddress -AddressFamily IPv4 +Get-NetTCPConnection -State Listen -LocalPort 6008 | + Select-Object LocalAddress, LocalPort, OwningProcess +# For a known PID from the result: +# Get-CimInstance Win32_Process -Filter 'ProcessId=' | +# Select-Object ProcessId, CommandLine +``` + +Keep the server running while the preview is in use. For shutdown, stop only the confirmed Track B listener (Ctrl+C in its owning terminal, or its identified PID). Do not stop other tracks or other Node processes. + +The 2026-10-07 session reused an existing localhost-only Track B listener and replaced only the LAN listener with an explicit 6008 command. Thus localhost and LAN may be served by different processes using the same Track B source. Verify both addresses after subsequent changes. + +## QA viewports + +Use Chrome at browser zoom 100% and Storybook zoom 100%. Select a preset from the toolbar; use enough browser height to avoid clipping the configured iframe height. + +- HM Desktop · 1280: 1280 × 900 +- HM Desktop Boundary · 1024: 1024 × 900; Desktop GNB/LNB +- HM Mobile Boundary · 1023: 1023 × 900; Mobile GNB/LNB +- HM Mobile · 390: 390 × 844 + +These are QA presets, not production breakpoint changes. GNB Mobile and Mobile Menu Open start at the shared `hmMobile390` preset. LNB's existing `hmMobile` 390 × 844 preset and direct iframe Story IDs remain intact. + +The Codex in-app browser showed a fractional-width boundary anomaly at 1023: `innerWidth` rounded to 1023 while both `(max-width: 1023px)` and `(min-width: 1024px)` were false. Chrome at devicePixelRatio 1 passed the exact boundary. Use Chrome for these two boundary checks; do not compensate by changing Production CSS. + +## Hierarchy and classification + +The sidebar order remains Foundation → Components → Patterns → Center Graphic → Theme. Existing `verification` filtering hides QA/tool stories by default while preserving direct URLs and tag-filter access. + +Refresh the full inventory from Track B: + +```powershell +node .storybook/qa/generate-inventory.mjs +``` + +See `STORY-INVENTORY.md` and `story-inventory.json`. The helper uses the installed Storybook 10.6.1 CSF parser and reads every `src/**/*.stories.ts(x)` file. Tags in this inventory are source-defined meta/story tags; framework-generated dev/test/manifest tags are available in Storybook's index.json. + +KEEP and HIDE describe current navigation. MERGE_CANDIDATE is an advisory review flag, not a removal decision. The current batch does not delete stories or rename titles/exports/IDs. Confirm equivalent QA coverage and search references before future merging or deletion. + +## Preserve the boundary + +All edits must stay in Storybook configuration, stories, or Storybook-only helpers/layout/documentation. Do not modify Track A, packages, themes, Runtime Theme contracts/loaders, package exports/CSS contracts, package versions, publishing, CI/CD, staticDirs for Publishing, or official hosting. Do not merge, rebase, or cherry-pick this batch into main. + +For each completed cleanup batch, run typecheck, build-storybook, and git diff --check against the final source state. Build outputs are validation artifacts, not a deployment. + +## LAN verification + +Same-host LAN IP success does not prove access from another physical PC. On a second physical PC, check manager/sidebar/preview, fonts/themes/assets, navigation and HMR. Until then record **SECOND_PHYSICAL_PC: NOT_VERIFIED**. + +The current network profile was Public, with firewall profiles enabled and pre-existing inbound Node allow rules. No firewall rule or network profile was changed. If a second PC cannot connect, diagnose routing, client isolation and existing firewall policy; do not automatically add rules or change profiles. diff --git a/.storybook/qa/STORY-INVENTORY.md b/.storybook/qa/STORY-INVENTORY.md new file mode 100644 index 0000000..60ed74f --- /dev/null +++ b/.storybook/qa/STORY-INVENTORY.md @@ -0,0 +1,614 @@ +# Internal Story inventory (ZIP excluded) + +Current references reconciled against the running Storybook index (288 entries). The historical Track B generator is not a recipient tool; this QA record is excluded from the test ZIP. + +This is a classification record. No Story IDs, exports or titles are renamed. HIDE uses the existing verification filter, not deletion. MERGE_CANDIDATE is advisory and can coexist with HIDE. Utility includes executable checks and compatibility/playground tools. No legacy or delete candidate is asserted without evidence. + +## Summary + +- PRIMARY: 65 +- VERIFICATION: 210 +- UTILITY: 13 +- LEGACY / DUPLICATE: 0 +- KEEP: 65 +- HIDE: 223 +- MERGE_CANDIDATE: 31 +- DELETE_CANDIDATE: 0 +- Story files: 61 +- Stories: 288 +- Hierarchy: Foundation → Components → Patterns → Center Graphic → Theme + +## Full inventory + +### Center Graphic/Facility + +Source: `src/center/center-facility.stories.tsx`; meta.tags: [] + +- Tunnel — 터널; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Bridge — 교량; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Intersection — 교차로; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Junction — 분기점; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Interchange — 나들목; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Roadside — 휴게소 · 졸음쉼터; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Linked — 연계 시설물; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- TunnelPlayground — 직접 테스트 (터널); tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Center Graphic/Information + +Source: `src/center/center-information.stories.tsx`; meta.tags: [] + +- Information — Information; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Center Graphic/Overview + +Source: `src/center/center-overview.stories.tsx`; meta.tags: [] + +- Overview — Overview; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Center Graphic/Spatial + +Source: `src/center/center-spatial.stories.tsx`; meta.tags: [] + +- Spatial — Spatial; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Center Graphic/Title + +Source: `src/center/center-title.stories.tsx`; meta.tags: [] + +- Compositions — Title; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Disclosure/Accordion + +Source: `src/components/accordion.stories.tsx`; meta.tags: [] + +- All — Accordion; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Single — Single; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Multiple — Multiple; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Alert Dialog + +Source: `src/components/alert-dialog.stories.tsx`; meta.tags: [] + +- All — Alert Dialog; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- ConfirmationOpen — Confirmation; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- DestructiveOpen — Destructive; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Feedback/Alert + +Source: `src/components/alert.stories.tsx`; meta.tags: [] + +- All — Alert; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Destructive — Destructive; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Aspect Ratio + +Source: `src/components/aspect-ratio.stories.tsx`; meta.tags: [] + +- All — Aspect Ratio; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Wide — 16:9; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Square — 1:1; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Avatar + +Source: `src/components/avatar.stories.tsx`; meta.tags: [] + +- All — Avatar; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Image — Image; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- FallbackError — Fallback; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Badge + +Source: `src/components/badge.stories.tsx`; meta.tags: [] + +- All — Badge; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Variants — Variants; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Navigation/Breadcrumb + +Source: `src/components/breadcrumb.stories.tsx`; meta.tags: [] + +- All — Breadcrumb; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Basic3 — Basic; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WithEllipsis — Ellipsis; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongPath — Long Path; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Button + +Source: `src/components/button.stories.tsx`; meta.tags: [] + +- All — Button; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Roles — Roles; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Sizes — Sizes; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- States — Brand States; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- StandardStates — Standard States; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongLabel — Long Label; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- KoreanLabels — Korean Labels; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- IconTextNarrow — Icon Text Narrow; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Destructive — Destructive; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Compatibility — Compatibility; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Search & Date/Calendar + +Source: `src/components/calendar.stories.tsx`; meta.tags: [] + +- All — Calendar; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- SingleSelection — Single; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- DisabledSelection — Disabled Dates; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- RangeSelection — Range; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Card + +Source: `src/components/card.stories.tsx`; meta.tags: [] + +- All — Card; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Surfaces — Surfaces; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Action — Action; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Compatibility — Compatibility; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Carousel + +Source: `src/components/carousel.stories.tsx`; meta.tags: [] + +- All — Carousel; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- BasicCarousel — Basic; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- MultipleItems — Multiple Items; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LoopCarousel — Loop; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- VerticalCarousel — Vertical; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Checkbox + +Source: `src/components/checkbox.stories.tsx`; meta.tags: [] + +- All — Checkbox; tags: ["play-fn"]; PRIMARY; KEEP. Representative story or distinct reference page. +- Unchecked — Unchecked; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Checked — Checked; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Indeterminate — Indeterminate; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Error — Error; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Disclosure/Collapsible + +Source: `src/components/collapsible.stories.tsx`; meta.tags: [] + +- All — Collapsible; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Closed — Closed; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Search & Date/Combobox + +Source: `src/components/combobox.stories.tsx`; meta.tags: [] + +- All — Combobox; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- EmptyCombobox — Empty; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ControlledCombobox — Controlled; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongOption — Long Option; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenBottom — Open Bottom; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenTop — Open Top; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Search & Date/Command + +Source: `src/components/command.stories.tsx`; meta.tags: [] + +- All — Command; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Search — Search; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- GroupedList — Grouped; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Context Menu + +Source: `src/components/context-menu.stories.tsx`; meta.tags: [] + +- All — Context Menu; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Search & Date/Date Picker + +Source: `src/components/date-picker.stories.tsx`; meta.tags: [] + +- All — Date Picker; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- EmptyPicker — Empty; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- InvalidFocus — Invalid Focus; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SelectedPicker — Selected; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ControlledPicker — Controlled; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Dialog + +Source: `src/components/dialog.stories.tsx`; meta.tags: [] + +- All — Dialog; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongContentOpen — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WideContentOpen — Wide Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- BodyScrollOpen — Body Scroll; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- DeleteConfirmationOpen — Delete Confirmation; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Drawer + +Source: `src/components/drawer.stories.tsx`; meta.tags: [] + +- All — Drawer; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Form — Form; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Dropdown Menu + +Source: `src/components/dropdown-menu.stories.tsx`; meta.tags: [] + +- All — Dropdown Menu; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Hover Card + +Source: `src/components/hover-card.stories.tsx`; meta.tags: [] + +- All — Hover Card; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Input + +Source: `src/components/input.stories.tsx`; meta.tags: [] + +- All — Input; tags: ["play-fn"]; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Focus — Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Error — Error; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- InvalidFocus — Invalid Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ErrorMessage — Error Message; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Required — Required; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Readonly — Readonly; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Sizes — Compare Sizes; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Small — Small; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Medium — Medium; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Large — Large; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Label + +Source: `src/components/label.stories.tsx`; meta.tags: [] + +- All — Label; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Components/Navigation/Menubar + +Source: `src/components/menubar.stories.tsx`; meta.tags: [] + +- All — Menubar; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- TriggerFocus — Trigger Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenFocus — Open Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Navigation/Navigation Menu + +Source: `src/components/navigation-menu.stories.tsx`; meta.tags: [] + +- All — Navigation Menu; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- NoViewport — No Viewport; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- IndicatorAlignment — Indicator Alignment; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Navigation/Pagination + +Source: `src/components/pagination.stories.tsx`; meta.tags: [] + +- All — Pagination; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- FirstPage — First Page; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LastPage — Last Page; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongRange — Long Range; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Popover + +Source: `src/components/popover.stories.tsx`; meta.tags: [] + +- All — Popover; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Progress + +Source: `src/components/progress.stories.tsx`; meta.tags: [] + +- All — Progress; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Zero — Zero; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Middle — Middle; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Full — Full; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- NonDefaultMax — Non Default Max; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Radio Group + +Source: `src/components/radio-group.stories.tsx`; meta.tags: [] + +- All — Radio Group; tags: ["play-fn"]; PRIMARY; KEEP. Representative story or distinct reference page. +- Vertical — Vertical; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Horizontal — Horizontal; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Error — Error; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Disclosure/Resizable + +Source: `src/components/resizable.stories.tsx`; meta.tags: [] + +- All — Resizable; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- HorizontalLayout — Horizontal; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- VerticalLayout — Vertical; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- MinMaxLayout — Min Max; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Scroll Area + +Source: `src/components/scroll-area.stories.tsx`; meta.tags: [] + +- All — Scroll Area; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- VerticalScroll — Vertical; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- HorizontalScroll — Horizontal; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Select + +Source: `src/components/select.stories.tsx`; meta.tags: [] + +- All — Select; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- InvalidFocus — Invalid Focus; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Selected — Selected; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Sizes — Compare Sizes; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Medium — Medium; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Large — Large; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenLargeShort — Open Large Short; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenMediumLong — Open Medium Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenLargeLong — Open Large Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenTop — Open Top; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- OpenTopLong — Open Top Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ListSizeComparison — Medium Trigger / List Size Comparison; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Scroll — Scroll; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Separator + +Source: `src/components/separator.stories.tsx`; meta.tags: [] + +- All — Separator; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Horizontal — Horizontal; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Vertical — Vertical; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. + +### Components/Overlay/Sheet + +Source: `src/components/sheet.stories.tsx`; meta.tags: [] + +- All — Sheet; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Right — Right; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Left — Left; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Top — Top; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Bottom — Bottom; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Form — Form; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Data & Display/Skeleton + +Source: `src/components/skeleton.stories.tsx`; meta.tags: [] + +- All — Skeleton; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Text — Text; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- CardList — Card List; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Slider + +Source: `src/components/slider.stories.tsx`; meta.tags: [] + +- All — Slider; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- SingleValue — Single; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- RangeValue — Range; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- DisabledSlider — Disabled; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Focus — Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Feedback/Sonner + +Source: `src/components/sonner.stories.tsx`; meta.tags: [] + +- All — Sonner; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Components/Form/Switch + +Source: `src/components/switch.stories.tsx`; meta.tags: [] + +- All — Switch; tags: ["play-fn"]; PRIMARY; KEEP. Representative story or distinct reference page. +- Off — Off; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- On — On; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. + +### Components/Data & Display/Table + +Source: `src/components/table.stories.tsx`; meta.tags: [] + +- All — Table; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Basic — Basic; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Selected — Selected; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- LongText — Long Text; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Overflow — Horizontal Overflow; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Navigation/Tabs + +Source: `src/components/tabs.stories.tsx`; meta.tags: [] + +- All — Tabs; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Secondary — Secondary; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SelectedFirst — Selected First; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SelectedLast — Selected Last; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Focus — Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Overflow — Overflow; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SecondaryOverflow — Secondary Overflow; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongLabel — Long Label; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Textarea + +Source: `src/components/textarea.stories.tsx`; meta.tags: [] + +- All — Textarea; tags: ["play-fn"]; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Focus — Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Error — Error; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- InvalidFocus — Invalid Focus; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ErrorMessage — Error Message; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Required — Required; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Readonly — Readonly; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Disabled — Disabled; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Playground — Playground; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Toggle Group + +Source: `src/components/toggle-group.stories.tsx`; meta.tags: [] + +- All — Toggle Group; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- SingleSelection — Single; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- MultipleSelection — Multiple; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- VerticalGroup — Vertical; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ContentNodes — Content Nodes; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Form/Toggle + +Source: `src/components/toggle.stories.tsx`; meta.tags: [] + +- All — Toggle; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- States — States; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- IconOnly — Icon Only; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- DisabledToggle — Disabled; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ContentNodes — Content Nodes; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Components/Overlay/Tooltip + +Source: `src/components/tooltip.stories.tsx`; meta.tags: [] + +- All — Tooltip; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Open — Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Foundation/Color + +Source: `src/foundation/color.stories.tsx`; meta.tags: [] + +- All — Color; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Foundation/Layout + +Source: `src/foundation/layout.stories.tsx`; meta.tags: [] + +- All — Layout; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Foundation/Typography + +Source: `src/foundation/typography.stories.tsx`; meta.tags: [] + +- All — Typography; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Patterns/Layout/Dashboard + +Source: `src/patterns/dashboard-layout.stories.tsx`; meta.tags: [] + +- All — Dashboard; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- WithSidebar — Dashboard Header + Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ThirdLevel — Third Level; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SidebarOnly — Dashboard Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Loading — Loading; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Empty — Empty; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Framed — Framed; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Patterns/Data/Data Table + +Source: `src/patterns/data-table.stories.tsx`; meta.tags: [] + +- All — Data Table; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- WithFilterBarStory — With Filter Bar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Loading — Loading; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Empty — Empty; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Patterns/Data/Filter Bar + +Source: `src/patterns/filter-bar.stories.tsx`; meta.tags: [] + +- All — Filter Bar; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + +### Patterns/Layout/GNB + +Source: `src/patterns/gnb.stories.tsx`; meta.tags: [] + +- All — GNB; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Sticky — Sticky; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- NavigationMenuOpen — Navigation Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ShortDepthOpen — Short Depth Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- RightEdgeLongDepth — Right Edge Long Depth; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- FirstDepthShort — First Depth Short; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- FirstDepthLong — First Depth Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- MiddleDepthShort — Middle Depth Short; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- MiddleDepthLong — Middle Depth Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LastDepthShort — Last Depth Short; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LastDepthLong — Last Depth Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- EdgeFirstLong — Edge First Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- EdgeLastLong — Edge Last Long; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- UserMenuOpen — User Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongLabels — Long Labels; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WithLogo — With Logo; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Utilities — Utilities; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Mobile — Mobile; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- MobileMenuOpen — Mobile Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Patterns/Layout/LNB + +Source: `src/patterns/lnb.stories.tsx`; meta.tags: [] + +- All — LNB; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Selected — Selected; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. +- Expanded — Expanded; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SelectedChild — Selected Child; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- CollapsedWithSelection — Collapsed With Selection; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WithLogo — With Logo; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongLabels — Long Labels; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Scroll — Scroll; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ThirdLevel — Third Level; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ThirdLevelLongLabels — Third Level Long Labels; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ThirdLevelScroll — Third Level Scroll; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WithGNB — With GNB; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- StandaloneDesktop — LNB Standalone Desktop; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LNBProfileMenuOpen — LNB Profile Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- StandaloneMobile — LNB Standalone Mobile; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- StandaloneMobileMenuOpen — LNB Standalone Mobile Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Patterns/Layout/Loading Screen + +Source: `src/patterns/loading-screen.stories.tsx`; meta.tags: [] + +- All — Loading Screen; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Inline — Inline; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Theme/Designer Playground + +Source: `src/theme/designer-theme.stories.tsx`; meta.tags: [] + +- Editor — Designer Playground; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- LiveVerification — Live Verification; tags: ["verification","play-fn"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Theme/Runtime Contract/Loader + +Source: `src/theme/runtime-loader.stories.tsx`; meta.tags: ["verification"] + +- CdnVerification — Cdn Verification; tags: ["verification","play-fn"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + +### Theme/Runtime Contract + +Source: `src/theme/runtime-theme.stories.tsx`; meta.tags: ["verification"] + +- Comparison — Comparison; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- GlobalExternal — Global External; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Verification — Verification; tags: ["verification","play-fn"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Dashboard — Dashboard; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. diff --git a/.storybook/qa/generate-inventory.mjs b/.storybook/qa/generate-inventory.mjs new file mode 100644 index 0000000..8511d8c --- /dev/null +++ b/.storybook/qa/generate-inventory.mjs @@ -0,0 +1,38 @@ +import fs from 'node:fs' +import path from 'node:path' +import { loadCsf } from 'storybook/internal/csf-tools' + +// Run from the Track B root: node .storybook/qa/generate-inventory.mjs +// Only writes Storybook documentation; never renames or removes stories. +const root = process.cwd() +if (path.basename(root) !== 'HMDesignSystem-storybook') throw new Error('Run only from Track B') +const walk = dir => fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => e.isDirectory() ? walk(path.join(dir, e.name)) : [path.join(dir, e.name)]) +const files = walk(path.join(root, 'src')).filter(f => /\.stories\.tsx?$/.test(f)).sort() +const inventory = files.map(file => { + const csf = loadCsf(fs.readFileSync(file, 'utf8'), { makeTitle: title => title }).parse() + const title = csf.meta.title + if (!title) throw new Error(`Missing meta.title: ${file}`) + const metaTags = csf.meta.tags ?? [] + const stories = Object.entries(csf._stories).map(([exportName, story]) => { + const storyTags = story.tags ?? [] + const effectiveTags = [...new Set([...metaTags, ...storyTags])] + const utility = /Playground|Compatibility/.test(exportName) || /Verification$/.test(exportName) || exportName === 'Verification' + const category = utility ? 'UTILITY' : effectiveTags.includes('verification') ? 'VERIFICATION' : 'PRIMARY' + const duplicateCandidate = effectiveTags.includes('verification') && /^(Default|Sizes|Roles|Variants|States|Basic|Selected|Disabled|Horizontal|Vertical)$/.test(exportName) + return { id: story.id, exportName, name: story.name, storyTags, effectiveTags, category, decision: effectiveTags.includes('verification') ? 'HIDE' : 'KEEP', candidate: duplicateCandidate ? 'MERGE_CANDIDATE' : null, reason: duplicateCandidate ? 'Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed.' : effectiveTags.includes('verification') ? 'Keep direct URL and tag-filter access; excluded from default sidebar.' : 'Representative story or distinct reference page.' } + }) + return { file: path.relative(root, file).replaceAll('\\', '/'), title, metaTags, stories } +}) +const all = inventory.flatMap(f => f.stories) +const summary = { storyFiles: files.length, stories: all.length, categories: Object.fromEntries(['PRIMARY', 'VERIFICATION', 'UTILITY', 'LEGACY / DUPLICATE'].map(c => [c, all.filter(s => s.category === c).length])), decisions: Object.fromEntries(['KEEP', 'HIDE', 'MERGE_CANDIDATE', 'DELETE_CANDIDATE'].map(c => [c, all.filter(s => s.decision === c || s.candidate === c).length])), hierarchy: ['Foundation', 'Components', 'Patterns', 'Center Graphic', 'Theme'] } +const output = path.join(root, '.storybook', 'qa') +fs.mkdirSync(output, { recursive: true }) +fs.writeFileSync(path.join(output, 'story-inventory.json'), JSON.stringify({ summary, inventory }, null, 2) + '\n') +const lines = ['# Track B Story inventory', '', 'Generated from every `src/**/*.stories.ts(x)` using the installed Storybook CSF parser. Run `node .storybook/qa/generate-inventory.mjs` from Track B to refresh.', '', 'This is a classification record. No Story IDs, exports or titles are renamed. HIDE uses the existing verification filter, not deletion. MERGE_CANDIDATE is advisory and can coexist with HIDE. Utility includes executable checks and compatibility/playground tools. No legacy or delete candidate is asserted without evidence.', '', '## Summary', '', ...Object.entries(summary.categories).map(([k,v]) => `- ${k}: ${v}`), ...Object.entries(summary.decisions).map(([k,v]) => `- ${k}: ${v}`), `- Story files: ${summary.storyFiles}`, `- Stories: ${summary.stories}`, `- Hierarchy: ${summary.hierarchy.join(' → ')}`, '', '## Full inventory', ''] +for (const f of inventory) { + lines.push(`### ${f.title}`, '', `Source: \`${f.file}\`; meta.tags: ${JSON.stringify(f.metaTags)}`, '') + for (const s of f.stories) lines.push(`- ${s.exportName} — ${s.name}; tags: ${JSON.stringify(s.effectiveTags)}; ${s.category}; ${s.decision}${s.candidate ? `; ${s.candidate}` : ''}. ${s.reason}`) + lines.push('') +} +fs.writeFileSync(path.join(output, 'STORY-INVENTORY.md'), lines.join('\n')) +console.log(JSON.stringify(summary)) diff --git a/.storybook/qa/story-inventory.json b/.storybook/qa/story-inventory.json new file mode 100644 index 0000000..0d28449 --- /dev/null +++ b/.storybook/qa/story-inventory.json @@ -0,0 +1,4556 @@ +{ + "summary": { + "storyFiles": 61, + "stories": 288, + "categories": { + "PRIMARY": 65, + "VERIFICATION": 210, + "UTILITY": 13, + "LEGACY / DUPLICATE": 0 + }, + "decisions": { + "KEEP": 65, + "HIDE": 223, + "MERGE_CANDIDATE": 31, + "DELETE_CANDIDATE": 0 + }, + "hierarchy": [ + "Foundation", + "Components", + "Patterns", + "Center Graphic", + "Theme" + ] + }, + "inventory": [ + { + "file": "src/center/center-facility.stories.tsx", + "title": "Center Graphic/Facility", + "metaTags": [], + "stories": [ + { + "id": "center-graphic-facility--tunnel", + "exportName": "Tunnel", + "name": "터널", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--bridge", + "exportName": "Bridge", + "name": "교량", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--intersection", + "exportName": "Intersection", + "name": "교차로", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--junction", + "exportName": "Junction", + "name": "분기점", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--interchange", + "exportName": "Interchange", + "name": "나들목", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--roadside", + "exportName": "Roadside", + "name": "휴게소 · 졸음쉼터", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--linked", + "exportName": "Linked", + "name": "연계 시설물", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-facility--tunnel-playground", + "exportName": "TunnelPlayground", + "name": "직접 테스트 (터널)", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/center/center-information.stories.tsx", + "title": "Center Graphic/Information", + "metaTags": [], + "stories": [ + { + "id": "center-graphic-information--information", + "exportName": "Information", + "name": "Information", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/center/center-overview.stories.tsx", + "title": "Center Graphic/Overview", + "metaTags": [], + "stories": [ + { + "id": "center-graphic-overview--overview", + "exportName": "Overview", + "name": "Overview", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/center/center-spatial.stories.tsx", + "title": "Center Graphic/Spatial", + "metaTags": [], + "stories": [ + { + "id": "center-graphic-spatial--spatial", + "exportName": "Spatial", + "name": "Spatial", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/center/center-title.stories.tsx", + "title": "Center Graphic/Title", + "metaTags": [], + "stories": [ + { + "id": "center-graphic-title--compositions", + "exportName": "Compositions", + "name": "Title", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "center-graphic-title--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/accordion.stories.tsx", + "title": "Components/Disclosure/Accordion", + "metaTags": [], + "stories": [ + { + "id": "components-disclosure-accordion--all", + "exportName": "All", + "name": "Accordion", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-disclosure-accordion--single", + "exportName": "Single", + "name": "Single", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-disclosure-accordion--multiple", + "exportName": "Multiple", + "name": "Multiple", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-disclosure-accordion--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-disclosure-accordion--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/alert-dialog.stories.tsx", + "title": "Components/Overlay/Alert Dialog", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-alert-dialog--all", + "exportName": "All", + "name": "Alert Dialog", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-alert-dialog--confirmation-open", + "exportName": "ConfirmationOpen", + "name": "Confirmation", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-alert-dialog--destructive-open", + "exportName": "DestructiveOpen", + "name": "Destructive", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/alert.stories.tsx", + "title": "Components/Feedback/Alert", + "metaTags": [], + "stories": [ + { + "id": "components-feedback-alert--all", + "exportName": "All", + "name": "Alert", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-feedback-alert--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-feedback-alert--destructive", + "exportName": "Destructive", + "name": "Destructive", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/aspect-ratio.stories.tsx", + "title": "Components/Data & Display/Aspect Ratio", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-aspect-ratio--all", + "exportName": "All", + "name": "Aspect Ratio", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-aspect-ratio--wide", + "exportName": "Wide", + "name": "16:9", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-aspect-ratio--square", + "exportName": "Square", + "name": "1:1", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/avatar.stories.tsx", + "title": "Components/Data & Display/Avatar", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-avatar--all", + "exportName": "All", + "name": "Avatar", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-avatar--image", + "exportName": "Image", + "name": "Image", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-avatar--fallback-error", + "exportName": "FallbackError", + "name": "Fallback", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/badge.stories.tsx", + "title": "Components/Data & Display/Badge", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-badge--all", + "exportName": "All", + "name": "Badge", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-badge--variants", + "exportName": "Variants", + "name": "Variants", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-data-display-badge--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/breadcrumb.stories.tsx", + "title": "Components/Navigation/Breadcrumb", + "metaTags": [], + "stories": [ + { + "id": "components-navigation-breadcrumb--all", + "exportName": "All", + "name": "Breadcrumb", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-navigation-breadcrumb--basic-3", + "exportName": "Basic3", + "name": "Basic", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-breadcrumb--with-ellipsis", + "exportName": "WithEllipsis", + "name": "Ellipsis", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-breadcrumb--long-path", + "exportName": "LongPath", + "name": "Long Path", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/button.stories.tsx", + "title": "Components/Form/Button", + "metaTags": [], + "stories": [ + { + "id": "components-form-button--all", + "exportName": "All", + "name": "Button", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-button--roles", + "exportName": "Roles", + "name": "Roles", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-button--sizes", + "exportName": "Sizes", + "name": "Sizes", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-button--states", + "exportName": "States", + "name": "Brand States", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-button--standard-states", + "exportName": "StandardStates", + "name": "Standard States", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-button--long-label", + "exportName": "LongLabel", + "name": "Long Label", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-button--korean-labels", + "exportName": "KoreanLabels", + "name": "Korean Labels", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-button--icon-text-narrow", + "exportName": "IconTextNarrow", + "name": "Icon Text Narrow", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-button--destructive", + "exportName": "Destructive", + "name": "Destructive", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-button--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-button--compatibility", + "exportName": "Compatibility", + "name": "Compatibility", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/calendar.stories.tsx", + "title": "Components/Search & Date/Calendar", + "metaTags": [], + "stories": [ + { + "id": "components-search-date-calendar--all", + "exportName": "All", + "name": "Calendar", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-search-date-calendar--single-selection", + "exportName": "SingleSelection", + "name": "Single", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-calendar--disabled-selection", + "exportName": "DisabledSelection", + "name": "Disabled Dates", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-calendar--range-selection", + "exportName": "RangeSelection", + "name": "Range", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/card.stories.tsx", + "title": "Components/Data & Display/Card", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-card--all", + "exportName": "All", + "name": "Card", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-card--surfaces", + "exportName": "Surfaces", + "name": "Surfaces", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-card--action", + "exportName": "Action", + "name": "Action", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-card--compatibility", + "exportName": "Compatibility", + "name": "Compatibility", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/carousel.stories.tsx", + "title": "Components/Data & Display/Carousel", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-carousel--all", + "exportName": "All", + "name": "Carousel", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-carousel--basic-carousel", + "exportName": "BasicCarousel", + "name": "Basic", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-carousel--multiple-items", + "exportName": "MultipleItems", + "name": "Multiple Items", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-carousel--loop-carousel", + "exportName": "LoopCarousel", + "name": "Loop", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-carousel--vertical-carousel", + "exportName": "VerticalCarousel", + "name": "Vertical", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/checkbox.stories.tsx", + "title": "Components/Form/Checkbox", + "metaTags": [], + "stories": [ + { + "id": "components-form-checkbox--all", + "exportName": "All", + "name": "Checkbox", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "play-fn" + ], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-checkbox--unchecked", + "exportName": "Unchecked", + "name": "Unchecked", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-checkbox--checked", + "exportName": "Checked", + "name": "Checked", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-checkbox--indeterminate", + "exportName": "Indeterminate", + "name": "Indeterminate", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-checkbox--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-checkbox--error", + "exportName": "Error", + "name": "Error", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-checkbox--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/collapsible.stories.tsx", + "title": "Components/Disclosure/Collapsible", + "metaTags": [], + "stories": [ + { + "id": "components-disclosure-collapsible--all", + "exportName": "All", + "name": "Collapsible", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-disclosure-collapsible--closed", + "exportName": "Closed", + "name": "Closed", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-disclosure-collapsible--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-disclosure-collapsible--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-disclosure-collapsible--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/combobox.stories.tsx", + "title": "Components/Search & Date/Combobox", + "metaTags": [], + "stories": [ + { + "id": "components-search-date-combobox--all", + "exportName": "All", + "name": "Combobox", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-search-date-combobox--empty-combobox", + "exportName": "EmptyCombobox", + "name": "Empty", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-combobox--controlled-combobox", + "exportName": "ControlledCombobox", + "name": "Controlled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-combobox--long-option", + "exportName": "LongOption", + "name": "Long Option", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-combobox--open-bottom", + "exportName": "OpenBottom", + "name": "Open Bottom", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-combobox--open-top", + "exportName": "OpenTop", + "name": "Open Top", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/command.stories.tsx", + "title": "Components/Search & Date/Command", + "metaTags": [], + "stories": [ + { + "id": "components-search-date-command--all", + "exportName": "All", + "name": "Command", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-search-date-command--search", + "exportName": "Search", + "name": "Search", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-command--grouped-list", + "exportName": "GroupedList", + "name": "Grouped", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/context-menu.stories.tsx", + "title": "Components/Overlay/Context Menu", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-context-menu--all", + "exportName": "All", + "name": "Context Menu", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-context-menu--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/date-picker.stories.tsx", + "title": "Components/Search & Date/Date Picker", + "metaTags": [], + "stories": [ + { + "id": "components-search-date-date-picker--all", + "exportName": "All", + "name": "Date Picker", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-search-date-date-picker--empty-picker", + "exportName": "EmptyPicker", + "name": "Empty", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-date-picker--invalid-focus", + "exportName": "InvalidFocus", + "name": "Invalid Focus", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-date-picker--selected-picker", + "exportName": "SelectedPicker", + "name": "Selected", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-search-date-date-picker--controlled-picker", + "exportName": "ControlledPicker", + "name": "Controlled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/dialog.stories.tsx", + "title": "Components/Overlay/Dialog", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-dialog--all", + "exportName": "All", + "name": "Dialog", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-dialog--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-dialog--long-content-open", + "exportName": "LongContentOpen", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-dialog--wide-content-open", + "exportName": "WideContentOpen", + "name": "Wide Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-dialog--body-scroll-open", + "exportName": "BodyScrollOpen", + "name": "Body Scroll", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-dialog--delete-confirmation-open", + "exportName": "DeleteConfirmationOpen", + "name": "Delete Confirmation", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/drawer.stories.tsx", + "title": "Components/Overlay/Drawer", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-drawer--all", + "exportName": "All", + "name": "Drawer", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-drawer--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-drawer--form", + "exportName": "Form", + "name": "Form", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-drawer--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/dropdown-menu.stories.tsx", + "title": "Components/Overlay/Dropdown Menu", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-dropdown-menu--all", + "exportName": "All", + "name": "Dropdown Menu", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-dropdown-menu--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/hover-card.stories.tsx", + "title": "Components/Overlay/Hover Card", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-hover-card--all", + "exportName": "All", + "name": "Hover Card", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-hover-card--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/input.stories.tsx", + "title": "Components/Form/Input", + "metaTags": [], + "stories": [ + { + "id": "components-form-input--all", + "exportName": "All", + "name": "Input", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "play-fn" + ], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-input--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-input--focus", + "exportName": "Focus", + "name": "Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--error", + "exportName": "Error", + "name": "Error", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--invalid-focus", + "exportName": "InvalidFocus", + "name": "Invalid Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--error-message", + "exportName": "ErrorMessage", + "name": "Error Message", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--required", + "exportName": "Required", + "name": "Required", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-input--readonly", + "exportName": "Readonly", + "name": "Readonly", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--sizes", + "exportName": "Sizes", + "name": "Compare Sizes", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-input--small", + "exportName": "Small", + "name": "Small", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--medium", + "exportName": "Medium", + "name": "Medium", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--large", + "exportName": "Large", + "name": "Large", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-input--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/label.stories.tsx", + "title": "Components/Form/Label", + "metaTags": [], + "stories": [ + { + "id": "components-form-label--all", + "exportName": "All", + "name": "Label", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/components/menubar.stories.tsx", + "title": "Components/Navigation/Menubar", + "metaTags": [], + "stories": [ + { + "id": "components-navigation-menubar--all", + "exportName": "All", + "name": "Menubar", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-navigation-menubar--trigger-focus", + "exportName": "TriggerFocus", + "name": "Trigger Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-menubar--open-focus", + "exportName": "OpenFocus", + "name": "Open Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/navigation-menu.stories.tsx", + "title": "Components/Navigation/Navigation Menu", + "metaTags": [], + "stories": [ + { + "id": "components-navigation-navigation-menu--all", + "exportName": "All", + "name": "Navigation Menu", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-navigation-navigation-menu--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-navigation-navigation-menu--no-viewport", + "exportName": "NoViewport", + "name": "No Viewport", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-navigation-menu--indicator-alignment", + "exportName": "IndicatorAlignment", + "name": "Indicator Alignment", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/pagination.stories.tsx", + "title": "Components/Navigation/Pagination", + "metaTags": [], + "stories": [ + { + "id": "components-navigation-pagination--all", + "exportName": "All", + "name": "Pagination", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-navigation-pagination--first-page", + "exportName": "FirstPage", + "name": "First Page", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-pagination--last-page", + "exportName": "LastPage", + "name": "Last Page", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-pagination--long-range", + "exportName": "LongRange", + "name": "Long Range", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/popover.stories.tsx", + "title": "Components/Overlay/Popover", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-popover--all", + "exportName": "All", + "name": "Popover", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-popover--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/progress.stories.tsx", + "title": "Components/Data & Display/Progress", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-progress--all", + "exportName": "All", + "name": "Progress", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-progress--zero", + "exportName": "Zero", + "name": "Zero", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-progress--middle", + "exportName": "Middle", + "name": "Middle", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-progress--full", + "exportName": "Full", + "name": "Full", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-progress--non-default-max", + "exportName": "NonDefaultMax", + "name": "Non Default Max", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/radio-group.stories.tsx", + "title": "Components/Form/Radio Group", + "metaTags": [], + "stories": [ + { + "id": "components-form-radio-group--all", + "exportName": "All", + "name": "Radio Group", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "play-fn" + ], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-radio-group--vertical", + "exportName": "Vertical", + "name": "Vertical", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-radio-group--horizontal", + "exportName": "Horizontal", + "name": "Horizontal", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-radio-group--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-radio-group--error", + "exportName": "Error", + "name": "Error", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/resizable.stories.tsx", + "title": "Components/Disclosure/Resizable", + "metaTags": [], + "stories": [ + { + "id": "components-disclosure-resizable--all", + "exportName": "All", + "name": "Resizable", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-disclosure-resizable--horizontal-layout", + "exportName": "HorizontalLayout", + "name": "Horizontal", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-disclosure-resizable--vertical-layout", + "exportName": "VerticalLayout", + "name": "Vertical", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-disclosure-resizable--min-max-layout", + "exportName": "MinMaxLayout", + "name": "Min Max", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/scroll-area.stories.tsx", + "title": "Components/Data & Display/Scroll Area", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-scroll-area--all", + "exportName": "All", + "name": "Scroll Area", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-scroll-area--vertical-scroll", + "exportName": "VerticalScroll", + "name": "Vertical", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-scroll-area--horizontal-scroll", + "exportName": "HorizontalScroll", + "name": "Horizontal", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/select.stories.tsx", + "title": "Components/Form/Select", + "metaTags": [], + "stories": [ + { + "id": "components-form-select--all", + "exportName": "All", + "name": "Select", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-select--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-select--invalid-focus", + "exportName": "InvalidFocus", + "name": "Invalid Focus", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--selected", + "exportName": "Selected", + "name": "Selected", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-select--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-select--sizes", + "exportName": "Sizes", + "name": "Compare Sizes", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-select--medium", + "exportName": "Medium", + "name": "Medium", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--large", + "exportName": "Large", + "name": "Large", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--open-large-short", + "exportName": "OpenLargeShort", + "name": "Open Large Short", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--open-medium-long", + "exportName": "OpenMediumLong", + "name": "Open Medium Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--open-large-long", + "exportName": "OpenLargeLong", + "name": "Open Large Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--open-top", + "exportName": "OpenTop", + "name": "Open Top", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--open-top-long", + "exportName": "OpenTopLong", + "name": "Open Top Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--list-size-comparison", + "exportName": "ListSizeComparison", + "name": "Medium Trigger / List Size Comparison", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--scroll", + "exportName": "Scroll", + "name": "Scroll", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-select--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/separator.stories.tsx", + "title": "Components/Data & Display/Separator", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-separator--all", + "exportName": "All", + "name": "Separator", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-separator--horizontal", + "exportName": "Horizontal", + "name": "Horizontal", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-data-display-separator--vertical", + "exportName": "Vertical", + "name": "Vertical", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + } + ] + }, + { + "file": "src/components/sheet.stories.tsx", + "title": "Components/Overlay/Sheet", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-sheet--all", + "exportName": "All", + "name": "Sheet", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-sheet--right", + "exportName": "Right", + "name": "Right", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-sheet--left", + "exportName": "Left", + "name": "Left", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-sheet--top", + "exportName": "Top", + "name": "Top", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-sheet--bottom", + "exportName": "Bottom", + "name": "Bottom", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-sheet--form", + "exportName": "Form", + "name": "Form", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-overlay-sheet--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/skeleton.stories.tsx", + "title": "Components/Data & Display/Skeleton", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-skeleton--all", + "exportName": "All", + "name": "Skeleton", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-skeleton--text", + "exportName": "Text", + "name": "Text", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-skeleton--card-list", + "exportName": "CardList", + "name": "Card List", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/slider.stories.tsx", + "title": "Components/Form/Slider", + "metaTags": [], + "stories": [ + { + "id": "components-form-slider--all", + "exportName": "All", + "name": "Slider", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-slider--single-value", + "exportName": "SingleValue", + "name": "Single", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-slider--range-value", + "exportName": "RangeValue", + "name": "Range", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-slider--disabled-slider", + "exportName": "DisabledSlider", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-slider--focus", + "exportName": "Focus", + "name": "Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/sonner.stories.tsx", + "title": "Components/Feedback/Sonner", + "metaTags": [], + "stories": [ + { + "id": "components-feedback-sonner--all", + "exportName": "All", + "name": "Sonner", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/components/switch.stories.tsx", + "title": "Components/Form/Switch", + "metaTags": [], + "stories": [ + { + "id": "components-form-switch--all", + "exportName": "All", + "name": "Switch", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "play-fn" + ], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-switch--off", + "exportName": "Off", + "name": "Off", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-switch--on", + "exportName": "On", + "name": "On", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-switch--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + } + ] + }, + { + "file": "src/components/table.stories.tsx", + "title": "Components/Data & Display/Table", + "metaTags": [], + "stories": [ + { + "id": "components-data-display-table--all", + "exportName": "All", + "name": "Table", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-data-display-table--basic", + "exportName": "Basic", + "name": "Basic", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-data-display-table--selected", + "exportName": "Selected", + "name": "Selected", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-data-display-table--long-text", + "exportName": "LongText", + "name": "Long Text", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-data-display-table--overflow", + "exportName": "Overflow", + "name": "Horizontal Overflow", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/tabs.stories.tsx", + "title": "Components/Navigation/Tabs", + "metaTags": [], + "stories": [ + { + "id": "components-navigation-tabs--all", + "exportName": "All", + "name": "Tabs", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-navigation-tabs--secondary", + "exportName": "Secondary", + "name": "Secondary", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--selected-first", + "exportName": "SelectedFirst", + "name": "Selected First", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--selected-last", + "exportName": "SelectedLast", + "name": "Selected Last", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--focus", + "exportName": "Focus", + "name": "Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-navigation-tabs--overflow", + "exportName": "Overflow", + "name": "Overflow", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--secondary-overflow", + "exportName": "SecondaryOverflow", + "name": "Secondary Overflow", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--long-label", + "exportName": "LongLabel", + "name": "Long Label", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/textarea.stories.tsx", + "title": "Components/Form/Textarea", + "metaTags": [], + "stories": [ + { + "id": "components-form-textarea--all", + "exportName": "All", + "name": "Textarea", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "play-fn" + ], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-textarea--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-textarea--focus", + "exportName": "Focus", + "name": "Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--error", + "exportName": "Error", + "name": "Error", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--invalid-focus", + "exportName": "InvalidFocus", + "name": "Invalid Focus", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--error-message", + "exportName": "ErrorMessage", + "name": "Error Message", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--required", + "exportName": "Required", + "name": "Required", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--readonly", + "exportName": "Readonly", + "name": "Readonly", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--disabled", + "exportName": "Disabled", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-textarea--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-textarea--playground", + "exportName": "Playground", + "name": "Playground", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/toggle-group.stories.tsx", + "title": "Components/Form/Toggle Group", + "metaTags": [], + "stories": [ + { + "id": "components-form-toggle-group--all", + "exportName": "All", + "name": "Toggle Group", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-toggle-group--single-selection", + "exportName": "SingleSelection", + "name": "Single", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-toggle-group--multiple-selection", + "exportName": "MultipleSelection", + "name": "Multiple", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-toggle-group--vertical-group", + "exportName": "VerticalGroup", + "name": "Vertical", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-toggle-group--content-nodes", + "exportName": "ContentNodes", + "name": "Content Nodes", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/toggle.stories.tsx", + "title": "Components/Form/Toggle", + "metaTags": [], + "stories": [ + { + "id": "components-form-toggle--all", + "exportName": "All", + "name": "Toggle", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-form-toggle--states", + "exportName": "States", + "name": "States", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "components-form-toggle--icon-only", + "exportName": "IconOnly", + "name": "Icon Only", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-toggle--disabled-toggle", + "exportName": "DisabledToggle", + "name": "Disabled", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-form-toggle--content-nodes", + "exportName": "ContentNodes", + "name": "Content Nodes", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/components/tooltip.stories.tsx", + "title": "Components/Overlay/Tooltip", + "metaTags": [], + "stories": [ + { + "id": "components-overlay-tooltip--all", + "exportName": "All", + "name": "Tooltip", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "components-overlay-tooltip--open", + "exportName": "Open", + "name": "Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/foundation/color.stories.tsx", + "title": "Foundation/Color", + "metaTags": [], + "stories": [ + { + "id": "foundation-color--all", + "exportName": "All", + "name": "Color", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/foundation/layout.stories.tsx", + "title": "Foundation/Layout", + "metaTags": [], + "stories": [ + { + "id": "foundation-layout--all", + "exportName": "All", + "name": "Layout", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/foundation/typography.stories.tsx", + "title": "Foundation/Typography", + "metaTags": [], + "stories": [ + { + "id": "foundation-typography--all", + "exportName": "All", + "name": "Typography", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/patterns/dashboard-layout.stories.tsx", + "title": "Patterns/Layout/Dashboard", + "metaTags": [], + "stories": [ + { + "id": "patterns-layout-dashboard-layout--all", + "exportName": "All", + "name": "Dashboard", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "patterns-layout-dashboard-layout--with-sidebar", + "exportName": "WithSidebar", + "name": "Dashboard Header + Sidebar", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-dashboard-layout--third-level", + "exportName": "ThirdLevel", + "name": "Third Level", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-dashboard-layout--sidebar-only", + "exportName": "SidebarOnly", + "name": "Dashboard Sidebar", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-dashboard-layout--long-content", + "exportName": "LongContent", + "name": "Long Content", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-dashboard-layout--loading", + "exportName": "Loading", + "name": "Loading", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-dashboard-layout--empty", + "exportName": "Empty", + "name": "Empty", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-dashboard-layout--framed", + "exportName": "Framed", + "name": "Framed", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/patterns/data-table.stories.tsx", + "title": "Patterns/Data/Data Table", + "metaTags": [], + "stories": [ + { + "id": "patterns-data-data-table--all", + "exportName": "All", + "name": "Data Table", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "patterns-data-data-table--with-filter-bar-story", + "exportName": "WithFilterBarStory", + "name": "With Filter Bar", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-data-data-table--loading", + "exportName": "Loading", + "name": "Loading", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-data-data-table--empty", + "exportName": "Empty", + "name": "Empty", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/patterns/filter-bar.stories.tsx", + "title": "Patterns/Data/Filter Bar", + "metaTags": [], + "stories": [ + { + "id": "patterns-data-filter-bar--all", + "exportName": "All", + "name": "Filter Bar", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/patterns/gnb.stories.tsx", + "title": "Patterns/Layout/GNB", + "metaTags": [], + "stories": [ + { + "id": "patterns-layout-gnb--all", + "exportName": "All", + "name": "GNB", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "patterns-layout-gnb--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "patterns-layout-gnb--sticky", + "exportName": "Sticky", + "name": "Sticky", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--navigation-menu-open", + "exportName": "NavigationMenuOpen", + "name": "Navigation Menu Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--short-depth-open", + "exportName": "ShortDepthOpen", + "name": "Short Depth Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--right-edge-long-depth", + "exportName": "RightEdgeLongDepth", + "name": "Right Edge Long Depth", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--first-depth-short", + "exportName": "FirstDepthShort", + "name": "First Depth Short", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--first-depth-long", + "exportName": "FirstDepthLong", + "name": "First Depth Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--middle-depth-short", + "exportName": "MiddleDepthShort", + "name": "Middle Depth Short", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--middle-depth-long", + "exportName": "MiddleDepthLong", + "name": "Middle Depth Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--last-depth-short", + "exportName": "LastDepthShort", + "name": "Last Depth Short", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--last-depth-long", + "exportName": "LastDepthLong", + "name": "Last Depth Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--edge-first-long", + "exportName": "EdgeFirstLong", + "name": "Edge First Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--edge-last-long", + "exportName": "EdgeLastLong", + "name": "Edge Last Long", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--user-menu-open", + "exportName": "UserMenuOpen", + "name": "User Menu Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--long-labels", + "exportName": "LongLabels", + "name": "Long Labels", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--with-logo", + "exportName": "WithLogo", + "name": "With Logo", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--utilities", + "exportName": "Utilities", + "name": "Utilities", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--mobile", + "exportName": "Mobile", + "name": "Mobile", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-gnb--mobile-menu-open", + "exportName": "MobileMenuOpen", + "name": "Mobile Menu Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/patterns/lnb.stories.tsx", + "title": "Patterns/Layout/LNB", + "metaTags": [], + "stories": [ + { + "id": "patterns-layout-lnb--all", + "exportName": "All", + "name": "LNB", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "patterns-layout-lnb--default", + "exportName": "Default", + "name": "Default", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "patterns-layout-lnb--selected", + "exportName": "Selected", + "name": "Selected", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": "MERGE_CANDIDATE", + "reason": "Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed." + }, + { + "id": "patterns-layout-lnb--expanded", + "exportName": "Expanded", + "name": "Expanded", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--selected-child", + "exportName": "SelectedChild", + "name": "Selected Child", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--collapsed-with-selection", + "exportName": "CollapsedWithSelection", + "name": "Collapsed With Selection", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--with-logo", + "exportName": "WithLogo", + "name": "With Logo", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--long-labels", + "exportName": "LongLabels", + "name": "Long Labels", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--scroll", + "exportName": "Scroll", + "name": "Scroll", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--third-level", + "exportName": "ThirdLevel", + "name": "Third Level", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--third-level-long-labels", + "exportName": "ThirdLevelLongLabels", + "name": "Third Level Long Labels", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--third-level-scroll", + "exportName": "ThirdLevelScroll", + "name": "Third Level Scroll", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--with-gnb", + "exportName": "WithGNB", + "name": "With GNB", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--standalone-desktop", + "exportName": "StandaloneDesktop", + "name": "LNB Standalone Desktop", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--lnb-profile-menu-open", + "exportName": "LNBProfileMenuOpen", + "name": "LNB Profile Menu Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--standalone-mobile", + "exportName": "StandaloneMobile", + "name": "LNB Standalone Mobile", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "patterns-layout-lnb--standalone-mobile-menu-open", + "exportName": "StandaloneMobileMenuOpen", + "name": "LNB Standalone Mobile Menu Open", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/patterns/loading-screen.stories.tsx", + "title": "Patterns/Layout/Loading Screen", + "metaTags": [], + "stories": [ + { + "id": "patterns-layout-loading-screen--all", + "exportName": "All", + "name": "Loading Screen", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "patterns-layout-loading-screen--inline", + "exportName": "Inline", + "name": "Inline", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/theme/designer-theme.stories.tsx", + "title": "Theme/Designer Playground", + "metaTags": [], + "stories": [ + { + "id": "theme-designer-playground--editor", + "exportName": "Editor", + "name": "Designer Playground", + "storyTags": [], + "effectiveTags": [], + "category": "PRIMARY", + "decision": "KEEP", + "candidate": null, + "reason": "Representative story or distinct reference page." + }, + { + "id": "theme-designer-playground--live-verification", + "exportName": "LiveVerification", + "name": "Live Verification", + "storyTags": [ + "verification", + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/theme/runtime-loader.stories.tsx", + "title": "Theme/Runtime Contract/Loader", + "metaTags": [ + "verification" + ], + "stories": [ + { + "id": "theme-runtime-contract-loader--cdn-verification", + "exportName": "CdnVerification", + "name": "Cdn Verification", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, + { + "file": "src/theme/runtime-theme.stories.tsx", + "title": "Theme/Runtime Contract", + "metaTags": [ + "verification" + ], + "stories": [ + { + "id": "theme-runtime-contract--comparison", + "exportName": "Comparison", + "name": "Comparison", + "storyTags": [], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "theme-runtime-contract--global-external", + "exportName": "GlobalExternal", + "name": "Global External", + "storyTags": [], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "theme-runtime-contract--verification", + "exportName": "Verification", + "name": "Verification", + "storyTags": [ + "play-fn" + ], + "effectiveTags": [ + "verification", + "play-fn" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "theme-runtime-contract--dashboard", + "exportName": "Dashboard", + "name": "Dashboard", + "storyTags": [], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + } + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6512fc6 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,76 @@ +# HMDesignSystem 작업 지침 + +## AI 화면 구현 Quick Start + +화면 구현 요청을 받으면 아래 순서를 그대로 따른다. 파일이나 API의 존재를 확인할 수 없으면 추측하지 말고 미확인으로 기록한다. + +1. **프로젝트 구조와 Runtime 확인**: 제공된 파일 범위를 살펴보고 `REACT_VITE | STATIC_HTML | VANILLA_WEB | OTHER_RUNTIME` 중 대상을 정한다. Component·Pattern 파일이 실제 범위에 있는지 확인한다. +2. **Component 검색**: `packages/ui/src/components/ui/`와 `packages/ui/src/index.ts`에서 공개 Component·Props를 찾고, `docs/components/.md`를 확인한다. 기존 공개 Component를 우선 사용한다. +3. **Pattern 검색**: `packages/ui/src/patterns/`, `src/patterns/`, `packages/ui/src/index.ts`와 `docs/patterns/`에서 맞는 Pattern·공개 API를 찾아 구조와 상태 규칙을 따른다. +4. **Storybook 확인**: `src/components/`, `src/patterns/`, `src/theme/`의 대표 `All` Story와 관련 문서를 확인한다. `verification` 전용 Story는 일반 화면의 기준으로 쓰지 않는다. Storybook을 실행할 수 없으면 Story 소스와 문서만 확인하고 시각 동작은 추측하지 않는다. +5. **Theme 연결**: `themes/default.json`, `packages/ui/src/theme.css`, Runtime 계약과 Token 가이드를 확인해 기존 CSS Custom Properties 구조를 사용한다. +6. **화면 구성**: Component와 Pattern을 조합하고 아래 단일 Navigation 정책을 지킨다. 기존 구성으로 해결되지 않을 때만 새 Component를 검토한다. +7. **결과 확인 및 보고**: 실제 적용한 Component·Pattern, Theme 연결, Navigation Mode, 실행/화면 확인 결과와 미검증 항목을 보고한다. + +**Storybook / 화면 생성 분리**: Storybook은 기존 Component·Pattern·Theme·Variant·State·Interaction을 확인하는 Reference 환경이다. 일반 업무 화면 생성은 Storybook Story·사이드바에 등록하지 않고, 기존 대표 Story를 생성 화면으로 교체하지 않으며, Storybook 실행을 필수 확인 경로로 요구하지 않는다. 별도의 React 화면과 독립 미리보기를 만들고 화면별 빌드·필요한 검증을 수행한다. H-2(AI 생성 화면의 자동 빌드·캡처·검증, Storybook 배포와 별도이며 구현 완료 여부는 현재 파일로 확인)나 유사 자동화가 사용 가능한 경우에도 `preview.html`·desktop/mobile 이미지 같은 독립 미리보기 산출에만 사용하며 Storybook 등록과 혼동하지 않는다. Storybook 수정은 Component/Pattern 추가·수정, 대표/Verification Story 작성 또는 Storybook Reference 개선을 사용자가 명시한 경우에만 허용한다. Storybook 전용 helper/CSS는 운영 화면에 사용하지 않는다. + +상세 규칙은 이 문서의 [구현 기본값](#ai-implementation-defaults), [Runtime 계약](#runtime-portability-policy), [단일 Navigation 정책](#navigation-single-mode-policy)과 [Component / Pattern Reference Index](docs/guidelines/component-pattern-reference.md), [Component 구현 원칙](docs/guidelines/shadcn-기반-구현-원칙.md), [Runtime Theme](docs/guidelines/runtime-theme.md), [Navigation 정책](docs/guidelines/navigation-single-mode.md)을 따른다. + +**검색 원칙(도구 중립)**: 변경과 관련된 소스 경로부터 좁혀 검색하고, 생성물(`node_modules`, `dist`, `storybook-static`)과 로컬 작업본은 기본 제외한다. 필요한 최소 범위만 검증하며 실제로 확인하지 않은 기능을 PASS로 보고하지 않는다. 존재하지 않는 Component·Pattern·공개 API를 임의로 만들지 않고 없으면 없다고 보고한다. + +**Story 구분**: 운영 기준은 대표 `All` Story이고 `verification`·Compatibility·Fixture Story는 운영 예제가 아니다. Storybook 문서 화면은 `StoryPage` 공통 Inner를 사용하며, 이는 Storybook 표현용으로 `@hm-design/ui` 공개 API가 아니다. + +**화면 생성 시 필수 확인**: Navigation을 지정받으면 우선 적용하고, 미지정이면 화면 구조·대표 Pattern에서 선택 근거를 기록한다([선택 기준](docs/guidelines/navigation-single-mode.md#선택-근거)). GNB는 Desktop 메뉴와 `GNBMobileMenu`를 함께 구성하며 내부 Sidebar도 모바일 Sheet 하나만 사용한다([GNB 모바일](docs/patterns/gnb.md#8-desktop--mobile)). Dashboard 운영 화면에는 Sidebar를 생성하지 않는다. `DashboardSidebar` 공개 Export와 선택적 `sidebar` slot은 기존 호환성·검증용이며 운영 허용을 뜻하지 않는다. Dashboard 상태는 Theme 다음에 공개 Status CSS를 로드하고 기존 Badge mapping을 소비한다([상태 예제](docs/patterns/dashboard-layout.md#dashboard-업무-상태-공통-계약)). FilterBar의 입력 조건과 조회 적용 조건·초기화 책임을 구분한다([조회 예제](docs/patterns/filter-bar.md#61-조회-버튼으로-적용하는-소비-예제)). `REACT_VITE` 독립 화면은 bootstrap에서 기존 앱 CSS entry(`@/index.css` → `@/hm-theme.css` → 필요 시 공개 상태 CSS)를 로드하며, `@hm-design/ui/tailwind.css`만 단독 import하지 않는다. 빌드 후 `.flex`, `.grid` 등 대표 utility의 생성·실제 적용과 캡처 레이아웃을 확인하고, 빌드 성공만으로 화면 검증을 PASS 처리하지 않는다. React 소비자는 Tailwind 검색 범위와 HM package source를 확인하고([Tailwind](docs/guidelines/runtime-portability.md#21-tailwind-v4-소비자-검색-범위)), 폰트·Asset 경로는 실행 위치별로 확인한다([Asset](docs/guidelines/runtime-portability.md#4-asset과-실행-위치)). + +## AI Implementation Defaults + +**사용자가 별도로 "기존 컴포넌트/디자인 시스템을 사용해줘", "테마를 적용해줘", "Semantic Token을 사용해줘", "Navigation 정책을 따라줘"라고 요청하지 않아도 AI는 이를 모든 신규·수정 UI의 기본 구현 조건으로 자동 적용한다.** + +- 구현 전에 `STORYBOOK_ONLY | PRODUCTION_UI | BOTH` 중 변경 범위를 정한다. +- 기존 Component와 유사 대표 Story를 먼저 검색한다. `packages/ui`, `src/components`, `src/patterns`, `src/center`, 기존 Storybook stories와 shared helpers를 확인하고 도메인에 맞는 구현을 재사용한다. +- 재사용 순서: 기존 HM Component → 기존 Variant / Prop → 기존 Composition → shadcn → Radix Primitive → 최소 Custom. 새 Component는 기존 Component·Composition으로 표현 불가능하고 Variant 확장이 부적절하며 shadcn/Radix 조합도 어려운 경우에만 만든다. 재사용 불가 사유와 최소 추가 범위를 설명한다. +- Storybook은 구현 Reference Catalog다. 대표/PRIMARY Story의 시각 언어와 공통 상태 mapping을 재사용하고 검증 전용 Story를 운영 예제로 삼지 않는다. 신규 업무 화면의 등록 대상이 아니며, 생성 결과는 별도 React 화면·독립 미리보기로 제공한다. +- Theme은 구현 시작부터 적용한다. 기존 Semantic Token → 해당 Component Token → 기존 CSS Variable을 먼저 확인하고, 없을 때만 필요한 최소 Semantic 확장을 검토한다. 기존 Token이 있는 색상·spacing·값을 하드코딩하거나 같은 Token을 여러 파일에 중복 정의하지 않는다. +- Theme 사용은 별도 승인 없이 기본 적용한다. Theme Source/값 수정은 명시적 필요성과 영향 범위를 확인한다. `themes/*.json` → Runtime Loader → CSS Custom Properties → Semantic → Component Mapping과 Primary / On Accent의 독립 fallback을 유지한다. +- 한 페이지에 `GNB | LNB | DASHBOARD` 중 하나만 사용한다. 반응형도 같은 Navigation Mode를 유지한다. +- 화면 구현 순서는 Quick Start를 따른다. 변경 작업 범위(STORYBOOK_ONLY | PRODUCTION_UI | BOTH)는 구현 전에 정한다. +- package exports, public props/types, package entry, Story ID, `meta.title`을 불필요하게 변경하지 않는다. 필요한 변경은 소비처·호환성 영향을 먼저 확인한다. Storybook 표현 문제와 Production 동작 문제는 해당 범위에서 해결한다. +- Badge/Button/FilterBar를 재사용하더라도 동일한 상태 ID·라벨·필터 매칭이 반복되면 화면별 helper로 남기지 말고 Runtime-neutral 상태 계약을 먼저 검토한다. 계약은 React 구현과 분리하고 `data-status`, 독립 flag attribute, `aria-pressed` 등 다른 Runtime이 재현할 수 있는 상태 vocabulary를 정의한다. +- 명시적 실험·Theme 미적용 prototype·외부 디자인 완전 재현·독립 POC만 별도 격리 범위에서 예외를 검토한다. Navigation 혼합은 아래 단일 모드 정책의 별도 기준을 따른다. +- Button은 `brand | solid | outline`의 사용 위치와 세트 규칙을 `docs/components/button.md` 기준으로 따른다: 실제 화면 Brand 최대 1개·팝업 Brand 금지, 일반 확인/작성은 Solid 세트, 삭제 확인·행/반복 작업은 Outline 세트이며 세트의 appearance와 size를 통일한다. Runtime 변환은 `docs/guidelines/runtime-portability.md`의 DOM/Theme/keyboard 계약을 함께 보존한다. +- Dashboard 운영 화면은 전용 Header(48px)·업무 상태 필터와 Content로 구성하며 Sidebar 사용을 금지한다. Dashboard 대표 `Patterns/Layout/Dashboard → Dashboard` Story와 `docs/patterns/dashboard-layout.md`를 기준으로 하며, Header를 GNB로 대체하거나 일반 LNB/GNB를 함께 넣지 않는다. Sidebar verification Story는 신규 운영 화면의 Reference가 아니다. +- Dashboard 및 DataTable은 기존 Pattern 구조를 우선한다. `Data Table` 대표 Story를 기준으로 하고 숨김 `With Filter Bar`는 verification 용도다. +- Story의 fixture나 장식은 Component API나 Production 규칙과 구분한다. 대표 Story와 공개 API·사용 문서를 함께 확인한다. + +상세 Source of Truth: [AI/Component 구현 원칙](docs/guidelines/shadcn-기반-구현-원칙.md), [Theme/Token 정책](docs/guidelines/테마-토큰-가이드.md), [Runtime Theme 계약](docs/guidelines/runtime-theme.md). 이 기본 정책은 과거 문서·예제의 상충 지침보다 우선한다. + +## Runtime Portability Policy + +- 설치 없는 테스트 배포의 현재 검증 기준은 `HMDesignTest-Portable-Test-r3.zip`이다(r2·원본 `HMDesignTest-Portable-Test.zip`은 과거 기록). 포터블 ZIP은 기본 포트 6007이며 충돌 시 자동으로 다른 포트를 쓰고, 개발 저장소 Storybook은 6024다. 이 ZIP은 `start-preview.bat`과 동봉된 Windows x64 Node 런타임으로 사전 빌드된 Storybook을 실행한다. 테스트 사용자는 ZIP 해제 → 배치 더블클릭 → 표시된 로컬 URL 열기만 수행하며 Node/npm 설치, npm 명령, Git 작업, CDN/운영 업로드가 필요 없다. 아래 의존성 복원·빌드 절차는 소스 개발자 전용이다. [테스트 배포 안내](docs/guidelines/portable-test-distribution.md). +- Runtime 구분: React 화면은 실제 `packages/ui` 공개 API와 workspace를 쓰는 `REACT_VITE`; 압축 ZIP에서 React 개발환경을 쓸 때는 먼저 `npm ci`로 의존성을 복원한다. `STATIC_HTML`은 파일을 `file://`로 직접 여는 HTML이며 Vite나 bare package import에 의존하지 않는다. `VANILLA_WEB`은 서버/정적 호스팅의 HTML·CSS·JS다. Storybook은 구현 기준을 확인하는 Reference 환경이며 Production Runtime을 뜻하지 않는다. +- `HMDesignTest.zip`에는 `.storybook/` 설정·Story 소스·패키지 소스와 dist가 있지만 `node_modules/`, `storybook-static/`은 없다. 실제 포함 파일과 `package.json` scripts를 먼저 확인한다. `HMDesignTest.zip`의 Storybook 개발 서버는 기본 포트 6007이며(현재 개발 저장소는 6024) 정적 빌드가 필요하면 의존성을 복원한 뒤 `npm run build-storybook`을 실행한다. 현재 개발 저장소와 ZIP의 설정 차이가 있으면 대상 `package.json`을 다시 확인한다. 생성되기 전의 `storybook-static/`을 필수 Reference로 요구하지 않는다. +- React는 구현 방식 중 하나일 뿐이다. 신규·변환 작업은 먼저 `REACT_VITE | STATIC_HTML | VANILLA_WEB | OTHER_RUNTIME` 중 Target Runtime을 정하고, HM Design/Theme/Component Contract를 유지한다. +- `themes/default.json`, `packages/ui/src/theme/contract.ts`, `packages/ui/src/theme/runtime.ts`, `packages/ui/src/theme.css`와 [Runtime 계약표](docs/guidelines/runtime-theme-contract.md)를 실제 Source of Truth로 사용한다. 색상·spacing·radius·Typography를 추측하거나 하드코딩하지 않는다. +- HTML/다른 Runtime 결과물은 기존 React를 대체하거나 별도 디자인으로 재작성하지 않고, 동일한 시각·상태·접근성·responsive 계약을 소비하는 adapter로 만든다. `STATIC_HTML`은 `file://` 직접 실행 검증을 포함한다. +- Dashboard 상태 색상은 `src/patterns/dashboard-status-contract.css`의 Runtime-neutral 변수(`--hm-dashboard-status-active|hold|done|issue`)를 우선 사용한다. 이 파일의 active/hold/issue 값은 기존 승인된 Story 색상을 보존하는 compatibility mapping이며 Public Theme token으로 승격하지 않는다. 실제 Production 공통 소비가 확인될 때만 Semantic Token 승격을 제안한다. +- 상세한 Target Runtime, asset 경로, 변환 규칙과 검증 기준은 [Runtime Portability Policy](docs/guidelines/runtime-portability.md)를 따른다. + +## Navigation Single Mode Policy + +**ONE PAGE = ONE NAVIGATION MODE = ONE HEADER/NAVIGATION EXPERIENCE** + +- 페이지 구현 전에 `navigationMode = GNB | LNB | DASHBOARD` 중 정확히 하나를 선택한다. +- 허용: GNB ONLY, LNB ONLY, DASHBOARD ONLY. +- GNB ONLY 내부 Sidebar는 기존 LNB 부품을 재사용하는 허용 Composition이며 독립 LNB Mode가 아니다. 브랜드·사용자와 모바일 Header/Sheet는 GNB 하나가 담당한다([상세 범위](docs/guidelines/navigation-single-mode.md#gnb-only-내부-sidebar)). +- 금지: GNB + LNB, GNB + Dashboard, LNB + Dashboard, GNB + LNB + Dashboard. +- 이 정책은 AI 기반 코드 생성·제안, 신규 페이지 구현, 기존 화면 수정, 프로토타입, Storybook, 실제 Production 구현 전체에 동일하게 적용한다. +- AI는 별도 요청이 없는 한 Navigation 조합을 생성하거나 제안하지 않는다. 별도 요청이 있더라도 기존 단일 모드 정책과 충돌함을 알리고 정책 변경인지 기술 검증인지 구분한다. +- 반응형에서도 선택한 체계를 유지한다. LNB Desktop(≥1024px) → LNB Mobile Header(≤1023px)는 같은 LNB Mode이며 GNB로 전환하지 않는다. +- Dashboard 운영 화면은 전용 Header/Menu와 Content만 사용하며 Sidebar는 금지한다. 외부 GNB나 일반 LNB를 slot에 넣지 않는다. 기존 `DashboardSidebar`와 `sidebar` slot은 호환성·검증용으로 유지하며 신규 운영 화면에 사용하지 않는다. +- Dashboard Header는 GNB가 아니다. Dashboard 화면은 Dashboard Pattern과 대표 Story를 따르고, Header의 역할을 GNB로 대체하지 않는다. +- GNB/LNB/Dashboard의 함께 쓰기는 위 단일 모드 허용 범위만 따른다. 과거 혼합 Navigation verification Story는 운영 화면 기준으로 사용하지 않는다. +- Dashboard 운영 최소 폭은 Sidebar 없는 960px이며 그 이하에서는 Dashboard 내부 가로 스크롤을 유지한다. Sidebar 포함 1232px은 기존 호환성·검증 구성의 값으로 현재 운영 기준이 아니다. +- Navigation 간 CSS 변수, height/width 계산, breakpoint, spacing override의 교차 의존성을 추가하지 않는다. +- 대표/PRIMARY Story는 단일 모드만 사용한다. 기존 혼합 Story를 기술 검증 목적으로 유지할 경우 `verification` 태그와 **VERIFICATION ONLY** 표시를 모두 적용하고 운영 예제로 추천하지 않는다. + +상세 기준: [Navigation Single Mode Policy](docs/guidelines/navigation-single-mode.md). 이 사용자 확정 정책은 이전 Navigation 조합 문서·예제보다 우선한다. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..df99a57 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,69 @@ +# HMDesignSystem + +shadcn/ui(Radix, Tailwind v4, CVA) 기반 HM 디자인 시스템 시제품. React + TypeScript + Vite, 개발 Storybook 포트 6024(포터블 테스트 ZIP은 기본 6007, 충돌 시 자동 변경). 실행법과 파일 위치는 `README.md`를 따른다. + +## 지침 문서 + +규칙의 기준은 아래 문서이며, 이 파일에는 규칙 본문을 복사하지 않는다. 작업 전에 해당 문서를 읽는다. + +- `AGENTS.md` — AI Implementation Defaults와 Navigation 필수 요약. 기존 Component / Storybook Reference 우선, Theme / Semantic 기본 적용, 단일 Navigation, 최소 Custom은 사용자의 별도 지시 없이 모든 신규·수정 UI에 적용한다. +- 일반 업무 화면은 Storybook에 등록하지 않고 별도 React 화면·독립 미리보기로 제공한다. Storybook 수정은 Component/Pattern 또는 명시적 Storybook Reference 작업에 한정한다. 상세한 Storybook / 화면 생성 분리는 `AGENTS.md`와 `docs/guidelines/shadcn-기반-구현-원칙.md`를 따른다. +- `docs/guidelines/프로젝트-지침.md` — §6 컴포넌트·Storybook·CSS 작업 규칙 (모든 컴포넌트 작업 전 필독) +- `docs/guidelines/shadcn-기반-구현-원칙.md` — Component / Pattern / Story의 재사용 우선순위와 Custom 구현 허용 조건 (관련 작업 전 필독) +- `docs/guidelines/테마-토큰-가이드.md` — Token 구조, 이름 규칙, HM Default/External Runtime 계약, Light/Dark 없음 +- `docs/guidelines/개발-적용-가이드.md`, `운영-배포-가이드.md`, `theme-delivery.md` — JSON 원본/Runtime URL Loader/독립 Theme 배포와 향후 실제 CDN·Publish의 경계 + +> `docs/guidelines/`의 개정안/검토용 표기와 관계없이 2026-10-07 사용자 확정 AI 기본 구현 정책과 2026-10-08 Navigation Sidebar 운영 정책은 필수 적용한다. 과거 로컬 지침은 현재 정책을 대체하지 않는다. + +## 반드시 지킬 것 (요약) + +- 구현 우선순위는 기존 `@hm-design/ui` → 기존 Variant / Prop → 기존 Composition → shadcn → Radix Primitive → 최소 Custom이다. Visual은 처음부터 기존 Theme / Semantic / Component Mapping에 연결한다. 상세 탐색·범위 분류·예외 조건은 `docs/guidelines/shadcn-기반-구현-원칙.md`를 따른다. +- Behavior / Structure는 shadcn/Radix를 유지하고 Visual은 HM Theme로 조정한다. 디자인 차이만으로 별도 Component, interaction logic 또는 Production API를 만들지 않는다. +- HM이 값을 지정한 속성은 Theme Token으로, 지정하지 않은 속성은 shadcn 기본 구현 그대로 둔다. +- shadcn 구조(CVA, `data-slot`, `cn(..., className)`)와 공개 API를 불필요하게 바꾸지 않는다. wrapper를 추가하지 않는다. +- ``). 기본 `variant="primary"` `appearance="solid"`, `size` 기본 `default`. 위험한 작업은 `variant="destructive" appearance="outline"`. +- `AlertDialogCancel`: 같은 방식, 기본 `variant="cancel"` `appearance="solid"`. +- `AlertDialogAction variant="destructive"`만 지정하면 기본 `solid`가 적용되어 **미지원 Destructive Solid**가 된다. 삭제 확인은 Action과 Cancel 모두 `appearance="outline"`을 명시하고 같은 size를 사용한다. 자동 appearance 변경은 없다. [Button 허용 조합·기본값 주의](button.md#62-기본값별칭합성-주의)를 따른다. +- 합성한 Radix 요소에는 `data-slot`을 붙이지 않는다. 붙이면 `asChild` Slot 병합에서 자식 값이 이겨 Button의 `data-slot="button"`이 사라지고 HM Button 스타일이 적용되지 않는다(Dialog에서 해결한 문제와 같은 원인). 출력 속성은 Button의 `data-slot="button"`, `data-variant`, `data-appearance`, `data-size`. +- Alert Dialog 자체에는 Button 스타일을 정의하지 않는다. Button API(`variant`/`appearance`/`size`)만 전달한다. +- `AlertDialogContent`에 Close(X)는 없다. 새 prop(`size`, `variant`, `destructive`)은 만들지 않았다. + +## 6. Token + +Component Mapping 위치: `packages/ui/src/theme/alert-dialog.css` (`--hm-alert-dialog-*`). + +`background`, `text`, `border-width`, `border-color`, `radius`, `shadow`, `width`, `edge`, `padding`, `header-gap`, `actions-gap`, `actions-margin-top`, `title-*`, `description-*`. Semantic과 Theme palette만 참조하며 `--hm-dialog-*`를 참조하지 않는다. 신규 Primitive 없음. Button 색은 Button 문서의 token을 따른다. + +## 7. shadcn / Radix에서 유지한 항목 + +Radix AlertDialog 구조 전부: Portal·Overlay·modal·focus trap·초기 focus(Cancel)·Escape·바깥 클릭 무시·focus 복귀·`role=alertdialog`·`aria-labelledby/describedby`, 중앙 위치 class, 열림/닫힘 animation class, `data-slot`(Content·Header·Footer·Title·Description). + +## 8. HM에서 변경한 항목 + +- shadcn `bg-background`, `ring-1`, `rounded-xl`, `p-4`, `gap-4`, `max-w-xs`/`sm:max-w-sm`, size 변형 제거 → theme CSS. +- `AlertDialogAction`/`Cancel`을 `buttonVariants` class 적용 방식에서 **HM Button 합성**으로 변경. 선택지 `variant`/`appearance`/`size`를 Button API로 받는다. +- 방향별 Header/Footer 배치와 `AlertDialogMedia` 같은 shadcn 부가 요소는 추가하지 않았다. + +## 9. Storybook + +- 대표 Story `All`("Alert Dialog"): 트리거 2개(로그아웃 = 일반 확인, 삭제 = destructive)가 각각 실제 Alert Dialog를 연다. +- verification: `Confirmation`, `Destructive`(처음부터 열림). + +## 10. 사용자 확정 결정 + +- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다. + +## 11. 미확정 / 보류 + +- 수치(400px, 18px Title, 24px padding, 그림자)는 현재 HM 규격이며 디자인 확정 시 재검토. +- Overlay 색·blur, Motion은 shadcn 기본 유지. +- 파괴적 작업의 기본 강조(Red outline)는 Dialog 삭제 확인과 같은 조합이며 Alert Dialog 고유 규칙이 아니다. +- 아이콘/Media slot, non-dismissible 정책 변경(예: Escape 막기)은 만들지 않았다. +- 소비 화면의 공통 header/footer 높이 대응 없음. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/alert-dialog.tsx +packages/ui/src/theme/alert-dialog.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export) +src/components/alert-dialog.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- 실제 interaction(Browser pane): 키보드(Enter)로 열기, `role=alertdialog`, 초기 focus가 "취소", Tab으로 "삭제" 이동 후 다시 Tab하면 "취소"로 순환(focus trap), 바깥 클릭에 닫히지 않음, Escape 닫힘과 trigger focus 복귀, Cancel·Action 클릭 닫힘과 trigger 복귀, Close(X) 없음. +- Button 합성: Action·Cancel 모두 `data-slot="button"`, `data-variant`(destructive/cancel), `data-appearance`가 유지되고 HM 색(Cancel `rgb(118,118,118)`, 삭제 글자 `rgb(211,47,47)`)이 적용됨. +- computed style: width 400px, radius 8px, padding 24px, shadow `0 0 12px .2`. +- 390×844: 폭 358px(좌우 16px), Footer 세로 쌓임(Cancel 아래), 가로 overflow 없음. +- 미확인: Enter/Space로 Action 키보드 실행, Radix `AlertDialogAction` 비동기 처리, 닫기 animation 시각. diff --git a/docs/components/alert.md b/docs/components/alert.md new file mode 100644 index 0000000..9ae2d79 --- /dev/null +++ b/docs/components/alert.md @@ -0,0 +1,89 @@ +# Alert + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Alert 고유의 API·Token·상태·결정만 기록한다. + +## 1. 상태 + +| 항목 | 내용 | +|---|---| +| 구현 상태 | 구현 완료 (현재 HM 규격) | +| 기준일 | 2026-10-02 | +| 기반 | shadcn/ui Alert (CVA, grid + 선행 svg) | +| 대표 Story | `Components/Feedback/Alert` → `All` (이름 "Alert") | +| 보류 | 아래 §11 | + +## 2. 적용 근거 + +- **참고 근거**: ui-viz 기존 테스트 규격에는 Alert 규격이 없다(후보 목록, 상태 색 success/warning/information 미정). Toast와 별개로 참고만 했다. +- **현재 HM 규격**: §3~§4. +- **구현 방식**: shadcn Alert 구조(`role="alert"`, 선행 svg 아이콘 grid, `AlertTitle`, `AlertDescription`)를 유지하고 `data-variant`로 theme CSS를 선택. +- **보류**: §11. + +## 3. 기본 규격 + +| 항목 | 값 | +|---|---| +| Container | border 1px, radius 4px, padding `12px 16px`, 면 `--hm-sem-bg-surface` | +| Icon | 16px (소비자가 lucide 등 svg를 첫 자식으로), 컬럼 gap 12px | +| Title | 14 / 20 / 500 | +| Description | 14 / 20 / 400, 색 `--hm-sem-fg-secondary` | +| Close action | 없음 | + +## 4. 상태 규격 + +| variant | border | Title·icon 색 | +|---|---|---| +| `default` | `--hm-sem-stroke-default` | `--hm-sem-fg-primary` | +| `destructive` | `--hm-sem-action-destructive` | `--hm-sem-action-destructive` | + +아이콘이 없으면 텍스트가 왼쪽에서 시작한다. + +## 5. API + +``` +Alert: variant?: "default" | "destructive" 기본 "default" +AlertTitle, AlertDescription +``` +`Alert`는 `role="alert"`로 렌더. 출력 속성: `data-slot="alert|alert-title|alert-description"`, `data-variant`. + +## 6. Token + +`packages/ui/src/theme/alert.css` (`--hm-alert-*`): `radius`, `padding-x/y`, `border-width`, `icon-size`, Title/Description typography, variant별 `{default,destructive}-{background,border-color,title-color,description-color}`. Semantic 참조. Primitive 신규 추가 없음. + +## 7. shadcn / Radix에서 유지한 항목 + +`role="alert"`, grid 레이아웃(`has-[>svg]`), `data-slot`, `cn(..., className)`, `AlertTitle` line-clamp. + +## 8. HM에서 변경한 항목 + +shadcn `rounded-lg border px-4 py-3 text-sm`, destructive의 색 class 제거 → theme CSS. 아이콘을 16px로 고정. + +## 9. Storybook + +대표 Story `All`: `default`, `destructive`, `no-icon`, `title-only`, `long`. verification: `Default`, `Destructive`. + +## 10. 사용자 확정 결정 + +2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 이 컴포넌트에 대한 개별 수치 결정은 없다. + +## 11. 미확정 / 보류 + +- success / warning / information variant: HM 상태 색 미정이라 만들지 않았다(neutral·destructive만). +- 닫기 버튼, action 슬롯: 없음. Toast와 혼합하지 않는다. +- 수치(12/16 padding, 16px 아이콘)는 현재 HM 규격이며 디자인 확정 시 재검토. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/alert.tsx +packages/ui/src/theme/alert.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export Alert, AlertTitle, AlertDescription) +src/components/alert.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- computed style: `role=alert`, border 1px, radius 4px, padding `12px 16px`, Title 14px/500, destructive border·Title `rgb(211,47,47)`, 아이콘 16px, 아이콘 유무에 따른 Title 시작 위치(45px / 17px) 확인. +- 미확인: 사이드바 화면, 390px 폭. diff --git a/docs/components/aspect-ratio.md b/docs/components/aspect-ratio.md new file mode 100644 index 0000000..a3adde6 --- /dev/null +++ b/docs/components/aspect-ratio.md @@ -0,0 +1,79 @@ +# Aspect Ratio + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Aspect Ratio 고유의 API·결정만 기록한다. + +## 1. 상태와 성격 + +| 항목 | 내용 | +|---|---| +| 성격 | **Layout primitive** (비율 유지). 시각 디자인이 없는 utility | +| 구현 상태 | 구현 완료 | +| 기준일 | 2026-10-03 | +| 기반 | shadcn/ui Aspect Ratio = Radix `AspectRatio` | +| 대표 Story | `Components/Data & Display/Aspect Ratio` → `All` (이름 "Aspect Ratio") | +| 보류 | 아래 §10 | + +## 2. 적용 근거 + +- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Aspect Ratio가 없다. 참고할 수치가 없다. +- **현재 HM 규격**: 시각 규격이 없다. 비율 박스만 제공하며 surface, background, radius, border, 이미지 처리는 소비자가 자식에 구성한다(HM 디자인을 이유로 이미지 스타일을 내부에 넣지 않는다). +- **구현 방식**: Radix AspectRatio를 그대로 감싼다(`data-slot="aspect-ratio"`). +- **보류**: §10. + +## 3. 기본 규격 + +| 항목 | 값 | +|---|---| +| Ratio | `ratio` prop(너비 / 높이, 예: `16 / 9`). 기본 1 | +| 폭 | 부모 폭 100%(Radix wrapper가 `padding-bottom`으로 높이를 계산) | +| 시각 | 없음(배경, 윤곽, radius 모두 투명/0) | +| 자식 | Radix가 `position:absolute; inset:0`으로 채운다. 이미지는 소비자가 `size-full object-cover rounded-md` 등으로 구성 | + +## 4. 상태 규격 + +상태 없음. + +## 5. API + +Radix AspectRatio props 그대로. export: `AspectRatio`. + +- `ratio`, `asChild`, 그리고 `div` props. `size`, `variant`, `mediaType`은 만들지 않았다. + +## 6. Dependency + +새 dependency 없음(`radix-ui`에 이미 포함). + +## 7. Token / Theme CSS + +**없다.** 시각 규격이 없어 `--hm-aspect-ratio-*` Token과 `aspect-ratio.css`를 만들지 않았고 `theme.css`에도 연결하지 않았다. + +## 8. 소비자 확장성 + +- 폭은 부모가 정한다(Story는 wrapper의 `max-width`). `className`/`style`이 그대로 적용된다. +- 이미지 fit, radius, border는 자식 요소에서 소비자가 정한다. Card와 조합해도 AspectRatio는 Card에 의존하지 않는다(Story에서 Card 안에 4:3으로 조합). + +## 9. Storybook + +- 대표 Story `All`("Aspect Ratio"): 16:9, 4:3, 1:1 이미지(inline data URI, 네트워크 비의존), video-like placeholder, Card 안 조합을 한 페이지에 둔다. +- verification: `16:9`, `1:1`. + +## 10. 사용자 확정 결정 / 보류 + +- 2026-10-03: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 시각 규격은 정하지 않는다. +- 보류: 비율 preset(예: `ratio="video"`), 이미지 로딩 placeholder, 모서리 radius 기본값은 만들지 않았다. +- CSS `aspect-ratio` 속성 기반 구현(Radix는 `padding-bottom` hack)은 Radix 구현을 따르며 바꾸지 않았다. + +## 11. 관련 파일 + +``` +packages/ui/src/components/ui/aspect-ratio.tsx +packages/ui/src/index.ts (export) +src/components/aspect-ratio.stories.tsx +``` + +## 12. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-03). +- 레이아웃(Browser pane, 390×844): 16:9는 320×180(비율 1.778), 4:3은 320×240(1.333), 1:1은 320×320(1.000), video placeholder 320×180, Card 안 4:3은 270×203(1.333). wrapper style은 `position: relative; width: 100%; padding-bottom: 56.25%` 등 Radix 계산이며 이미지가 같은 크기로 채워지고(`object-fit: cover`, 로드됨) 배경·border·radius가 없다. 페이지 가로 overflow 없음. +- 부모 폭이 바뀌면 높이가 비율대로 변한다(Story의 max-width 320 → 390px 뷰포트에서 320, Card 안에서는 270). +- 미확인: 부모 폭을 연속으로 resize할 때의 reflow, `ratio` 동적 변경. diff --git a/docs/components/avatar.md b/docs/components/avatar.md new file mode 100644 index 0000000..a5ea97d --- /dev/null +++ b/docs/components/avatar.md @@ -0,0 +1,96 @@ +# Avatar + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Avatar 고유의 API·Token·상태·결정만 기록한다. + +## 1. 상태 + +| 항목 | 내용 | +|---|---| +| 구현 상태 | 구현 완료 (현재 HM 규격) | +| 기준일 | 2026-10-02 | +| 기반 | shadcn/ui Avatar (Radix `Avatar`) | +| 대표 Story | `Components/Data & Display/Avatar` → `All` (이름 "Avatar") | +| 보류 | 아래 §11 | + +## 2. 적용 근거 + +- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Avatar가 컴포넌트 이름으로만 있고 규격은 없다. 참고할 수치가 없었다. +- **현재 HM 규격**: 아래 §3. 현재 HM Semantic(accent 면)으로 새로 정했다. size 체계는 Button/Input size를 복사하지 않고 shadcn Avatar API(`sm`/`default`/`lg`)에 맞춰 24/32/40px로 정했다. +- **구현 방식**: shadcn / Radix Avatar 구조(Root, Image, Fallback)를 유지하고 theme CSS로 연결. +- **보류**: §11. + +## 3. 기본 규격 (현재 HM 규격) + +| 항목 | 값 | +|---|---| +| Size | `sm` 24px, `default` 32px, `lg` 40px (정사각형) | +| Shape | 원형(radius 9999px), `overflow: hidden` | +| Image | `size-full`, `object-cover`, `aspect-square` (중앙 crop) | +| Fallback 면 | `--accent` (green tint), 글자 `--accent-foreground` | +| Fallback typography | weight 500, `sm` 10px / `default` 12px / `lg` 14px, line-height 1, 한 줄 `nowrap`, 넘치면 잘림 | + +## 4. 상태 규격 + +| 상태 | 값 | +|---|---| +| 이미지 로드 성공 | Radix가 Image를 렌더하고 Fallback은 렌더하지 않는다 | +| 이미지 로드 실패 / 로딩 중 | Fallback 표시(`delayMs` prop으로 지연 가능) | +| Focus / Disabled | 없음(비 interactive) | + +## 5. API + +Radix Avatar props 그대로. export: `Avatar`, `AvatarImage`, `AvatarFallback`. + +- `Avatar`: `size` `"sm" | "default" | "lg"`(기본 `default`, shadcn API 유지). 출력 `data-size`. +- `AvatarImage`: `src`, `alt`, `onLoadingStatusChange`. `AvatarFallback`: `delayMs`. +- 접근성: 이름이 보이는 곳에서는 `alt=""`, 단독으로 쓰면 `alt`로 이름을 준다. +- 새 prop은 만들지 않았다. 크기는 `size` prop(또는 `--hm-avatar-size-*` Token)으로만 바꾼다. theme CSS가 레이어 밖이라 `className="size-12"` 같은 utility는 이기지 못한다. +- shadcn 최신 버전에 있는 `AvatarBadge`/`AvatarGroup`은 만들지 않았다(필요가 생기면 추가). + +## 6. Token + +Component Mapping 위치: `packages/ui/src/theme/avatar.css` (`--hm-avatar-*`). + +`size-sm|md|lg`, `radius`, `fallback-background`, `fallback-text`, `fallback-font-weight`, `fallback-font-size-sm|md|lg`. Semantic(`--accent`, `--accent-foreground`)만 참조한다. 신규 Primitive 없음. + +## 7. shadcn / Radix에서 유지한 항목 + +Radix 이미지 로드 상태 처리와 Fallback 표시 규칙, 구조, `size` prop, `data-slot`, `cn(..., className)`. + +## 8. HM에서 변경한 항목 + +- shadcn `size-8`/`data-[size=lg]:size-10`, `bg-muted` fallback, 글자 크기 class를 제거하고 Token으로 연결. 크기는 24/32/40. +- Fallback 면을 `muted`가 아니라 accent(green tint)로 했다(HM 팔레트에서 대비가 있는 면). + +## 9. Storybook + +- 대표 Story `All`("Avatar"): Image(sm/default/lg, data URI 이미지로 네트워크 비의존), Fallback(깨진 이미지 주소로 initials: 한글 두 글자, 영문 두 글자, 한 글자, 긴 fallback), Composition(이름 + 소속)을 한 페이지에 둔다. +- verification: `Image`, `Fallback`. + +## 10. 사용자 확정 결정 + +- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다. + +## 11. 미확정 / 보류 + +- Size(24/32/40)와 fallback 색은 현재 HM 규격이며 Dashboard/GNB의 실제 사용이 정해지면 재검토. +- 긴 fallback(예: 10자)은 가운데에서 잘린다. 1~2자 initials가 전제이며 줄임 규칙은 만들지 않았다. +- Icon fallback(사람 아이콘) 규칙, 상태 badge, 겹침 그룹은 정의하지 않았다. +- 이미지 비율이 다른 원본은 중앙 crop(`object-cover`)만 한다. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/avatar.tsx +packages/ui/src/theme/avatar.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export) +src/components/avatar.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- 실제 렌더(Browser pane): 로드되는 이미지 Avatar는 `img`만 있고 fallback 요소가 없음(24/32/40px, radius 9999px). 깨진 이미지 주소의 Avatar는 `img` 없이 fallback이 렌더되고 `김한`/`HM`/`한`/`ABCDEFGHIJ`가 32×32 안에서 레이아웃 변동 없이 표시, 배경 `rgb(189,223,212)`·글자 `rgb(18,51,40)`·12px. +- Responsive(390×844): Avatar 구성이 한 줄로 들어가고 가로 overflow 없음. +- 미확인: 이미지가 느리게 로드될 때 fallback에서 image로 전환되는 순간의 깜빡임, `delayMs`, 대비 비율의 수치 측정. diff --git a/docs/components/badge.md b/docs/components/badge.md new file mode 100644 index 0000000..ee9543e --- /dev/null +++ b/docs/components/badge.md @@ -0,0 +1,94 @@ +# Badge + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Badge 고유의 API·Token·상태·결정만 기록한다. + +## 1. 상태 + +| 항목 | 내용 | +|---|---| +| 구현 상태 | 구현 완료 (현재 HM 규격) | +| 기준일 | 2026-10-02 | +| 기반 | shadcn/ui Badge (CVA, `asChild` Slot) | +| 대표 Story | `Components/Data & Display/Badge` → `All` (이름 "Badge") | +| 보류 | 아래 §11 | + +## 2. 적용 근거 + +- **참고 근거**: ui-viz 기존 테스트 규격에는 Badge 문서가 없다(Border 1px 등 일부 언급만). 참고만 했다. +- **현재 HM 규격**: §3~§4. +- **구현 방식**: shadcn Badge 구조(CVA `variant`, `data-slot`, `asChild`, `cn(..., className)`)를 유지하고 `data-variant`로 theme CSS를 선택. Button variant를 재사용하지 않는다. +- **보류**: §11. + +## 3. 기본 규격 + +| 항목 | 값 | +|---|---| +| Typography | 12 / 18 / 500 | +| Padding | `2px 8px` (높이 24px) | +| Border / Radius | 1px / `--hm-radius-control-md` (4px) | +| Gap | shadcn `gap-1`, 아이콘 12px (`size-3`) | + +## 4. 상태 규격 + +| variant | 면 | 글자 | 테두리 | +|---|---|---|---| +| `default` | `--hm-sem-action-primary` (Primary Green `#123328`) | `--hm-sem-on-action-primary` (White) | 면과 동일 | +| `secondary` | `--hm-sem-bg-subtle` | `--hm-sem-fg-secondary` | 면과 동일 | +| `destructive` | `--hm-sem-action-destructive` | White | 면과 동일 | +| `outline` | `--hm-sem-bg-surface` | `--hm-sem-fg-primary` | `--hm-sem-stroke-default` | + +상호작용 상태 없음 (`asChild`로 링크/버튼에 감쌀 때 그 요소가 담당). + +## 5. API + +``` +Badge: + variant?: "default" | "secondary" | "destructive" | "outline" 기본 "default" + asChild?: boolean +``` +나머지는 `` props. 출력 속성: `data-slot="badge"`, `data-variant`. `badgeVariants`도 export. + +## 6. Token + +`packages/ui/src/theme/badge.css` (`--hm-badge-*`): `radius`, `padding-x/y`, `border-width`, `font-size/line-height/font-weight`, variant별 `{default,secondary,destructive,outline}-{background,text,border-color}`. Semantic 참조. Primitive 신규 추가 없음. + +## 7. shadcn / Radix에서 유지한 항목 + +CVA 구조, `asChild`(Radix Slot), `data-slot`, `cn(..., className)`, 아이콘 크기 규칙(`[&>svg]:size-3`). + +## 8. HM에서 변경한 항목 + +shadcn `rounded-full`·`px-2 py-0.5 text-xs`·focus/invalid ring·hover class 제거 → theme CSS(radius 4). variant 이름은 shadcn 4종을 유지하고 색만 HM으로 매핑. + +## 9. Storybook + +대표 Story `All`: `variants`, `icon`, `long`, `link`(asChild). verification: `Variants`, `Playground`. + +## 10. 사용자 확정 결정 + +2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 이 컴포넌트에 대한 개별 수치 결정은 없다. + +2026-10-06 (Badge 원복): 직전 Active Semantic 후속에서 `default`를 Light 2(`--hm-sem-state-active`)로 바꾼 변경을 취소했다. `default`는 Strong Brand / Primary인 Primary Green(`--hm-sem-action-primary` / `--hm-sem-on-action-primary`)을 쓰며 Active Semantic 적용 대상이 아니다. Component Token 이름은 유지하고 참조만 되돌렸다. `secondary`·`outline`·`destructive`는 그대로다. Badge는 비인터랙티브 label이라 hover 면을 두지 않는다. + +## 11. 미확정 / 보류 + +- success / warning / information 같은 상태 variant: HM 상태 색이 정해지지 않아 만들지 않았다. +- size 변형, 점(dot) badge: 미결정. +- 수치(12/18/500, 2/8, radius 4)는 현재 HM 규격이며 디자인 확정 시 재검토. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/badge.tsx +packages/ui/src/theme/badge.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export Badge, badgeVariants) +src/components/badge.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- computed style: 4 variant 모두 12px/18px/500, padding `2px 8px`, radius 4px, 높이 24px, 색 위 표와 일치, 아이콘 12px. +- 미확인: `asChild` 링크 focus 표시, 사이드바 화면, 390px 폭. +- 2026-10-06 Badge 원복: `default` 면·테두리 `rgb(18,51,40)`(`#123328`), 글자 `rgb(255,255,255)`. secondary·destructive·outline 값 변화 없음. diff --git a/docs/components/breadcrumb.md b/docs/components/breadcrumb.md new file mode 100644 index 0000000..53ed71f --- /dev/null +++ b/docs/components/breadcrumb.md @@ -0,0 +1,102 @@ +# Breadcrumb + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Breadcrumb 고유의 API·Token·상태·결정만 기록한다. + +## 1. 상태 + +| 항목 | 내용 | +|---|---| +| 구현 상태 | 구현 완료 (현재 HM 규격) | +| 기준일 | 2026-10-02 | +| 기반 | shadcn/ui Breadcrumb (Radix 없음, `nav > ol > li` + `Slot`) | +| 대표 Story | `Components/Navigation/Breadcrumb` → `All` (이름 "Breadcrumb") | +| 보류 | 아래 §11 | + +## 2. 적용 근거 + +- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Breadcrumb이 컴포넌트 이름으로만 있고 규격은 없다. 참고할 수치가 없었다. +- **현재 HM 규격**: 아래 §3~§4. 현재 HM Semantic 체계(fg-primary/secondary)로 새로 정했다. +- **구현 방식**: shadcn Breadcrumb 구조 유지, theme CSS로 HM 스타일 연결. 페이지 안에 직접 놓이는 navigation primitive라 Portal·overlay가 없다. +- **역할 범위**: 현재 위치와 상위 이동만 담당한다. 메뉴 시스템·자동 접기(`maxItems`)는 없다. +- **보류**: §11. + +## 3. 기본 규격 (현재 HM 규격) + +| 항목 | 값 | +|---|---| +| Typography | 14 / 20 / 400 | +| Item 간격 | 8px(Item 안 gap, 목록 gap) | +| Link | `--hm-sem-fg-secondary`, hover 시 `--hm-sem-fg-primary` + 밑줄(offset 4px) | +| Current Page | `--hm-sem-fg-primary`, weight 700, 기본 커서 | +| Separator | shadcn `ChevronRightIcon` 16px, `--hm-sem-fg-secondary` | +| Ellipsis | 20px 영역, 아이콘 16px, `--hm-sem-fg-secondary` | +| 긴 경로 | `flex-wrap`으로 줄바꿈 (`break-words`) | + +## 4. 상태 규격 + +| 상태 | 값 | +|---|---| +| Hover (Link) | 전경 primary + 밑줄 | +| Focus (`:focus-visible`) | ring 모양 `0 0 0 3px ring 50%`, radius 2px | +| Current | `aria-current="page"`, `aria-disabled="true"`, `role="link"` (shadcn), 링크가 아닌 `span` | +| Disabled | 없음(없는 상태를 만들지 않음) | + +## 5. API + +export: `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, `BreadcrumbSeparator`, `BreadcrumbEllipsis`. + +- `BreadcrumbLink`: 기본 `a`, `asChild`로 router Link 등과 조합(특정 router에 의존하지 않음). +- `BreadcrumbPage`: 현재 위치 표시용 `span`. 클릭할 수 없다. +- `BreadcrumbSeparator`: 기본 chevron, `children`으로 교체(예: `/`). `aria-hidden`, `role="presentation"`. +- `BreadcrumbEllipsis`: 표시용(`aria-hidden`, sr-only "More"). Dropdown과 자동 결합하지 않는다. 메뉴 연결은 소비 조합(Story 예시). +- `variant`, `size`, `maxItems`는 만들지 않았다. + +## 6. Token + +Component Mapping 위치: `packages/ui/src/theme/breadcrumb.css` (`--hm-breadcrumb-*`). + +`font-size`, `line-height`, `font-weight`, `gap`, `link-color`, `link-hover-color`, `page-color`, `page-font-weight`, `separator-color`, `separator-size`, `ellipsis-size`, `focus-radius`. Semantic만 참조하고 다른 컴포넌트 Token을 쓰지 않는다. 신규 Primitive 없음. + +## 7. shadcn에서 유지한 항목 + +`nav aria-label="breadcrumb"`, `ol`/`li` 구조, `BreadcrumbPage`의 ARIA, separator의 `aria-hidden`, `asChild`(Slot), chevron·`MoreHorizontal` 아이콘, `data-slot`, `cn(..., className)`. + +## 8. HM에서 변경한 항목 + +- shadcn `text-sm text-muted-foreground gap-1.5 sm:gap-2.5`, Link `hover:text-foreground`, Page `font-normal text-foreground`, 아이콘 `size-3.5`/`size-9` class 제거 → theme CSS. +- Current Page를 weight 700으로 강조(shadcn은 normal). +- Link에 밑줄 hover 및 focus ring 추가(shadcn은 색 변화만). + +## 9. Storybook + +- 대표 Story `All`("Breadcrumb"): Basic(3 depth), Ellipsis(Dropdown Menu 조합), Long path, Custom link(asChild)와 custom separator를 한 페이지에 둔다. +- verification: `Basic`, `Ellipsis`, `Long Path`. + +## 10. 사용자 확정 결정 + +- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다. + +## 11. 미확정 / 보류 + +- 수치(14/20, gap 8px, Current weight 700, chevron 16px)는 현재 HM 규격이며 디자인 확정 시 재검토. +- 긴 경로의 자동 접기(`maxItems`)와 모바일 축약 표현은 만들지 않았다. 지금은 줄바꿈만 한다. +- Separator 색은 Link와 같은 secondary(별도 색 결정 없음). +- Ellipsis는 표시용이다. 접힌 항목을 보여주는 메뉴 규칙은 소비 조합으로 남긴다. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/breadcrumb.tsx +packages/ui/src/theme/breadcrumb.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export) +src/components/breadcrumb.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- 실제 interaction(Browser pane): Link는 `a`, Current Page는 `span`(`aria-current="page"`, `aria-disabled="true"`, 커서 default), Tab으로 Link 사이 이동하고 Current Page는 포커스 대상이 아님, Separator·Ellipsis `aria-hidden`. +- computed style: Link `rgb(110,103,89)` 14/20, Current `rgb(10,34,29)` 700. +- Responsive: 390×844에서 목록 폭 358px, 긴 경로(7 depth)는 4줄(76px)로 줄바꿈되고 가로 overflow 없음. +- 미확인: 실제 pointer hover/focus ring 시각, Ellipsis 안 Dropdown Menu 키보드 흐름. diff --git a/docs/components/button.md b/docs/components/button.md new file mode 100644 index 0000000..e4c765d --- /dev/null +++ b/docs/components/button.md @@ -0,0 +1,191 @@ +# Button + +현재 HM 구현과 Runtime-neutral 사용 기준 문서. 공통 원칙은 `AGENTS.md`와 `docs/guidelines/runtime-portability.md`를 따른다. 이 문서는 Button API·Token·상태·사용 위치·조합 계약을 기록한다. + +## 1. 상태 + +| 항목 | 내용 | +|---|---| +| 구현 상태 | HM 스타일 적용 완료 (`hm-*` class 없음) | +| 기준일 | 2026-10-08 (Role × Appearance 계약·현재 Story 대조) | +| 기반 | shadcn/ui Button (CVA, Radix `Slot`) | +| 대표 Story | `Components/Form/Button` → `All` (이름 "Button") | +| 보류 | Brand label Text Border 두께(EXPERIMENTAL, 미적용), Focus(ui_viz TBD) | + +## 2. 적용 근거 + +- **ui_viz Confirmed**: size, padding-x, label size/line-height(button.md §26, D-087), Brand gradient·bevel(D-087), Standard Solid/Outline 색(§28, D-086, D-087), Hover/Pressed 동일 Black 20%(§12), Red Outline Hover/Pressed Red 8%. +- **HM 사용자 확정 규칙**: 실제 화면 Brand 최대 1개·팝업 Brand 금지, 일반 확인/작성 Solid 세트, 삭제 확인·행/반복 작업 Outline 세트, 같은 세트의 appearance와 size 통일(§5). Disabled `0.6`은 ui_viz 규격 값이며, Input·Select의 2026-10-02 결정과는 별개다. +- **shadcn 유지**: 구조(CVA, `data-slot`, `cn(..., className)`), focus/invalid ring class, `ghost`·`link` variant와 기존 size 이름. +- **미정의/TBD**: Focus 표현(ui_viz TBD → shadcn 기본 focus ring 유지), `aria-invalid` 표현(ui_viz에 없음 → shadcn class 유지), `ghost`·`link`의 규격(ui_viz 없음). + +## 3. 기본 규격 + +| Size | Height(min) | Padding-x | Font / Line-height | +|---|---|---|---| +| `sm` | 28 | 10 | 12 / 18 | +| `default` | 34 | 14 | 13 / 20 | +| `lg` | 42 | 18 | 15 / 22 | + +- 높이는 `min-height`다. 기본 라벨은 `nowrap`으로 한 줄을 유지한다. 여러 줄이 필요한 경우 기존 `className="whitespace-normal"`을 명시하며, 이때만 줄바꿈에 따라 높이가 늘어난다(가운데 정렬, `overflow-wrap: anywhere`). +- 세로 padding은 `(height − line-height) / 2 − border`로 계산한다. +- Radius `--hm-radius-button`, font-family `--hm-font-family-base`. +- Weight: Brand 700, Standard 500. +- 아이콘 전용 size(`icon*`)와 `xs`는 shadcn 기본이며 HM 규격이 없다(`data-appearance`가 있는 버튼에 한해 `default|sm|lg`만 HM 크기 적용). + +## 4. 상태 규격 + +| 상태 | 값 | 근거 | +|---|---|---| +| Default | treatment별 face/border (아래 Token) | ui_viz Confirmed | +| Hover / Pressed (Brand, Solid) | face 위에 Black 20% overlay (`inset` shadow) | ui_viz §12 Confirmed | +| Hover (Outline) | role ink로 채움 + 흰 글자 | ui_viz Confirmed | +| Pressed (Outline) | 흰 face + Black 20% overlay | ui_viz Confirmed | +| Red Outline Hover/Pressed | Red 8% face, Black overlay 없음 | ui_viz D-087 Confirmed | +| Disabled | `opacity: 0.6`, `cursor: not-allowed`, Default paint 유지 | ui_viz Confirmed (HM roles) | +| Focus | shadcn 기본 `focus-visible` ring | ui_viz TBD → shadcn 유지 | +| Invalid | shadcn `aria-invalid` ring | ui_viz 미정의 → shadcn 유지 | + +- Brand/Solid의 Hover·Pressed는 `:not(:disabled)`에만 적용된다. +- transition과 눌림 이동(`translate-y-px`)은 HM role에서 제거한다. +- **`ghost`·`link`의 Disabled는 `opacity-50`(shadcn 기본)** 이다. HM role(`0.6`)과 값이 다르다. 규격이 없는 호환 variant라 그대로 둔다(아래 §11). + +## 5. 사용 위치와 버튼 세트 + +- `brand`: 페이지 대표 실행에 사용한다. 실제 서비스 화면당 최대 1개이며 팝업에서는 사용하지 않는다. Storybook 비교 갤러리는 예외다. +- `solid`: 일반 실행과 등록·저장·확인에 사용한다. +- `outline`: 반복·보조 작업과 표/목록 행 작업에 사용한다. +- 일반 작성·수정창: 취소 Solid + 등록/저장 Solid. +- 일반 확인창: 취소 Solid + 확인 Solid. +- 삭제 확인창: 취소 Outline + 삭제 Outline. +- 표/목록 행과 반복·보조 작업: 동일 `appearance`의 Outline 조합. +- FilterBar 대표 Story의 초기화·조회: `cancel` + `primary`, 둘 다 Outline·`size="default"`. 목록의 반복 조회·보조 작업 세트다. +- 하나의 세트는 `appearance`와 `size`를 통일한다. 역할 차이는 variant의 기존 역할색과 문구로 표현한다. + +사용 위치를 기준으로 appearance를 명시하며 버튼 문구만으로 판단하지 않는다. 컴포넌트는 appearance 생략 시 기본 `brand`를 출력하므로, 팝업·버튼 세트에서는 기본값에 의존하지 않는다. + +## 6. API + +``` +variant: shadcn "default | outline | secondary | ghost | destructive | link" + → + HM "primary | secondary | modify | destructive | cancel" + + 별칭 "default"(= primary), "outline"(= primary + outline appearance) + + 호환 "ghost", "link" +appearance: (신규) "brand | solid | outline" 기본 "brand" +size: "default | xs | sm | lg | icon | icon-xs | icon-sm | icon-lg" (shadcn 그대로) +asChild: shadcn 그대로 +``` + +- 기본값: `variant="primary"`, `appearance="brand"`, `size="default"`. +- 출력 속성: `data-slot="button"`, `data-variant`(별칭은 role로 정규화), `data-appearance`(`ghost`/`link`은 없음), `data-size`. +- `secondary`의 의미가 바뀌었다: shadcn 회색 보조 → ui_viz Brown 역할. 기존 사용처 영향은 별도 확인하지 않았다. + +### 6.1 허용 Role × Appearance (AI Reference) + +현재 `button.css`의 역할별 Mapping과 대표 `All` Story의 `rolesFor`가 지원하는 조합은 다음과 같다. + +- `primary` (Green): `brand`, `solid`, `outline`. +- `secondary` (Brown): `brand`, `solid`, `outline`. +- `modify` (Orange): `brand`, `solid`. **`outline` 미지원**. +- `cancel` (Gray): `solid`, `outline`. **`brand` 미지원**. +- `destructive` (Red): `outline`만 지원. **`brand`, `solid` 미지원**. + +공개 Props는 `variant`와 `appearance`를 독립적으로 받는다. TypeScript 통과나 Storybook Controls에서 선택 가능하다는 사실은 해당 조합의 디자인 지원을 뜻하지 않는다. 미지원 조합에는 필요한 역할별 CSS Custom Property가 없으며 자동 보정·fallback도 없다. 기존 CSS 변수에 임의 fallback이나 새로운 역할색을 추가하지 않는다. + +### 6.2 기본값·별칭·합성 주의 + +- ` + + +// Alert Dialog 삭제 확인: 두 요소 모두 Outline을 명시 +취소 +삭제 +``` + +미지원 조합 추가, role별 자동 appearance 선택, 기본 appearance 변경은 사용자 결정이 필요한 별도 변경이다. 현재 계약과 공개 API를 유지한다. + +## 7. Token + +Component Mapping 위치: `packages/ui/src/theme/button.css` (`--hm-button-*`). 공통 값은 `packages/ui/src/theme/default.css`, Primitive는 `packages/ui/src/theme/primitives.css`. + +- 공통 Semantic 재사용: `--hm-radius-button`, `--hm-font-family-base`, `--hm-font-weight-*`, `--hm-color-text-inverse`, `--hm-color-surface-default`, `--hm-color-action-*`, `--hm-sem-surface/border-action-primary`. +- `--hm-button-height-md`는 `--hm-control-height-md`(34)를 참조한다. sm/lg 높이는 Button 전용 값이다. +- Brand/Solid의 색 Token은 role별(`green|brown|orange|gray`)로 Component 안에 둔다. Brown/Orange gradient의 일부 stop과 Gray 값은 `default.css`의 공개 Theme palette를 참조한다. `button.css`에 직접 HEX는 남아 있지 않다. +- Input·Select와 높이 값(28/34/42)이 겹쳐도 alias하지 않는다. 각 ui_viz 규격이 따로 확정한 값이기 때문이다. + +## 8. Runtime-neutral DOM 계약 + +React가 아닌 Runtime도 배포된 `@hm-design/ui/theme.css` 또는 승인된 standalone Theme CSS를 먼저 로드하고, semantic ` + + + +) +``` + +Uncontrolled 예제는 `checked` / `onCheckedChange` 대신 `defaultChecked={true}`를 사용하며 부모의 Reset 상태 관리는 필요하지 않다. + +## 6. Token + +Component Mapping 위치: `packages/ui/src/theme/checkbox.css` (`--hm-checkbox-*`). + +`size`, `radius`, `border-width`, `icon-size`, `background`, `border-color`, `hover-border-color`, `checked-background`, `checked-border-color`, `checked-hover-overlay`, `indicator-color`, `error-border-color`. Semantic(`--hm-sem-*`)을 참조하고 Input 등 다른 컴포넌트 Token을 쓰지 않는다. Primitive 신규 추가 없음(`--hm-sem-action-destructive`는 기존 Primitive를 직접 참조). + +## 7. shadcn / Radix에서 유지한 항목 + +Radix 구조·ARIA(`role=checkbox`, `aria-checked`)·키보드, `data-slot`, `cn(..., className)`, `transition-colors`, `outline-none` + focus ring, disabled 커서, lucide `CheckIcon`. + +## 8. HM에서 변경한 항목 + +- shadcn `size-4 rounded-[4px] border shadow-xs`, `dark:` 및 `data-[state=checked]:bg-primary` 계열 class 제거 → theme CSS. +- `MinusIcon` 추가(shadcn 기본은 indeterminate 아이콘이 없다). 두 아이콘은 `data-state`에 따라 CSS로 전환. +- Disabled opacity 50 → 60. + +## 9. Storybook + +- 대표 Story `All`("Checkbox"): `unchecked`, `checked`, `indeterminate`, `interactive`(controlled), `hover`, `focus`, `error`, `disabled`, `long`. Focus는 `play`로 실제 focus. +- verification: `Unchecked`, `Checked`, `Indeterminate`, `Disabled`, `Error`, `Playground`. + +## 10. 사용자 확정 결정 + +- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다. + +## 11. 미확정 / 보류 + +- Checkbox Group 간격, group Label·Error 슬롯, option label 전경 role: 미결정. +- 수치(18px, 14px 아이콘 등)는 현재 HM 규격이며 디자인 확정 시 재검토. +- Checkbox Group, Error 문구 슬롯: 컴포넌트 없음(소비 조합). +- Hit area 확장(24px 타깃 등) 미적용. +- Disabled opacity `0.6`: 이 컴포넌트 값이며 전역 Disabled 규칙으로 승격하지 않는다. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/checkbox.tsx +packages/ui/src/theme/checkbox.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export Checkbox) +src/components/checkbox.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- Storybook computed style: 18×18, border 1px `rgb(154,146,132)`, radius 4px; Checked/Indeterminate bg·border `rgb(18,51,40)`; Error border `rgb(211,47,47)`; Disabled opacity 0.6; 체크/마이너스 아이콘이 상태에 따라 하나만 표시. +- 실제 interaction: controlled indeterminate → click → checked → click → unchecked, Label 클릭으로 토글 확인. +- 2026-10-08, React 19.3 / Radix Checkbox 1.3.11: 프로젝트 외부 격리 환경의 수정된 HM 패키지로 실제 브라우저 검증. 수정 전 외부 Form의 true·false·indeterminate Reset 불일치를 재현했고, 수정 후 모두 PASS. +- Form 내부·외부에서 Space로 상태 변경 후 Native Reset 버튼 및 `form.reset()`을 실행: `defaultChecked` true·false·indeterminate와 Controlled 부모 `onReset`의 상태·ARIA·숨김 input·FormData 복원 확인. Indeterminate 복원 시 숨김 input은 `checked=false`, `indeterminate=true`, 제출값 없음. +- 기존 Radix 동작도 구분해 확인: 최초 indeterminate 렌더에서는 숨김 input의 `indeterminate` 속성이 false다(내부·외부 및 수정 전·후 동일). UI/ARIA는 mixed, input의 `checked=false`, FormData 제외는 정상이며 이번 외부 Form Reset 수정에서 이 초기화 동작은 변경하지 않는다. +- Native Form HTTP POST 8건: 내부·외부·Controlled의 Checked 값은 `choice=accepted`, Unchecked·indeterminate·Disabled 값은 제외. `required` native 유효성 및 ARIA, Disabled Tab 제외, BUTTON ref 유지 확인. +- 수정 전·후 같은 Theme에서 Checked·Unchecked·Indeterminate·Disabled computed style과 아이콘 일치: 18×18px, border 1px, radius 4px, icon 14px, Disabled opacity 0.6. 키보드 focus-visible 3px ring도 일치. CSS·Theme·대표 Story 변경 없음. +- UI 전체 타입 검사 및 공식 UI 패키지 빌드를 격리 경로에서 통과. 테스트에 사용한 Checkbox 소스와 실제 수정본의 SHA-256 일치. 공용 dist는 이 검증에서 빌드하지 않음. +- 미확인: 실제 pointer hover, 사이드바 화면, 390px 폭. Hover 관련 CSS와 class는 변경하지 않음. diff --git a/docs/components/collapsible.md b/docs/components/collapsible.md new file mode 100644 index 0000000..f28a717 --- /dev/null +++ b/docs/components/collapsible.md @@ -0,0 +1,102 @@ +# Collapsible + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Collapsible 고유의 API·Token·상태·결정만 기록한다. Accordion(`accordion.md`)과 값을 공유하지 않는다. + +## 1. 상태 + +| 항목 | 내용 | +|---|---| +| 구현 상태 | 구현 완료 (현재 HM 규격) | +| 기준일 | 2026-10-02 | +| 기반 | shadcn/ui Collapsible (Radix `Collapsible`) | +| 대표 Story | `Components/Disclosure/Collapsible` → `All` (이름 "Collapsible") | +| 보류 | 아래 §11 | + +## 2. 적용 근거 + +- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Collapsible이 없다. 참고할 수치가 없었다. +- **현재 HM 규격**: 아래 §3~§4. 현재 HM Semantic 체계로 새로 정했다. +- **구현 방식**: shadcn / Radix Collapsible 구조(Root·Trigger·Content)를 유지. shadcn Collapsible은 스타일이 없는 primitive라, 일반 trigger(asChild 없음)에만 텍스트 trigger 모양을 주고 content 간격·타이포를 정했다. +- **Accordion과의 구분**: Collapsible은 단일 영역의 간단한 disclosure다. divider 구조와 chevron 내장이 없다. Accordion을 항목 하나로 쓰는 용도가 아니다. +- **보류**: §11. + +## 3. 기본 규격 (현재 HM 규격) + +| 항목 | 값 | +|---|---| +| Trigger(일반) | inline-flex, gap 8px, 배경·border·padding 없음, 14 / 20 / 500, `--hm-sem-fg-primary`, 포인터 커서 | +| Trigger(`asChild`) | 자식이 자기 스타일을 유지(예: HM Button). HM trigger 스타일은 적용되지 않는다 | +| Indicator | trigger 바로 아래 자식 `svg`가 있으면 16px, `--hm-sem-fg-secondary`, 열리면 180° 회전 | +| Content | 위 padding 8px, 14 / 20 / 400, `--hm-sem-fg-primary` | +| 긴 텍스트 | `overflow-wrap: anywhere`, 고정 높이 없음 | +| Divider / border | 없음 | + +## 4. 상태 규격 + +| 상태 | 값 | +|---|---| +| Closed / Open | Radix `data-state`. 닫힌 content는 `hidden`(display: none) | +| Hover | 일반 trigger 밑줄(disabled 제외) | +| Focus (`:focus-visible`) | ring 모양 `0 0 0 3px ring 50%` (일반 trigger) | +| Disabled | `opacity: 0.6`(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), `cursor: not-allowed` | +| Animation | 없음(shadcn Collapsible에 없고 HM Motion 규칙도 없다) | + +## 5. API + +Radix Collapsible props 그대로. export: `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent`. + +- `Collapsible`: `open`/`defaultOpen`/`onOpenChange`, `disabled`. +- `CollapsibleTrigger`: `asChild`. **`asChild`일 때는 `data-slot`을 붙이지 않는다**(DialogClose와 같은 규칙). 붙이면 자식 Button의 `data-slot="button"`이 덮여 HM Button 스타일이 사라진다. 일반 trigger 출력은 `data-slot="collapsible-trigger"`. +- `CollapsibleContent`: 출력 `data-slot="collapsible-content"`. +- 새 prop은 만들지 않았다. + +## 6. Token + +Component Mapping 위치: `packages/ui/src/theme/collapsible.css` (`--hm-collapsible-*`). + +`trigger-text`, `trigger-gap`, `trigger-font-size|line-height|font-weight`, `icon-size`, `icon-color`, `content-gap`, `content-text`, `content-font-size|line-height`, `disabled-opacity`. Semantic만 참조하고 `--hm-accordion-*` 등 다른 컴포넌트의 Token을 쓰지 않는다. 신규 Primitive 없음. + +## 7. shadcn / Radix에서 유지한 항목 + +Radix 구조·ARIA(`aria-expanded`, `aria-controls`)·Enter/Space·controlled/uncontrolled·disabled·`asChild`, `data-slot`, `cn(..., className)`, Content의 `hidden` 처리. + +## 8. HM에서 변경한 항목 + +- 일반 trigger의 텍스트 trigger 스타일, Content 간격·타이포, indicator 회전은 shadcn에 없던 시각 규격을 추가한 것이다. +- `CollapsibleTrigger`의 `asChild` data-slot 처리(위 §5). +- Animation은 추가하지 않았다. + +## 9. Storybook + +- 대표 Story `All`("Collapsible"): `StateBoard`로 Closed, Open, Disabled, Long Content, Controlled + Button trigger를 한 페이지에 둔다. trigger 안 chevron은 일반 소비 조합(`lucide-react` 아이콘)이다. +- verification: `Closed`, `Open`, `Disabled`, `Long Content`. + +## 10. 사용자 확정 결정 + +- 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 개별 수치에 대한 별도 결정은 없다. + +## 11. 미확정 / 보류 + +- 수치(14/20/500, gap 8px, Disabled opacity 0.6)는 현재 HM 규격이며 디자인 확정 시 재검토. +- 열림/닫힘 animation 없음. Motion 규칙이 정해지면 Accordion과 함께 재검토. +- indicator는 컴포넌트에 내장하지 않고 소비 조합으로 둔다(Accordion과 다름). 내장 여부는 사용 사례가 쌓이면 결정. +- 박스형(border/배경) Content variant는 만들지 않았다. +- Hover 밑줄은 일반 trigger에만 적용한다. + +## 12. 관련 파일 + +``` +packages/ui/src/components/ui/collapsible.tsx +packages/ui/src/theme/collapsible.css +packages/ui/src/theme.css (import) +packages/ui/src/index.ts (export) +src/components/collapsible.stories.tsx +``` + +## 13. 검증 상태 + +- `npm run typecheck`, `npm run build`: 통과(2026-10-02). +- 실제 interaction(Browser pane): Enter로 열기(`aria-expanded=true`, indicator 180° 회전), Space로 닫기, 닫힌 content `display: none`, disabled 클릭 무반응(opacity 0.6, cursor not-allowed), controlled(Button trigger asChild)에서 클릭으로 열리고 상태 문구와 버튼 라벨(`접기`)이 갱신됨. +- Button trigger: `data-slot="button"`, `data-variant="primary"`와 HM Button 색이 유지됨. +- computed style: 일반 trigger 14/20/500, `rgb(10,34,29)`, 배경 투명, icon 16px, content 위 padding 8px. 긴 콘텐츠에서 가로 overflow 없음. +- 미확인: 포인터 클릭(JS click과 키보드만 사용), focus ring 시각, 390px 폭, uncontrolled `defaultOpen=false`에서 trigger 클릭(키보드 Enter로 대체 확인). diff --git a/docs/components/combobox.md b/docs/components/combobox.md new file mode 100644 index 0000000..7079971 --- /dev/null +++ b/docs/components/combobox.md @@ -0,0 +1,132 @@ +# Combobox + +현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Combobox 고유의 API·Token·상태·결정만 기록한다. + +## 1. 상태와 성격 + +| 항목 | 내용 | +|---|---| +| 성격 | **Reusable component, Popover + Command 조합** (typed `options` wrapper) | +| 구현 상태 | 구현 완료 (single-select, 현재 HM 규격) | +| 기준일 | 2026-10-02 | +| 기반 | `Popover`(public API) + `Command`(public API) 조합. shadcn 공식 Combobox는 예시 조합이다 | +| 대표 Story | `Components/Search & Date/Combobox` → `All` (이름 "Combobox") | +| 보류 | 아래 §11 | + +## 2. 적용 근거 + +- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Combobox가 없다(Select와 Search Field는 다른 컴포넌트). 참고할 수치가 없었다. +- **Component로 만든 이유**: 검색 가능한 단일 선택은 서비스에서 반복되고(trigger 텍스트, 검색, 선택 표시, 닫힘, focus 복귀가 동일) 최소 API(`options`)로 충분해서 typed wrapper를 제공한다. 더 복잡한 항목(그룹, 아이콘 등)은 소비자가 `Command`를 직접 조합한다. +- **구현 방식**: Popover(Portal, collision, Escape, focus 복귀)와 Command(검색, 필터링, 키보드, listbox ARIA)를 그대로 조합한다. Select의 구현이나 연결형 윤곽을 복제하지 않았다. +- **보류**: §11. + +## 3. Select와의 구분 + +| | Select | Combobox | +|---|---|---| +| 용도 | 정해진 짧은 목록에서 선택, 검색 불필요 | 검색/필터가 필요한 선택 | +| 구현 | Radix Select | Popover + Command(cmdk) | +| 목록 | trigger에 붙은 연결형 윤곽, 선택 항목은 면으로 표시 | 독립 floating 패널(검색 Input + 목록), 선택 항목은 Accent 45% 면 + check 아이콘 | +| 접근성 모델 | Radix Select(listbox) | `role=combobox` trigger + 패널 안 cmdk combobox/listbox | + +## 4. 기본 규격 (현재 HM 규격) + +| 항목 | 값 | +|---|---| +| Size | `large`(기본, 44px, 16 / 24), `medium`(34px, 14 / 20) — Select와 같은 두 크기 | +| Trigger | 폭 100%(부모 폭), padding-x 12px, 1px `--hm-sem-stroke-input-default`, radius 4px, 흰 면, 오른쪽 chevron 18px | +| 값 / placeholder | 선택된 option의 label, 없으면 placeholder("선택", `--hm-sem-fg-placeholder`), 길면 말줄임 | +| Popup | padding 0, 폭 = trigger 폭(`--radix-popover-trigger-width`), 독립 4px gap. Focus Light 2 border, input radius 4px(네 모서리), shadow `0 2px 24px black 12%` | +| 검색 | Command Input(placeholder 기본 "검색...") | +| 목록 | Command List(최대 280px), Item 높이 36px·padding-x 12px·radius 0·weight 400. Large 16/24, Medium 14/20. 선택 check 16px, 긴 label은 말줄임 | +| Empty | "결과가 없습니다."(`emptyText`) | + +## 5. 상태 규격 + +| 상태 | 값 | +|---|---| +| Focus / Open | trigger 테두리 전체가 `--hm-sem-stroke-input-focus` (Input·Select와 같은 방식) | +| Highlighted option | `--hm-sem-surface-highlight` (Accent 45%) | +| Selected option | check 아이콘 + Accent 45% 면(`data-checked`). Command highlight가 다른 항목으로 옮겨져도 유지 | +| Disabled (combobox) | opacity 0.6(이 컴포넌트 값), cursor not-allowed, 열리지 않음 | +| Disabled option | Command의 disabled(opacity 0.6), 방향키 이동·선택 제외 | +| 선택 후 | 값 갱신, Popover 닫힘, focus가 trigger로 복귀. 다시 열면 검색어가 비어 있다(Command가 닫을 때 unmount됨) | + +## 6. API + +export: `Combobox`, types `ComboboxOption`(`{ value, label, disabled? }`), `ComboboxSize`. + +- `options`(필수), `value`/`defaultValue`/`onValueChange(value)`, `placeholder`, `searchPlaceholder`, `emptyText`, `size`, `disabled`, 그리고 `button` props(`id`, `aria-label`, `className`…). +- 필터링: option의 `value`와 `label`(keywords)에 cmdk 기본 fuzzy 필터를 쓴다. +- 이미 선택된 option을 다시 선택해도 값은 유지된다(해제 UI는 없다). +- **접근성**: Trigger는 `role="combobox"`, `aria-haspopup="listbox"`, `aria-expanded`. 패널 안 Command Input이 `role=combobox`(`aria-controls` → `role=listbox`), option은 `role=option`. 이름은 `