@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,260 @@
|
|
|
1
|
+
# `AddressInput` (custom — Korean) — spec
|
|
2
|
+
|
|
3
|
+
> Status: example artifact for **custom components**. Korean addresses require Daum Postcode lookup and a two-line structure that doesn't exist in any upstream design system.
|
|
4
|
+
>
|
|
5
|
+
> Cited knowledge: [`knowledge/patterns/form-design.md`](../knowledge/patterns/form-design.md), [`knowledge/i18n/korean-product-conventions.md`](../knowledge/i18n/korean-product-conventions.md)
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Captures a Korean address. Uses **Daum Postcode API** (the universal standard) to look up postal codes + main address; user adds detail line manually.
|
|
10
|
+
|
|
11
|
+
**Never accept Korean addresses as free text.** Address validation is a real problem; Daum solves it for free; users expect it.
|
|
12
|
+
|
|
13
|
+
## Anatomy
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────┐
|
|
17
|
+
│ 우편번호 │
|
|
18
|
+
│ ┌──────────────┐ ┌─────────────────┐ │
|
|
19
|
+
│ │ 06234 │ │ 주소 검색 │ │ ← read-only zip + lookup button
|
|
20
|
+
│ └──────────────┘ └─────────────────┘ │
|
|
21
|
+
│ │
|
|
22
|
+
│ 기본 주소 │
|
|
23
|
+
│ ┌──────────────────────────────────────┐ │
|
|
24
|
+
│ │ 서울특별시 강남구 테헤란로 123 │ │ ← read-only, populated by lookup
|
|
25
|
+
│ └──────────────────────────────────────┘ │
|
|
26
|
+
│ │
|
|
27
|
+
│ 상세 주소 (선택) │
|
|
28
|
+
│ ┌──────────────────────────────────────┐ │
|
|
29
|
+
│ │ 5층 501호 │ │ ← user-editable
|
|
30
|
+
│ └──────────────────────────────────────┘ │
|
|
31
|
+
└─────────────────────────────────────────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Slot | Required | Notes |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| Zip code field | yes | Read-only, populated by Daum lookup |
|
|
37
|
+
| Lookup button | yes | "주소 검색" — opens Daum Postcode |
|
|
38
|
+
| Main address | yes | Read-only, from Daum (도로명 or 지번 — choose) |
|
|
39
|
+
| Detail line | optional | User-typed (apt #, suite, instructions) |
|
|
40
|
+
| Address-type toggle | optional | 도로명 vs 지번 (street name vs lot number) |
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<AddressInput
|
|
46
|
+
value={address}
|
|
47
|
+
onValueChange={setAddress}
|
|
48
|
+
required
|
|
49
|
+
onLookupClick={openDaumPostcode}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
// where address is:
|
|
53
|
+
type Address = {
|
|
54
|
+
postalCode: string; // "06234"
|
|
55
|
+
mainAddress: string; // "서울특별시 강남구 테헤란로 123"
|
|
56
|
+
detailAddress: string; // "5층 501호"
|
|
57
|
+
addressType: "road" | "lot"; // 도로명 (road) | 지번 (lot)
|
|
58
|
+
englishAddress?: string; // "123 Teheran-ro, Gangnam-gu, Seoul" — for international
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `value` | `Address \| null` | — | The full address object |
|
|
65
|
+
| `onValueChange` | `(address: Address) => void` | — | |
|
|
66
|
+
| `required` | `boolean` | `false` | All three fields required (typically just zip + main) |
|
|
67
|
+
| `onLookupClick` | `() => void` | — | Triggers Daum Postcode UI; required to wire up |
|
|
68
|
+
| `disabled` | `boolean` | `false` | |
|
|
69
|
+
| `error` / `errorText` | — | — | Validation state |
|
|
70
|
+
| `addressTypeToggle` | `boolean` | `false` | Allow user to switch between 도로명 / 지번 |
|
|
71
|
+
| `requireDetail` | `boolean` | `false` | If detail address is mandatory (e.g., apartment # required) |
|
|
72
|
+
| `country` | `"KR"` | `"KR"` | Reserved for future i18n support |
|
|
73
|
+
|
|
74
|
+
## Daum Postcode integration
|
|
75
|
+
|
|
76
|
+
Daum (Kakao) provides a free embedded postcode lookup:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// In a real implementation, load the script:
|
|
80
|
+
// <script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
|
|
81
|
+
|
|
82
|
+
const openDaumPostcode = () => {
|
|
83
|
+
new window.daum.Postcode({
|
|
84
|
+
oncomplete: (data) => {
|
|
85
|
+
const newAddress: Partial<Address> = {
|
|
86
|
+
postalCode: data.zonecode,
|
|
87
|
+
mainAddress: data.address, // 도로명 by default
|
|
88
|
+
addressType: data.userSelectedType === "R" ? "road" : "lot",
|
|
89
|
+
englishAddress: data.addressEnglish,
|
|
90
|
+
};
|
|
91
|
+
// Detail address is left for the user to fill
|
|
92
|
+
setAddress({ ...address, ...newAddress, detailAddress: "" });
|
|
93
|
+
|
|
94
|
+
// Focus the detail input next
|
|
95
|
+
detailInputRef.current?.focus();
|
|
96
|
+
},
|
|
97
|
+
}).open();
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The Daum widget opens a modal/popup with its own UI (search field, results list). Returns the selected address.
|
|
102
|
+
|
|
103
|
+
For React Native / non-web: use a webview to embed the Daum widget OR a vendor's wrapper (e.g., `react-native-daum-postcode`).
|
|
104
|
+
|
|
105
|
+
## Behavior
|
|
106
|
+
|
|
107
|
+
### Lookup flow
|
|
108
|
+
|
|
109
|
+
1. User clicks "주소 검색" button.
|
|
110
|
+
2. Daum Postcode modal opens.
|
|
111
|
+
3. User searches by keyword (`강남역`, `테헤란로 123`, `청담동 100-1`).
|
|
112
|
+
4. User picks a result from the list.
|
|
113
|
+
5. Modal closes; zip + main address populate.
|
|
114
|
+
6. Focus moves to the detail address input (best practice).
|
|
115
|
+
7. User types apartment / suite / instructions.
|
|
116
|
+
|
|
117
|
+
### Editing existing address
|
|
118
|
+
|
|
119
|
+
If `value` is already populated and user clicks "주소 검색" again:
|
|
120
|
+
- All three fields are **replaced** with the new lookup.
|
|
121
|
+
- Confirm with user if detail was already filled (toast: "상세 주소가 초기화됩니다").
|
|
122
|
+
|
|
123
|
+
### Address type (도로명 vs 지번)
|
|
124
|
+
|
|
125
|
+
도로명 (road name): "서울특별시 강남구 테헤란로 123" — modern, since 2014.
|
|
126
|
+
지번 (lot number): "서울특별시 강남구 역삼동 821-1" — legacy, still common in older systems and rural areas.
|
|
127
|
+
|
|
128
|
+
Daum lookup returns both. Default to 도로명 unless the user prefers 지번. Provide a toggle if your form needs both displayed.
|
|
129
|
+
|
|
130
|
+
### Detail address rules
|
|
131
|
+
|
|
132
|
+
- Free-text, max ~50 chars.
|
|
133
|
+
- Common formats:
|
|
134
|
+
- "5층 501호" (5th floor, room 501)
|
|
135
|
+
- "302호" (room 302 — for 빌라 / villa-style buildings)
|
|
136
|
+
- "B동 1502호" (building B, room 1502)
|
|
137
|
+
- Optional unless your business requires it.
|
|
138
|
+
- Don't validate beyond character length — variations are too many.
|
|
139
|
+
|
|
140
|
+
## States
|
|
141
|
+
|
|
142
|
+
| State | Visual |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| Empty | All fields empty, "주소 검색" button prominent |
|
|
145
|
+
| Looking up | Daum modal is open (this component's UI is mostly inactive) |
|
|
146
|
+
| Populated | Fields filled, "주소 검색" button still available for re-lookup |
|
|
147
|
+
| Disabled | All fields and button disabled |
|
|
148
|
+
| Error | Red border on whichever field failed validation |
|
|
149
|
+
| Read-only (existing user) | All fields show, no edit affordances, no lookup button |
|
|
150
|
+
|
|
151
|
+
## Tokens consumed
|
|
152
|
+
|
|
153
|
+
Inherited from Input + Button. No new tokens.
|
|
154
|
+
|
|
155
|
+
## Accessibility
|
|
156
|
+
|
|
157
|
+
- Each field is a labeled input. Group with `<fieldset><legend>주소</legend>`.
|
|
158
|
+
- "주소 검색" is a `<button>` with clear label.
|
|
159
|
+
- Daum's modal is its own dialog — focus management is handled by Daum (not by this component).
|
|
160
|
+
- After lookup completes, focus moves to detail input via `ref.focus()` — improves keyboard flow.
|
|
161
|
+
- `aria-required="true"` on required fields.
|
|
162
|
+
- Error: `aria-invalid="true"` + `aria-describedby` to error text.
|
|
163
|
+
- Postal code field: `aria-readonly="true"` (don't let users guess they can type).
|
|
164
|
+
|
|
165
|
+
## Korean considerations
|
|
166
|
+
|
|
167
|
+
Per [`knowledge/i18n/korean-product-conventions.md`](../knowledge/i18n/korean-product-conventions.md):
|
|
168
|
+
|
|
169
|
+
- Always use Daum Postcode. Korean users expect it; free-form is broken.
|
|
170
|
+
- Address-type defaults to 도로명 for modern apps (matches government standardization).
|
|
171
|
+
- Display labels: "우편번호" / "기본 주소" / "상세 주소" — these are conventional. Don't translate.
|
|
172
|
+
- For international shipments: optionally include `englishAddress` from Daum.
|
|
173
|
+
- Server-side: store the full structured address (all 4 fields), not just the formatted string. Lets you re-display in different formats later.
|
|
174
|
+
|
|
175
|
+
## Mobile patterns
|
|
176
|
+
|
|
177
|
+
- The lookup button: full-width on mobile, alongside the zip field on desktop.
|
|
178
|
+
- Daum's modal is responsive — trust it.
|
|
179
|
+
- After lookup, scroll the detail input into view (it's the next thing user types in).
|
|
180
|
+
|
|
181
|
+
## Code example
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
function CheckoutForm() {
|
|
185
|
+
const [address, setAddress] = useState<Address | null>(null);
|
|
186
|
+
const detailRef = useRef<HTMLInputElement>(null);
|
|
187
|
+
|
|
188
|
+
const openLookup = () => {
|
|
189
|
+
new window.daum.Postcode({
|
|
190
|
+
oncomplete: (data) => {
|
|
191
|
+
setAddress({
|
|
192
|
+
postalCode: data.zonecode,
|
|
193
|
+
mainAddress: data.address,
|
|
194
|
+
detailAddress: "",
|
|
195
|
+
addressType: data.userSelectedType === "R" ? "road" : "lot",
|
|
196
|
+
englishAddress: data.addressEnglish,
|
|
197
|
+
});
|
|
198
|
+
setTimeout(() => detailRef.current?.focus(), 100);
|
|
199
|
+
},
|
|
200
|
+
}).open();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<Form>
|
|
205
|
+
<AddressInput
|
|
206
|
+
label="배송지"
|
|
207
|
+
value={address}
|
|
208
|
+
onValueChange={setAddress}
|
|
209
|
+
onLookupClick={openLookup}
|
|
210
|
+
detailInputRef={detailRef}
|
|
211
|
+
required
|
|
212
|
+
errorText={address && !address.postalCode ? "주소를 검색해 주세요" : undefined}
|
|
213
|
+
/>
|
|
214
|
+
|
|
215
|
+
<Form.Field name="recipient">
|
|
216
|
+
<Form.Label>받는 사람</Form.Label>
|
|
217
|
+
<Form.Control><Input /></Form.Control>
|
|
218
|
+
</Form.Field>
|
|
219
|
+
|
|
220
|
+
<Form.Field name="phone">
|
|
221
|
+
<Form.Label>연락처</Form.Label>
|
|
222
|
+
<Form.Control><Input type="tel" /></Form.Control>
|
|
223
|
+
</Form.Field>
|
|
224
|
+
</Form>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Edge cases
|
|
230
|
+
|
|
231
|
+
- **No internet**: Daum widget can't load. Show error + retry button. Don't allow free-form fallback.
|
|
232
|
+
- **Daum API fails / banned**: rare, but vendors do change. Have a backup vendor (Naver Maps geocoding, juso.go.kr official API).
|
|
233
|
+
- **Address that Daum doesn't recognize**: rare for residential. For very new construction, Daum might lag — let user note in detail address.
|
|
234
|
+
- **PO Box / virtual addresses**: Korean PO Boxes use a separate system; out of scope for residential AddressInput. Different field.
|
|
235
|
+
- **Multi-tenant address** (e.g., apartment complex with multiple buildings): Daum returns the building-level address; user adds 동 (building) and 호 (unit) in detail.
|
|
236
|
+
- **International orders**: AddressInput is KR-only here. For multi-country: separate `InternationalAddressInput` component with country dropdown.
|
|
237
|
+
- **Server-side normalization**: even with Daum's clean output, server should normalize whitespace and casing.
|
|
238
|
+
|
|
239
|
+
## Don't
|
|
240
|
+
|
|
241
|
+
- Don't accept Korean addresses as free text.
|
|
242
|
+
- Don't roll your own postal-code database.
|
|
243
|
+
- Don't make all three fields free-form (zip + main + detail). Zip and main come from lookup.
|
|
244
|
+
- Don't auto-validate detail address against a real-world database (impossible).
|
|
245
|
+
- Don't ship without a lookup button — there's no other way to get the right zip.
|
|
246
|
+
- Don't store only the formatted string — store all 4 components for future flexibility.
|
|
247
|
+
- Don't use `<input type="text">` for postal code with `pattern` regex as the only validation. Use Daum.
|
|
248
|
+
|
|
249
|
+
## API rationale
|
|
250
|
+
|
|
251
|
+
- **`value: Address` (structured object)**: matches what Daum returns; frees the consumer from re-parsing.
|
|
252
|
+
- **`onLookupClick: () => void`**: lookup is side-effectful (DOM manipulation, script load). Consumer wires it; component doesn't bake in Daum dependency.
|
|
253
|
+
- **No "free-form" mode**: forcing the lookup is the point. If a consumer wants free-form, they shouldn't use this component.
|
|
254
|
+
|
|
255
|
+
## Cross-reference
|
|
256
|
+
|
|
257
|
+
- [`knowledge/i18n/korean-product-conventions.md`](../knowledge/i18n/korean-product-conventions.md) — Daum Postcode, address conventions
|
|
258
|
+
- [`knowledge/patterns/form-design.md`](../knowledge/patterns/form-design.md) — address-field pattern
|
|
259
|
+
- [`examples/component-input.md`](component-input.md) — base Input
|
|
260
|
+
- [Daum Postcode Service](https://postcode.map.daum.net/guide) — official documentation
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# `Affix` (sticky positioning) — spec
|
|
2
|
+
|
|
3
|
+
> Citing Ant Design `Affix`, MUI (no dedicated — use CSS `position: sticky`), shadcn-ui (CSS only)
|
|
4
|
+
>
|
|
5
|
+
> Mostly **a CSS pattern** in modern browsers. The Affix component wraps that pattern with React-friendly props (target scroll container, offset, callbacks).
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Makes an element stick to a position (top of viewport, scroll container) as the user scrolls past it. Used for: sticky table headers, sticky CTAs, side panels that lock to top, sub-navigation bars.
|
|
10
|
+
|
|
11
|
+
## When Affix vs CSS `position: sticky`
|
|
12
|
+
|
|
13
|
+
| Scenario | Use |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Element follows viewport on scroll | CSS `position: sticky` (native, no JS) |
|
|
16
|
+
| Element follows viewport with **callback** when sticky toggles | Affix component (or write custom IntersectionObserver) |
|
|
17
|
+
| Element sticks within a **specific container** (not page) | Affix or scoped sticky |
|
|
18
|
+
| Sticky inside a Modal that has its own scroll | Affix with `target` prop |
|
|
19
|
+
|
|
20
|
+
For 80% of cases, native CSS is the right answer:
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
.sticky-header {
|
|
24
|
+
position: sticky;
|
|
25
|
+
top: 0;
|
|
26
|
+
background: var(--color-bg-default);
|
|
27
|
+
z-index: 10;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This works in all modern browsers, no JavaScript, no perf cost. **Reach for the Affix component only when** you need:
|
|
32
|
+
- Container-scoped sticky (not page-scroll)
|
|
33
|
+
- Callbacks on stick / unstick
|
|
34
|
+
- Programmatic offset adjustment
|
|
35
|
+
|
|
36
|
+
## Anatomy
|
|
37
|
+
|
|
38
|
+
The Affix wraps a child element. It positions normally until the user scrolls past a threshold, then it sticks to the configured position.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
[normal scroll position]
|
|
42
|
+
┌───────────────────────┐
|
|
43
|
+
│ Affixed element │ ← scrolls naturally with page
|
|
44
|
+
└───────────────────────┘
|
|
45
|
+
[content below]
|
|
46
|
+
|
|
47
|
+
[user scrolls down]
|
|
48
|
+
┌───────────────────────┐
|
|
49
|
+
│ Affixed element │ ← stays at top of viewport (or container)
|
|
50
|
+
└───────────────────────┘
|
|
51
|
+
[content scrolls behind]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Affix offsetTop={0} onChange={(affixed) => setIsSticky(affixed)}>
|
|
58
|
+
<SubNavBar />
|
|
59
|
+
</Affix>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| `children` | `ReactNode` | — | The element to affix |
|
|
65
|
+
| `offsetTop` | `number` | `0` | Px from top when sticky |
|
|
66
|
+
| `offsetBottom` | `number` | — | Alternative: stick to bottom |
|
|
67
|
+
| `target` | `() => HTMLElement` | window | Scroll container (for inner scroll cases) |
|
|
68
|
+
| `onChange` | `(affixed: boolean) => void` | — | Fires on stick/unstick |
|
|
69
|
+
|
|
70
|
+
## Behavior
|
|
71
|
+
|
|
72
|
+
- Element renders in flow normally.
|
|
73
|
+
- As user scrolls, Affix watches the scroll position.
|
|
74
|
+
- When the element's natural top reaches `offsetTop`, it switches to `position: fixed` (or `sticky`) at that offset.
|
|
75
|
+
- When user scrolls back up past the threshold, it returns to flow.
|
|
76
|
+
|
|
77
|
+
In practice: Ant's Affix uses `position: sticky` under the hood for modern browsers, falling back to `position: fixed` + scroll listener for older.
|
|
78
|
+
|
|
79
|
+
## Use cases
|
|
80
|
+
|
|
81
|
+
### 1. Sticky sub-navigation
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<Affix offsetTop={64}> {/* Below a 64px main nav */}
|
|
85
|
+
<SubNav />
|
|
86
|
+
</Affix>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The sub-nav scrolls with the page until it hits the main nav, then sticks below.
|
|
90
|
+
|
|
91
|
+
### 2. Sticky table header
|
|
92
|
+
|
|
93
|
+
For long tables, the header should stay visible:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<table className="sticky-header">
|
|
97
|
+
<thead>...</thead>
|
|
98
|
+
<tbody>...</tbody>
|
|
99
|
+
</table>
|
|
100
|
+
|
|
101
|
+
/* Or in CSS: */
|
|
102
|
+
table thead { position: sticky; top: 0; z-index: 1; }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
CSS-only is preferred here.
|
|
106
|
+
|
|
107
|
+
### 3. Sticky form CTA on mobile
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<Affix offsetBottom={0}>
|
|
111
|
+
<div className="footer-cta">
|
|
112
|
+
<Button fullWidth>저장</Button>
|
|
113
|
+
</div>
|
|
114
|
+
</Affix>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The CTA sticks to the bottom of viewport. On mobile, this is the canonical pattern for primary form actions.
|
|
118
|
+
|
|
119
|
+
### 4. Sticky filter sidebar in scrollable container
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<div ref={containerRef} className="overflow-auto h-screen">
|
|
123
|
+
<Affix target={() => containerRef.current!}>
|
|
124
|
+
<FilterPanel />
|
|
125
|
+
</Affix>
|
|
126
|
+
<Results />
|
|
127
|
+
</div>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The filter sticks within the container (not viewport).
|
|
131
|
+
|
|
132
|
+
## Sizes / states
|
|
133
|
+
|
|
134
|
+
Affix doesn't have its own visual states — it transparently relays the wrapped element. Visual decisions (shadow, background, border) belong to the wrapped element when it becomes sticky.
|
|
135
|
+
|
|
136
|
+
A common pattern: add a subtle border/shadow only when sticky:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Affix onChange={setIsSticky}>
|
|
140
|
+
<div className={isSticky ? "sticky-with-shadow" : ""}>
|
|
141
|
+
<SubNav />
|
|
142
|
+
</div>
|
|
143
|
+
</Affix>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```css
|
|
147
|
+
.sticky-with-shadow {
|
|
148
|
+
box-shadow: 0 1px 0 var(--color-border-default), 0 4px 12px rgba(0,0,0,0.04);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This hints "I've moved; you scrolled" without screaming.
|
|
153
|
+
|
|
154
|
+
## Tokens consumed
|
|
155
|
+
|
|
156
|
+
Affix itself doesn't consume tokens. The wrapped element does. For sticky-specific styling:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
--color-bg-default (sticky element bg, often opaque to hide content behind)
|
|
160
|
+
--color-border-default (subtle separator when sticky)
|
|
161
|
+
--shadow-card (subtle shadow when sticky)
|
|
162
|
+
--z-sticky (z-index above page content)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Accessibility
|
|
166
|
+
|
|
167
|
+
Affix is purely visual — no a11y implications beyond what the wrapped element provides. Keyboard navigation continues normally.
|
|
168
|
+
|
|
169
|
+
If the affixed element is interactive (buttons in a sticky CTA bar): standard focus rules apply.
|
|
170
|
+
|
|
171
|
+
For sticky **alerts / banners**: respect `prefers-reduced-motion` and don't animate the stick transition. The position change itself (relative to the viewport) doesn't violate motion preferences, but added animations should be optional.
|
|
172
|
+
|
|
173
|
+
## Don't
|
|
174
|
+
|
|
175
|
+
- Don't reach for Affix when CSS `position: sticky` works. Native is faster, lighter, and supported everywhere modern.
|
|
176
|
+
- Don't stick large blocks (>50% viewport height). They eat scroll space.
|
|
177
|
+
- Don't stack 3+ sticky elements (header + sub-nav + filter bar). The visible viewport shrinks.
|
|
178
|
+
- Don't forget background color on sticky elements — content scrolls behind, transparency = visual chaos.
|
|
179
|
+
- Don't auto-show sticky alerts that don't have a dismiss action — user is stuck with them.
|
|
180
|
+
|
|
181
|
+
## References
|
|
182
|
+
|
|
183
|
+
- Ant Design: [`refs/ant-design/components/affix/`](../refs/ant-design/components/affix/) — `Affix`. Wraps native sticky with React-friendly callbacks.
|
|
184
|
+
- MUI / shadcn-ui: no dedicated component. Use CSS `position: sticky`.
|
|
185
|
+
|
|
186
|
+
## Cross-reference
|
|
187
|
+
|
|
188
|
+
- [`knowledge/layout/spacing-and-grid.md`](../knowledge/layout/spacing-and-grid.md) — broader layout patterns
|
|
189
|
+
- [`knowledge/patterns/mobile-navigation.md`](../knowledge/patterns/mobile-navigation.md) — sticky bottom-tab-bar (similar pattern)
|
|
190
|
+
- [`examples/component-table.md`](component-table.md) — sticky table headers
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# `AlertDialog` — spec
|
|
2
|
+
|
|
3
|
+
> Synthesized from shadcn-ui `alert-dialog` (Radix). Modal confirmation dialog for actions that require explicit user acknowledgment. Distinct from `Modal` (general-purpose) and `Toast` (transient).
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- **Destructive actions** that can't be undone: "Delete account?", "Discard unsaved changes?"
|
|
8
|
+
- **Significant state changes**: "Cancel subscription?", "Sign out everywhere?"
|
|
9
|
+
- **Critical confirmations** in payment / financial flows.
|
|
10
|
+
|
|
11
|
+
When NOT to use:
|
|
12
|
+
- Routine confirmations (use Toast with undo instead).
|
|
13
|
+
- Form submissions (let the form's submit button speak for itself).
|
|
14
|
+
- Loading / progress (use Modal or Toast).
|
|
15
|
+
|
|
16
|
+
## AlertDialog vs Modal vs Toast
|
|
17
|
+
|
|
18
|
+
| | AlertDialog | Modal | Toast |
|
|
19
|
+
| --- | --- | --- | --- |
|
|
20
|
+
| Cancellable via Esc / outside-click | **Yes for cancel; no destructive default** | Yes | n/a |
|
|
21
|
+
| Default focus | Cancel button | First focusable | n/a |
|
|
22
|
+
| Use | Destructive confirmation | Forms, settings, content | Transient feedback |
|
|
23
|
+
| Backdrop click | Closes (= cancel) | Closes (= dismiss) | n/a |
|
|
24
|
+
|
|
25
|
+
The key behavioral difference: AlertDialog defaults focus to **Cancel** so accidental Enter doesn't fire a destructive action.
|
|
26
|
+
|
|
27
|
+
## Anatomy
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
┌──────────────────────────────────┐
|
|
31
|
+
│ │
|
|
32
|
+
│ ⚠ Delete this project? │ ← title with icon
|
|
33
|
+
│ │
|
|
34
|
+
│ This will permanently delete │ ← description
|
|
35
|
+
│ the project and all its data. │
|
|
36
|
+
│ This action cannot be undone. │
|
|
37
|
+
│ │
|
|
38
|
+
│ [Cancel] [Delete] │ ← Cancel default focus
|
|
39
|
+
│ ↑ │
|
|
40
|
+
│ default focus │
|
|
41
|
+
└──────────────────────────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<AlertDialog>
|
|
48
|
+
<AlertDialog.Trigger asChild>
|
|
49
|
+
<Button variant="destructive">Delete project</Button>
|
|
50
|
+
</AlertDialog.Trigger>
|
|
51
|
+
<AlertDialog.Content>
|
|
52
|
+
<AlertDialog.Header>
|
|
53
|
+
<AlertDialog.Title>Delete this project?</AlertDialog.Title>
|
|
54
|
+
<AlertDialog.Description>
|
|
55
|
+
This will permanently delete the project and all its data.
|
|
56
|
+
This action cannot be undone.
|
|
57
|
+
</AlertDialog.Description>
|
|
58
|
+
</AlertDialog.Header>
|
|
59
|
+
<AlertDialog.Footer>
|
|
60
|
+
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
|
|
61
|
+
<AlertDialog.Action onClick={handleDelete}>
|
|
62
|
+
Delete
|
|
63
|
+
</AlertDialog.Action>
|
|
64
|
+
</AlertDialog.Footer>
|
|
65
|
+
</AlertDialog.Content>
|
|
66
|
+
</AlertDialog>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Composition
|
|
70
|
+
|
|
71
|
+
| Part | Purpose |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `Trigger` | Element that opens the dialog |
|
|
74
|
+
| `Content` | The modal panel |
|
|
75
|
+
| `Header` | Title + description region |
|
|
76
|
+
| `Title` | Heading; identifies the question |
|
|
77
|
+
| `Description` | Explanation of consequences |
|
|
78
|
+
| `Footer` | Action row |
|
|
79
|
+
| `Cancel` | Safe / dismissive action (default focus) |
|
|
80
|
+
| `Action` | Confirming / destructive action |
|
|
81
|
+
|
|
82
|
+
## States
|
|
83
|
+
|
|
84
|
+
| State | Visual |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| Closed | Hidden |
|
|
87
|
+
| Opening | Backdrop fade + content scale (200ms) |
|
|
88
|
+
| Open | Visible, focus on Cancel, body scroll locked |
|
|
89
|
+
| Action loading | Action button shows spinner; both buttons disabled |
|
|
90
|
+
| Closing | Reverse |
|
|
91
|
+
|
|
92
|
+
## Tokens consumed
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
--color-bg-overlay-scrim (backdrop)
|
|
96
|
+
--color-bg-default (dialog bg)
|
|
97
|
+
--color-fg-default
|
|
98
|
+
--color-error-default (destructive variant title icon + Action)
|
|
99
|
+
--color-warning-default (warning variant)
|
|
100
|
+
--shadow-overlay
|
|
101
|
+
--radius-lg
|
|
102
|
+
--space-md, --space-lg
|
|
103
|
+
--motion-medium
|
|
104
|
+
--ease-out
|
|
105
|
+
--z-overlay
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Accessibility
|
|
109
|
+
|
|
110
|
+
- `Content`: `role="alertdialog"` (NOT `dialog`) — interrupts screen readers.
|
|
111
|
+
- `aria-labelledby` references `Title`.
|
|
112
|
+
- `aria-describedby` references `Description`.
|
|
113
|
+
- **Focus trap on open**; focus defaults to `Cancel` (the safe option).
|
|
114
|
+
- `Esc` triggers `Cancel` (not `Action`).
|
|
115
|
+
- Backdrop click triggers `Cancel`.
|
|
116
|
+
- `Enter` on focused button activates that button (default = Cancel = safe).
|
|
117
|
+
- Touch target ≥ 44pt for both buttons.
|
|
118
|
+
|
|
119
|
+
## Korean conventions
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
삭제하시겠습니까?
|
|
123
|
+
|
|
124
|
+
이 프로젝트와 관련된 모든 데이터가 영구적으로 삭제되며,
|
|
125
|
+
복구할 수 없습니다.
|
|
126
|
+
|
|
127
|
+
[취소] [삭제]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- 합쇼체 typical for destructive confirmations ("...하시겠습니까?")
|
|
131
|
+
- 해요체 OK for casual brand voice ("...할까요?")
|
|
132
|
+
- "취소" for Cancel; "삭제" / "확인" / "동의" for Action
|
|
133
|
+
- Avoid "OK" / "예" alone — use a verb that names the action
|
|
134
|
+
|
|
135
|
+
## Code example
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
function DeleteProjectButton({ project }: Props) {
|
|
139
|
+
const [loading, setLoading] = useState(false);
|
|
140
|
+
|
|
141
|
+
const handleDelete = async () => {
|
|
142
|
+
setLoading(true);
|
|
143
|
+
try {
|
|
144
|
+
await api.deleteProject(project.id);
|
|
145
|
+
toast.success("프로젝트가 삭제되었습니다");
|
|
146
|
+
} finally {
|
|
147
|
+
setLoading(false);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<AlertDialog>
|
|
153
|
+
<AlertDialog.Trigger asChild>
|
|
154
|
+
<Button variant="destructive">
|
|
155
|
+
<TrashIcon /> 프로젝트 삭제
|
|
156
|
+
</Button>
|
|
157
|
+
</AlertDialog.Trigger>
|
|
158
|
+
<AlertDialog.Content>
|
|
159
|
+
<AlertDialog.Header>
|
|
160
|
+
<AlertDialog.Title>이 프로젝트를 삭제하시겠습니까?</AlertDialog.Title>
|
|
161
|
+
<AlertDialog.Description>
|
|
162
|
+
<strong>{project.name}</strong> 및 관련된 모든 데이터가
|
|
163
|
+
영구적으로 삭제되며, 복구할 수 없습니다.
|
|
164
|
+
</AlertDialog.Description>
|
|
165
|
+
</AlertDialog.Header>
|
|
166
|
+
<AlertDialog.Footer>
|
|
167
|
+
<AlertDialog.Cancel disabled={loading}>취소</AlertDialog.Cancel>
|
|
168
|
+
<AlertDialog.Action
|
|
169
|
+
onClick={handleDelete}
|
|
170
|
+
disabled={loading}
|
|
171
|
+
className="bg-destructive"
|
|
172
|
+
>
|
|
173
|
+
{loading ? "삭제 중..." : "삭제"}
|
|
174
|
+
</AlertDialog.Action>
|
|
175
|
+
</AlertDialog.Footer>
|
|
176
|
+
</AlertDialog.Content>
|
|
177
|
+
</AlertDialog>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Edge cases
|
|
183
|
+
|
|
184
|
+
- **Async action with error**: keep dialog open after error; show inline error message; let user retry or cancel.
|
|
185
|
+
- **Action requires more context**: pre-fill Description with consequence detail (file count, last edit date, undoable for X days).
|
|
186
|
+
- **Multi-step destructive flow**: chain AlertDialogs OR convert to a wizard with summary at end.
|
|
187
|
+
- **Type-to-confirm pattern** (delete repo on GitHub): replace simple Cancel/Action with a text input requiring exact name match.
|
|
188
|
+
- **Stacking**: avoid AlertDialog inside AlertDialog. Sequence them or rethink flow.
|
|
189
|
+
- **RTL**: button order reverses (Action left, Cancel right) per RTL convention.
|
|
190
|
+
- **Reduced motion**: skip scale animation; instant.
|
|
191
|
+
|
|
192
|
+
## Don't
|
|
193
|
+
|
|
194
|
+
- Don't use AlertDialog for routine confirmations — use Toast with Undo for delete-with-undo flows.
|
|
195
|
+
- Don't make Action the default-focused button. Cancel is safer.
|
|
196
|
+
- Don't omit the Description for destructive actions. Users need consequence info.
|
|
197
|
+
- Don't use vague labels like "OK / Cancel". Name the action ("Delete project").
|
|
198
|
+
- Don't auto-close after Action without showing success feedback (Toast / inline).
|
|
199
|
+
- Don't disable Esc — accessibility requires it.
|
|
200
|
+
|
|
201
|
+
## References
|
|
202
|
+
|
|
203
|
+
- shadcn-ui: [`alert-dialog`](../refs/shadcn-ui/apps/v4/registry/new-york-v4/ui/alert-dialog.tsx) (Radix)
|
|
204
|
+
- WAI-ARIA: [`alertdialog`](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/)
|
|
205
|
+
|
|
206
|
+
## Cross-reference
|
|
207
|
+
|
|
208
|
+
- [`examples/component-modal.md`](component-modal.md) — general-purpose modal
|
|
209
|
+
- [`examples/component-toast.md`](component-toast.md) — transient feedback (with Undo)
|
|
210
|
+
- [`knowledge/a11y/keyboard-and-focus.md`](../knowledge/a11y/keyboard-and-focus.md)
|