@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,145 @@
|
|
|
1
|
+
# `OutlinedInput` — spec
|
|
2
|
+
|
|
3
|
+
> Synthesized from MUI `OutlinedInput`. The outlined-border input variant — clearer affordance than filled or underline. Default for most product UIs.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- Default form input for product UIs.
|
|
8
|
+
- When users need a clear "this is a field" affordance (vs filled which can blend into card backgrounds).
|
|
9
|
+
- For dense forms with many fields, consider `FilledInput` (less visual noise) — but accessibility default is outlined.
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
┌── Label ─────────────┐
|
|
15
|
+
│ Input value │ ← label notches the border on focus
|
|
16
|
+
└──────────────────────┘
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## API
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<FormControl variant="outlined" fullWidth>
|
|
23
|
+
<FormLabel htmlFor="email">이메일</FormLabel>
|
|
24
|
+
<OutlinedInput
|
|
25
|
+
id="email"
|
|
26
|
+
type="email"
|
|
27
|
+
value={email}
|
|
28
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
29
|
+
placeholder="hong@example.com"
|
|
30
|
+
startAdornment={<InputAdornment position="start"><EmailIcon /></InputAdornment>}
|
|
31
|
+
/>
|
|
32
|
+
</FormControl>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Default | Description |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| `value` / `defaultValue` | `string` | — | Controlled / uncontrolled |
|
|
38
|
+
| `onChange` | `(e) => void` | — | Change handler |
|
|
39
|
+
| `type` | `string` | `'text'` | HTML input type |
|
|
40
|
+
| `placeholder` | `string` | — | Placeholder (NOT a substitute for label) |
|
|
41
|
+
| `startAdornment` | `ReactNode` | — | Leading content (icon, prefix) |
|
|
42
|
+
| `endAdornment` | `ReactNode` | — | Trailing content (clear button, unit) |
|
|
43
|
+
| `multiline` | `boolean` | `false` | Render as textarea |
|
|
44
|
+
| `rows` | `number` | — | Multi-line height |
|
|
45
|
+
| `maxRows` / `minRows` | `number` | — | Auto-resize bounds |
|
|
46
|
+
| `disabled` | `boolean` | `false` | |
|
|
47
|
+
| `error` | `boolean` | inherited | From parent FormControl |
|
|
48
|
+
| `fullWidth` | `boolean` | inherited | |
|
|
49
|
+
| `inputProps` | `InputHTMLAttributes` | — | Pass-through to native input (autoComplete, inputMode, pattern) |
|
|
50
|
+
| `inputRef` | `Ref` | — | Ref to native input |
|
|
51
|
+
| `notched` | `boolean` | derived | Notch label-cutout (auto when label is visible) |
|
|
52
|
+
|
|
53
|
+
## States
|
|
54
|
+
|
|
55
|
+
| State | Visual |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| Default | 1px border-default |
|
|
58
|
+
| Hover | border-strong |
|
|
59
|
+
| Focus | 2px brand border, label fg-primary |
|
|
60
|
+
| Error | 2px error border, label fg-error |
|
|
61
|
+
| Disabled | muted border, muted fg |
|
|
62
|
+
| Read-only | same as default but no caret |
|
|
63
|
+
|
|
64
|
+
## Tokens consumed
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
--input-border-default
|
|
68
|
+
--input-border-hover
|
|
69
|
+
--input-border-focus /* brand */
|
|
70
|
+
--input-border-error
|
|
71
|
+
--input-bg /* surface */
|
|
72
|
+
--input-radius
|
|
73
|
+
--input-padding-x
|
|
74
|
+
--input-min-height-40 /* touch-friendly */
|
|
75
|
+
--input-min-height-32 /* small */
|
|
76
|
+
--font-size-body
|
|
77
|
+
--space-sm /* gap to adornment */
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Accessibility
|
|
81
|
+
|
|
82
|
+
- Pair with `FormLabel` via `id` ↔ `htmlFor`.
|
|
83
|
+
- For required: `aria-required="true"` (in addition to FormControl `required` for the visual asterisk).
|
|
84
|
+
- For error: `aria-invalid="true"` + `aria-describedby` matching FormHelperText `id`.
|
|
85
|
+
- Touch target: 40px min (default `medium` size). For dense forms `small` (32px) — desktop only.
|
|
86
|
+
- `inputProps={{ autoComplete: '...' }}` — set semantically (`email`, `name`, `tel`, `address-line1`, etc.) so password managers and autofill work.
|
|
87
|
+
|
|
88
|
+
## Edge cases
|
|
89
|
+
|
|
90
|
+
- **Korean IME composition** — `onChange` fires on each keystroke including IME composition characters. For "search-as-you-type" scenarios that should wait for committed input, use `onCompositionEnd`. Cite [`knowledge/i18n/korean-typography.md`](../knowledge/i18n/korean-typography.md).
|
|
91
|
+
- **Long values** — input scrolls horizontally inside the field. For multi-line, set `multiline maxRows={4}`.
|
|
92
|
+
- **Clear button** — not built-in. Add via `endAdornment` with a small IconButton; show only when `value.length > 0`.
|
|
93
|
+
- **Password visibility toggle** — same pattern: `endAdornment` IconButton that toggles `type` between `password` and `text`.
|
|
94
|
+
|
|
95
|
+
## Code example
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
function PasswordField({ value, onChange, error }) {
|
|
99
|
+
const [show, setShow] = useState(false);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<FormControl error={!!error} fullWidth>
|
|
103
|
+
<FormLabel htmlFor="pw">비밀번호</FormLabel>
|
|
104
|
+
<OutlinedInput
|
|
105
|
+
id="pw"
|
|
106
|
+
type={show ? 'text' : 'password'}
|
|
107
|
+
value={value}
|
|
108
|
+
onChange={(e) => onChange(e.target.value)}
|
|
109
|
+
inputProps={{ autoComplete: 'current-password', minLength: 8 }}
|
|
110
|
+
endAdornment={
|
|
111
|
+
<InputAdornment position="end">
|
|
112
|
+
<IconButton
|
|
113
|
+
aria-label={show ? '비밀번호 숨기기' : '비밀번호 보기'}
|
|
114
|
+
onClick={() => setShow(!show)}
|
|
115
|
+
edge="end"
|
|
116
|
+
>
|
|
117
|
+
{show ? <VisibilityOffIcon /> : <VisibilityIcon />}
|
|
118
|
+
</IconButton>
|
|
119
|
+
</InputAdornment>
|
|
120
|
+
}
|
|
121
|
+
/>
|
|
122
|
+
<FormHelperText>{error ?? '8자 이상, 영문 + 숫자 + 특수문자'}</FormHelperText>
|
|
123
|
+
</FormControl>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Don't
|
|
129
|
+
|
|
130
|
+
- Don't use `placeholder` as the only label — disappears on focus, accessibility issue.
|
|
131
|
+
- Don't omit `autoComplete` — autofill won't work, slowing user-friction in onboarding.
|
|
132
|
+
- Don't toggle `disabled` based on transient state without restoring focus on re-enable.
|
|
133
|
+
|
|
134
|
+
## References
|
|
135
|
+
|
|
136
|
+
- MUI: [`OutlinedInput`](../refs/mui/packages/mui-material/src/OutlinedInput/) + [`InputBase`](../refs/mui/packages/mui-material/src/InputBase/)
|
|
137
|
+
|
|
138
|
+
## Cross-reference
|
|
139
|
+
|
|
140
|
+
- [`component-input.md`](component-input.md)
|
|
141
|
+
- [`component-input-base.md`](component-input-base.md)
|
|
142
|
+
- [`component-filled-input.md`](component-filled-input.md)
|
|
143
|
+
- [`component-input-adornment.md`](component-input-adornment.md)
|
|
144
|
+
- [`component-form-control.md`](component-form-control.md)
|
|
145
|
+
- [`knowledge/patterns/form-design.md`](../knowledge/patterns/form-design.md)
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<!-- hand-written -->
|
|
2
|
+
# `PageTransition` (custom — route-level animation wrapper) — spec
|
|
3
|
+
|
|
4
|
+
> Wraps the route outlet of an SPA to animate transitions between pages. One component handles cross-fade, slide-in, and shared-element variants. Pairs with [`knowledge/motion/app-loading-sequences.md`](../knowledge/motion/app-loading-sequences.md) and [`knowledge/motion/choreography-depth.md`](../knowledge/motion/choreography-depth.md).
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
- Smooth transitions between routes without per-page boilerplate.
|
|
9
|
+
- One opinion per app (cross-fade vs slide vs hero), not per-screen ad-hoc choices.
|
|
10
|
+
- Reduced-motion safe.
|
|
11
|
+
- Compatible with React Router, TanStack Router, Next.js App Router.
|
|
12
|
+
|
|
13
|
+
Without this: route change is an instant DOM swap. Reads as a page reload, breaks spatial sense.
|
|
14
|
+
|
|
15
|
+
## Anatomy
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
<RootLayout>
|
|
19
|
+
<Header />
|
|
20
|
+
<PageTransition variant="fade">
|
|
21
|
+
{/* current route */}
|
|
22
|
+
<Outlet />
|
|
23
|
+
</PageTransition>
|
|
24
|
+
<Footer />
|
|
25
|
+
</RootLayout>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or for slide-style native iOS feel:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
<PageTransition variant="slide" direction={navDirection}>
|
|
32
|
+
<Outlet />
|
|
33
|
+
</PageTransition>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<PageTransition
|
|
40
|
+
variant="fade" // "fade" | "slide" | "hero"
|
|
41
|
+
routeKey={pathname} // change → trigger transition
|
|
42
|
+
direction="forward" // for slide variant
|
|
43
|
+
duration={200}
|
|
44
|
+
reducedMotionFallback="instant" // "instant" | "fade-only"
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</PageTransition>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Prop | Type | Default | Description |
|
|
51
|
+
| --- | --- | --- | --- |
|
|
52
|
+
| `variant` | `"fade" \| "slide" \| "hero"` | `"fade"` | Transition style |
|
|
53
|
+
| `routeKey` | `string` | — | Unique key per route (typically pathname); change = transition |
|
|
54
|
+
| `direction` | `"forward" \| "back"` | `"forward"` | Slide direction (slide variant only) |
|
|
55
|
+
| `duration` | `number` | `200` | Ms; capped at 300 even if higher passed |
|
|
56
|
+
| `easing` | `string` | `"ease-out"` | CSS easing (typically `var(--ease-out)`) |
|
|
57
|
+
| `reducedMotionFallback` | `"instant" \| "fade-only"` | `"fade-only"` | What to do when user prefers reduced motion |
|
|
58
|
+
| `onTransitionEnd` | `() => void` | — | Fires after exit + enter complete |
|
|
59
|
+
| `children` | `ReactNode` | — | Current route content |
|
|
60
|
+
|
|
61
|
+
## Variants
|
|
62
|
+
|
|
63
|
+
### `fade` (default)
|
|
64
|
+
|
|
65
|
+
200ms cross-fade. Calm, content-led. Default for content apps (docs, dashboards, forms).
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
.page--enter { opacity: 0; transform: translateY(8px); }
|
|
69
|
+
.page--enter-active { opacity: 1; transform: translateY(0); transition: 200ms ease-out; }
|
|
70
|
+
.page--exit { opacity: 1; }
|
|
71
|
+
.page--exit-active { opacity: 0; transition: 150ms ease-out; }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `slide` (native iOS feel)
|
|
75
|
+
|
|
76
|
+
New page slides in from right (forward) or left (back). Old page slides off the opposite side.
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
forward: [old]────► offscreen left [new]◄──── from right
|
|
80
|
+
back: [old]────► offscreen right [new]◄──── from left
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For slide direction tracking, expose it via router state (e.g. `useNavigationDirection()` hook returning `"forward" | "back"`).
|
|
84
|
+
|
|
85
|
+
### `hero` (shared element)
|
|
86
|
+
|
|
87
|
+
Uses View Transitions API or Framer Motion `layoutId`. An element on outgoing page morphs into an element on incoming page.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
// On screen A:
|
|
91
|
+
<motion.img layoutId="user-avatar-42" src={avatar} />
|
|
92
|
+
|
|
93
|
+
// On screen B (detail view):
|
|
94
|
+
<motion.img layoutId="user-avatar-42" src={avatar} />
|
|
95
|
+
// Same id = morph between positions
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For browser-native (Chrome / Edge / Safari TP):
|
|
99
|
+
|
|
100
|
+
```css
|
|
101
|
+
.user-avatar { view-transition-name: user-avatar-42; }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
document.startViewTransition(() => {
|
|
106
|
+
navigate("/users/42");
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## States
|
|
111
|
+
|
|
112
|
+
| State | Visual |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `idle` | Current route fully visible |
|
|
115
|
+
| `exiting` | Outgoing page transitioning out (opacity fade or x-translate) |
|
|
116
|
+
| `entering` | Incoming page transitioning in |
|
|
117
|
+
| `done` | New route fully visible; `onTransitionEnd` fires |
|
|
118
|
+
|
|
119
|
+
`exiting` and `entering` overlap during cross-fade; for slide they sequence.
|
|
120
|
+
|
|
121
|
+
## Tokens consumed
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
--motion-fast (150ms exit)
|
|
125
|
+
--motion-medium (200ms enter)
|
|
126
|
+
--motion-slow (300ms slide)
|
|
127
|
+
--ease-out (default)
|
|
128
|
+
--ease-in-out (slide)
|
|
129
|
+
--space-sm (translate-Y for fade)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Behavior rules
|
|
133
|
+
|
|
134
|
+
- **Don't transition on initial mount** — first render is not a navigation. Watch for `routeKey` change after mount only.
|
|
135
|
+
- **Don't transition between hash changes** on same path (`/page#a` → `/page#b`).
|
|
136
|
+
- **Cancel previous transition** if route changes mid-transition (rapid back-tap during animation). Snap to new route.
|
|
137
|
+
- **Maintain scroll position**: page-transition wrapper does NOT manage scroll; router does. But the wrapper must not interfere (e.g., `overflow: hidden` on root breaks scroll-restoration).
|
|
138
|
+
|
|
139
|
+
## Reduced motion
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
const reduced = useReducedMotion();
|
|
143
|
+
const effectiveVariant = reduced
|
|
144
|
+
? (reducedMotionFallback === "instant" ? null : "fade")
|
|
145
|
+
: variant;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Setting | Behavior |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `instant` | No transition; instant DOM swap |
|
|
151
|
+
| `fade-only` (default) | Cross-fade at duration 100ms regardless of variant; no translate / slide |
|
|
152
|
+
|
|
153
|
+
Slide and hero variants both downgrade to fade-only — translation triggers vestibular issues.
|
|
154
|
+
|
|
155
|
+
## Implementation — Framer Motion
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
159
|
+
|
|
160
|
+
function PageTransition({ children, routeKey, variant = "fade", duration = 200 }: Props) {
|
|
161
|
+
const reduced = useReducedMotion();
|
|
162
|
+
|
|
163
|
+
const variants = reduced
|
|
164
|
+
? { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }
|
|
165
|
+
: variantsByName[variant];
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<AnimatePresence mode="wait" initial={false}>
|
|
169
|
+
<motion.div
|
|
170
|
+
key={routeKey}
|
|
171
|
+
initial={variants.initial}
|
|
172
|
+
animate={variants.animate}
|
|
173
|
+
exit={variants.exit}
|
|
174
|
+
transition={{ duration: duration / 1000, ease: "easeOut" }}
|
|
175
|
+
>
|
|
176
|
+
{children}
|
|
177
|
+
</motion.div>
|
|
178
|
+
</AnimatePresence>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const variantsByName = {
|
|
183
|
+
fade: {
|
|
184
|
+
initial: { opacity: 0, y: 8 },
|
|
185
|
+
animate: { opacity: 1, y: 0 },
|
|
186
|
+
exit: { opacity: 0, y: -8 },
|
|
187
|
+
},
|
|
188
|
+
slide: {
|
|
189
|
+
initial: { opacity: 0, x: 24 },
|
|
190
|
+
animate: { opacity: 1, x: 0 },
|
|
191
|
+
exit: { opacity: 0, x: -24 },
|
|
192
|
+
},
|
|
193
|
+
// hero handled via layoutId on individual elements; this wrapper just cross-fades the rest
|
|
194
|
+
hero: {
|
|
195
|
+
initial: { opacity: 0 },
|
|
196
|
+
animate: { opacity: 1 },
|
|
197
|
+
exit: { opacity: 0 },
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Implementation — View Transitions API (modern web)
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
function PageTransition({ children, routeKey }: Props) {
|
|
206
|
+
const previousKey = useRef(routeKey);
|
|
207
|
+
|
|
208
|
+
useLayoutEffect(() => {
|
|
209
|
+
if (previousKey.current === routeKey) return;
|
|
210
|
+
if (!document.startViewTransition) {
|
|
211
|
+
previousKey.current = routeKey;
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
document.startViewTransition(() => {
|
|
215
|
+
previousKey.current = routeKey;
|
|
216
|
+
// React already updated; this just signals the browser to capture before/after
|
|
217
|
+
});
|
|
218
|
+
}, [routeKey]);
|
|
219
|
+
|
|
220
|
+
return <>{children}</>;
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```css
|
|
225
|
+
::view-transition-old(root),
|
|
226
|
+
::view-transition-new(root) {
|
|
227
|
+
animation-duration: 200ms;
|
|
228
|
+
animation-timing-function: var(--ease-out);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@media (prefers-reduced-motion: reduce) {
|
|
232
|
+
::view-transition-old(root),
|
|
233
|
+
::view-transition-new(root) {
|
|
234
|
+
animation: none;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Korean app convention
|
|
240
|
+
|
|
241
|
+
- **Mobile webview / hybrid app**: prefer `slide` variant — matches native iOS / Android feel users expect from Toss / KakaoBank.
|
|
242
|
+
- **Desktop web (toss.im, naver.com)**: `fade` is standard.
|
|
243
|
+
- **Marketing landing pages**: skip PageTransition entirely; full reload feel is fine.
|
|
244
|
+
|
|
245
|
+
## Accessibility
|
|
246
|
+
|
|
247
|
+
- `aria-live="polite"` on the page region so screen readers announce route change. (Don't use `assertive`; that interrupts.)
|
|
248
|
+
- After transition: focus moves to `<h1>` of new page (router-level concern, not PageTransition's).
|
|
249
|
+
- `prefers-reduced-motion`: enforce via the variant downgrade above.
|
|
250
|
+
- For long transitions (>250ms), risk of focus loss; keep transitions short.
|
|
251
|
+
|
|
252
|
+
## Edge cases
|
|
253
|
+
|
|
254
|
+
- **Modal-on-route**: don't apply page transition when the route change is just opening a modal (use `<Outlet />` only on actual page changes; modals stack above).
|
|
255
|
+
- **Tab-style navigation**: tabs that swap content within the same route should NOT trigger PageTransition. Use a separate `<Tabs>` animation.
|
|
256
|
+
- **Programmatic navigation during transition**: cancel current transition; new one wins. Framer Motion's `mode="wait"` handles this.
|
|
257
|
+
- **Server-rendered first paint**: first hydration must not trigger transition. Use `initial={false}` (Framer Motion) or check `previousKey.current` (View Transitions).
|
|
258
|
+
- **Memory of scroll position**: each route should restore its scroll. PageTransition itself doesn't scroll; do this via router scroll-restoration.
|
|
259
|
+
|
|
260
|
+
## Don't
|
|
261
|
+
|
|
262
|
+
- Don't use slide variant on desktop wide screens — large translation feels heavy.
|
|
263
|
+
- Don't transition every route — landing → app, login → dashboard, payment success — sometimes reset feels right.
|
|
264
|
+
- Don't apply PageTransition twice (e.g., wrapping both layout + page). Pick one level.
|
|
265
|
+
- Don't run more than 300ms — feels slow on rapid-tap navigation.
|
|
266
|
+
- Don't animate `width`/`height` of the wrapper — causes layout thrash.
|
|
267
|
+
|
|
268
|
+
## References
|
|
269
|
+
|
|
270
|
+
Patterns drawn from:
|
|
271
|
+
- React Router v6 + Framer Motion AnimatePresence patterns
|
|
272
|
+
- Next.js 14 App Router + View Transitions API
|
|
273
|
+
- React Native Stack Navigator transition presets
|
|
274
|
+
|
|
275
|
+
## Cross-reference
|
|
276
|
+
|
|
277
|
+
- [`knowledge/motion/app-loading-sequences.md`](../knowledge/motion/app-loading-sequences.md) — when to use which variant
|
|
278
|
+
- [`knowledge/motion/choreography-depth.md`](../knowledge/motion/choreography-depth.md) — shared-element / hero patterns
|
|
279
|
+
- [`knowledge/motion/principles.md`](../knowledge/motion/principles.md) — duration tiers
|
|
280
|
+
- [`examples/component-loading-sequence.md`](component-loading-sequence.md) — splash before first route
|
|
281
|
+
- [`examples/component-scroll-reveal.md`](component-scroll-reveal.md) — within-page motion
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# `Pagination` — spec
|
|
2
|
+
|
|
3
|
+
> Citing Ant Design `Pagination`, MUI `Pagination`, shadcn-ui `pagination`
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Lets users navigate large result sets in chunks. Three modes — pick the right one for context:
|
|
8
|
+
|
|
9
|
+
| Mode | Use |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| **Numbered pagination** | Tables, search results, content where users want to know position. |
|
|
12
|
+
| **Cursor-based "Load more"** | Feeds, social timelines, infinite content. |
|
|
13
|
+
| **Infinite scroll** | Engagement-driven feeds (Instagram, TikTok). See [knowledge/patterns/list-and-feed.md](../knowledge/patterns/list-and-feed.md). |
|
|
14
|
+
|
|
15
|
+
This spec covers numbered pagination + Load more. Infinite scroll is a list pattern, not a "Pagination component".
|
|
16
|
+
|
|
17
|
+
## Anatomy — numbered
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
[‹] [1] [2] [3] 4 [5] ... [12] [›] 총 142개
|
|
21
|
+
↑ active Page size: [20 ▾] (1–20 of 142)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
| Slot | Required | Notes |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| Previous (‹) | yes | Disabled on page 1 |
|
|
27
|
+
| Page numbers | yes | Subset shown with ellipsis for many pages |
|
|
28
|
+
| Next (›) | yes | Disabled on last page |
|
|
29
|
+
| Page size selector | optional | Common in tables, rare in mobile |
|
|
30
|
+
| Total count display | optional | "총 142개" / "1–20 of 142" |
|
|
31
|
+
| Jump to page | optional | Input field to type a page number (desktop only) |
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<Pagination
|
|
37
|
+
page={currentPage}
|
|
38
|
+
pageSize={20}
|
|
39
|
+
total={142}
|
|
40
|
+
onPageChange={setPage}
|
|
41
|
+
onPageSizeChange={setPageSize}
|
|
42
|
+
pageSizeOptions={[10, 20, 50, 100]}
|
|
43
|
+
showTotal
|
|
44
|
+
showJumper
|
|
45
|
+
siblingCount={1}
|
|
46
|
+
boundaryCount={1}
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Prop | Type | Default | Description |
|
|
51
|
+
| --- | --- | --- | --- |
|
|
52
|
+
| `page` | `number` | — | Current page (1-indexed) |
|
|
53
|
+
| `pageSize` | `number` | `20` | Items per page |
|
|
54
|
+
| `total` | `number` | — | Total item count (server-provided) |
|
|
55
|
+
| `onPageChange` | `(page: number) => void` | — | Fires on navigation |
|
|
56
|
+
| `onPageSizeChange` | `(size: number) => void` | — | Fires on size change |
|
|
57
|
+
| `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Options in size selector |
|
|
58
|
+
| `showSizeChanger` | `boolean` | `true` | Show page size dropdown |
|
|
59
|
+
| `showTotal` | `boolean` | `true` | Show total count |
|
|
60
|
+
| `showJumper` | `boolean` | `false` | Show "go to page X" input (desktop only) |
|
|
61
|
+
| `siblingCount` | `number` | `1` | Pages shown on each side of current (e.g., 4 [5] 6) |
|
|
62
|
+
| `boundaryCount` | `number` | `1` | Pages always shown at start/end (e.g., [1] ... [12]) |
|
|
63
|
+
| `simple` | `boolean` | `false` | Compact mobile mode: just `< 5/12 >` |
|
|
64
|
+
|
|
65
|
+
## Numbered display algorithm
|
|
66
|
+
|
|
67
|
+
For `total=142, pageSize=20, currentPage=5, siblingCount=1, boundaryCount=1`:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
[‹] [1] ... [4] [5] [6] ... [8] [›]
|
|
71
|
+
↑boundary ↑siblings ↑boundary
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Pages: `1, ..., 4, 5, 6, ..., 8`.
|
|
75
|
+
|
|
76
|
+
When near the start: `[1] [2] [3] [4] ... [8]`.
|
|
77
|
+
When near the end: `[1] ... [5] [6] [7] [8]`.
|
|
78
|
+
When few pages (≤ 7): show all without ellipsis.
|
|
79
|
+
|
|
80
|
+
## Variants
|
|
81
|
+
|
|
82
|
+
### `simple` mode (mobile)
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
[‹] 5 / 12 [›]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Just prev/next + position display. Avoids cramped numbered buttons on mobile.
|
|
89
|
+
|
|
90
|
+
### Compact (no size changer, no total)
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
[‹] [1] [2] [3] ... [12] [›]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
For minimal UIs where the count and page size are not user-relevant.
|
|
97
|
+
|
|
98
|
+
### Load more variant
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
... existing items ...
|
|
102
|
+
[Load more (122 remaining)]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Single button at the bottom of the list. Replaces numbered pagination for feed-like content.
|
|
106
|
+
|
|
107
|
+
## States
|
|
108
|
+
|
|
109
|
+
| State | Visual |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| Default page button | Border `--color-border-default`, text `--color-text-primary` |
|
|
112
|
+
| Hover | Border `--color-border-strong`, bg `--color-bg-subtle` |
|
|
113
|
+
| Active (current page) | Background `--color-primary-default`, text `--color-on-primary` |
|
|
114
|
+
| Disabled (prev on page 1, next on last) | Opacity 0.4, no events |
|
|
115
|
+
| Focus-visible | 2px ring |
|
|
116
|
+
|
|
117
|
+
## Sizes
|
|
118
|
+
|
|
119
|
+
| Size | Button height | Font |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `sm` | 28px | 13px |
|
|
122
|
+
| `md` (default) | 32px | 14px |
|
|
123
|
+
| `lg` | 40px | 16px |
|
|
124
|
+
|
|
125
|
+
## Tokens consumed
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
--color-bg-default
|
|
129
|
+
--color-bg-subtle (hover)
|
|
130
|
+
--color-primary-default (active page bg)
|
|
131
|
+
--color-on-primary (active page text)
|
|
132
|
+
--color-text-primary
|
|
133
|
+
--color-text-secondary (disabled)
|
|
134
|
+
--color-border-default
|
|
135
|
+
--color-border-strong
|
|
136
|
+
--color-focus-ring
|
|
137
|
+
--space-xs, --space-sm
|
|
138
|
+
--radius-md (button corners) or radius-full (pill style)
|
|
139
|
+
--font-size-sm
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Accessibility
|
|
143
|
+
|
|
144
|
+
- `<nav role="navigation" aria-label="Pagination">` wraps the controls.
|
|
145
|
+
- Each page button is a `<button>` (or `<a href="?page=N">` for crawlable pages).
|
|
146
|
+
- Current page: `aria-current="page"` on the button. Don't use `disabled` on the current page — the user might want to click it (no-op, but accessible).
|
|
147
|
+
- Previous/Next buttons:
|
|
148
|
+
- `aria-label="이전 페이지"` / `"다음 페이지"`
|
|
149
|
+
- `disabled` when at boundary
|
|
150
|
+
- Page size selector: standard Select with `aria-label="페이지당 항목 수"`.
|
|
151
|
+
- Total count: live region `aria-live="polite"` so screen reader announces when it changes (e.g., after filtering).
|
|
152
|
+
|
|
153
|
+
### Keyboard
|
|
154
|
+
|
|
155
|
+
| Key | Behavior |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `Tab` | Reach individual buttons in order |
|
|
158
|
+
| `Enter` / `Space` | Activate focused button |
|
|
159
|
+
| Arrow keys | NOT used — user tabs through |
|
|
160
|
+
|
|
161
|
+
## URL sync (highly recommended)
|
|
162
|
+
|
|
163
|
+
Pagination should sync to URL:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
/transactions?page=5&size=20
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Benefits:
|
|
170
|
+
- Browser back/forward works.
|
|
171
|
+
- Shareable links to specific pages.
|
|
172
|
+
- Refresh preserves position.
|
|
173
|
+
- SEO: search crawlers see all pages.
|
|
174
|
+
|
|
175
|
+
Implementation: read from `useSearchParams` in React Router / Next.js. Update query on `onPageChange`.
|
|
176
|
+
|
|
177
|
+
## Korean copy
|
|
178
|
+
|
|
179
|
+
| English | Korean |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| Previous | 이전 |
|
|
182
|
+
| Next | 다음 |
|
|
183
|
+
| First | 처음 |
|
|
184
|
+
| Last | 마지막 |
|
|
185
|
+
| Page X of Y | X 페이지 / 총 Y 페이지 |
|
|
186
|
+
| Go to page | 페이지로 이동 |
|
|
187
|
+
| 1–20 of 142 | 142개 중 1–20 |
|
|
188
|
+
| Items per page | 페이지당 항목 수 |
|
|
189
|
+
| Show N | N개씩 보기 |
|
|
190
|
+
|
|
191
|
+
## Edge cases
|
|
192
|
+
|
|
193
|
+
- **Only 1 page**: hide pagination entirely. Don't render an empty navigation.
|
|
194
|
+
- **Empty results**: render the empty state instead of page 1 with no items.
|
|
195
|
+
- **Invalid page in URL**: clamp to valid range (`?page=999` with 12 pages → page 12). Surface to user with a toast: "페이지 999는 존재하지 않아 마지막 페이지로 이동했습니다".
|
|
196
|
+
- **Page size change reset**: when user changes page size, reset to page 1 (otherwise, current page might no longer exist).
|
|
197
|
+
- **Filter change reset**: when filters change, reset to page 1.
|
|
198
|
+
- **Server returns total=null** (cursor pagination, unknown total): use Load More variant instead.
|
|
199
|
+
- **Very large totals (10,000+ pages)**: jumper input is essential. Numbered pagination becomes unwieldy past ~100 pages.
|
|
200
|
+
- **Fast-clicking through pages**: debounce 100ms or cancel previous request on new click. Don't blast 5 requests.
|
|
201
|
+
|
|
202
|
+
## Don't
|
|
203
|
+
|
|
204
|
+
- Don't show "Page 1 of 1" — hide the component.
|
|
205
|
+
- Don't disable the active page button. Use `aria-current` instead.
|
|
206
|
+
- Don't put pagination ABOVE the list. Always below (and optionally also above for very long pages, like > 50 items per page).
|
|
207
|
+
- Don't auto-scroll to top on page change. Let the user choose. (Exception: if pagination is far below the fold, auto-scroll **into view** is OK.)
|
|
208
|
+
- Don't combine numbered pagination + infinite scroll.
|
|
209
|
+
- Don't lose scroll position on page change for inline tables (where pagination is at the table footer).
|
|
210
|
+
|
|
211
|
+
## References
|
|
212
|
+
|
|
213
|
+
- Ant Design: [`refs/ant-design/components/pagination/`](../refs/ant-design/components/pagination/) — most exhaustive: `simple`, `total`, `showSizeChanger`, `showJumper`, `itemRender`, `disabled`, `responsive`. Common in admin tables.
|
|
214
|
+
- MUI: [`refs/mui/packages/mui-material/src/Pagination/`](../refs/mui/packages/mui-material/src/Pagination/) — `Pagination` + `PaginationItem`. Cleaner API, fewer features.
|
|
215
|
+
- shadcn-ui: [`refs/shadcn-ui/apps/v4/registry/new-york-v4/ui/pagination.tsx`](../refs/shadcn-ui/apps/v4/registry/new-york-v4/ui/pagination.tsx) — primitive only (`Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink`, `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis`). Compose for behavior. **No built-in pagination logic** — pair with TanStack Table or your own state.
|
|
216
|
+
|
|
217
|
+
API choices made:
|
|
218
|
+
- **`siblingCount` and `boundaryCount` exposed**: lets the consumer tune the numbered display algorithm without overriding rendering.
|
|
219
|
+
- **`simple` mode** for mobile: separate concept, not just CSS — different ARIA semantics (a single button group with status text vs. a list of page buttons).
|
|
220
|
+
- **`showJumper` opt-in, off by default**: useful for power users (admin, analytics), noise for consumers.
|
|
221
|
+
- **No `itemRender` prop** (Ant has it): too much surface area. If you need custom rendering, compose `PaginationItem` manually.
|
|
222
|
+
|
|
223
|
+
## Cross-reference
|
|
224
|
+
|
|
225
|
+
- [examples/component-table.md](component-table.md) — pagination at the table footer
|
|
226
|
+
- [knowledge/patterns/list-and-feed.md](../knowledge/patterns/list-and-feed.md) — Load More vs infinite scroll vs pagination
|