@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,279 @@
|
|
|
1
|
+
<!-- hand-written -->
|
|
2
|
+
# `LottiePlayer` (custom — designer-led After Effects animation in product UI) — spec
|
|
3
|
+
|
|
4
|
+
> Renders a Lottie JSON animation with sane defaults: lazy-load, pause when offscreen, respect reduced motion, fall back to a poster image. Pairs with [`knowledge/motion/motion-tools.md`](../knowledge/motion/motion-tools.md) and [`knowledge/motion/marketing-motion.md`](../knowledge/motion/marketing-motion.md).
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Designers create motion in After Effects → export with Bodymovin → engineer drops the JSON in via `LottiePlayer`. This component handles the **lifecycle** concerns engineers usually forget:
|
|
9
|
+
|
|
10
|
+
1. Lazy-load the JSON (don't ship a 200kB animation in the main bundle).
|
|
11
|
+
2. Pause when offscreen (battery + perf).
|
|
12
|
+
3. Provide a poster image for `prefers-reduced-motion`, slow connections, and SSR.
|
|
13
|
+
4. Standardize play / loop / once-only semantics across the app.
|
|
14
|
+
|
|
15
|
+
Without this: each Lottie usage hand-rolls these concerns, and at least one will skip them.
|
|
16
|
+
|
|
17
|
+
## Anatomy
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
┌──────────────────────────────────┐
|
|
21
|
+
│ │
|
|
22
|
+
│ [Lottie animation] │ ← canvas / SVG render
|
|
23
|
+
│ │
|
|
24
|
+
└──────────────────────────────────┘
|
|
25
|
+
↓ if reduced-motion or no JS
|
|
26
|
+
┌──────────────────────────────────┐
|
|
27
|
+
│ │
|
|
28
|
+
│ [Poster image — last frame] │ ← static fallback
|
|
29
|
+
│ │
|
|
30
|
+
└──────────────────────────────────┘
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<LottiePlayer
|
|
37
|
+
src="/animations/checkmark-success.json"
|
|
38
|
+
poster="/animations/checkmark-success.png"
|
|
39
|
+
mode="once"
|
|
40
|
+
onComplete={() => navigate("/done")}
|
|
41
|
+
width={240}
|
|
42
|
+
height={240}
|
|
43
|
+
ariaLabel="결제 완료"
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Description |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| `src` | `string \| object` | — | URL to JSON or imported JSON object |
|
|
50
|
+
| `poster` | `string` | — | Poster image URL for fallback (REQUIRED for animations >50kB or above-fold) |
|
|
51
|
+
| `mode` | `"once" \| "loop" \| "controlled"` | `"once"` | Play behavior |
|
|
52
|
+
| `autoplay` | `boolean` | `true` (loop & once); `false` (controlled) | Start playing on mount |
|
|
53
|
+
| `playing` | `boolean` | — | Controlled play state (when `mode="controlled"`) |
|
|
54
|
+
| `speed` | `number` | `1` | Playback speed multiplier |
|
|
55
|
+
| `direction` | `1 \| -1` | `1` | Forward or reverse |
|
|
56
|
+
| `loop` | `boolean \| number` | derived from mode | Override loop count |
|
|
57
|
+
| `pauseOffscreen` | `boolean` | `true` | Auto-pause when not in viewport |
|
|
58
|
+
| `width` | `number \| string` | — | Render dimensions |
|
|
59
|
+
| `height` | `number \| string` | — | Render dimensions |
|
|
60
|
+
| `ariaLabel` | `string` | — | REQUIRED — describes what the animation conveys |
|
|
61
|
+
| `onComplete` | `() => void` | — | Fires after `mode="once"` completes |
|
|
62
|
+
| `onLoop` | `() => void` | — | Fires each loop iteration |
|
|
63
|
+
| `renderer` | `"svg" \| "canvas"` | `"svg"` | SVG = sharper; canvas = better perf for complex |
|
|
64
|
+
| `disabled` | `boolean` | auto via `prefers-reduced-motion` | Show poster instead |
|
|
65
|
+
|
|
66
|
+
## Modes
|
|
67
|
+
|
|
68
|
+
### `once`
|
|
69
|
+
|
|
70
|
+
Plays through, fires `onComplete`, holds last frame. Used for success states, error states, milestones.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<LottiePlayer src={successAnim} poster={successPng} mode="once" onComplete={dismiss} />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `loop`
|
|
77
|
+
|
|
78
|
+
Plays continuously. Used for ambient brand moments, loading indicators with character.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<LottiePlayer src={loadingAnim} poster={loadingPng} mode="loop" pauseOffscreen />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `controlled`
|
|
85
|
+
|
|
86
|
+
Parent controls play / pause via `playing` prop. Used for scroll-triggered, hover-triggered, gesture-triggered.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
const [hovered, setHovered] = useState(false);
|
|
90
|
+
<div onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>
|
|
91
|
+
<LottiePlayer src={iconAnim} mode="controlled" playing={hovered} />
|
|
92
|
+
</div>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## States
|
|
96
|
+
|
|
97
|
+
| State | Visual | Why |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| Loading JSON | Poster image | First paint while ~50–150kB JSON streams in |
|
|
100
|
+
| Playing | Animation rendering | Normal |
|
|
101
|
+
| Paused (offscreen) | Last rendered frame held | Saves CPU + battery |
|
|
102
|
+
| Paused (controlled false) | Last rendered frame held | Parent decided |
|
|
103
|
+
| Reduced motion | Poster image, never animates | a11y |
|
|
104
|
+
| Error (JSON failed to load) | Poster image | Graceful degradation |
|
|
105
|
+
| No JS / SSR | Poster image | `<noscript>` |
|
|
106
|
+
|
|
107
|
+
## Tokens consumed
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
--motion-fast (poster → animation cross-fade once JSON loads)
|
|
111
|
+
--ease-out
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
LottiePlayer is mostly a wrapper around an external JSON; it does not consume color / typography tokens directly. The animation itself was authored in After Effects with brand colors baked in.
|
|
115
|
+
|
|
116
|
+
## Korean fintech use cases
|
|
117
|
+
|
|
118
|
+
| Scenario | Animation type | Mode |
|
|
119
|
+
| --- | --- | --- |
|
|
120
|
+
| 송금 완료 (transfer success) | Money flying / checkmark | `once` |
|
|
121
|
+
| 결제 실패 (payment failed) | Error pulse / X mark | `once` |
|
|
122
|
+
| 가입 환영 (signup welcome) | Brand mascot wave | `once` |
|
|
123
|
+
| 로딩 (loading with character) | Mascot bouncing | `loop` (paused after 5s if still loading) |
|
|
124
|
+
| 잔액 변화 (balance change) | Number counter | `controlled` (triggered by data update) |
|
|
125
|
+
|
|
126
|
+
For all of the above: provide a poster image showing the **end state** (checkmark, X, mascot final pose) so reduced-motion users still get the meaning.
|
|
127
|
+
|
|
128
|
+
## Accessibility
|
|
129
|
+
|
|
130
|
+
- `ariaLabel` is REQUIRED. Describe what the animation conveys, not what it visually does:
|
|
131
|
+
- ✓ "결제가 완료되었습니다"
|
|
132
|
+
- ✗ "체크마크가 그려지는 애니메이션"
|
|
133
|
+
- Wrap in `role="img"` (animation conveys meaning) OR `role="presentation"` (purely decorative + adjacent text already conveys meaning). Pick one consciously.
|
|
134
|
+
- For `mode="once"` success / error states: also fire `role="status"` text announcement adjacent. Don't rely on the animation alone.
|
|
135
|
+
- `prefers-reduced-motion: reduce` → show poster, never animate. Required.
|
|
136
|
+
- Don't auto-play sound (Lottie can carry audio in some exports — strip it in Bodymovin export).
|
|
137
|
+
|
|
138
|
+
## Lazy-loading strategy
|
|
139
|
+
|
|
140
|
+
For Lottie files >30kB:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
const SuccessLottie = lazy(() =>
|
|
144
|
+
import("./animations/success.json").then(mod => ({
|
|
145
|
+
default: () => <LottiePlayer src={mod.default} poster="/success.png" mode="once" />
|
|
146
|
+
}))
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
<Suspense fallback={<img src="/success.png" alt="결제 완료" />}>
|
|
150
|
+
<SuccessLottie />
|
|
151
|
+
</Suspense>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For above-the-fold animations: include the poster as `<link rel="preload" as="image">` and let the JSON stream in.
|
|
155
|
+
|
|
156
|
+
## Implementation hints
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { useEffect, useRef, useState } from "react";
|
|
160
|
+
import lottie, { AnimationItem } from "lottie-web";
|
|
161
|
+
|
|
162
|
+
function LottiePlayer({
|
|
163
|
+
src, poster, mode = "once", autoplay, playing, pauseOffscreen = true,
|
|
164
|
+
ariaLabel, onComplete, renderer = "svg", width, height,
|
|
165
|
+
}: Props) {
|
|
166
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
167
|
+
const animRef = useRef<AnimationItem | null>(null);
|
|
168
|
+
const [loaded, setLoaded] = useState(false);
|
|
169
|
+
const [errored, setErrored] = useState(false);
|
|
170
|
+
const reduced = usePrefersReducedMotion();
|
|
171
|
+
|
|
172
|
+
// Skip everything if reduced motion
|
|
173
|
+
if (reduced || errored) {
|
|
174
|
+
return <img src={poster} alt={ariaLabel} width={width} height={height} />;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Load + initialize
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (!containerRef.current) return;
|
|
180
|
+
let cancelled = false;
|
|
181
|
+
(async () => {
|
|
182
|
+
try {
|
|
183
|
+
const data = typeof src === "string" ? await fetch(src).then(r => r.json()) : src;
|
|
184
|
+
if (cancelled) return;
|
|
185
|
+
animRef.current = lottie.loadAnimation({
|
|
186
|
+
container: containerRef.current!,
|
|
187
|
+
renderer,
|
|
188
|
+
loop: mode === "loop",
|
|
189
|
+
autoplay: mode !== "controlled" && (autoplay ?? true),
|
|
190
|
+
animationData: data,
|
|
191
|
+
});
|
|
192
|
+
animRef.current.addEventListener("complete", () => onComplete?.());
|
|
193
|
+
setLoaded(true);
|
|
194
|
+
} catch {
|
|
195
|
+
setErrored(true);
|
|
196
|
+
}
|
|
197
|
+
})();
|
|
198
|
+
return () => {
|
|
199
|
+
cancelled = true;
|
|
200
|
+
animRef.current?.destroy();
|
|
201
|
+
};
|
|
202
|
+
}, [src]);
|
|
203
|
+
|
|
204
|
+
// Controlled mode play/pause
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
if (mode !== "controlled" || !animRef.current) return;
|
|
207
|
+
if (playing) animRef.current.play();
|
|
208
|
+
else animRef.current.pause();
|
|
209
|
+
}, [playing, mode]);
|
|
210
|
+
|
|
211
|
+
// Pause offscreen
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (!pauseOffscreen || !containerRef.current) return;
|
|
214
|
+
const obs = new IntersectionObserver(([entry]) => {
|
|
215
|
+
if (!animRef.current) return;
|
|
216
|
+
if (entry.isIntersecting) animRef.current.play();
|
|
217
|
+
else animRef.current.pause();
|
|
218
|
+
});
|
|
219
|
+
obs.observe(containerRef.current);
|
|
220
|
+
return () => obs.disconnect();
|
|
221
|
+
}, [pauseOffscreen]);
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div
|
|
225
|
+
ref={containerRef}
|
|
226
|
+
role="img"
|
|
227
|
+
aria-label={ariaLabel}
|
|
228
|
+
style={{ width, height, position: "relative" }}
|
|
229
|
+
>
|
|
230
|
+
{!loaded && poster && <img src={poster} alt="" style={{ position: "absolute", inset: 0 }} />}
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Bundle / performance
|
|
237
|
+
|
|
238
|
+
| Library | Bundle | Notes |
|
|
239
|
+
| --- | --- | --- |
|
|
240
|
+
| `lottie-web` | ~150kB | Full-featured, all renderers |
|
|
241
|
+
| `lottie-react` | ~150kB (wraps lottie-web) | React API |
|
|
242
|
+
| `lottie-react-light` | ~50kB | Subset (svg renderer only) |
|
|
243
|
+
| `@lottiefiles/dotlottie-react` | ~80kB | Smaller binary `.lottie` format |
|
|
244
|
+
|
|
245
|
+
Recommendation: use `lottie-react-light` if you only need SVG (sharpest, most cases). Use canvas renderer only for complex animations with many shapes.
|
|
246
|
+
|
|
247
|
+
For `.lottie` (compressed binary): 30–50% smaller files than JSON. Worth the swap if you ship 5+ animations.
|
|
248
|
+
|
|
249
|
+
## Edge cases
|
|
250
|
+
|
|
251
|
+
- **JSON fails to load (404, network error)**: show poster image. Never blank.
|
|
252
|
+
- **Animation has audio**: strip it in Bodymovin export (`Audio: Off`). Don't auto-play sound.
|
|
253
|
+
- **User rapid-mounts/unmounts (e.g., toast notifications)**: each mount creates lottie instance. Ensure `destroy()` runs in cleanup.
|
|
254
|
+
- **Animation longer than 5 seconds**: reconsider — Lottie is for moments, not videos. Use a real video element.
|
|
255
|
+
- **Same animation in 50 instances on a page (icon list)**: lottie-web shares JSON parse cache, but DOM nodes still cost. Consider sprite or static SVG instead.
|
|
256
|
+
- **SSR (Next.js)**: lottie-web requires DOM. Wrap in `dynamic(() => import("./LottiePlayer"), { ssr: false })`.
|
|
257
|
+
|
|
258
|
+
## Don't
|
|
259
|
+
|
|
260
|
+
- Don't ship Lottie for what CSS can do (fade-in, simple icon flip). Bundle cost not justified.
|
|
261
|
+
- Don't auto-play decorative loops on every page — battery drain on mobile.
|
|
262
|
+
- Don't skip the poster — it's the fallback for reduced motion, slow networks, errors, and SSR.
|
|
263
|
+
- Don't ship animations with embedded fonts (massively inflates JSON). Convert text to outlines in After Effects.
|
|
264
|
+
- Don't ship 60fps animations when 30fps reads identically. Cuts file size in half.
|
|
265
|
+
- Don't include color in the JSON if the surrounding theme might change (dark mode). Use `expressions` or post-load color override.
|
|
266
|
+
|
|
267
|
+
## References
|
|
268
|
+
|
|
269
|
+
- [`lottie-web`](https://github.com/airbnb/lottie-web) — Airbnb's renderer
|
|
270
|
+
- [`lottie-react`](https://lottiereact.com/) — React wrapper
|
|
271
|
+
- [Bodymovin plugin](https://aescripts.com/bodymovin/) — After Effects → JSON exporter
|
|
272
|
+
- [LottieFiles](https://lottiefiles.com/) — animation library + dotlottie format
|
|
273
|
+
|
|
274
|
+
## Cross-reference
|
|
275
|
+
|
|
276
|
+
- [`knowledge/motion/motion-tools.md`](../knowledge/motion/motion-tools.md) — when Lottie vs Rive vs CSS
|
|
277
|
+
- [`knowledge/motion/marketing-motion.md`](../knowledge/motion/marketing-motion.md) — Lottie in marketing
|
|
278
|
+
- [`examples/component-empty-state.md`](component-empty-state.md) — uses Lottie for empty illustrations
|
|
279
|
+
- [`examples/component-result.md`](component-result.md) — success / error states
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# `Masonry` — spec
|
|
2
|
+
|
|
3
|
+
> Synthesized from MUI `Masonry`. A Pinterest-style staggered grid where items pack vertically into columns, varying heights aligned cleanly. Distinct from Grid (uniform cells) and Flex (single-direction layout).
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
- **Image gallery** with varied aspect ratios (Pinterest, Unsplash, gallery wall).
|
|
8
|
+
- **Card grid** where cards have varied content lengths.
|
|
9
|
+
- **Quote / testimonial wall** with naturally varying lengths.
|
|
10
|
+
|
|
11
|
+
When NOT to use:
|
|
12
|
+
- Uniform-height cards (use Grid).
|
|
13
|
+
- Reading flow / accessible content sequence (Masonry's visual order ≠ DOM order — bad for screen readers).
|
|
14
|
+
- Tables / data — use Table.
|
|
15
|
+
|
|
16
|
+
## Anatomy
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Column 1 Column 2 Column 3
|
|
20
|
+
┌──────┐ ┌──────┐ ┌──────┐
|
|
21
|
+
│ │ │ │ │ │
|
|
22
|
+
│ │ ├──────┤ │ │
|
|
23
|
+
├──────┤ │ │ │ │
|
|
24
|
+
│ │ │ │ ├──────┤
|
|
25
|
+
│ │ ├──────┤ │ │
|
|
26
|
+
│ │ │ │ │ │
|
|
27
|
+
├──────┤ │ │ │ │
|
|
28
|
+
│ │ │ │ ├──────┤
|
|
29
|
+
└──────┘ └──────┘ │ │
|
|
30
|
+
└──────┘
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Items flow vertically into columns; each item lands at the bottom of the shortest column.
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<Masonry columns={3} spacing={2}>
|
|
39
|
+
{photos.map(photo => (
|
|
40
|
+
<PhotoCard key={photo.id} photo={photo} />
|
|
41
|
+
))}
|
|
42
|
+
</Masonry>
|
|
43
|
+
|
|
44
|
+
<Masonry columns={{ mobile: 1, tablet: 2, desktop: 3, wide: 4 }} spacing={2}>
|
|
45
|
+
{items}
|
|
46
|
+
</Masonry>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Default | Description |
|
|
50
|
+
| --- | --- | --- | --- |
|
|
51
|
+
| `columns` | `number \| { mobile, tablet, desktop, wide }` | `3` | Column count (responsive object also accepted) |
|
|
52
|
+
| `spacing` | `number` | `2` | Gap between items (typically token-multiplier) |
|
|
53
|
+
| `defaultColumns` | `number` | `3` | SSR fallback before client measures |
|
|
54
|
+
| `sequential` | `boolean` | `false` | If true, items fill columns left-to-right (DOM order = visual order) |
|
|
55
|
+
| `children` | `ReactNode[]` | — | Items |
|
|
56
|
+
|
|
57
|
+
## Implementation approaches
|
|
58
|
+
|
|
59
|
+
### CSS Multicolumn (modern, simplest)
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
.masonry {
|
|
63
|
+
column-count: 3;
|
|
64
|
+
column-gap: var(--space-md);
|
|
65
|
+
}
|
|
66
|
+
.masonry > * {
|
|
67
|
+
break-inside: avoid;
|
|
68
|
+
margin-bottom: var(--space-md);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Pros**: pure CSS, no JS measurement, SSR-friendly.
|
|
73
|
+
**Cons**: items flow top-to-bottom in column 1 first (column 1 fills, then column 2). DOM order ≠ visual reading order.
|
|
74
|
+
|
|
75
|
+
### JS column-measurement (Pinterest-style)
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// Measure each item's height after render
|
|
79
|
+
// Place each new item in the currently-shortest column
|
|
80
|
+
// Update on resize / item changes
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Pros**: items appear in DOM order (column 1 row 1, column 2 row 1, etc.) so reading order matches.
|
|
84
|
+
**Cons**: requires JS measurement, layout flicker on first render, more complex.
|
|
85
|
+
|
|
86
|
+
MUI `Masonry` uses the JS approach with React refs.
|
|
87
|
+
|
|
88
|
+
### CSS Grid masonry (future)
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
.masonry {
|
|
92
|
+
display: grid;
|
|
93
|
+
grid-template-rows: masonry; /* CSS Grid Level 3 */
|
|
94
|
+
grid-template-columns: repeat(3, 1fr);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`grid-template-rows: masonry` is a draft spec (Firefox-only as of 2024). Wait for Chromium / Safari before adopting.
|
|
99
|
+
|
|
100
|
+
## Variants
|
|
101
|
+
|
|
102
|
+
### Responsive columns
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<Masonry columns={{ mobile: 1, tablet: 2, desktop: 3, wide: 4 }} />
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Different column counts per breakpoint.
|
|
109
|
+
|
|
110
|
+
### Sequential mode
|
|
111
|
+
|
|
112
|
+
`sequential={true}` makes items fill left-to-right per row (like Grid but with varied heights). Loses the Pinterest "shortest column wins" advantage but preserves DOM order strictly.
|
|
113
|
+
|
|
114
|
+
## States
|
|
115
|
+
|
|
116
|
+
Stateless — Masonry is layout only.
|
|
117
|
+
|
|
118
|
+
## Tokens consumed
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
--space-sm, --space-md (gap)
|
|
122
|
+
--motion-medium (item enter animation, optional)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Accessibility
|
|
126
|
+
|
|
127
|
+
- **Reading order matters**. Masonry's visual columns may not match DOM order. Screen readers read DOM order.
|
|
128
|
+
- For accessible Masonry: ensure DOM order = priority order (most important first), even if columns shuffle visually.
|
|
129
|
+
- Each item is independently accessible (semantic markup inside).
|
|
130
|
+
- Keyboard: Tab moves through items in DOM order (which may zigzag visually — acceptable for galleries; not for reading).
|
|
131
|
+
- Avoid Masonry for primary content / reading flow. Reserve for media-grid surfaces.
|
|
132
|
+
|
|
133
|
+
## Performance
|
|
134
|
+
|
|
135
|
+
- For 1000+ items: virtualize using `react-virtuoso` or similar Masonry-aware virtualizer.
|
|
136
|
+
- For images: lazy-load (`loading="lazy"`) so off-screen items don't block initial paint.
|
|
137
|
+
- Measure-once on resize (debounce 150ms) — measuring per-frame kills performance.
|
|
138
|
+
- For aspect-ratio-known items (image with width / height): provide ratio so layout settles before image loads.
|
|
139
|
+
|
|
140
|
+
## Code example
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
function PhotoGallery({ photos }: Props) {
|
|
144
|
+
return (
|
|
145
|
+
<Masonry columns={{ mobile: 1, tablet: 2, desktop: 3 }} spacing={2}>
|
|
146
|
+
{photos.map(photo => (
|
|
147
|
+
<figure key={photo.id} className="photo-card">
|
|
148
|
+
<img
|
|
149
|
+
src={photo.url}
|
|
150
|
+
width={photo.width}
|
|
151
|
+
height={photo.height}
|
|
152
|
+
alt={photo.alt}
|
|
153
|
+
loading="lazy"
|
|
154
|
+
style={{ aspectRatio: `${photo.width}/${photo.height}` }}
|
|
155
|
+
/>
|
|
156
|
+
<figcaption>{photo.caption}</figcaption>
|
|
157
|
+
</figure>
|
|
158
|
+
))}
|
|
159
|
+
</Masonry>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Edge cases
|
|
165
|
+
|
|
166
|
+
- **All items same height**: result looks like Grid. Use Grid instead.
|
|
167
|
+
- **One very tall item**: it dominates one column; others may look empty. Cap maximum height OR balance manually.
|
|
168
|
+
- **Lazy-loaded items popping in**: layout reflows. With `aspect-ratio` set, reflow is minimal.
|
|
169
|
+
- **Server-render with unknown heights**: SSR shows items in their column-fill order; client re-measures and adjusts. Brief flicker possible — accept or use CSS-multicolumn as SSR-stable fallback.
|
|
170
|
+
- **RTL**: column order reverses (rightmost = column 1). Verify your layout.
|
|
171
|
+
- **Print**: CSS multicolumn handles print well; JS-based Masonry may not — provide a print stylesheet that disables Masonry (`column-count: 1`).
|
|
172
|
+
|
|
173
|
+
## Don't
|
|
174
|
+
|
|
175
|
+
- Don't use Masonry for forms or data tables. Wrong tool.
|
|
176
|
+
- Don't use Masonry for content that requires linear reading. Confusing.
|
|
177
|
+
- Don't put items with vastly different sizes (5x height differences) — looks broken.
|
|
178
|
+
- Don't omit `aspect-ratio` on images. Without it, layout shifts as images load.
|
|
179
|
+
- Don't run measurement per scroll-event. Measure once on resize / item change.
|
|
180
|
+
|
|
181
|
+
## References
|
|
182
|
+
|
|
183
|
+
- MUI: [`Masonry`](../refs/mui/packages/mui-material/src/Masonry)
|
|
184
|
+
- CSS Multicolumn Layout (MDN)
|
|
185
|
+
- Pinterest engineering blog on Masonry layout
|
|
186
|
+
|
|
187
|
+
## Cross-reference
|
|
188
|
+
|
|
189
|
+
- [`examples/component-image.md`](component-image.md) — common Masonry child
|
|
190
|
+
- [`examples/component-card.md`](component-card.md) — alternative for uniform cards
|
|
191
|
+
- [`knowledge/layout/spacing-and-grid.md`](../knowledge/layout/spacing-and-grid.md)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# `Mention` (@-trigger autocomplete) — spec
|
|
2
|
+
|
|
3
|
+
> Citing Ant Design `Mentions`, MUI (no built-in), shadcn-ui (composition with `command`)
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
A textarea-style input where typing a trigger character (`@`, `#`, `:`) opens a popover with suggestions. The picked suggestion is inserted into the text as a "mention chip" (highlighted token).
|
|
8
|
+
|
|
9
|
+
Used for: comments, chat, descriptions with @-mentions of users, #-tags, :emoji shortcodes, /commands.
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
┌──────────────────────────────────────────────────────┐
|
|
15
|
+
│ Hey @mi| │ ← cursor after @mi
|
|
16
|
+
└──────────────────────────────────────────────────────┘
|
|
17
|
+
│
|
|
18
|
+
▼
|
|
19
|
+
┌──────────────────┐
|
|
20
|
+
│ ▶ 김민지 (@minji) │ ← active suggestion
|
|
21
|
+
│ 김민호 (@minho) │
|
|
22
|
+
│ 민혜진 (@hye) │
|
|
23
|
+
└──────────────────┘
|
|
24
|
+
|
|
25
|
+
After picking "김민지":
|
|
26
|
+
┌──────────────────────────────────────────────────────┐
|
|
27
|
+
│ Hey [김민지] │ ← chip rendered as highlighted token
|
|
28
|
+
└──────────────────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Mention
|
|
35
|
+
value={text}
|
|
36
|
+
onValueChange={setText}
|
|
37
|
+
triggers={[
|
|
38
|
+
{
|
|
39
|
+
char: "@",
|
|
40
|
+
onSearch: (query) => searchUsers(query),
|
|
41
|
+
renderItem: (user) => <UserItem user={user} />,
|
|
42
|
+
onSelect: (user) => ({ value: `@${user.username}`, label: user.name, data: user }),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
char: "#",
|
|
46
|
+
onSearch: (query) => searchTags(query),
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
placeholder="댓글을 작성하세요..."
|
|
50
|
+
/>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Default | Description |
|
|
54
|
+
| --- | --- | --- | --- |
|
|
55
|
+
| `value` | `string` | — | Text content (with chips encoded as tokens) |
|
|
56
|
+
| `onValueChange` | `(value: string, mentions: Mention[]) => void` | — | Returns plain text + structured mentions |
|
|
57
|
+
| `triggers` | `Trigger[]` | — | Per-trigger config |
|
|
58
|
+
| `placeholder` | `string` | — | |
|
|
59
|
+
| `multiline` | `boolean` | `true` | Textarea vs input |
|
|
60
|
+
| `maxLength` | `number` | — | |
|
|
61
|
+
| `disabled` | `boolean` | `false` | |
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
type Trigger = {
|
|
65
|
+
char: string; // "@", "#", ":", "/"
|
|
66
|
+
onSearch: (query: string) => Promise<Option[]>;
|
|
67
|
+
renderItem?: (item: Option) => ReactNode;
|
|
68
|
+
onSelect: (item: Option) => { value: string; label: string; data?: unknown };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type Mention = {
|
|
72
|
+
trigger: string;
|
|
73
|
+
value: string;
|
|
74
|
+
data: unknown; // The original Option's data
|
|
75
|
+
position: { start: number; end: number };
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Behavior
|
|
80
|
+
|
|
81
|
+
### Trigger detection
|
|
82
|
+
|
|
83
|
+
When user types a trigger char:
|
|
84
|
+
1. Detect cursor position immediately after.
|
|
85
|
+
2. Open popover anchored at cursor.
|
|
86
|
+
3. As user continues typing, send query (the chars after trigger) to `onSearch`.
|
|
87
|
+
4. Show suggestions in popover.
|
|
88
|
+
|
|
89
|
+
### Picking a suggestion
|
|
90
|
+
|
|
91
|
+
When user selects a suggestion (click or Enter):
|
|
92
|
+
1. Replace `@query` with the chip representation.
|
|
93
|
+
2. Close popover.
|
|
94
|
+
3. Cursor advances past the chip.
|
|
95
|
+
4. The chip is **non-editable** as a unit — backspace deletes the whole chip, not character-by-character.
|
|
96
|
+
|
|
97
|
+
### Cancelling
|
|
98
|
+
|
|
99
|
+
Type `Esc`, click outside, or type a space: closes popover, leaves typed text as-is (no chip created).
|
|
100
|
+
|
|
101
|
+
## Storage format
|
|
102
|
+
|
|
103
|
+
Two common storage approaches:
|
|
104
|
+
|
|
105
|
+
### A. Plain text + structured mentions
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
{
|
|
109
|
+
text: "Hey @minji and @minho, please review",
|
|
110
|
+
mentions: [
|
|
111
|
+
{ trigger: "@", value: "minji", position: { start: 4, end: 10 } },
|
|
112
|
+
{ trigger: "@", value: "minho", position: { start: 15, end: 21 } },
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Mentions array references positions in the text. Render-time: replace those positions with chips.
|
|
118
|
+
|
|
119
|
+
### B. Markdown-like syntax
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
"Hey @[김민지](minji) and @[김민호](minho)"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Storage is plain string with markup. Easier for back-end indexing; harder to render WYSIWYG live.
|
|
126
|
+
|
|
127
|
+
For chat (sub-second updates): A. For documents: either.
|
|
128
|
+
|
|
129
|
+
## States
|
|
130
|
+
|
|
131
|
+
| State | Visual |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| Empty | Placeholder, no popover |
|
|
134
|
+
| Typing (no trigger) | Standard textarea |
|
|
135
|
+
| Trigger typed | Popover opens at cursor |
|
|
136
|
+
| Searching | Spinner in popover |
|
|
137
|
+
| Has suggestions | List visible |
|
|
138
|
+
| Picked | Chip inserted; popover closed |
|
|
139
|
+
| Empty results | "결과 없음" or hide popover |
|
|
140
|
+
|
|
141
|
+
## Accessibility — WAI-ARIA Combobox pattern
|
|
142
|
+
|
|
143
|
+
For the dropdown:
|
|
144
|
+
- Container has `role="combobox"` characteristics applied to the textarea + popover.
|
|
145
|
+
- Each suggestion: `role="option"`, `aria-selected` on highlighted.
|
|
146
|
+
- Use `aria-activedescendant` on the textarea pointing at the highlighted option.
|
|
147
|
+
|
|
148
|
+
### Keyboard
|
|
149
|
+
|
|
150
|
+
| Key | Behavior |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| Type trigger char | Opens popover at cursor |
|
|
153
|
+
| `↓` / `↑` | Move highlight (in popover) |
|
|
154
|
+
| `Enter` | Pick highlighted suggestion |
|
|
155
|
+
| `Esc` | Close popover; keep typed text |
|
|
156
|
+
| `Backspace` (after a chip) | Deletes the chip (whole unit) |
|
|
157
|
+
| `Backspace` (during search) | Deletes character; if last trigger char deleted, close popover |
|
|
158
|
+
|
|
159
|
+
### Screen reader
|
|
160
|
+
|
|
161
|
+
When chip inserted: announce via live region: "김민지 mentioned" or similar.
|
|
162
|
+
|
|
163
|
+
## Korean considerations
|
|
164
|
+
|
|
165
|
+
- `@` is the standard mention trigger (universal).
|
|
166
|
+
- `#` for hashtags works; some apps use Korean characters as triggers but stick with `@`/`#` for compatibility.
|
|
167
|
+
- Korean usernames: typically `@박민지` (full name) or `@minji` (romanized). Both should work.
|
|
168
|
+
- IME composition: don't fire suggestions during composition. Wait for `compositionend`. See [`knowledge/i18n/korean-typography.md`](../knowledge/i18n/korean-typography.md).
|
|
169
|
+
- Chip rendering: Korean names sometimes longer (3-4 chars) — chip width adapts.
|
|
170
|
+
|
|
171
|
+
## Don't
|
|
172
|
+
|
|
173
|
+
- Don't make chips editable as text. Backspace deletes whole chip, single Tab moves through.
|
|
174
|
+
- Don't use Mentions for fields where suggestions are NOT inserted as text. Use AutoComplete.
|
|
175
|
+
- Don't allow free-text @-mentions that bypass the picker (creates broken mentions). Either pick from suggestions OR show error.
|
|
176
|
+
- Don't render chips with critical styling that breaks copy-paste — they should stringify gracefully.
|
|
177
|
+
|
|
178
|
+
## References
|
|
179
|
+
|
|
180
|
+
- Ant Design: [`refs/ant-design/components/mentions/`](../refs/ant-design/components/mentions/) — `Mentions`. Multi-trigger support, async data.
|
|
181
|
+
- MUI: no built-in. Use `react-mentions` or build on Slate.
|
|
182
|
+
- shadcn-ui: no built-in. Compose with `command` (cmdk) + textarea + custom popover positioning.
|
|
183
|
+
|
|
184
|
+
## Cross-reference
|
|
185
|
+
|
|
186
|
+
- [`examples/component-auto-complete.md`](component-auto-complete.md) — when no trigger character is needed
|
|
187
|
+
- [`examples/component-select.md`](component-select.md) — combobox pattern
|
|
188
|
+
- [`knowledge/i18n/korean-typography.md`](../knowledge/i18n/korean-typography.md) — IME composition
|
|
189
|
+
- [`knowledge/patterns/search-ux.md`](../knowledge/patterns/search-ux.md) — typeahead patterns
|