@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,198 @@
|
|
|
1
|
+
# `List` — spec
|
|
2
|
+
|
|
3
|
+
> Synthesized from Ant Design `List` and MUI `List` + `ListItem`. The semantic + styled wrapper around a list of `Item` rows. Distinct from `Item` (single row) and `Table` (tabular data).
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Vertical stack of similar entities (notifications, files, contacts).
|
|
8
|
+
- With pagination, infinite scroll, or filtering.
|
|
9
|
+
- When `<ul>` + items need consistent styling + a11y.
|
|
10
|
+
|
|
11
|
+
When NOT to use:
|
|
12
|
+
- Tabular data (use `Table`).
|
|
13
|
+
- Single row of actions (use `Toolbar` / `Flex`).
|
|
14
|
+
- Heterogeneous content (use composition of `Card`s).
|
|
15
|
+
|
|
16
|
+
## Anatomy
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
[List header (optional)]
|
|
20
|
+
─────────────────────────
|
|
21
|
+
[Item 1]
|
|
22
|
+
[Item 2]
|
|
23
|
+
[Item 3]
|
|
24
|
+
─────────────────────────
|
|
25
|
+
[List footer (optional, e.g., Load more)]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
<List
|
|
32
|
+
header={<h3>Recent activity</h3>}
|
|
33
|
+
footer={<Button onClick={loadMore}>Load more</Button>}
|
|
34
|
+
loading={loading}
|
|
35
|
+
empty={<Empty description="No activity yet" />}
|
|
36
|
+
>
|
|
37
|
+
{activities.map(a => (
|
|
38
|
+
<Item key={a.id}>
|
|
39
|
+
<ItemMedia>
|
|
40
|
+
<Avatar src={a.user.avatar} />
|
|
41
|
+
</ItemMedia>
|
|
42
|
+
<ItemContent>
|
|
43
|
+
<ItemTitle>{a.user.name}</ItemTitle>
|
|
44
|
+
<ItemDescription>{a.action}</ItemDescription>
|
|
45
|
+
</ItemContent>
|
|
46
|
+
</Item>
|
|
47
|
+
))}
|
|
48
|
+
</List>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Prop | Type | Default | Description |
|
|
52
|
+
| --- | --- | --- | --- |
|
|
53
|
+
| `header` | `ReactNode` | — | Sticky header above items |
|
|
54
|
+
| `footer` | `ReactNode` | — | Below items (Load more, totals) |
|
|
55
|
+
| `loading` | `boolean` | `false` | Show Skeleton instead of items |
|
|
56
|
+
| `empty` | `ReactNode` | — | Shown when 0 items |
|
|
57
|
+
| `dividers` | `boolean` | `true` | Show separators between items |
|
|
58
|
+
| `density` | `"compact" \| "comfortable" \| "relaxed"` | `"comfortable"` | Item padding |
|
|
59
|
+
| `as` | `"ul" \| "ol" \| "div"` | `"ul"` | Underlying element |
|
|
60
|
+
|
|
61
|
+
## States
|
|
62
|
+
|
|
63
|
+
| State | Visual |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| Loading | Skeleton items |
|
|
66
|
+
| Empty | `empty` prop content |
|
|
67
|
+
| With items | Standard render |
|
|
68
|
+
| Error | (handled externally; pass error UI in children) |
|
|
69
|
+
|
|
70
|
+
## Variants
|
|
71
|
+
|
|
72
|
+
### With dividers (default)
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
[Item 1]
|
|
76
|
+
─────
|
|
77
|
+
[Item 2]
|
|
78
|
+
─────
|
|
79
|
+
[Item 3]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Without dividers (cards spaced)
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
[Item 1]
|
|
86
|
+
|
|
87
|
+
[Item 2]
|
|
88
|
+
|
|
89
|
+
[Item 3]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use when items are full Cards with their own boundaries.
|
|
93
|
+
|
|
94
|
+
### With sections (grouped)
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Yesterday
|
|
98
|
+
─────
|
|
99
|
+
[Item 1]
|
|
100
|
+
[Item 2]
|
|
101
|
+
|
|
102
|
+
Last week
|
|
103
|
+
─────
|
|
104
|
+
[Item 3]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Group via wrapping headings + smaller sub-lists.
|
|
108
|
+
|
|
109
|
+
## Tokens consumed
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
--list-bg
|
|
113
|
+
--list-divider
|
|
114
|
+
--list-header-bg (sticky header bg)
|
|
115
|
+
--list-empty-fg
|
|
116
|
+
--space-md, --space-lg
|
|
117
|
+
--font-size-sm (header)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Accessibility
|
|
121
|
+
|
|
122
|
+
- Use `<ul>` (default) or `<ol>` if order matters.
|
|
123
|
+
- Each `<Item>` is `<li>` semantically.
|
|
124
|
+
- Header outside `<ul>` (separate element).
|
|
125
|
+
- For load-more: announce new items via `aria-live="polite"`.
|
|
126
|
+
- Empty state: `role="status"`.
|
|
127
|
+
|
|
128
|
+
## Pagination + infinite scroll
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
{/* Pagination at bottom */}
|
|
132
|
+
<List footer={<Pagination ... />}>...</List>
|
|
133
|
+
|
|
134
|
+
{/* Infinite scroll (trigger near bottom) */}
|
|
135
|
+
<List footer={<InfiniteTrigger onTrigger={loadMore} loading={loadingMore} />}>
|
|
136
|
+
...
|
|
137
|
+
</List>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For long lists (1000+ items): virtualize. Use react-virtual or react-virtuoso.
|
|
141
|
+
|
|
142
|
+
## Code example
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
function NotificationList() {
|
|
146
|
+
const { data, isLoading, error } = useNotifications();
|
|
147
|
+
|
|
148
|
+
if (error) return <ErrorState error={error} />;
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<List
|
|
152
|
+
header={<h2>알림</h2>}
|
|
153
|
+
loading={isLoading}
|
|
154
|
+
empty={
|
|
155
|
+
<EmptyState
|
|
156
|
+
illustration="no-notifications"
|
|
157
|
+
title="알림이 없어요"
|
|
158
|
+
description="새 활동이 있을 때 여기에 표시돼요."
|
|
159
|
+
/>
|
|
160
|
+
}
|
|
161
|
+
>
|
|
162
|
+
{data?.map(n => (
|
|
163
|
+
<Item key={n.id} interactive onClick={() => markRead(n.id)}>
|
|
164
|
+
<ItemMedia><n.icon /></ItemMedia>
|
|
165
|
+
<ItemContent>
|
|
166
|
+
<ItemTitle>{n.title}</ItemTitle>
|
|
167
|
+
<ItemDescription>{n.message}</ItemDescription>
|
|
168
|
+
</ItemContent>
|
|
169
|
+
<ItemActions>
|
|
170
|
+
<time>{formatRelative(n.timestamp)}</time>
|
|
171
|
+
{!n.read && <UnreadDot />}
|
|
172
|
+
</ItemActions>
|
|
173
|
+
</Item>
|
|
174
|
+
))}
|
|
175
|
+
</List>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Don't
|
|
181
|
+
|
|
182
|
+
- Don't use List for tabular data. Use Table.
|
|
183
|
+
- Don't omit empty state. Empty list = unclear if loading or really empty.
|
|
184
|
+
- Don't render 10,000 items unvirtualized. Performance dies.
|
|
185
|
+
- Don't use raw `<ul>` for item-style data. Use List + Item primitives.
|
|
186
|
+
|
|
187
|
+
## References
|
|
188
|
+
|
|
189
|
+
- Ant: [`List`](../refs/ant-design/components/list)
|
|
190
|
+
- MUI: [`List`](../refs/mui/packages/mui-material/src/List) + [`ListItem`](../refs/mui/packages/mui-material/src/ListItem)
|
|
191
|
+
- HTML5 `<ul>` / `<ol>` / `<li>`
|
|
192
|
+
|
|
193
|
+
## Cross-reference
|
|
194
|
+
|
|
195
|
+
- [`examples/component-item.md`](component-item.md) — single row
|
|
196
|
+
- [`examples/component-table.md`](component-table.md) — tabular alternative
|
|
197
|
+
- [`examples/component-empty-state.md`](component-empty-state.md) — full-page empty
|
|
198
|
+
- [`knowledge/patterns/list-and-feed.md`](../knowledge/patterns/list-and-feed.md)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# `LoadingButton` (pattern) — spec
|
|
2
|
+
|
|
3
|
+
> **Pattern, not a separate component.** As of MUI v6, `LoadingButton` was merged into `Button` (`<Button loading>`); shadcn / Ant don't ship a separate "loading button" either. This spec documents the **loading-state pattern** that any `Button` should support — applied to `Button` props in your design system.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Any button that triggers an async operation (form submit, API call, file upload).
|
|
8
|
+
- Especially destructive actions (delete, leave, transfer) — without a loading state, users double-click and trigger duplicate operations.
|
|
9
|
+
|
|
10
|
+
## Anatomy
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Default state Loading state
|
|
14
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
15
|
+
│ [icon] Label │ → │ ⠋ Loading │
|
|
16
|
+
└─────────────────┘ └─────────────────┘
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The label may stay or change ("저장하기" → "저장 중..."). The leading icon is replaced with a spinner. The button stays the same width (loading shouldn't reflow the layout).
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
Add these props to `Button`:
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| `loading` | `boolean` | `false` | Show spinner; disable click |
|
|
28
|
+
| `loadingPosition` | `'start' \| 'end' \| 'center'` | `'center'` | Spinner position; `start`/`end` keeps the label visible |
|
|
29
|
+
| `loadingIndicator` | `ReactNode` | `<CircularProgress size={16} />` | Custom indicator |
|
|
30
|
+
| `loadingText` | `string` | — | Optional label override during loading (e.g., "저장 중...") |
|
|
31
|
+
|
|
32
|
+
## States
|
|
33
|
+
|
|
34
|
+
| State | Visual | Interaction |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| Default | Standard button | Clickable |
|
|
37
|
+
| Hover | bg-hover | Clickable |
|
|
38
|
+
| Loading | Spinner + label/loadingText; bg + fg muted slightly | **NOT clickable**; cursor remains pointer (signals "we got your click, working on it") |
|
|
39
|
+
| Disabled | reduced opacity | Not clickable |
|
|
40
|
+
| Loading + disabled | Same as loading | (functionally equivalent) |
|
|
41
|
+
|
|
42
|
+
## Tokens consumed
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
--button-bg-default
|
|
46
|
+
--button-bg-hover
|
|
47
|
+
--button-fg-default
|
|
48
|
+
--spinner-size-16 /* default loading indicator */
|
|
49
|
+
--space-sm /* gap between spinner and label */
|
|
50
|
+
--button-min-width /* width-locked during loading */
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
|
|
55
|
+
- `aria-busy="true"` while loading. Screen readers announce "busy" status.
|
|
56
|
+
- `aria-disabled="true"` while loading (NOT the `disabled` HTML attribute — that removes the button from the tab order, breaks Esc/screen reader recovery).
|
|
57
|
+
- Focus stays on the button during loading. After completion, focus management depends on outcome:
|
|
58
|
+
- Success: move focus to next logical step OR a success toast.
|
|
59
|
+
- Error: announce the error via `aria-live="polite"` toast; keep focus on the button so user can retry.
|
|
60
|
+
- Cite [`knowledge/a11y/keyboard-and-focus.md`](../knowledge/a11y/keyboard-and-focus.md).
|
|
61
|
+
|
|
62
|
+
## Edge cases
|
|
63
|
+
|
|
64
|
+
- **Long async operation (>10s)** — show progress percentage if known, or supplement with a toast "잠시만 기다려 주세요". 10s+ silent loading reads as broken.
|
|
65
|
+
- **Network failure** — exit loading, restore label, surface error inline or via toast. Don't leave the button in loading state on error.
|
|
66
|
+
- **Korean labels** — "저장하기" → "저장 중..." (3 dots, not "..." typography). The 중 character signals progress; pair with spinner for redundancy.
|
|
67
|
+
- **Optimistic UI** — for cheap operations (toggle a switch), prefer optimistic update over loading button.
|
|
68
|
+
|
|
69
|
+
## Code example
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { Button, CircularProgress } from "@mui/material";
|
|
73
|
+
|
|
74
|
+
function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
|
|
75
|
+
const [loading, setLoading] = useState(false);
|
|
76
|
+
const [error, setError] = useState<string | null>(null);
|
|
77
|
+
|
|
78
|
+
async function handleClick() {
|
|
79
|
+
setLoading(true);
|
|
80
|
+
setError(null);
|
|
81
|
+
try {
|
|
82
|
+
await onSave();
|
|
83
|
+
} catch (e) {
|
|
84
|
+
setError(e instanceof Error ? e.message : "알 수 없는 오류");
|
|
85
|
+
} finally {
|
|
86
|
+
setLoading(false);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<>
|
|
92
|
+
<Button
|
|
93
|
+
variant="contained"
|
|
94
|
+
onClick={handleClick}
|
|
95
|
+
loading={loading}
|
|
96
|
+
loadingPosition="start"
|
|
97
|
+
startIcon={<SaveIcon />}
|
|
98
|
+
aria-busy={loading}
|
|
99
|
+
>
|
|
100
|
+
{loading ? "저장 중..." : "저장하기"}
|
|
101
|
+
</Button>
|
|
102
|
+
{error && (
|
|
103
|
+
<Alert severity="error" sx={{ mt: 1 }} role="alert">
|
|
104
|
+
{error}
|
|
105
|
+
</Alert>
|
|
106
|
+
)}
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Don't
|
|
113
|
+
|
|
114
|
+
- Don't disable the button without showing a loading indicator — users think it's broken.
|
|
115
|
+
- Don't move focus away during loading; users may want to cancel (Esc, navigate back).
|
|
116
|
+
- Don't reset the form on click before the async resolves — if it fails, you've lost the user's input.
|
|
117
|
+
- Don't show "Loading..." with no spinner. Spinner is the universal "we're working" signal.
|
|
118
|
+
- Don't allow double-click during loading. Set `loading={true}` synchronously before the async starts.
|
|
119
|
+
|
|
120
|
+
## Cross-reference
|
|
121
|
+
|
|
122
|
+
- [`knowledge/components/INDEX.md`](../knowledge/components/INDEX.md)
|
|
123
|
+
- [`component-button.md`](component-button.md)
|
|
124
|
+
- [`knowledge/patterns/empty-states.md`](../knowledge/patterns/empty-states.md) — what to show during long loads
|
|
125
|
+
- [`knowledge/a11y/keyboard-and-focus.md`](../knowledge/a11y/keyboard-and-focus.md)
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<!-- hand-written -->
|
|
2
|
+
# `LoadingSequence` (custom — splash + first-screen coordination) — spec
|
|
3
|
+
|
|
4
|
+
> Coordinates the cold-launch experience: brand splash → biometric / auth gate (optional) → first screen reveal. Wraps the moments after process start and before the user sees real content. Pairs with [`knowledge/motion/app-loading-sequences.md`](../knowledge/motion/app-loading-sequences.md).
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Make cold launch feel **intentional** instead of like a stall:
|
|
9
|
+
|
|
10
|
+
1. Show brand-led splash that matches the first screen's background (no flash).
|
|
11
|
+
2. Optionally chain a biometric gate (Korean fintech default).
|
|
12
|
+
3. Cross-fade into the real app content as soon as it's ready.
|
|
13
|
+
4. Respect `prefers-reduced-motion`.
|
|
14
|
+
|
|
15
|
+
Without this: blank screen → flash of unstyled content → spinner → app. Reads as broken.
|
|
16
|
+
|
|
17
|
+
## Anatomy
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
┌──────────────────────────────────────┐
|
|
21
|
+
│ │
|
|
22
|
+
│ │
|
|
23
|
+
│ │
|
|
24
|
+
│ [logo / wordmark] │ ← splash (brand bg)
|
|
25
|
+
│ │
|
|
26
|
+
│ │
|
|
27
|
+
│ │
|
|
28
|
+
└──────────────────────────────────────┘
|
|
29
|
+
↓ (optional)
|
|
30
|
+
┌──────────────────────────────────────┐
|
|
31
|
+
│ 🔒 Face ID │
|
|
32
|
+
│ 본인 확인이 필요합니다 │ ← biometric gate
|
|
33
|
+
│ [지문으로 잠금 해제] │
|
|
34
|
+
└──────────────────────────────────────┘
|
|
35
|
+
↓
|
|
36
|
+
┌──────────────────────────────────────┐
|
|
37
|
+
│ Header │
|
|
38
|
+
│ [content cross-fades in] │ ← first screen
|
|
39
|
+
│ ... │
|
|
40
|
+
└──────────────────────────────────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<LoadingSequence
|
|
47
|
+
brand={{ logo: <Logo />, backgroundColor: "var(--color-brand-bg)" }}
|
|
48
|
+
minDurationMs={600}
|
|
49
|
+
gate={biometricEnabled ? <BiometricGate onUnlock={resolve} /> : null}
|
|
50
|
+
onReady={() => setAppReady(true)}
|
|
51
|
+
>
|
|
52
|
+
<App />
|
|
53
|
+
</LoadingSequence>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `brand` | `{ logo: ReactNode; backgroundColor: string }` | — | Splash content + bg color (must match first screen) |
|
|
59
|
+
| `minDurationMs` | `number` | `600` | Floor on splash duration to avoid flash if app boots in 80ms |
|
|
60
|
+
| `gate` | `ReactNode \| null` | `null` | Optional auth gate (biometric, PIN). When present, splash → gate → content. |
|
|
61
|
+
| `onReady` | `() => void` | — | Fires when content is fully visible |
|
|
62
|
+
| `children` | `ReactNode` | — | Real app content; rendered behind splash, revealed on transition |
|
|
63
|
+
| `disableAnimation` | `boolean` | auto via `prefers-reduced-motion` | Skip cross-fade |
|
|
64
|
+
|
|
65
|
+
## Behavior
|
|
66
|
+
|
|
67
|
+
### State machine
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
┌─────────┐
|
|
71
|
+
│ booting │ ← process started; splash fully opaque
|
|
72
|
+
└────┬────┘
|
|
73
|
+
│ minDurationMs reached AND app ready
|
|
74
|
+
▼
|
|
75
|
+
┌─────────┐
|
|
76
|
+
│ gate? │ ← if `gate` prop, render it (splash bg stays)
|
|
77
|
+
└────┬────┘
|
|
78
|
+
│ gate resolves (or no gate)
|
|
79
|
+
▼
|
|
80
|
+
┌─────────────┐
|
|
81
|
+
│ revealing │ ← 200ms cross-fade splash → content
|
|
82
|
+
└──────┬──────┘
|
|
83
|
+
│
|
|
84
|
+
▼
|
|
85
|
+
┌────────┐
|
|
86
|
+
│ ready │ ← onReady fires; LoadingSequence unmounts splash
|
|
87
|
+
└────────┘
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Timing budget
|
|
91
|
+
|
|
92
|
+
| Phase | Duration | Notes |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| Splash visible | `max(minDurationMs, timeToAppReady)` | Floor prevents flash; ceiling is real boot time |
|
|
95
|
+
| Gate (if present) | User-driven | Typically 1–3s for biometric; can be longer |
|
|
96
|
+
| Cross-fade | 200ms | Cross-fade splash out + content in simultaneously |
|
|
97
|
+
| Total cold launch | 0.6–3s typical | Korean fintech: 1.5–3s with biometric |
|
|
98
|
+
|
|
99
|
+
### Cross-fade
|
|
100
|
+
|
|
101
|
+
```css
|
|
102
|
+
.splash {
|
|
103
|
+
opacity: 1;
|
|
104
|
+
transition: opacity var(--motion-medium) var(--ease-out);
|
|
105
|
+
}
|
|
106
|
+
.splash[data-state="revealing"] { opacity: 0; }
|
|
107
|
+
|
|
108
|
+
.content {
|
|
109
|
+
opacity: 0;
|
|
110
|
+
transition: opacity var(--motion-medium) var(--ease-out);
|
|
111
|
+
}
|
|
112
|
+
.content[data-state="revealing"],
|
|
113
|
+
.content[data-state="ready"] { opacity: 1; }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Both run together — true cross-fade, not sequential.
|
|
117
|
+
|
|
118
|
+
## States
|
|
119
|
+
|
|
120
|
+
| State | Splash | Gate | Content |
|
|
121
|
+
| --- | --- | --- | --- |
|
|
122
|
+
| `booting` | Visible (opacity 1) | hidden | Hidden behind splash |
|
|
123
|
+
| `gating` | Hidden | Visible | Behind gate |
|
|
124
|
+
| `revealing` | Fading out | Hidden | Fading in |
|
|
125
|
+
| `ready` | Unmounted | Unmounted | Visible |
|
|
126
|
+
|
|
127
|
+
## Tokens consumed
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
--color-brand-bg (splash + first screen background — MUST match)
|
|
131
|
+
--color-brand-fg (logo color on splash)
|
|
132
|
+
--motion-medium (200ms cross-fade)
|
|
133
|
+
--ease-out (cross-fade easing)
|
|
134
|
+
--z-splash (above all app chrome)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Korean fintech convention
|
|
138
|
+
|
|
139
|
+
Toss / KakaoBank pattern:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<LoadingSequence
|
|
143
|
+
brand={{ logo: <TossLogo />, backgroundColor: "#0064FF" }}
|
|
144
|
+
minDurationMs={400}
|
|
145
|
+
gate={
|
|
146
|
+
user.biometricEnrolled ? (
|
|
147
|
+
<BiometricGate
|
|
148
|
+
prompt="Face ID로 잠금 해제"
|
|
149
|
+
fallback={<PINGate />}
|
|
150
|
+
/>
|
|
151
|
+
) : null
|
|
152
|
+
}
|
|
153
|
+
>
|
|
154
|
+
<Dashboard />
|
|
155
|
+
</LoadingSequence>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- Brand color splash (full bleed)
|
|
159
|
+
- Biometric gate appears almost immediately (within minDurationMs floor)
|
|
160
|
+
- Gate dominates the cold-launch experience; actual app boot hides underneath
|
|
161
|
+
- After unlock: 200ms cross-fade to dashboard
|
|
162
|
+
|
|
163
|
+
See [`examples/component-biometric-gate.md`](component-biometric-gate.md).
|
|
164
|
+
|
|
165
|
+
## Accessibility
|
|
166
|
+
|
|
167
|
+
- Splash: `role="presentation"` (decorative). Logo gets `alt="[brand name]"` if `<img>`, or `aria-label` if `<svg>`.
|
|
168
|
+
- Gate: standard a11y from gate component.
|
|
169
|
+
- During splash: focus is trapped on splash root with `tabindex="-1"` so screen readers don't read app behind it prematurely.
|
|
170
|
+
- Content reveal: announce via `<div role="status" aria-live="polite">앱 준비 완료</div>` only if minDurationMs is hit and reveal is delayed beyond 1s. Otherwise silent.
|
|
171
|
+
- `prefers-reduced-motion`: skip cross-fade; instant swap. Splash still shows for `minDurationMs`.
|
|
172
|
+
|
|
173
|
+
## Implementation hints
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
function LoadingSequence({ brand, minDurationMs = 600, gate, onReady, children }: Props) {
|
|
177
|
+
const [phase, setPhase] = useState<"booting" | "gating" | "revealing" | "ready">("booting");
|
|
178
|
+
const mountedAt = useRef(Date.now());
|
|
179
|
+
const [appReady, setAppReady] = useState(false);
|
|
180
|
+
const reduced = usePrefersReducedMotion();
|
|
181
|
+
|
|
182
|
+
// Wait for app content to render at least once
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
requestIdleCallback(() => setAppReady(true));
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
// Transition booting → gating/revealing once minDuration + appReady
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (!appReady) return;
|
|
190
|
+
const elapsed = Date.now() - mountedAt.current;
|
|
191
|
+
const wait = Math.max(0, minDurationMs - elapsed);
|
|
192
|
+
const id = setTimeout(() => {
|
|
193
|
+
setPhase(gate ? "gating" : "revealing");
|
|
194
|
+
}, wait);
|
|
195
|
+
return () => clearTimeout(id);
|
|
196
|
+
}, [appReady, gate, minDurationMs]);
|
|
197
|
+
|
|
198
|
+
// Reveal → ready
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (phase !== "revealing") return;
|
|
201
|
+
const id = setTimeout(() => {
|
|
202
|
+
setPhase("ready");
|
|
203
|
+
onReady?.();
|
|
204
|
+
}, reduced ? 0 : 200);
|
|
205
|
+
return () => clearTimeout(id);
|
|
206
|
+
}, [phase, onReady, reduced]);
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<>
|
|
210
|
+
{phase !== "ready" && (
|
|
211
|
+
<div
|
|
212
|
+
className="splash"
|
|
213
|
+
data-state={phase}
|
|
214
|
+
style={{ background: brand.backgroundColor }}
|
|
215
|
+
role="presentation"
|
|
216
|
+
>
|
|
217
|
+
{phase === "gating" ? gate : brand.logo}
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
220
|
+
<div className="content" data-state={phase}>
|
|
221
|
+
{children}
|
|
222
|
+
</div>
|
|
223
|
+
</>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Edge cases
|
|
229
|
+
|
|
230
|
+
- **App boots faster than `minDurationMs`**: splash still shown until floor. Prevents 80ms flash.
|
|
231
|
+
- **App boots slower than splash floor**: splash stays until app is ready. No premature reveal.
|
|
232
|
+
- **Gate fails (biometric denied 3×)**: bubble up to gate's `onFallback`; LoadingSequence stays in `gating` until resolved.
|
|
233
|
+
- **App backgrounded during splash**: pause timers via `visibilitychange` listener; resume on focus.
|
|
234
|
+
- **Hot reload in dev**: `LoadingSequence` should NOT re-trigger on every HMR — only on real cold mount.
|
|
235
|
+
- **Server-side rendering**: render content directly without splash on SSR; client-side mount triggers splash only if `document.readyState !== "complete"` at mount.
|
|
236
|
+
|
|
237
|
+
## Don't
|
|
238
|
+
|
|
239
|
+
- Don't put a spinner on the splash. Static logo only. Looping spinner reads as stuck.
|
|
240
|
+
- Don't animate the logo with bounce / pulse on every cold launch — once the user has seen it, animation feels precious.
|
|
241
|
+
- Don't mismatch `brand.backgroundColor` with the first screen's bg. The whole point is seamless transition.
|
|
242
|
+
- Don't keep splash up longer than 3 seconds without a gate. User thinks the app froze.
|
|
243
|
+
- Don't tie `onReady` to data fetching — fires when first paint completes, not when feed is loaded.
|
|
244
|
+
|
|
245
|
+
## References
|
|
246
|
+
|
|
247
|
+
No upstream component matches. This composes splash + gate + cross-fade in a single shell.
|
|
248
|
+
|
|
249
|
+
## Cross-reference
|
|
250
|
+
|
|
251
|
+
- [`knowledge/motion/app-loading-sequences.md`](../knowledge/motion/app-loading-sequences.md) — strategy
|
|
252
|
+
- [`knowledge/motion/principles.md`](../knowledge/motion/principles.md) — duration / easing
|
|
253
|
+
- [`examples/component-biometric-gate.md`](component-biometric-gate.md) — gate component
|
|
254
|
+
- [`examples/component-page-transition.md`](component-page-transition.md) — route-level transitions (after splash)
|