@design-ai/cli 4.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +164 -0
- package/AGENTS.ko.md +126 -0
- package/AGENTS.md +182 -0
- package/CHANGELOG.md +6883 -0
- package/CLAUDE.md +39 -0
- package/LICENSE +21 -0
- package/README.ko.md +245 -0
- package/README.md +277 -0
- package/agents/README.md +14 -0
- package/agents/a11y-reviewer.md +121 -0
- package/agents/component-architect.md +57 -0
- package/agents/design-critic.md +46 -0
- package/agents/token-extractor.md +63 -0
- package/cli/bin/design-ai-mcp.mjs +6 -0
- package/cli/bin/design-ai.mjs +40 -0
- package/cli/commands/audit.mjs +111 -0
- package/cli/commands/check.mjs +226 -0
- package/cli/commands/doctor.mjs +141 -0
- package/cli/commands/examples.mjs +58 -0
- package/cli/commands/help.mjs +271 -0
- package/cli/commands/install.mjs +161 -0
- package/cli/commands/learn.mjs +1871 -0
- package/cli/commands/list.mjs +143 -0
- package/cli/commands/mcp.mjs +27 -0
- package/cli/commands/pack.mjs +211 -0
- package/cli/commands/prompt.mjs +199 -0
- package/cli/commands/route.mjs +247 -0
- package/cli/commands/search.mjs +60 -0
- package/cli/commands/show.mjs +52 -0
- package/cli/commands/site.mjs +558 -0
- package/cli/commands/status.mjs +161 -0
- package/cli/commands/uninstall.mjs +119 -0
- package/cli/commands/update.mjs +184 -0
- package/cli/commands/version.mjs +94 -0
- package/cli/commands/workspace.mjs +176 -0
- package/cli/lib/brief.mjs +52 -0
- package/cli/lib/check.mjs +642 -0
- package/cli/lib/dispatch.mjs +109 -0
- package/cli/lib/doctor.mjs +505 -0
- package/cli/lib/examples.mjs +258 -0
- package/cli/lib/exec.mjs +63 -0
- package/cli/lib/help-flags.mjs +5 -0
- package/cli/lib/learn.mjs +3694 -0
- package/cli/lib/log.mjs +47 -0
- package/cli/lib/mcp-server.mjs +426 -0
- package/cli/lib/output.mjs +101 -0
- package/cli/lib/pack.mjs +599 -0
- package/cli/lib/paths.mjs +63 -0
- package/cli/lib/prompt.mjs +603 -0
- package/cli/lib/route.mjs +740 -0
- package/cli/lib/search.mjs +158 -0
- package/cli/lib/show.mjs +161 -0
- package/cli/lib/signals.mjs +2337 -0
- package/cli/lib/site.mjs +6617 -0
- package/cli/lib/skill-proposals.mjs +2046 -0
- package/cli/lib/suggest.mjs +66 -0
- package/cli/lib/workspace.mjs +1056 -0
- package/commands/README.md +25 -0
- package/commands/component-spec.md +34 -0
- package/commands/conversational.md +44 -0
- package/commands/design-from-brief.md +141 -0
- package/commands/design-review.md +62 -0
- package/commands/document-from-brief.md +47 -0
- package/commands/extract-tokens.md +38 -0
- package/commands/game-ui.md +40 -0
- package/commands/illustration.md +37 -0
- package/commands/iterate.md +167 -0
- package/commands/motion-design.md +54 -0
- package/commands/palette-from-brand.md +50 -0
- package/commands/print.md +49 -0
- package/commands/slide-deck.md +46 -0
- package/commands/spatial.md +47 -0
- package/commands/stability-review.md +74 -0
- package/commands/video.md +42 -0
- package/commands/website-improvement.md +77 -0
- package/docs/AGENT-DEVELOPMENT.md +658 -0
- package/docs/AI-LEARNING.md +429 -0
- package/docs/AIDER-INTEGRATION.md +224 -0
- package/docs/ARCHITECTURE.ko.md +183 -0
- package/docs/ARCHITECTURE.md +105 -0
- package/docs/CODEX-INTEGRATION.md +311 -0
- package/docs/COMPANY-WEBSITE-DOGFOOD.ko.md +144 -0
- package/docs/COMPANY-WEBSITE-DOGFOOD.md +150 -0
- package/docs/COMPANY-WEBSITE-INTAKE-TEMPLATE.ko.md +153 -0
- package/docs/COMPANY-WEBSITE-INTAKE-TEMPLATE.md +154 -0
- package/docs/CONTRIBUTING.ko.md +203 -0
- package/docs/CONTRIBUTING.md +242 -0
- package/docs/CURSOR-INTEGRATION.md +220 -0
- package/docs/DISTRIBUTION.ko.md +248 -0
- package/docs/DISTRIBUTION.md +253 -0
- package/docs/DOGFOOD-FINDINGS.md +180 -0
- package/docs/DOGFOOD-V4-FINDINGS.md +139 -0
- package/docs/DOGFOOD-V4-MKDOCS-FINDINGS.md +233 -0
- package/docs/DOGFOOD-V4-NPM-FINDINGS.md +157 -0
- package/docs/DOGFOOD-V4-VSCODE-FINDINGS.md +135 -0
- package/docs/FIGMA-INTEGRATION.md +222 -0
- package/docs/MCP-INTEGRATION.md +243 -0
- package/docs/MIGRATION-v4.md +121 -0
- package/docs/PLUGIN-PACKAGING.md +240 -0
- package/docs/PRODUCT-READINESS.md +66 -0
- package/docs/QUICKSTART.ko.md +178 -0
- package/docs/QUICKSTART.md +179 -0
- package/docs/RELEASE-CHECKLIST.md +292 -0
- package/docs/ROADMAP.md +19495 -0
- package/docs/SESSION-LOG.md +940 -0
- package/docs/TOKEN-SYNC.md +237 -0
- package/docs/USING.ko.md +206 -0
- package/docs/USING.md +218 -0
- package/docs/WEBSITE-IMPROVEMENT.ko.md +75 -0
- package/docs/WEBSITE-IMPROVEMENT.md +89 -0
- package/docs/announcements/README.md +56 -0
- package/docs/announcements/dev-to-korea.md +206 -0
- package/docs/announcements/hashnode-post.ko.md +232 -0
- package/docs/announcements/okky-post.ko.md +158 -0
- package/docs/announcements/press-kit.md +101 -0
- package/docs/announcements/reddit-r-korea.md +196 -0
- package/docs/announcements/show-hn.md +114 -0
- package/docs/announcements/twitter-thread.md +255 -0
- package/docs/external-status.md +34 -0
- package/docs/integrations/aider-walkthrough.ko.md +275 -0
- package/docs/integrations/aider-walkthrough.md +274 -0
- package/docs/integrations/codex-walkthrough.ko.md +297 -0
- package/docs/integrations/codex-walkthrough.md +297 -0
- package/docs/integrations/cursor-walkthrough.ko.md +281 -0
- package/docs/integrations/cursor-walkthrough.md +284 -0
- package/docs/integrations/design-ai-mcp-server.md +169 -0
- package/docs/integrations/figma-mcp.md +149 -0
- package/docs/integrations/github-mcp.md +178 -0
- package/docs/integrations/linear-mcp.md +215 -0
- package/docs/integrations/notion-mcp.md +174 -0
- package/docs/integrations/sdk-walkthrough.ko.md +375 -0
- package/docs/integrations/sdk-walkthrough.md +375 -0
- package/docs/integrations/slack-mcp.md +208 -0
- package/docs/integrations/vscode-walkthrough.ko.md +202 -0
- package/docs/integrations/vscode-walkthrough.md +205 -0
- package/docs/requirements.txt +16 -0
- package/docs/site-overrides/extra.css +83 -0
- package/docs/site-overrides/favicon.svg +4 -0
- package/docs/site-overrides/logo.svg +10 -0
- package/docs/site-overrides/main.html +18 -0
- package/docs/website-console/app.js +2540 -0
- package/docs/website-console/index.html +24 -0
- package/docs/website-console/styles.css +975 -0
- package/examples/README.md +252 -0
- package/examples/cases/dogfood-v4-korean-hr-onboarding.md +356 -0
- package/examples/component-accordion-actions.md +153 -0
- package/examples/component-accordion-details.md +147 -0
- package/examples/component-accordion-summary.md +180 -0
- package/examples/component-accordion.md +229 -0
- package/examples/component-account-card.md +284 -0
- package/examples/component-address-input.md +260 -0
- package/examples/component-affix.md +190 -0
- package/examples/component-alert-dialog.md +210 -0
- package/examples/component-alert-title.md +77 -0
- package/examples/component-alert.md +192 -0
- package/examples/component-amount-input.md +278 -0
- package/examples/component-anchor.md +198 -0
- package/examples/component-app-bar.md +249 -0
- package/examples/component-aspect-ratio.md +156 -0
- package/examples/component-auto-complete.md +199 -0
- package/examples/component-avatar-group.md +170 -0
- package/examples/component-avatar.md +191 -0
- package/examples/component-back-top.md +123 -0
- package/examples/component-backdrop.md +148 -0
- package/examples/component-badge.md +181 -0
- package/examples/component-banner.md +237 -0
- package/examples/component-biometric-gate.md +281 -0
- package/examples/component-blockquote.md +153 -0
- package/examples/component-border-beam.md +207 -0
- package/examples/component-bottom-navigation.md +226 -0
- package/examples/component-box.md +112 -0
- package/examples/component-breadcrumb.md +222 -0
- package/examples/component-button-base.md +190 -0
- package/examples/component-button-group.md +117 -0
- package/examples/component-button.md +213 -0
- package/examples/component-calendar.md +280 -0
- package/examples/component-callout.md +131 -0
- package/examples/component-card-actions.md +101 -0
- package/examples/component-card-content.md +90 -0
- package/examples/component-card-header.md +122 -0
- package/examples/component-card-media.md +79 -0
- package/examples/component-card.md +233 -0
- package/examples/component-carousel.md +266 -0
- package/examples/component-cascader.md +135 -0
- package/examples/component-category-picker.md +271 -0
- package/examples/component-chart.md +237 -0
- package/examples/component-chat-interface.md +399 -0
- package/examples/component-checkbox.md +149 -0
- package/examples/component-click-away-listener.md +87 -0
- package/examples/component-code.md +213 -0
- package/examples/component-collapsible.md +219 -0
- package/examples/component-color-picker.md +110 -0
- package/examples/component-combobox.md +267 -0
- package/examples/component-command.md +299 -0
- package/examples/component-config-provider.md +190 -0
- package/examples/component-context-menu.md +179 -0
- package/examples/component-css-baseline.md +148 -0
- package/examples/component-date-picker.md +249 -0
- package/examples/component-descriptions.md +242 -0
- package/examples/component-dialog-actions.md +111 -0
- package/examples/component-dialog-content-text.md +76 -0
- package/examples/component-dialog-content.md +105 -0
- package/examples/component-dialog-title.md +104 -0
- package/examples/component-dialog.md +176 -0
- package/examples/component-divider.md +182 -0
- package/examples/component-doc-page.md +217 -0
- package/examples/component-drawer.md +257 -0
- package/examples/component-dropdown.md +220 -0
- package/examples/component-email-layout.md +253 -0
- package/examples/component-empty-state.md +269 -0
- package/examples/component-empty.md +172 -0
- package/examples/component-fade.md +87 -0
- package/examples/component-feature-grid.md +201 -0
- package/examples/component-field.md +266 -0
- package/examples/component-filled-input.md +114 -0
- package/examples/component-flex.md +117 -0
- package/examples/component-float-button.md +214 -0
- package/examples/component-form-control-label.md +134 -0
- package/examples/component-form-control.md +113 -0
- package/examples/component-form-controls.md +251 -0
- package/examples/component-form-group.md +86 -0
- package/examples/component-form-helper-text.md +95 -0
- package/examples/component-form-label.md +98 -0
- package/examples/component-form.md +291 -0
- package/examples/component-game-hud.md +362 -0
- package/examples/component-game-menu.md +327 -0
- package/examples/component-grid.md +140 -0
- package/examples/component-grow.md +79 -0
- package/examples/component-hero-block.md +252 -0
- package/examples/component-hover-card.md +211 -0
- package/examples/component-icon-button.md +142 -0
- package/examples/component-icon.md +102 -0
- package/examples/component-illustration.md +280 -0
- package/examples/component-image-list.md +107 -0
- package/examples/component-image.md +282 -0
- package/examples/component-input-adornment.md +101 -0
- package/examples/component-input-base.md +134 -0
- package/examples/component-input-number.md +141 -0
- package/examples/component-input-otp.md +271 -0
- package/examples/component-input.md +213 -0
- package/examples/component-item.md +243 -0
- package/examples/component-kbd.md +197 -0
- package/examples/component-krw-amount.md +219 -0
- package/examples/component-label.md +118 -0
- package/examples/component-layout.md +217 -0
- package/examples/component-link.md +223 -0
- package/examples/component-list-item-avatar.md +72 -0
- package/examples/component-list-item-button.md +139 -0
- package/examples/component-list-item-icon.md +82 -0
- package/examples/component-list-item-text.md +98 -0
- package/examples/component-list-item.md +116 -0
- package/examples/component-list-subheader.md +70 -0
- package/examples/component-list.md +198 -0
- package/examples/component-loading-button.md +125 -0
- package/examples/component-loading-sequence.md +254 -0
- package/examples/component-lottie-player.md +279 -0
- package/examples/component-masonry.md +191 -0
- package/examples/component-mentions.md +189 -0
- package/examples/component-menu-item.md +102 -0
- package/examples/component-menu-list.md +98 -0
- package/examples/component-menu.md +139 -0
- package/examples/component-menubar.md +259 -0
- package/examples/component-message.md +162 -0
- package/examples/component-mobile-stepper.md +155 -0
- package/examples/component-modal.md +220 -0
- package/examples/component-navigation-menu.md +295 -0
- package/examples/component-notification.md +166 -0
- package/examples/component-otp-countdown.md +245 -0
- package/examples/component-outlined-input.md +145 -0
- package/examples/component-page-transition.md +281 -0
- package/examples/component-pagination.md +226 -0
- package/examples/component-paper.md +199 -0
- package/examples/component-pass-auth.md +265 -0
- package/examples/component-payment-brand-button.md +253 -0
- package/examples/component-payment-method-selector.md +272 -0
- package/examples/component-payment-receipt.md +277 -0
- package/examples/component-popconfirm.md +144 -0
- package/examples/component-popover.md +239 -0
- package/examples/component-popper.md +129 -0
- package/examples/component-pricing-cards.md +247 -0
- package/examples/component-progress.md +195 -0
- package/examples/component-qr-code.md +201 -0
- package/examples/component-radio.md +167 -0
- package/examples/component-rate.md +202 -0
- package/examples/component-resizable.md +163 -0
- package/examples/component-result.md +270 -0
- package/examples/component-scroll-area.md +199 -0
- package/examples/component-scroll-reveal.md +320 -0
- package/examples/component-segmented.md +178 -0
- package/examples/component-select.md +258 -0
- package/examples/component-separator.md +177 -0
- package/examples/component-sheet.md +277 -0
- package/examples/component-sidebar.md +257 -0
- package/examples/component-skeleton.md +178 -0
- package/examples/component-slide.md +89 -0
- package/examples/component-slider.md +208 -0
- package/examples/component-snackbar-content.md +92 -0
- package/examples/component-snackbar.md +121 -0
- package/examples/component-sonner.md +165 -0
- package/examples/component-space.md +125 -0
- package/examples/component-spatial-locomotion.md +373 -0
- package/examples/component-spatial-panel.md +330 -0
- package/examples/component-speed-dial-action.md +78 -0
- package/examples/component-speed-dial.md +177 -0
- package/examples/component-spin.md +173 -0
- package/examples/component-spinner.md +204 -0
- package/examples/component-splitter.md +210 -0
- package/examples/component-stack.md +130 -0
- package/examples/component-statistic.md +282 -0
- package/examples/component-step-button.md +157 -0
- package/examples/component-step-connector.md +157 -0
- package/examples/component-step-content.md +121 -0
- package/examples/component-step-icon.md +115 -0
- package/examples/component-step-label.md +101 -0
- package/examples/component-step.md +84 -0
- package/examples/component-steps.md +264 -0
- package/examples/component-stock-chart.md +251 -0
- package/examples/component-swipeable-drawer.md +149 -0
- package/examples/component-switch.md +99 -0
- package/examples/component-tab-scroll-button.md +180 -0
- package/examples/component-tab.md +120 -0
- package/examples/component-table-body.md +82 -0
- package/examples/component-table-cell.md +118 -0
- package/examples/component-table-container.md +75 -0
- package/examples/component-table-footer.md +72 -0
- package/examples/component-table-head.md +78 -0
- package/examples/component-table-pagination.md +137 -0
- package/examples/component-table-row.md +113 -0
- package/examples/component-table-sort-label.md +109 -0
- package/examples/component-table.md +202 -0
- package/examples/component-tabs.md +184 -0
- package/examples/component-tag-badge.md +309 -0
- package/examples/component-tag.md +102 -0
- package/examples/component-testimonial-carousel.md +218 -0
- package/examples/component-textarea-autosize.md +114 -0
- package/examples/component-textarea.md +173 -0
- package/examples/component-time-picker.md +205 -0
- package/examples/component-timeline.md +170 -0
- package/examples/component-toast.md +224 -0
- package/examples/component-toggle-button.md +129 -0
- package/examples/component-toggle.md +220 -0
- package/examples/component-toolbar.md +104 -0
- package/examples/component-tooltip.md +178 -0
- package/examples/component-tour.md +176 -0
- package/examples/component-transaction-list-item.md +243 -0
- package/examples/component-transfer.md +156 -0
- package/examples/component-tree-select.md +202 -0
- package/examples/component-tree.md +297 -0
- package/examples/component-typography.md +215 -0
- package/examples/component-upload.md +295 -0
- package/examples/component-video-hero.md +409 -0
- package/examples/component-video-player.md +340 -0
- package/examples/component-voice-input.md +408 -0
- package/examples/component-watermark.md +217 -0
- package/examples/component-zoom.md +92 -0
- package/examples/doc-explanation-example.md +196 -0
- package/examples/doc-how-to-example.md +205 -0
- package/examples/doc-tutorial-example.md +162 -0
- package/examples/dogfood-korean-fintech-system.md +433 -0
- package/examples/email-transactional-example.md +259 -0
- package/examples/palette-saas-violet.md +235 -0
- package/examples/print-business-card-spec.md +205 -0
- package/examples/print-packaging-spec.md +285 -0
- package/examples/report-example.md +228 -0
- package/examples/slide-deck-example.md +237 -0
- package/examples/website-improvement-report.md +128 -0
- package/examples/website-improvement-workspace.json +126 -0
- package/install.sh +189 -0
- package/knowledge/COVERAGE.md +740 -0
- package/knowledge/PRINCIPLES.md +108 -0
- package/knowledge/a11y/contrast.md +70 -0
- package/knowledge/a11y/keyboard-and-focus.md +108 -0
- package/knowledge/colors/color-theory.md +90 -0
- package/knowledge/colors/mui-palette-structure.md +105 -0
- package/knowledge/colors/palettes-by-product-type.md +876 -0
- package/knowledge/components/INDEX.md +231 -0
- package/knowledge/components/index.json +1470 -0
- package/knowledge/components/shadcn-registry.md +159 -0
- package/knowledge/conversational/ai-chat-interfaces.md +323 -0
- package/knowledge/conversational/chatbot-design.md +312 -0
- package/knowledge/conversational/conversational-ui-fundamentals.md +299 -0
- package/knowledge/conversational/korean-voice-conventions.md +281 -0
- package/knowledge/conversational/voice-ui-patterns.md +297 -0
- package/knowledge/design-tokens/ant-design.md +124 -0
- package/knowledge/design-tokens/material-3.md +235 -0
- package/knowledge/design-tokens/polaris-and-carbon.md +231 -0
- package/knowledge/design-tokens/tailwind-v4.md +284 -0
- package/knowledge/game-ui/game-accessibility.md +317 -0
- package/knowledge/game-ui/game-ui-fundamentals.md +281 -0
- package/knowledge/game-ui/hud-design.md +324 -0
- package/knowledge/game-ui/korean-gaming-conventions.md +312 -0
- package/knowledge/game-ui/menu-systems.md +388 -0
- package/knowledge/i18n/korean-app-store-visual.md +223 -0
- package/knowledge/i18n/korean-document-style.md +301 -0
- package/knowledge/i18n/korean-payments.md +211 -0
- package/knowledge/i18n/korean-product-conventions.md +96 -0
- package/knowledge/i18n/korean-publishing.md +139 -0
- package/knowledge/i18n/korean-typography.md +123 -0
- package/knowledge/icons/curated-sets.md +225 -0
- package/knowledge/illustration/hero-illustrations.md +234 -0
- package/knowledge/illustration/illustration-systems.md +221 -0
- package/knowledge/illustration/mascot-design.md +208 -0
- package/knowledge/illustration/spot-illustrations.md +246 -0
- package/knowledge/illustration/svg-optimization.md +308 -0
- package/knowledge/layout/spacing-and-grid.md +119 -0
- package/knowledge/motion/app-loading-sequences.md +277 -0
- package/knowledge/motion/choreography-depth.md +297 -0
- package/knowledge/motion/marketing-motion.md +223 -0
- package/knowledge/motion/micro-interactions.md +266 -0
- package/knowledge/motion/motion-tools.md +281 -0
- package/knowledge/motion/principles.md +126 -0
- package/knowledge/patterns/auth-flow-design.md +316 -0
- package/knowledge/patterns/b2b-onboarding-flows.md +182 -0
- package/knowledge/patterns/brand-identity.md +238 -0
- package/knowledge/patterns/brand-references.md +97 -0
- package/knowledge/patterns/chart-color-encoding.md +269 -0
- package/knowledge/patterns/chart-types.md +344 -0
- package/knowledge/patterns/dashboard-composition.md +299 -0
- package/knowledge/patterns/design-system-qa.md +356 -0
- package/knowledge/patterns/document-typography.md +248 -0
- package/knowledge/patterns/email-design.md +338 -0
- package/knowledge/patterns/empty-states.md +263 -0
- package/knowledge/patterns/error-states.md +318 -0
- package/knowledge/patterns/form-design.md +237 -0
- package/knowledge/patterns/information-architecture.md +341 -0
- package/knowledge/patterns/landing-hero-design.md +283 -0
- package/knowledge/patterns/landing-page-patterns.md +681 -0
- package/knowledge/patterns/list-and-feed.md +371 -0
- package/knowledge/patterns/mobile-navigation.md +278 -0
- package/knowledge/patterns/money-and-amount.md +301 -0
- package/knowledge/patterns/onboarding.md +300 -0
- package/knowledge/patterns/pricing-page-design.md +261 -0
- package/knowledge/patterns/realtime-data.md +280 -0
- package/knowledge/patterns/report-design.md +330 -0
- package/knowledge/patterns/search-ux.md +373 -0
- package/knowledge/patterns/settings-page.md +325 -0
- package/knowledge/patterns/slide-deck-design.md +316 -0
- package/knowledge/patterns/styles-catalog.md +2599 -0
- package/knowledge/patterns/technical-writing.md +306 -0
- package/knowledge/patterns/ui-reasoning.md +1853 -0
- package/knowledge/patterns/ux-guidelines.md +1454 -0
- package/knowledge/platforms/react-native.md +395 -0
- package/knowledge/print/brochures-and-flyers.md +276 -0
- package/knowledge/print/korean-print-conventions.md +284 -0
- package/knowledge/print/packaging.md +321 -0
- package/knowledge/print/print-fundamentals.md +238 -0
- package/knowledge/print/signage-and-posters.md +235 -0
- package/knowledge/print/stationery.md +262 -0
- package/knowledge/spatial/ar-patterns.md +253 -0
- package/knowledge/spatial/comfort-and-accessibility.md +245 -0
- package/knowledge/spatial/spatial-design-fundamentals.md +346 -0
- package/knowledge/spatial/spatial-ui-elements.md +343 -0
- package/knowledge/spatial/vr-patterns.md +302 -0
- package/knowledge/typography/font-pairings.md +332 -0
- package/knowledge/typography/mui-type-scale.md +57 -0
- package/knowledge/typography/type-scale-fundamentals.md +105 -0
- package/knowledge/video/in-product-video.md +277 -0
- package/knowledge/video/korean-video-conventions.md +247 -0
- package/knowledge/video/marketing-video.md +258 -0
- package/knowledge/video/social-and-short-form.md +257 -0
- package/knowledge/video/video-fundamentals.md +296 -0
- package/package.json +106 -0
- package/skills/README.md +39 -0
- package/skills/color-palette/PLAYBOOK.md +175 -0
- package/skills/color-palette/SKILL.md +6 -0
- package/skills/color-palette/TEMPLATE.md +103 -0
- package/skills/component-spec-writer/PLAYBOOK.md +169 -0
- package/skills/component-spec-writer/SKILL.md +6 -0
- package/skills/component-spec-writer/TEMPLATE.md +115 -0
- package/skills/conversational-ui-designer/PLAYBOOK.md +231 -0
- package/skills/conversational-ui-designer/SKILL.md +6 -0
- package/skills/design-broadcast/PLAYBOOK.md +146 -0
- package/skills/design-broadcast/SKILL.md +6 -0
- package/skills/design-critique/PLAYBOOK.md +137 -0
- package/skills/design-critique/SKILL.md +6 -0
- package/skills/design-pr-review/PLAYBOOK.md +171 -0
- package/skills/design-pr-review/SKILL.md +6 -0
- package/skills/design-system-builder/PLAYBOOK.md +218 -0
- package/skills/design-system-builder/SKILL.md +6 -0
- package/skills/design-system-qa/PLAYBOOK.md +163 -0
- package/skills/design-system-qa/SKILL.md +6 -0
- package/skills/document-author/PLAYBOOK.md +167 -0
- package/skills/document-author/SKILL.md +6 -0
- package/skills/figma-token-sync/PLAYBOOK.md +149 -0
- package/skills/figma-token-sync/SKILL.md +6 -0
- package/skills/game-ui-designer/PLAYBOOK.md +209 -0
- package/skills/game-ui-designer/SKILL.md +6 -0
- package/skills/handoff-spec/PLAYBOOK.md +170 -0
- package/skills/handoff-spec/SKILL.md +6 -0
- package/skills/illustration-designer/PLAYBOOK.md +166 -0
- package/skills/illustration-designer/SKILL.md +6 -0
- package/skills/motion-designer/PLAYBOOK.md +223 -0
- package/skills/motion-designer/SKILL.md +6 -0
- package/skills/print-designer/PLAYBOOK.md +209 -0
- package/skills/print-designer/SKILL.md +6 -0
- package/skills/slide-deck-author/PLAYBOOK.md +198 -0
- package/skills/slide-deck-author/SKILL.md +6 -0
- package/skills/spatial-designer/PLAYBOOK.md +238 -0
- package/skills/spatial-designer/SKILL.md +6 -0
- package/skills/ux-audit/PLAYBOOK.md +142 -0
- package/skills/ux-audit/SKILL.md +6 -0
- package/skills/video-designer/PLAYBOOK.md +240 -0
- package/skills/video-designer/SKILL.md +6 -0
- package/skills/website-improvement/PLAYBOOK.md +118 -0
- package/skills/website-improvement/SKILL.md +6 -0
- package/skills/website-improvement/TEMPLATE.md +103 -0
- package/tools/audit/changelog-generate.py +176 -0
- package/tools/audit/check-coverage.py +483 -0
- package/tools/audit/doctor_assertions.py +256 -0
- package/tools/audit/example-qa.py +108 -0
- package/tools/audit/frontmatter-check.py +199 -0
- package/tools/audit/integration-check.py +95 -0
- package/tools/audit/korean-copy-check.py +161 -0
- package/tools/audit/link-check.py +146 -0
- package/tools/audit/local-ci.py +558 -0
- package/tools/audit/package-contents.py +723 -0
- package/tools/audit/package-smoke.py +22443 -0
- package/tools/audit/raw-hex-check.py +252 -0
- package/tools/audit/registry-smoke.py +8188 -0
- package/tools/audit/release-metadata.py +9832 -0
- package/tools/audit/run-all.py +351 -0
- package/tools/audit/smoke_assertions.py +12742 -0
- package/tools/audit/stability-review.py +332 -0
- package/tools/audit/stale-check.py +156 -0
- package/tools/migrations/add-version-frontmatter.py +132 -0
- package/tools/migrations/bump-last-updated.py +131 -0
- package/tools/migrations/promote-stability.py +172 -0
- package/tools/preview/README.md +58 -0
- package/tools/preview/render-tokens.py +454 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# `Carousel` — spec
|
|
2
|
+
|
|
3
|
+
> Citing Ant Design `Carousel`, MUI (no built-in — composition), shadcn-ui `carousel` (Embla)
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
A horizontally rotating sequence of items. Used for: hero banners (marketing), product image galleries, onboarding screens, content highlights.
|
|
8
|
+
|
|
9
|
+
## When NOT to use a carousel
|
|
10
|
+
|
|
11
|
+
This is the most-misused component on the web. Before reaching for a carousel, ask:
|
|
12
|
+
- **"Will users actually scroll past slide 1?"** — Most don't. Average click-through on slide 2+ is < 5%.
|
|
13
|
+
- **"Is the content equally important?"** — If slide 1 is the priority, kill the carousel; commit to one hero.
|
|
14
|
+
- **"Is this auto-playing?"** — Auto-play hurts a11y, distracts users, and is widely disliked.
|
|
15
|
+
|
|
16
|
+
Use a carousel when:
|
|
17
|
+
- It's a genuine gallery (e.g., product photos — user expects multiple).
|
|
18
|
+
- Content is browsable (testimonials, news highlights — equally weighted).
|
|
19
|
+
- Touch swipe is the intended interaction model (mobile native pattern).
|
|
20
|
+
|
|
21
|
+
Don't use for:
|
|
22
|
+
- Hero banner where one message is the goal (use a single hero).
|
|
23
|
+
- Long-form content (use a list or scroll).
|
|
24
|
+
- Critical conversion elements (most users miss them past slide 1).
|
|
25
|
+
|
|
26
|
+
## Anatomy
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
◀ ┌──────────────────────────────────────┐ ▶
|
|
30
|
+
│ │
|
|
31
|
+
│ Slide 1 of 4 │
|
|
32
|
+
│ │
|
|
33
|
+
│ │
|
|
34
|
+
└──────────────────────────────────────┘
|
|
35
|
+
● ○ ○ ○ ← dot indicators
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Slot | Required | Notes |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| Slide content | yes | The items |
|
|
41
|
+
| Previous arrow | usually (desktop) | Hidden on mobile (rely on swipe) |
|
|
42
|
+
| Next arrow | usually (desktop) | |
|
|
43
|
+
| Indicators (dots / numbers / thumbs) | yes for > 1 slide | Shows position + count |
|
|
44
|
+
| Auto-play controls | only if auto-playing | Pause/play button required |
|
|
45
|
+
|
|
46
|
+
## API
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<Carousel
|
|
50
|
+
autoplay={false}
|
|
51
|
+
loop
|
|
52
|
+
showArrows
|
|
53
|
+
showDots
|
|
54
|
+
onSlideChange={setActive}
|
|
55
|
+
>
|
|
56
|
+
<Carousel.Slide>{slide1}</Carousel.Slide>
|
|
57
|
+
<Carousel.Slide>{slide2}</Carousel.Slide>
|
|
58
|
+
<Carousel.Slide>{slide3}</Carousel.Slide>
|
|
59
|
+
</Carousel>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `autoplay` | `boolean` | `false` | Auto-advance |
|
|
65
|
+
| `autoplayInterval` | `number` | `5000` | ms between slides |
|
|
66
|
+
| `loop` | `boolean` | `true` | Wrap from last to first |
|
|
67
|
+
| `showArrows` | `boolean` | `true` (desktop) / `false` (mobile) | Prev/next buttons |
|
|
68
|
+
| `showDots` | `boolean` | `true` | Dot indicators |
|
|
69
|
+
| `dotsPosition` | `"bottom" \| "top" \| "side"` | `"bottom"` | |
|
|
70
|
+
| `slidesPerView` | `number` | `1` | Multi-slide view (e.g., 3 product cards visible) |
|
|
71
|
+
| `slidesPerScroll` | `number` | `1` | How many to advance per click |
|
|
72
|
+
| `gap` | `number` | `0` | Space between slides (when slidesPerView > 1) |
|
|
73
|
+
| `swipeable` | `boolean` | `true` (touch) | Touch/drag swipe |
|
|
74
|
+
| `effect` | `"slide" \| "fade"` | `"slide"` | Transition |
|
|
75
|
+
| `value` | `number` | — | Controlled active index |
|
|
76
|
+
| `onSlideChange` | `(index) => void` | — | |
|
|
77
|
+
|
|
78
|
+
## Behavior
|
|
79
|
+
|
|
80
|
+
### Navigation
|
|
81
|
+
|
|
82
|
+
- **Arrows** (desktop): click to advance one slide.
|
|
83
|
+
- **Dots**: click any dot to jump to that slide.
|
|
84
|
+
- **Swipe** (touch): horizontal swipe past 50% of slide width advances.
|
|
85
|
+
- **Keyboard**: `←` / `→` when carousel is focused.
|
|
86
|
+
- **Auto-play** (if enabled): advances every `autoplayInterval` ms; pauses on hover or focus inside; resumes on leave.
|
|
87
|
+
|
|
88
|
+
### Loop vs no loop
|
|
89
|
+
|
|
90
|
+
- `loop: true`: clicking next on last slide goes to first; previous on first goes to last.
|
|
91
|
+
- `loop: false`: arrows disabled at boundary. Last slide visually marks "end".
|
|
92
|
+
|
|
93
|
+
### Multi-slide view
|
|
94
|
+
|
|
95
|
+
For browse-style carousels (product cards):
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
slidesPerView: 3
|
|
99
|
+
[card 1] [card 2] [card 3] [card 4 (off-screen)] [card 5 (off-screen)]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Click next: scrolls by `slidesPerScroll` (often 1 for incremental browsing, full = slidesPerView for "page" navigation).
|
|
103
|
+
|
|
104
|
+
## Sizes
|
|
105
|
+
|
|
106
|
+
Carousel container takes its parent's dimensions. The slide aspect ratio depends on content:
|
|
107
|
+
|
|
108
|
+
| Use | Aspect ratio (desktop) |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| Hero banner | 16:9 or 21:9 wide |
|
|
111
|
+
| Product gallery | 1:1 (square) |
|
|
112
|
+
| Testimonial cards | 4:3 or 5:3 |
|
|
113
|
+
| Mobile portrait | 4:5 or 9:16 |
|
|
114
|
+
|
|
115
|
+
For responsive: `aspect-ratio: 16 / 9` and `width: 100%` lets it scale.
|
|
116
|
+
|
|
117
|
+
## States
|
|
118
|
+
|
|
119
|
+
| State | Visual |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| Settled | Slide visible, indicator highlighted |
|
|
122
|
+
| Sliding | 250–400ms transition (slide or fade) |
|
|
123
|
+
| Auto-advance pause | When user hovers, focuses, or `prefers-reduced-motion` is set |
|
|
124
|
+
| Loading slides (lazy) | Skeleton in slide area until image loads |
|
|
125
|
+
|
|
126
|
+
## Tokens consumed
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
--color-bg-default
|
|
130
|
+
--color-text-primary
|
|
131
|
+
--color-text-on-image (text overlay on image carousels)
|
|
132
|
+
--color-bg-overlay (subtle overlay for text legibility)
|
|
133
|
+
--color-primary-default (active dot)
|
|
134
|
+
--color-text-tertiary (inactive dots)
|
|
135
|
+
--space-md
|
|
136
|
+
--radius-md, --radius-lg (rounded slide corners)
|
|
137
|
+
--motion-default, --motion-slow (slide transitions)
|
|
138
|
+
--easing-out
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Accessibility — high stakes
|
|
142
|
+
|
|
143
|
+
Carousels are notorious for a11y problems. Get these right.
|
|
144
|
+
|
|
145
|
+
### ARIA
|
|
146
|
+
|
|
147
|
+
- `role="region" aria-roledescription="carousel" aria-label="..."` on the container.
|
|
148
|
+
- Each slide: `role="group" aria-roledescription="slide" aria-label="Slide N of M"`.
|
|
149
|
+
- Hidden slides should have `aria-hidden="true"` and `tabindex="-1"` on focusable elements (so tab doesn't reach off-screen content).
|
|
150
|
+
- Live region: `aria-live="polite"` on the slide container — but only when **not auto-playing** (auto-play would chatter). Set `aria-live="off"` while auto-playing.
|
|
151
|
+
|
|
152
|
+
### Auto-play
|
|
153
|
+
|
|
154
|
+
- **Don't auto-play by default**. WCAG 2.2.2 requires controls for moving content > 5 seconds.
|
|
155
|
+
- If auto-playing: provide visible Pause/Play button.
|
|
156
|
+
- Pause on hover, focus, and **`prefers-reduced-motion`** — these are not optional.
|
|
157
|
+
|
|
158
|
+
### Keyboard
|
|
159
|
+
|
|
160
|
+
| Key | Behavior |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| `Tab` | Reach carousel (arrows + dots are tab stops) |
|
|
163
|
+
| `←` / `→` | Move slide (when carousel is focused) |
|
|
164
|
+
| Inside slide: `Tab` reaches links/buttons in the visible slide only |
|
|
165
|
+
|
|
166
|
+
### Touch
|
|
167
|
+
|
|
168
|
+
Swipe is a touch convention. For accessibility, ensure arrows are visible OR a clear alternative exists.
|
|
169
|
+
|
|
170
|
+
## Code example
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// Hero banner (no auto-play, single slide visible)
|
|
174
|
+
<Carousel showArrows showDots loop>
|
|
175
|
+
{banners.map(banner => (
|
|
176
|
+
<Carousel.Slide key={banner.id}>
|
|
177
|
+
<BannerSlide banner={banner} />
|
|
178
|
+
</Carousel.Slide>
|
|
179
|
+
))}
|
|
180
|
+
</Carousel>
|
|
181
|
+
|
|
182
|
+
// Product gallery (multi-slide, no auto-play, no loop)
|
|
183
|
+
<Carousel
|
|
184
|
+
slidesPerView={4}
|
|
185
|
+
slidesPerScroll={1}
|
|
186
|
+
gap={16}
|
|
187
|
+
loop={false}
|
|
188
|
+
showArrows
|
|
189
|
+
showDots={false}
|
|
190
|
+
>
|
|
191
|
+
{products.map(product => (
|
|
192
|
+
<Carousel.Slide key={product.id}>
|
|
193
|
+
<ProductCard product={product} />
|
|
194
|
+
</Carousel.Slide>
|
|
195
|
+
))}
|
|
196
|
+
</Carousel>
|
|
197
|
+
|
|
198
|
+
// Testimonials with auto-play (carefully)
|
|
199
|
+
<Carousel
|
|
200
|
+
autoplay
|
|
201
|
+
autoplayInterval={6000}
|
|
202
|
+
loop
|
|
203
|
+
showDots
|
|
204
|
+
showArrows
|
|
205
|
+
effect="fade"
|
|
206
|
+
>
|
|
207
|
+
{testimonials.map(t => (
|
|
208
|
+
<Carousel.Slide key={t.id}>
|
|
209
|
+
<TestimonialCard testimonial={t} />
|
|
210
|
+
</Carousel.Slide>
|
|
211
|
+
))}
|
|
212
|
+
</Carousel>
|
|
213
|
+
|
|
214
|
+
// Image gallery on a product detail page
|
|
215
|
+
<Carousel slidesPerView={1} loop showDots>
|
|
216
|
+
{product.images.map(img => (
|
|
217
|
+
<Carousel.Slide key={img.id}>
|
|
218
|
+
<Image src={img.url} alt={img.alt} />
|
|
219
|
+
</Carousel.Slide>
|
|
220
|
+
))}
|
|
221
|
+
</Carousel>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Mobile-specific behaviors
|
|
225
|
+
|
|
226
|
+
- Arrows hidden by default (rely on swipe).
|
|
227
|
+
- Dots smaller (4–6px).
|
|
228
|
+
- Snap to slide (`scroll-snap-type: x mandatory`).
|
|
229
|
+
- Don't auto-play on mobile — wastes battery, reduces user agency.
|
|
230
|
+
|
|
231
|
+
## Edge cases
|
|
232
|
+
|
|
233
|
+
- **Single slide**: don't render arrows/dots. Just the slide.
|
|
234
|
+
- **Loading slides** (async): render skeleton until image loads. Don't block carousel rendering.
|
|
235
|
+
- **Different slide heights**: cap with `align-items: center` or set fixed height. Inconsistent heights cause carousel to "jump".
|
|
236
|
+
- **Long text in slides** (testimonials): cap height and add `overflow: scroll` per slide if needed.
|
|
237
|
+
- **Auto-play with focus inside**: pause until focus leaves.
|
|
238
|
+
- **RTL**: arrow icons flip; swipe direction inverts.
|
|
239
|
+
- **No JS / SSR**: render as a horizontal scroll list with snap points. Carousel chrome is enhancement.
|
|
240
|
+
|
|
241
|
+
## Don't
|
|
242
|
+
|
|
243
|
+
- Don't auto-play by default.
|
|
244
|
+
- Don't use carousel for primary CTAs (most users miss slide 2+).
|
|
245
|
+
- Don't combine fade and slide effects in the same carousel.
|
|
246
|
+
- Don't show 3+ rows of carousels on one page (visual chaos).
|
|
247
|
+
- Don't disable the user's swipe direction (force them to use arrows). Touch users expect swipe.
|
|
248
|
+
- Don't make dots invisible on dark slides — ensure 3:1 contrast.
|
|
249
|
+
- Don't put videos in auto-playing carousels — accessibility nightmare.
|
|
250
|
+
|
|
251
|
+
## References
|
|
252
|
+
|
|
253
|
+
- Ant Design: [`refs/ant-design/components/carousel/`](../refs/ant-design/components/carousel/) — `Carousel`. Wraps `react-slick`. Has `autoplay`, `dots`, `effect`. Solid baseline.
|
|
254
|
+
- MUI: no dedicated component. Use `react-slick` or `swiper`.
|
|
255
|
+
- shadcn-ui: [`refs/shadcn-ui/apps/v4/registry/new-york-v4/ui/carousel.tsx`](../refs/shadcn-ui/apps/v4/registry/new-york-v4/ui/carousel.tsx) — wraps **Embla** carousel. Modern, framework-agnostic, excellent accessibility. **Default for new projects.**
|
|
256
|
+
|
|
257
|
+
API choices made:
|
|
258
|
+
- **`autoplay: false` default**: matches a11y best practice. Most carousels don't need it.
|
|
259
|
+
- **`slidesPerView` exposed**: covers product gallery use case without separate component.
|
|
260
|
+
- **`effect`**: fade for hero (each slide is the message), slide for sequential browsing.
|
|
261
|
+
|
|
262
|
+
## Cross-reference
|
|
263
|
+
|
|
264
|
+
- [`knowledge/motion/principles.md`](../knowledge/motion/principles.md) — auto-play + reduced motion
|
|
265
|
+
- [`knowledge/a11y/keyboard-and-focus.md`](../knowledge/a11y/keyboard-and-focus.md) — carousel keyboard contract
|
|
266
|
+
- [WAI-ARIA Carousel Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/carousel/)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# `Cascader` — spec
|
|
2
|
+
|
|
3
|
+
> Citing Ant Design `Cascader`, MUI (no built-in — composition), shadcn-ui (no built-in)
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Multi-level select. User picks a leaf value by drilling through hierarchical levels. Used for: address (state → city → district), category trees (electronics → phones → models), org structure.
|
|
8
|
+
|
|
9
|
+
## When Cascader vs alternatives
|
|
10
|
+
|
|
11
|
+
| Pattern | Use |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| **Cascader** | Hierarchical with 2–4 fixed levels; user picks ONE leaf |
|
|
14
|
+
| **Tree (with select)** | Variable depth, user might pick any node |
|
|
15
|
+
| **Cascader → drill-in screens** | Mobile, when each level needs more space |
|
|
16
|
+
| **Three Selects in a row** | When levels are independent (e.g., size, color, material — pick all three) |
|
|
17
|
+
|
|
18
|
+
## Anatomy
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Trigger: Open (3-level cascade):
|
|
22
|
+
┌──────────────────────┐ ┌──────┬──────┬──────┐
|
|
23
|
+
│ 서울 / 강남구 / 역삼동 ▾│ │ 서울 │ 강남구│ 역삼동│
|
|
24
|
+
└──────────────────────┘ │ 부산 │ 서초구│ 청담동│
|
|
25
|
+
│ 인천 │ 송파구│ 삼성동│
|
|
26
|
+
└──────┴──────┴──────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Slot | Required | Notes |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| Trigger | yes | Shows full path or placeholder |
|
|
32
|
+
| Per-level panel | yes | Each level renders its options |
|
|
33
|
+
| Hover/click expand | yes | Hovering a parent expands its children |
|
|
34
|
+
| Search input | optional | Searches across all levels |
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<Cascader
|
|
40
|
+
value={["seoul", "gangnam", "yeoksam"]}
|
|
41
|
+
onValueChange={setValue}
|
|
42
|
+
options={addressTree}
|
|
43
|
+
separator=" / "
|
|
44
|
+
searchable
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
// where addressTree is:
|
|
48
|
+
type CascaderOption = {
|
|
49
|
+
value: string;
|
|
50
|
+
label: string;
|
|
51
|
+
children?: CascaderOption[];
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
isLeaf?: boolean;
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| Prop | Type | Default | Description |
|
|
58
|
+
| --- | --- | --- | --- |
|
|
59
|
+
| `value` | `string[]` | — | Path to selected leaf (level values in order) |
|
|
60
|
+
| `onValueChange` | `(value: string[], path: CascaderOption[]) => void` | — | |
|
|
61
|
+
| `options` | `CascaderOption[]` | — | Hierarchical data |
|
|
62
|
+
| `separator` | `string` | `" / "` | Display separator in trigger |
|
|
63
|
+
| `placeholder` | `string` | — | When empty |
|
|
64
|
+
| `searchable` | `boolean` | `false` | Cross-level search |
|
|
65
|
+
| `expandTrigger` | `"click" \| "hover"` | `"click"` | How to expand levels |
|
|
66
|
+
| `changeOnSelect` | `boolean` | `false` | Allow selection at non-leaf levels |
|
|
67
|
+
| `showFullPath` | `boolean` | `true` | In trigger: full path vs leaf-only |
|
|
68
|
+
| `loadData` | `(option) => Promise<CascaderOption[]>` | — | Async lazy-load children |
|
|
69
|
+
| `disabled` | `boolean` | `false` | |
|
|
70
|
+
| `error` / `errorText` | — | — | |
|
|
71
|
+
|
|
72
|
+
## Behavior
|
|
73
|
+
|
|
74
|
+
- **Click a parent**: expands children to its right.
|
|
75
|
+
- **Click a leaf**: selects, closes popover.
|
|
76
|
+
- **`changeOnSelect`**: allows selecting at any level. Triggers `onValueChange` on every click.
|
|
77
|
+
- **Search mode**: input at top of popover; results show full paths matching query, flattened.
|
|
78
|
+
|
|
79
|
+
## States
|
|
80
|
+
|
|
81
|
+
| State | Visual |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| Default trigger | Placeholder or value path |
|
|
84
|
+
| Open | Popover with N panels visible |
|
|
85
|
+
| Hover row | Highlighted; if has children, panel to right opens |
|
|
86
|
+
| Selected leaf | Filled bg, checkmark |
|
|
87
|
+
| Loading (async) | Spinner in current panel |
|
|
88
|
+
|
|
89
|
+
## Accessibility
|
|
90
|
+
|
|
91
|
+
- Trigger: `role="combobox"`, `aria-expanded`, `aria-haspopup="tree"`, `aria-controls`.
|
|
92
|
+
- Each panel: `role="listbox"` (or "tree" for accessibility tools).
|
|
93
|
+
- Each option: `role="option"`, `aria-selected`, `aria-haspopup="listbox"` if has children.
|
|
94
|
+
|
|
95
|
+
### Keyboard
|
|
96
|
+
|
|
97
|
+
| Key | Behavior |
|
|
98
|
+
| --- | --- |
|
|
99
|
+
| `Tab` | Reach trigger |
|
|
100
|
+
| `Enter` / `Space` | Open popover |
|
|
101
|
+
| `↑` / `↓` | Move within current panel |
|
|
102
|
+
| `→` | Expand to next panel (if has children) |
|
|
103
|
+
| `←` | Back to previous panel |
|
|
104
|
+
| `Enter` (on leaf) | Select |
|
|
105
|
+
| `Esc` | Close |
|
|
106
|
+
|
|
107
|
+
## Mobile pattern
|
|
108
|
+
|
|
109
|
+
Cascader's horizontal panel UI breaks on mobile. Switch to a **drill-in screen pattern**:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
[Tap trigger] → [Level 1 list screen] → [Level 2 list] → [Level 3 list] → [back to form]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Each level is a full-screen list with a back button. Visual breadcrumb at top: "서울 > 강남구 > ..."
|
|
116
|
+
|
|
117
|
+
For Korean address: combine with Daum Postcode (more user-friendly for actual addresses) — see [`component-address-input.md`](component-address-input.md).
|
|
118
|
+
|
|
119
|
+
## Don't
|
|
120
|
+
|
|
121
|
+
- Don't use Cascader for non-hierarchical multi-select. Use a Tree.
|
|
122
|
+
- Don't use for 5+ levels — too deep; user gets lost.
|
|
123
|
+
- Don't use for hierarchies that change frequently (categories that move). Tree handles dynamic shape better.
|
|
124
|
+
- Don't combine `changeOnSelect` with deep hierarchies — user accidentally selects at level 1.
|
|
125
|
+
|
|
126
|
+
## References
|
|
127
|
+
|
|
128
|
+
- Ant Design: [`refs/ant-design/components/cascader/`](../refs/ant-design/components/cascader/) — `Cascader` with full feature set. Most exhaustive.
|
|
129
|
+
- MUI / shadcn-ui: no built-in. Compose from Select-like primitives.
|
|
130
|
+
|
|
131
|
+
## Cross-reference
|
|
132
|
+
|
|
133
|
+
- [`examples/component-tree.md`](component-tree.md) — when hierarchy is variable depth
|
|
134
|
+
- [`examples/component-select.md`](component-select.md) — flat alternative
|
|
135
|
+
- [`examples/component-address-input.md`](component-address-input.md) — Korean address pattern using Daum Postcode
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# `CategoryPicker` (custom — Korean fintech / 가계부) — spec
|
|
2
|
+
|
|
3
|
+
> Custom component pattern. Universal in Korean 가계부 (personal finance / expense tracker) apps. Not in any upstream design system.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Lets the user pick a category for a transaction (식비 / 교통 / 쇼핑 / etc.). Uses **emoji + Korean label** as the category visual identity — distinctive Korean app convention.
|
|
8
|
+
|
|
9
|
+
## Anatomy
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Horizontal scrollable pills (default):
|
|
13
|
+
┌───────────────────────────────────────────────────────┐
|
|
14
|
+
│ [🍽 식비] [🚌 교통] [🛍 쇼핑] [🏠 주거] [💊 건강] │
|
|
15
|
+
└───────────────────────────────────────────────────────┘
|
|
16
|
+
↑ active (filled bg)
|
|
17
|
+
|
|
18
|
+
Grid (alternative for many categories):
|
|
19
|
+
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
|
|
20
|
+
│ 🍽 │ │ 🚌 │ │ 🛍 │ │ 🏠 │
|
|
21
|
+
│ 식비 │ │ 교통 │ │ 쇼핑 │ │ 주거 │
|
|
22
|
+
└────────┘ └────────┘ └────────┘ └────────┘
|
|
23
|
+
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
|
|
24
|
+
│ 💊 │ │ 🎬 │ │ ✈ │ │ + │
|
|
25
|
+
│ 건강 │ │ 엔터 │ │ 여행 │ │ 추가 │
|
|
26
|
+
└────────┘ └────────┘ └────────┘ └────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Slot | Required | Notes |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| Category icon | yes | Emoji or pictogram (Korean 가계부 apps overwhelmingly use emoji) |
|
|
32
|
+
| Category label | yes | Korean term |
|
|
33
|
+
| "Add custom" affordance | optional | Lets user create their own category |
|
|
34
|
+
| Search / filter | optional | For 30+ categories |
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<CategoryPicker
|
|
40
|
+
value={categoryId}
|
|
41
|
+
onValueChange={setCategoryId}
|
|
42
|
+
categories={[
|
|
43
|
+
{ id: "food", icon: "🍽", label: "식비" },
|
|
44
|
+
{ id: "transport", icon: "🚌", label: "교통" },
|
|
45
|
+
{ id: "shopping", icon: "🛍", label: "쇼핑" },
|
|
46
|
+
// ...
|
|
47
|
+
]}
|
|
48
|
+
layout="horizontal"
|
|
49
|
+
allowCustom
|
|
50
|
+
onCustomAdd={(name, icon) => addCategory({ name, icon })}
|
|
51
|
+
/>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Prop | Type | Default | Description |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| `value` | `string \| null` | — | Selected category id |
|
|
57
|
+
| `onValueChange` | `(id: string \| null) => void` | — | |
|
|
58
|
+
| `categories` | `Category[]` | — | List of categories |
|
|
59
|
+
| `layout` | `"horizontal" \| "grid" \| "list"` | `"horizontal"` | |
|
|
60
|
+
| `allowCustom` | `boolean` | `false` | Show "+ 추가" button |
|
|
61
|
+
| `onCustomAdd` | `(name: string, icon: string) => void` | — | Open modal to create new |
|
|
62
|
+
| `searchable` | `boolean` | `false` | Search input above |
|
|
63
|
+
| `disabled` | `boolean` | `false` | |
|
|
64
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | |
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
type Category = {
|
|
68
|
+
id: string;
|
|
69
|
+
icon: string; // Emoji or icon name
|
|
70
|
+
label: string; // Korean
|
|
71
|
+
color?: string; // Optional bg tint
|
|
72
|
+
isCustom?: boolean; // User-created
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Default categories (Korean 가계부 standard)
|
|
77
|
+
|
|
78
|
+
Korean apps converge on this categorization. Use as a starting point:
|
|
79
|
+
|
|
80
|
+
| ID | Emoji | Korean | English |
|
|
81
|
+
| --- | --- | --- | --- |
|
|
82
|
+
| `food` | 🍽 | 식비 | Food |
|
|
83
|
+
| `cafe` | ☕ | 카페 / 간식 | Cafe / Snack |
|
|
84
|
+
| `transport` | 🚌 | 교통 | Transport |
|
|
85
|
+
| `shopping` | 🛍 | 쇼핑 | Shopping |
|
|
86
|
+
| `housing` | 🏠 | 주거 | Housing |
|
|
87
|
+
| `utilities` | 💡 | 공과금 | Utilities |
|
|
88
|
+
| `health` | 💊 | 건강 / 의료 | Health |
|
|
89
|
+
| `entertainment` | 🎬 | 엔터테인먼트 | Entertainment |
|
|
90
|
+
| `education` | 📚 | 교육 | Education |
|
|
91
|
+
| `travel` | ✈ | 여행 | Travel |
|
|
92
|
+
| `gift` | 🎁 | 선물 / 경조사 | Gift / Ceremony |
|
|
93
|
+
| `subscription` | 📺 | 구독 | Subscription |
|
|
94
|
+
| `salary` | 💰 | 급여 | Income — salary |
|
|
95
|
+
| `bonus` | 💵 | 부수입 | Income — side |
|
|
96
|
+
| `investment` | 📈 | 투자 | Investment |
|
|
97
|
+
| `other` | ✨ | 기타 | Other |
|
|
98
|
+
|
|
99
|
+
Income vs expense: usually two separate `CategoryPicker` instances OR a single picker with type filter.
|
|
100
|
+
|
|
101
|
+
## Layouts
|
|
102
|
+
|
|
103
|
+
### Horizontal scrollable pills (default for mobile)
|
|
104
|
+
|
|
105
|
+
Best for 6–12 categories. User swipes horizontally to see more.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
[🍽 식비] [🚌 교통] [🛍 쇼핑] ...→
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Grid (4-column on mobile, 6-column on tablet+)
|
|
112
|
+
|
|
113
|
+
Best for 12+ categories. All visible at once.
|
|
114
|
+
|
|
115
|
+
### List (vertical)
|
|
116
|
+
|
|
117
|
+
Best for accessibility / dense desktop forms.
|
|
118
|
+
|
|
119
|
+
## States
|
|
120
|
+
|
|
121
|
+
| State | Visual |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| Default (unselected) | Outline border, neutral bg |
|
|
124
|
+
| Hover | Slight bg shift |
|
|
125
|
+
| Selected | Filled bg `--color-primary-subtle-bg`, border `--color-primary-default`, text `--color-primary-default` |
|
|
126
|
+
| Custom (user-added) | Same as default but with subtle "edit" icon on hover |
|
|
127
|
+
| Disabled | Muted, no events |
|
|
128
|
+
|
|
129
|
+
## Tokens consumed
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
--color-bg-default
|
|
133
|
+
--color-bg-subtle
|
|
134
|
+
--color-primary-subtle-bg (selected bg)
|
|
135
|
+
--color-primary-default (selected border + text)
|
|
136
|
+
--color-text-primary
|
|
137
|
+
--color-text-secondary
|
|
138
|
+
--color-border-default
|
|
139
|
+
--color-focus-ring
|
|
140
|
+
--space-xs, --space-sm, --space-md
|
|
141
|
+
--radius-md, --radius-full (pill style for horizontal)
|
|
142
|
+
--font-size-sm
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Sizes
|
|
146
|
+
|
|
147
|
+
| Size | Pill height | Icon | Font |
|
|
148
|
+
| --- | --- | --- | --- |
|
|
149
|
+
| `sm` | 32px | 16px | 12px |
|
|
150
|
+
| `md` (default) | 40px | 20px | 14px |
|
|
151
|
+
| `lg` | 48px | 24px | 16px |
|
|
152
|
+
|
|
153
|
+
For grid layout: each cell is square (e.g., 80×80 for `md`), with icon on top + label below.
|
|
154
|
+
|
|
155
|
+
## Accessibility
|
|
156
|
+
|
|
157
|
+
- Container: `role="radiogroup"`, `aria-label="카테고리 선택"`.
|
|
158
|
+
- Each category: `role="radio"`, `aria-checked={value === id}`.
|
|
159
|
+
- Emoji icons: `aria-hidden="true"` (the label carries meaning for screen readers).
|
|
160
|
+
- "+ 추가" button: standard `<button>` with `aria-label="새 카테고리 추가"`.
|
|
161
|
+
|
|
162
|
+
### Keyboard
|
|
163
|
+
|
|
164
|
+
| Key | Behavior |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| `Tab` | Reach the active category (single tab stop) |
|
|
167
|
+
| `←` / `→` (horizontal) or `↑` / `↓` (grid) | Move + select next |
|
|
168
|
+
| `Home` / `End` | First / last |
|
|
169
|
+
| `Space` / `Enter` | Activate (or auto-activate on focus, configurable) |
|
|
170
|
+
|
|
171
|
+
## Korean conventions
|
|
172
|
+
|
|
173
|
+
- **Emoji is mandatory**, not decorative. Korean users associate emoji with categories — text-only feels sterile.
|
|
174
|
+
- **Label is short** — 1-3 syllables typically. "식비", "교통", "쇼핑".
|
|
175
|
+
- **Income categories use 💰 / 💵 / 📈 family**; expense uses everything else.
|
|
176
|
+
- **Subscription** (구독) is a major Korean fintech category — Netflix, YouTube Premium, music services.
|
|
177
|
+
- **경조사** (special-occasion gifts: weddings, funerals, holidays) is a uniquely Korean expense category — not in Western 가계부 apps.
|
|
178
|
+
|
|
179
|
+
## Custom category creation
|
|
180
|
+
|
|
181
|
+
When user clicks "+ 추가":
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
┌──────────────────────────────────┐
|
|
185
|
+
│ 새 카테고리 │
|
|
186
|
+
│ │
|
|
187
|
+
│ 이름: [____________] │
|
|
188
|
+
│ 아이콘: [😀] (tap to change) │
|
|
189
|
+
│ │
|
|
190
|
+
│ 색상: ● ● ● ● ● │
|
|
191
|
+
│ │
|
|
192
|
+
│ [취소] [추가] │
|
|
193
|
+
└──────────────────────────────────┘
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
- Name (required): Korean, 1-10 chars.
|
|
197
|
+
- Icon (emoji picker — use system emoji picker or grid of common ones).
|
|
198
|
+
- Color (optional): bg tint for visual distinction.
|
|
199
|
+
|
|
200
|
+
Save to user-scoped storage (server-side typically, with sync).
|
|
201
|
+
|
|
202
|
+
## Code example
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
function TransactionForm() {
|
|
206
|
+
const [categoryId, setCategoryId] = useState<string | null>(null);
|
|
207
|
+
const categories = useCategories();
|
|
208
|
+
const [createOpen, setCreateOpen] = useState(false);
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Form>
|
|
212
|
+
<Form.Field name="amount">
|
|
213
|
+
<AmountInput value={amount} onValueChange={setAmount} />
|
|
214
|
+
</Form.Field>
|
|
215
|
+
|
|
216
|
+
<Form.Field name="category">
|
|
217
|
+
<Form.Label>카테고리</Form.Label>
|
|
218
|
+
<CategoryPicker
|
|
219
|
+
value={categoryId}
|
|
220
|
+
onValueChange={setCategoryId}
|
|
221
|
+
categories={categories}
|
|
222
|
+
layout="horizontal"
|
|
223
|
+
allowCustom
|
|
224
|
+
onCustomAdd={() => setCreateOpen(true)}
|
|
225
|
+
/>
|
|
226
|
+
<Form.ErrorText />
|
|
227
|
+
</Form.Field>
|
|
228
|
+
|
|
229
|
+
<CreateCategoryModal
|
|
230
|
+
open={createOpen}
|
|
231
|
+
onOpenChange={setCreateOpen}
|
|
232
|
+
onSave={(c) => {
|
|
233
|
+
createCategory(c);
|
|
234
|
+
setCategoryId(c.id);
|
|
235
|
+
}}
|
|
236
|
+
/>
|
|
237
|
+
</Form>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Edge cases
|
|
243
|
+
|
|
244
|
+
- **Many custom categories** (50+): switch to grid layout + search.
|
|
245
|
+
- **No selection allowed** (clear): show null state, `value={null}`.
|
|
246
|
+
- **Income vs expense**: filter list based on transaction type. Don't show `food` for income.
|
|
247
|
+
- **Emoji rendering inconsistency** (Apple vs Android vs Web): emoji are rendered by OS; minor visual differences across platforms are acceptable.
|
|
248
|
+
- **Korean accessibility**: aria-checked state needs Korean translation if assistive tech localized.
|
|
249
|
+
|
|
250
|
+
## Don't
|
|
251
|
+
|
|
252
|
+
- Don't use category icons without labels — Korean users want to see "식비" written out.
|
|
253
|
+
- Don't omit emoji — text-only categorization feels lifeless to Korean users.
|
|
254
|
+
- Don't use Western category emoji standards (🍔 for food). Use 🍽 for "Korean meal" feel.
|
|
255
|
+
- Don't pre-select a default category. Let the user pick — wrong default is annoying.
|
|
256
|
+
- Don't allow > 30 categories without search. Cognitive overload.
|
|
257
|
+
|
|
258
|
+
## References
|
|
259
|
+
|
|
260
|
+
No upstream component is exactly this. Closest analogs:
|
|
261
|
+
- Ant Design: `Tag` checkable mode (but no emoji-first design)
|
|
262
|
+
- MUI: `Chip` with avatar (similar idea)
|
|
263
|
+
|
|
264
|
+
This is a **Korean-market-specific custom pattern**.
|
|
265
|
+
|
|
266
|
+
## Cross-reference
|
|
267
|
+
|
|
268
|
+
- [`knowledge/i18n/korean-product-conventions.md`](../knowledge/i18n/korean-product-conventions.md) — Korean app conventions
|
|
269
|
+
- [`examples/component-amount-input.md`](component-amount-input.md) — paired in transaction forms
|
|
270
|
+
- [`examples/component-form.md`](component-form.md) — form orchestration
|
|
271
|
+
- [`knowledge/patterns/form-design.md`](../knowledge/patterns/form-design.md)
|