├── CHANGELOG.md ├── .nvmrc ├── apps ├── web │ ├── test-sitemap.js │ ├── examples │ │ └── patterns │ │ │ ├── forms │ │ │ ├── index.ts │ │ │ └── text-field │ │ │ │ └── index.ts │ │ │ └── content-management │ │ │ ├── modal │ │ │ └── native.ts │ │ │ └── expandable-text │ │ │ └── basic.ts │ ├── postcss.config.mjs │ ├── app │ │ ├── favicon.ico │ │ ├── apple-icon.png │ │ ├── opengraph-image.png │ │ ├── (home) │ │ │ └── layout.tsx │ │ ├── about │ │ │ └── layout.tsx │ │ ├── blog │ │ │ └── layout.tsx │ │ ├── privacy-policy │ │ │ └── layout.tsx │ │ ├── robots.ts │ │ ├── api │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── layout.client.tsx │ │ ├── llms.mdx │ │ │ └── [...slug] │ │ │ │ └── route.ts │ │ ├── global-error.tsx │ │ └── llms-full.txt │ │ │ └── route.ts │ ├── content │ │ ├── glossary │ │ │ ├── v │ │ │ │ └── meta.json │ │ │ ├── a │ │ │ │ └── meta.json │ │ │ ├── d │ │ │ │ └── meta.json │ │ │ ├── k │ │ │ │ └── meta.json │ │ │ ├── l │ │ │ │ └── meta.json │ │ │ ├── p │ │ │ │ └── meta.json │ │ │ ├── t │ │ │ │ └── meta.json │ │ │ ├── c │ │ │ │ └── meta.json │ │ │ ├── s │ │ │ │ └── meta.json │ │ │ ├── meta.json │ │ │ └── index.mdx │ │ ├── meta.json │ │ ├── patterns │ │ │ ├── advanced │ │ │ │ ├── meta.json │ │ │ │ ├── search-results.mdx │ │ │ │ ├── command-palette.mdx │ │ │ │ └── wizard.mdx │ │ │ ├── media │ │ │ │ ├── meta.json │ │ │ │ ├── image-upload.mdx │ │ │ │ ├── image-gallery.mdx │ │ │ │ └── video-player.mdx │ │ │ ├── e-commerce │ │ │ │ ├── meta.json │ │ │ │ ├── product-card.mdx │ │ │ │ ├── checkout.mdx │ │ │ │ └── shopping-cart.mdx │ │ │ ├── social │ │ │ │ ├── meta.json │ │ │ │ ├── like-button.mdx │ │ │ │ ├── share-dialog.mdx │ │ │ │ ├── comment-system.mdx │ │ │ │ └── activity-feed.mdx │ │ │ ├── user-feedback │ │ │ │ ├── meta.json │ │ │ │ ├── cookie-consent.mdx │ │ │ │ ├── empty-states.mdx │ │ │ │ ├── skeleton.mdx │ │ │ │ ├── notification.mdx │ │ │ │ ├── progress-indicator.mdx │ │ │ │ └── loading-indicator.mdx │ │ │ ├── authentication │ │ │ │ ├── meta.json │ │ │ │ ├── password-reset.mdx │ │ │ │ ├── social-login.mdx │ │ │ │ ├── user-profile.mdx │ │ │ │ ├── signup.mdx │ │ │ │ ├── login.mdx │ │ │ │ ├── two-factor.mdx │ │ │ │ └── account-settings.mdx │ │ │ ├── content-management │ │ │ │ ├── meta.json │ │ │ │ └── drag-and-drop.mdx │ │ │ ├── navigation │ │ │ │ ├── meta.json │ │ │ │ ├── link.mdx │ │ │ │ ├── hambuger-menu.mdx │ │ │ │ ├── tabs.mdx │ │ │ │ ├── megamenu.mdx │ │ │ │ ├── sidebar.mdx │ │ │ │ └── navigation-menu.mdx │ │ │ ├── forms │ │ │ │ ├── color-picker.mdx │ │ │ │ ├── tag-input.mdx │ │ │ │ ├── time-input.mdx │ │ │ │ ├── date-range.mdx │ │ │ │ ├── signature-pad.mdx │ │ │ │ ├── date-input.mdx │ │ │ │ ├── phone-number.mdx │ │ │ │ ├── rating-input.mdx │ │ │ │ ├── date-picker.mdx │ │ │ │ ├── rich-text-editor.mdx │ │ │ │ ├── toggle.mdx │ │ │ │ ├── file-input.mdx │ │ │ │ ├── multi-select-input.mdx │ │ │ │ ├── textarea.mdx │ │ │ │ ├── currency-input.mdx │ │ │ │ ├── meta.json │ │ │ │ ├── code-confirmation.mdx │ │ │ │ ├── slider.mdx │ │ │ │ └── radio.mdx │ │ │ ├── ai-intelligence │ │ │ │ ├── meta.json │ │ │ │ ├── prompt-input.mdx │ │ │ │ ├── model-selector.mdx │ │ │ │ ├── token-counter.mdx │ │ │ │ ├── ai-error-states.mdx │ │ │ │ ├── response-feedback.mdx │ │ │ │ ├── context-window.mdx │ │ │ │ ├── ai-chat.mdx │ │ │ │ ├── ai-suggestions.mdx │ │ │ │ ├── ai-loading-states.mdx │ │ │ │ └── streaming-response.mdx │ │ │ ├── data-display │ │ │ │ ├── meta.json │ │ │ │ ├── calendar.mdx │ │ │ │ ├── chart.mdx │ │ │ │ ├── filter-panel.mdx │ │ │ │ ├── statistics.mdx │ │ │ │ ├── comparison-table.mdx │ │ │ │ ├── dashboard.mdx │ │ │ │ ├── tree-view.mdx │ │ │ │ ├── kanban-board.mdx │ │ │ │ ├── timeline.mdx │ │ │ │ ├── list-view.mdx │ │ │ │ └── card-grid.mdx │ │ │ └── meta.json │ │ └── pattern-guide │ │ │ ├── index.mdx │ │ │ └── meta.json │ ├── public │ │ ├── img │ │ │ ├── preview.png │ │ │ ├── ux-logo.png │ │ │ └── opengraph-image.png │ │ ├── books │ │ │ ├── do-dont-forms.jpg │ │ │ ├── practical-ui.jpg │ │ │ ├── refactoring-ui.jpg │ │ │ ├── designing-ux-forms.jpg │ │ │ ├── form-design-patterns.jpg │ │ │ ├── inclusive-design-patterns.jpg │ │ │ ├── smart-interface-design-patterns.jpg │ │ │ └── ux-fundamentals-non-ux-professionals.jpg │ │ ├── og │ │ │ └── opengraph-image.png │ │ ├── authors │ │ │ └── thedaviddias.webp │ │ ├── blog │ │ │ ├── leave-review-gpt.jpg │ │ │ ├── ux-patterns-gpt.jpg │ │ │ └── gpt-dropdown-menu.jpg │ │ ├── covers │ │ │ └── patterns │ │ │ │ ├── modal.png │ │ │ │ ├── table.png │ │ │ │ ├── button.png │ │ │ │ ├── breadcrumb.png │ │ │ │ ├── load-more.png │ │ │ │ ├── pagination.png │ │ │ │ ├── text-field.png │ │ │ │ ├── autocomplete.png │ │ │ │ └── back-to-top.png │ │ ├── examples │ │ │ ├── amazon-pagination.webp │ │ │ ├── pinterest-infinite.gif │ │ │ └── google-search-pagination.webp │ │ └── patterns │ │ │ └── code-confirmation │ │ │ └── do │ │ │ └── paypal.jpg │ ├── components │ │ ├── decision-flow │ │ │ ├── nodes │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── layout │ │ │ └── base-home.tsx │ │ ├── glossary │ │ │ ├── alphabet-nav.tsx │ │ │ ├── terms-list-container.tsx │ │ │ ├── term-page.tsx │ │ │ ├── structured-data.tsx │ │ │ ├── term-card.tsx │ │ │ └── letter.tsx │ │ ├── pattern-comparison │ │ │ ├── comparison-hero.tsx │ │ │ └── decision-flow-section.tsx │ │ ├── pattern-preview.tsx │ │ ├── feedback-wrapper.tsx │ │ ├── blog │ │ │ ├── mobile-toc.tsx │ │ │ ├── author-card.tsx │ │ │ ├── hash-scroll-handler.tsx │ │ │ └── authors.tsx │ │ ├── component-preview-wrapper.tsx │ │ ├── footer │ │ │ ├── footer-copyright.tsx │ │ │ └── footer-link.tsx │ │ ├── search.tsx │ │ ├── sections │ │ │ └── home-cta.tsx │ │ ├── pattern-guide-list.tsx │ │ ├── ui │ │ │ └── button.tsx │ │ └── suggest-pattern.tsx │ ├── constants │ │ ├── routes.ts │ │ └── project.ts │ ├── utils │ │ ├── date.ts │ │ ├── get-patterns.ts │ │ ├── generate-breadcrumb-schema.ts │ │ └── get-glossary-terms.ts │ ├── lib │ │ ├── cn.ts │ │ ├── merge-refs.ts │ │ └── is-active.ts │ ├── cli.json │ ├── next-env.d.ts │ ├── instrumentation.ts │ ├── .gitignore │ ├── sentry.server.config.ts │ ├── instrumentation-client.ts │ ├── sentry.edge.config.ts │ └── tsconfig.json ├── kit │ ├── lib │ │ ├── utils.ts │ │ ├── cn.ts │ │ ├── constants.ts │ │ ├── source.ts │ │ └── get-llm-text.ts │ ├── content │ │ └── docs │ │ │ ├── components │ │ │ ├── meta.json │ │ │ ├── expandable-text.mdx │ │ │ └── load-more.mdx │ │ │ ├── meta.json │ │ │ └── test.mdx │ ├── postcss.config.mjs │ ├── app │ │ ├── favicon.ico │ │ ├── icon1.png │ │ ├── apple-icon.png │ │ ├── robots.ts │ │ ├── api │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── (home) │ │ │ └── layout.tsx │ │ ├── llms-full.txt │ │ │ └── route.ts │ │ ├── sitemap.ts │ │ ├── manifest.json │ │ ├── llms.mdx │ │ │ └── [[...slug]] │ │ │ │ └── route.ts │ │ ├── global-error.tsx │ │ └── docs │ │ │ └── layout.tsx │ ├── public │ │ ├── avatar.jpg │ │ ├── img │ │ │ └── up-kit-logo.png │ │ ├── web-app-manifest-192x192.png │ │ └── web-app-manifest-512x512.png │ ├── utils │ │ └── date.ts │ ├── cli.json │ ├── constants │ │ ├── project.ts │ │ └── footer.ts │ ├── .gitignore │ ├── components │ │ ├── simple-icon.tsx │ │ ├── footer │ │ │ ├── footer-copyright.tsx │ │ │ └── footer-link.tsx │ │ └── ui │ │ │ └── button.tsx │ ├── tsconfig.json │ ├── next.config.mjs │ └── source.config.ts └── gallery │ ├── postcss.config.mjs │ ├── app │ ├── icon1.png │ ├── favicon.ico │ ├── apple-icon.png │ ├── robots.ts │ ├── api │ │ ├── search │ │ │ └── route.ts │ │ └── search-data │ │ │ └── route.ts │ └── manifest.json │ ├── lib │ ├── env.ts │ ├── breadcrumb-schema.ts │ ├── source.ts │ ├── utils.ts │ ├── image-utils.ts │ ├── types.ts │ └── search-context.tsx │ ├── public │ ├── web │ │ └── klingai.com │ │ │ ├── 001.png │ │ │ └── 002.png │ ├── web-app-manifest-192x192.png │ └── web-app-manifest-512x512.png │ ├── .env.example │ ├── data │ ├── websites.json │ └── id-mapping.json │ ├── components │ ├── common │ │ ├── footer.tsx │ │ ├── empty-state.tsx │ │ ├── keyboard-shortcuts.tsx │ │ ├── platform-badge.tsx │ │ ├── pattern-badge.tsx │ │ └── disclaimer-banner.tsx │ ├── header │ │ └── search-button.tsx │ ├── filters │ │ └── filter-chip.tsx │ ├── footer │ │ └── footer-copyright.tsx │ └── sections │ │ └── entries-grid.tsx │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── mdx-components.tsx │ ├── .gitignore │ ├── tsconfig.json │ └── content │ └── entries │ ├── carousel │ └── klingai │ │ └── carousel-auto-play.mdx │ └── popover │ └── klingai │ └── popover-auto-open.mdx ├── configs └── typescript │ ├── README.md │ ├── react-library.json │ ├── package.json │ ├── nextjs.json │ └── base.json ├── .github ├── CODEOWNERS ├── funding.yml ├── SECURITY.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── stale.yml ├── workflows │ └── link-checker.yml └── actions │ └── install │ └── action.yml ├── packages ├── constants │ ├── src │ │ ├── patterns.ts │ │ ├── index.ts │ │ ├── links.tsx │ │ └── social.tsx │ ├── tsconfig.json │ └── package.json ├── ui │ ├── src │ │ ├── components │ │ │ ├── custom │ │ │ │ ├── registry │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ └── code-display.test.ts │ │ │ │ │ └── component-source.tsx │ │ │ │ └── pattern-badge.tsx │ │ │ └── shadcn │ │ │ │ ├── aspect-ratio.tsx │ │ │ │ ├── collapsible.tsx │ │ │ │ └── input.tsx │ │ ├── test-setup.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── lib │ │ │ └── utils.ts │ │ └── hooks │ │ │ └── use-copy.ts │ ├── vitest.config.ts │ ├── tsconfig.json │ └── components.json ├── registry │ ├── registry │ │ └── default │ │ │ ├── lib │ │ │ └── utils.ts │ │ │ └── blocks │ │ │ └── button │ │ │ ├── button-soft.tsx │ │ │ ├── button-default.tsx │ │ │ ├── button-ghost.tsx │ │ │ ├── button-outline.tsx │ │ │ ├── button-destructive.tsx │ │ │ ├── button-link.tsx │ │ │ ├── button-sizes.tsx │ │ │ ├── button-with-keyboard-shortcut.tsx │ │ │ ├── button-with-sound.tsx │ │ │ ├── button-with-haptics.tsx │ │ │ ├── button-disabled.tsx │ │ │ ├── button-icon-left.tsx │ │ │ ├── button-full-width.tsx │ │ │ ├── button-icon-right.tsx │ │ │ └── button-variants.tsx │ ├── components.json │ ├── tsconfig.json │ └── test-setup.ts ├── hooks │ ├── src │ │ ├── use-mounted.ts │ │ ├── use-copy.ts │ │ ├── use-copy-to-clipboard.ts │ │ ├── use-config.ts │ │ └── use-in-view.ts │ ├── tsconfig.json │ └── package.json ├── seo │ ├── package.json │ └── src │ │ └── structured-data │ │ ├── base.ts │ │ └── breadcrumb.ts ├── newsletter │ └── package.json └── tracking │ ├── package.json │ └── src │ └── types.ts ├── .npmrc ├── .env.example ├── pnpm-workspace.yaml ├── banner.png ├── .vscode ├── extensions.json └── launch.json ├── .editorconfig ├── .syncpackrc ├── .biomeignore ├── lefthook.yml ├── lychee.toml └── biome.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.19.0 2 | -------------------------------------------------------------------------------- /apps/web/test-sitemap.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /configs/typescript/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @thedaviddias 2 | -------------------------------------------------------------------------------- /packages/constants/src/patterns.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | public-hoist-pattern[]=canvas 2 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: thedaviddias 2 | -------------------------------------------------------------------------------- /apps/kit/lib/utils.ts: -------------------------------------------------------------------------------- 1 | export { cn } from "@/registry/lib/utils"; 2 | -------------------------------------------------------------------------------- /apps/kit/lib/cn.ts: -------------------------------------------------------------------------------- 1 | export { twMerge as cn } from "tailwind-merge"; 2 | -------------------------------------------------------------------------------- /packages/ui/src/components/custom/registry/__tests__/code-display.test.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | -------------------------------------------------------------------------------- /apps/web/examples/patterns/forms/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./text-field"; 2 | -------------------------------------------------------------------------------- /apps/kit/content/docs/components/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": ["button"] 3 | } 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | MAILERLITE_API_KEY= 2 | MAILERLITE_GROUP_IDS= 3 | 4 | SENTRY_AUTH_TOKEN= 5 | -------------------------------------------------------------------------------- /apps/kit/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export { getWebAppUrl } from "@ux-patterns/ui/constants/urls"; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | - "configs/*" 5 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/banner.png -------------------------------------------------------------------------------- /apps/kit/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/gallery/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/kit/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/app/favicon.ico -------------------------------------------------------------------------------- /apps/kit/app/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/app/icon1.png -------------------------------------------------------------------------------- /apps/web/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/app/favicon.ico -------------------------------------------------------------------------------- /apps/web/content/glossary/v/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "V", 3 | "pages": ["viewport"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/constants/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./author"; 2 | export * from "./patterns"; 3 | export * from "./social"; 4 | -------------------------------------------------------------------------------- /apps/gallery/app/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/app/icon1.png -------------------------------------------------------------------------------- /apps/kit/public/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/public/avatar.jpg -------------------------------------------------------------------------------- /apps/web/content/glossary/a/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "A", 3 | "pages": ["aria-attributes"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/gallery/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/app/favicon.ico -------------------------------------------------------------------------------- /apps/kit/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/app/apple-icon.png -------------------------------------------------------------------------------- /apps/web/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/app/apple-icon.png -------------------------------------------------------------------------------- /apps/web/content/glossary/d/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "D", 3 | "pages": ["design-tokens", "dom"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/content/glossary/k/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "K", 3 | "pages": ["keyboard-navigation"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/content/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "UX Patterns for Devs", 3 | "pages": ["patterns", "pattern-guide", "glossary"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/gallery/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/app/apple-icon.png -------------------------------------------------------------------------------- /apps/web/public/img/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/img/preview.png -------------------------------------------------------------------------------- /apps/web/public/img/ux-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/img/ux-logo.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["biomejs.biome"], 3 | "unwantedRecommendations": ["esbenp.prettier-vscode"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/gallery/lib/env.ts: -------------------------------------------------------------------------------- 1 | export const getDocsUrl = () => { 2 | return process.env.NEXT_PUBLIC_DOCS_URL || "http://localhost:3061"; 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/app/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/app/opengraph-image.png -------------------------------------------------------------------------------- /apps/web/content/glossary/l/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "L", 3 | "pages": ["lazy-loading", "live-regions"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/kit/public/img/up-kit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/public/img/up-kit-logo.png -------------------------------------------------------------------------------- /apps/web/components/decision-flow/nodes/index.ts: -------------------------------------------------------------------------------- 1 | export { PatternNode } from "./PatternNode"; 2 | export { QuestionNode } from "./QuestionNode"; 3 | -------------------------------------------------------------------------------- /apps/web/content/glossary/p/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "P", 3 | "pages": ["pagination", "progressive-loading"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/content/glossary/t/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "T", 3 | "pages": ["throttle-debounce", "touch-targets"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/public/books/do-dont-forms.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/do-dont-forms.jpg -------------------------------------------------------------------------------- /apps/web/public/books/practical-ui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/practical-ui.jpg -------------------------------------------------------------------------------- /apps/web/public/img/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/img/opengraph-image.png -------------------------------------------------------------------------------- /apps/web/public/og/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/og/opengraph-image.png -------------------------------------------------------------------------------- /apps/web/constants/routes.ts: -------------------------------------------------------------------------------- 1 | export const ROUTES = { 2 | home: "/", 3 | patterns: "/patterns/getting-started", 4 | email: "/api/email", 5 | } as const; 6 | -------------------------------------------------------------------------------- /apps/web/content/glossary/c/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "C", 3 | "pages": ["canonical-tags", "cls-cumulative-layout-shift"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/public/authors/thedaviddias.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/authors/thedaviddias.webp -------------------------------------------------------------------------------- /apps/web/public/blog/leave-review-gpt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/blog/leave-review-gpt.jpg -------------------------------------------------------------------------------- /apps/web/public/blog/ux-patterns-gpt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/blog/ux-patterns-gpt.jpg -------------------------------------------------------------------------------- /apps/web/public/books/refactoring-ui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/refactoring-ui.jpg -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/modal.png -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/table.png -------------------------------------------------------------------------------- /apps/gallery/public/web/klingai.com/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/public/web/klingai.com/001.png -------------------------------------------------------------------------------- /apps/gallery/public/web/klingai.com/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/public/web/klingai.com/002.png -------------------------------------------------------------------------------- /apps/kit/public/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/public/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /apps/kit/public/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/kit/public/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /apps/web/content/glossary/s/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "S", 3 | "pages": ["screen-reader", "semantic-html", "skeleton-screen"], 4 | "defaultOpen": true 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/public/blog/gpt-dropdown-menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/blog/gpt-dropdown-menu.jpg -------------------------------------------------------------------------------- /apps/web/public/books/designing-ux-forms.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/designing-ux-forms.jpg -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/button.png -------------------------------------------------------------------------------- /apps/web/public/books/form-design-patterns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/form-design-patterns.jpg -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/breadcrumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/breadcrumb.png -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/load-more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/load-more.png -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/pagination.png -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/text-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/text-field.png -------------------------------------------------------------------------------- /apps/gallery/public/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/public/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /apps/gallery/public/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/gallery/public/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/autocomplete.png -------------------------------------------------------------------------------- /apps/web/public/covers/patterns/back-to-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/covers/patterns/back-to-top.png -------------------------------------------------------------------------------- /apps/web/public/examples/amazon-pagination.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/examples/amazon-pagination.webp -------------------------------------------------------------------------------- /apps/web/public/examples/pinterest-infinite.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/examples/pinterest-infinite.gif -------------------------------------------------------------------------------- /apps/web/public/books/inclusive-design-patterns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/inclusive-design-patterns.jpg -------------------------------------------------------------------------------- /apps/kit/content/docs/components/expandable-text.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Expandable text 3 | description: Show or hide additional text content on demand 4 | icon: PlusSquare 5 | --- 6 | -------------------------------------------------------------------------------- /apps/web/public/examples/google-search-pagination.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/examples/google-search-pagination.webp -------------------------------------------------------------------------------- /apps/web/public/patterns/code-confirmation/do/paypal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/patterns/code-confirmation/do/paypal.jpg -------------------------------------------------------------------------------- /apps/gallery/.env.example: -------------------------------------------------------------------------------- 1 | # Documentation site URL 2 | # In development: http://localhost:3061 3 | # In production: https://uxpatterns.dev 4 | NEXT_PUBLIC_DOCS_URL=http://localhost:3060 5 | -------------------------------------------------------------------------------- /apps/web/public/books/smart-interface-design-patterns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/smart-interface-design-patterns.jpg -------------------------------------------------------------------------------- /packages/ui/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface NpmCommands { 2 | __npmCommand__?: string; 3 | __yarnCommand__?: string; 4 | __pnpmCommand__?: string; 5 | __bunCommand__?: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/public/books/ux-fundamentals-non-ux-professionals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thedaviddias/ux-patterns-for-developers/HEAD/apps/web/public/books/ux-fundamentals-non-ux-professionals.jpg -------------------------------------------------------------------------------- /apps/kit/utils/date.ts: -------------------------------------------------------------------------------- 1 | export const formatDate = (date: Date): string => { 2 | return date.toLocaleDateString("en-US", { 3 | year: "numeric", 4 | month: "long", 5 | day: "numeric", 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /apps/web/utils/date.ts: -------------------------------------------------------------------------------- 1 | export const formatDate = (date: Date): string => { 2 | return date.toLocaleDateString("en-US", { 3 | year: "numeric", 4 | month: "long", 5 | day: "numeric", 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /apps/web/lib/cn.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | 10 | [Makefile] 11 | indent_style = tab 12 | -------------------------------------------------------------------------------- /apps/web/content/patterns/advanced/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Advanced", 3 | "description": "Advanced and complex UI patterns", 4 | "icon": "Zap", 5 | "pages": ["command-palette", "search-results", "wizard"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/content/patterns/media/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Media", 3 | "description": "Media handling and display patterns", 4 | "icon": "Image", 5 | "pages": ["image-gallery", "image-upload", "video-player"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /configs/typescript/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/gallery/data/websites.json: -------------------------------------------------------------------------------- 1 | { 2 | "klingai.com": { 3 | "name": "Kling AI", 4 | "description": "AI-powered creative platform with video generation and editing capabilities", 5 | "backgroundColor": "#080808" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/kit/content/docs/components/load-more.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Load more 3 | description: Build efficient content loading with the Load More pattern, focusing on user experience and performance optimization. 4 | icon: Plus 5 | --- 6 | -------------------------------------------------------------------------------- /apps/web/content/patterns/e-commerce/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "E-commerce", 3 | "description": "E-commerce and shopping patterns", 4 | "icon": "ShoppingCart", 5 | "pages": ["checkout", "product-card", "shopping-cart"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/components/shadcn/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root; 6 | 7 | export { AspectRatio }; 8 | -------------------------------------------------------------------------------- /apps/web/content/glossary/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Glossary", 3 | "description": "UX/UI terminology definitions", 4 | "icon": "BookOpen", 5 | "root": true, 6 | "pages": ["index", "a", "c", "d", "k", "l", "p", "s", "t", "v"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/registry/registry/default/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/content/patterns/social/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Social", 3 | "description": "Social interaction and engagement patterns", 4 | "icon": "Users", 5 | "pages": ["activity-feed", "comment-system", "like-button", "share-dialog"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: "jsdom", 6 | globals: true, 7 | setupFiles: ["./src/test-setup.ts"], 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/kit/app/robots.ts: -------------------------------------------------------------------------------- 1 | import { createSEORobots } from "@ux-patterns/seo/robots"; 2 | import { BASE_URL } from "@/constants/project"; 3 | 4 | export default function robots() { 5 | return createSEORobots(BASE_URL, { 6 | blockBadBots: true, 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /apps/kit/cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliases": { 3 | "uiDir": "./components/ui", 4 | "componentsDir": "./components", 5 | "blockDir": "./components", 6 | "cssDir": "./styles", 7 | "libDir": "./lib" 8 | }, 9 | "baseDir": "", 10 | "commands": {} 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | import BaseHome from "@/components/layout/base-home"; 2 | 3 | export default function Layout({ children }: LayoutProps<"/">) { 4 | return ( 5 | {children} 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/app/about/layout.tsx: -------------------------------------------------------------------------------- 1 | import BaseHome from "@/components/layout/base-home"; 2 | 3 | export default function Layout({ children }: LayoutProps<"/">) { 4 | return ( 5 | {children} 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliases": { 3 | "uiDir": "./components/ui", 4 | "componentsDir": "./components", 5 | "blockDir": "./components", 6 | "cssDir": "./styles", 7 | "libDir": "./lib" 8 | }, 9 | "baseDir": "", 10 | "commands": {} 11 | } 12 | -------------------------------------------------------------------------------- /apps/gallery/app/robots.ts: -------------------------------------------------------------------------------- 1 | import { createSEORobots } from "@ux-patterns/seo/robots"; 2 | import { siteConfig } from "@/lib/site.config"; 3 | 4 | export default function robots() { 5 | return createSEORobots(siteConfig.url, { 6 | blockBadBots: true, 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /apps/gallery/data/id-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "back-to-top/floating-bad": "6QzJwE8G", 3 | "load-more/infinite-scroll-bad": "kWBtCepb", 4 | "load-more/shop-grid-good": "6CgfQTgK", 5 | "popover/new-features-bad": "7kVyfugz", 6 | "popover/auto-open-bad": "5pLLexuP" 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/app/blog/layout.tsx: -------------------------------------------------------------------------------- 1 | import BaseHome from "@/components/layout/base-home"; 2 | 3 | export default function Layout({ children }: LayoutProps<"/">) { 4 | return ( 5 | {children} 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/app/privacy-policy/layout.tsx: -------------------------------------------------------------------------------- 1 | import BaseHome from "@/components/layout/base-home"; 2 | 3 | export default function Layout({ children }: LayoutProps<"/">) { 4 | return ( 5 | {children} 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /.syncpackrc: -------------------------------------------------------------------------------- 1 | { 2 | "versionGroups": [ 3 | { 4 | "label": "Use workspace protocol when developing local packages", 5 | "dependencies": ["$LOCAL"], 6 | "dependencyTypes": ["dev", "prod"], 7 | "pinVersion": "workspace:*" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /apps/gallery/components/common/footer.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeToggle } from "fumadocs-ui/components/layout/theme-toggle"; 2 | 3 | export const Footer = () => { 4 | return ( 5 | 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/hooks/src/use-mounted.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export function useMounted() { 4 | const [mounted, setMounted] = React.useState(false); 5 | 6 | React.useEffect(() => { 7 | setMounted(true); 8 | }, []); 9 | 10 | return mounted; 11 | } 12 | -------------------------------------------------------------------------------- /.biomeignore: -------------------------------------------------------------------------------- 1 | # Generated registry files 2 | packages/registry/.generated/ 3 | apps/kit/public/r/ 4 | 5 | .next/ 6 | out/ 7 | build/ 8 | dist/ 9 | node_modules/ 10 | .turbo/ 11 | .vercel/ 12 | .env.local 13 | .env.development.local 14 | .env.test.local 15 | .env.production.local 16 | -------------------------------------------------------------------------------- /apps/web/app/robots.ts: -------------------------------------------------------------------------------- 1 | import { createSEORobots } from "@ux-patterns/seo/robots"; 2 | import { BASE_URL } from "@/constants/project"; 3 | 4 | export default function robots() { 5 | return createSEORobots(BASE_URL, { 6 | blockBadBots: true, 7 | crawlDelay: 1, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/content/pattern-guide/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Choose the Right Pattern" 3 | index: true 4 | --- 5 | 6 | Compare similar patterns and make informed decisions based on your specific use case and requirements. 7 | 8 | ## Pattern Guides 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apps/kit/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { createFromSource } from "fumadocs-core/search/server"; 2 | import { source } from "@/lib/source"; 3 | 4 | export const { GET } = createFromSource(source, { 5 | // https://docs.orama.com/docs/orama-js/supported-languages 6 | language: "english", 7 | }); 8 | -------------------------------------------------------------------------------- /apps/gallery/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { createFromSource } from "fumadocs-core/search/server"; 2 | import { source } from "@/lib/source"; 3 | 4 | export const { GET } = createFromSource(source, { 5 | // https://docs.orama.com/docs/orama-js/supported-languages 6 | language: "english", 7 | }); 8 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 7 | -------------------------------------------------------------------------------- /configs/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@thedaviddias/config-typescript", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "base.json", 6 | "files": [ 7 | "*.json" 8 | ], 9 | "scripts": { 10 | "clean": "git clean -xdf .cache .turbo dist node_modules" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/gallery/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 7 | -------------------------------------------------------------------------------- /apps/web/components/layout/base-home.tsx: -------------------------------------------------------------------------------- 1 | import { HomeLayout } from "@ux-patterns/ui/components/custom/header"; 2 | import { baseOptions } from "@/lib/layout.shared"; 3 | 4 | export default function BaseHome({ children }: LayoutProps<"/">) { 5 | return {children}; 6 | } 7 | -------------------------------------------------------------------------------- /apps/kit/lib/source.ts: -------------------------------------------------------------------------------- 1 | import { loader } from "fumadocs-core/source"; 2 | import { docs } from "@/.source"; 3 | 4 | // See https://fumadocs.vercel.app/docs/headless/source-api for more info 5 | export const source = loader({ 6 | // it assigns a URL to your pages 7 | baseUrl: "/docs", 8 | source: docs.toFumadocsSource(), 9 | }); 10 | -------------------------------------------------------------------------------- /apps/gallery/next.config.mjs: -------------------------------------------------------------------------------- 1 | import { createMDX } from "fumadocs-mdx/next"; 2 | import { withPlausibleProxy } from "next-plausible"; 3 | 4 | const withMDX = createMDX(); 5 | 6 | /** @type {import('next').NextConfig} */ 7 | const config = { 8 | reactStrictMode: true, 9 | }; 10 | 11 | export default withPlausibleProxy()(withMDX(config)); 12 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "User Feedback", 3 | "description": "User feedback and status indicators", 4 | "icon": "MessageCircle", 5 | "pages": [ 6 | "cookie-consent", 7 | "empty-states", 8 | "loading-indicator", 9 | "notification", 10 | "progress-indicator", 11 | "skeleton" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/kit/constants/project.ts: -------------------------------------------------------------------------------- 1 | import { PROJECT } from "@ux-patterns/constants/author"; 2 | import { BASE_URL, getWebAppUrl } from "@ux-patterns/ui/constants/urls"; 3 | 4 | export { BASE_URL, getWebAppUrl }; 5 | 6 | export const GITHUB_REPO_URL = `${PROJECT.repository.url}/blob/main/apps/kit/`; 7 | 8 | export const PROJECT_URL = PROJECT.repository.url; 9 | -------------------------------------------------------------------------------- /apps/web/constants/project.ts: -------------------------------------------------------------------------------- 1 | import { PROJECT } from "@ux-patterns/constants/author"; 2 | import { BASE_URL, getWebAppUrl } from "@ux-patterns/ui/constants/urls"; 3 | 4 | export { BASE_URL, getWebAppUrl }; 5 | 6 | export const GITHUB_REPO_URL = `${PROJECT.repository.url}/blob/main/apps/web/`; 7 | 8 | export const PROJECT_URL = PROJECT.repository.url; 9 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Authentication", 3 | "description": "Authentication and user management patterns", 4 | "icon": "Shield", 5 | "pages": [ 6 | "account-settings", 7 | "login", 8 | "password-reset", 9 | "signup", 10 | "social-login", 11 | "two-factor", 12 | "user-profile" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/content/patterns/content-management/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Content Management", 3 | "description": "Content organization and management patterns", 4 | "icon": "LayoutGrid", 5 | "pages": [ 6 | "accordion", 7 | "carousel", 8 | "drag-and-drop", 9 | "expandable-text", 10 | "modal", 11 | "popover", 12 | "tooltip" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/kit/app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | // import { HomeLayout } from "fumadocs-ui/layouts/home"; 2 | import { HomeLayout } from "@ux-patterns/ui/components/custom/header"; 3 | import { baseOptions } from "@/lib/layout.shared"; 4 | 5 | export default function Layout({ children }: LayoutProps<"/">) { 6 | return {children}; 7 | } 8 | -------------------------------------------------------------------------------- /packages/seo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ux-patterns/seo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "type": "module", 6 | "exports": { 7 | "./*": "./src/*.ts" 8 | }, 9 | "dependencies": { 10 | "next": "15.5.7" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "24.3.0", 14 | "typescript": "^5.9.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/kit/content/docs/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "ui", 3 | "root": true, 4 | "pages": [ 5 | "---Getting Started---", 6 | "index", 7 | "installation", 8 | "---Input & Forms---", 9 | "../components/button", 10 | "---Layout & Navigation---", 11 | "../components/load-more", 12 | "---Content Management---", 13 | "../components/expandable-text" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/lib/merge-refs.ts: -------------------------------------------------------------------------------- 1 | import type * as React from "react"; 2 | 3 | export function mergeRefs( 4 | ...refs: (React.Ref | undefined)[] 5 | ): React.RefCallback { 6 | return (value) => { 7 | refs.forEach((ref) => { 8 | if (typeof ref === "function") { 9 | ref(value); 10 | } else if (ref) { 11 | ref.current = value; 12 | } 13 | }); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /apps/gallery/mdx-components.tsx: -------------------------------------------------------------------------------- 1 | import defaultMdxComponents from "fumadocs-ui/mdx"; 2 | import type { MDXComponents } from "mdx/types"; 3 | 4 | // use this function to get MDX components, you will need it for rendering MDX 5 | export function getMDXComponents(components?: MDXComponents): MDXComponents { 6 | return { 7 | ...defaultMdxComponents, 8 | ...components, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /apps/kit/content/docs/test.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Components 3 | description: Components 4 | --- 5 | 6 | ## Code Block 7 | 8 | ```js 9 | console.log('Hello World'); 10 | ``` 11 | 12 | ## Cards 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/kit/app/llms-full.txt/route.ts: -------------------------------------------------------------------------------- 1 | import { getLLMText } from "@/lib/get-llm-text"; 2 | import { source } from "@/lib/source"; 3 | 4 | // cached forever 5 | export const revalidate = false; 6 | 7 | export async function GET() { 8 | const scan = source.getPages().map(getLLMText); 9 | const scanned = await Promise.all(scan); 10 | 11 | return new Response(scanned.join("\n\n")); 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/instrumentation.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/nextjs"; 2 | 3 | export async function register() { 4 | if (process.env.NEXT_RUNTIME === "nodejs") { 5 | await import("./sentry.server.config"); 6 | } 7 | 8 | if (process.env.NEXT_RUNTIME === "edge") { 9 | await import("./sentry.edge.config"); 10 | } 11 | } 12 | 13 | export const onRequestError = Sentry.captureRequestError; 14 | -------------------------------------------------------------------------------- /packages/hooks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@thedaviddias/config-typescript/react-library.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": ".", 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "src", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | } 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/shadcn/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; 2 | 3 | const Collapsible = CollapsiblePrimitive.Root; 4 | 5 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 6 | 7 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 8 | 9 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 10 | -------------------------------------------------------------------------------- /packages/constants/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@thedaviddias/config-typescript/react-library.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": ".", 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "rootDir": "src", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | } 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Navigation", 3 | "description": "Navigation components and patterns", 4 | "icon": "Navigation", 5 | "pages": [ 6 | "back-to-top", 7 | "breadcrumb", 8 | "hambuger-menu", 9 | "infinite-scroll", 10 | "link", 11 | "load-more", 12 | "megamenu", 13 | "navigation-menu", 14 | "pagination", 15 | "sidebar", 16 | "tabs" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/newsletter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ux-patterns/newsletter", 3 | "version": "0.0.1", 4 | "private": true, 5 | "type": "module", 6 | "exports": { 7 | "./*": "./src/*.ts" 8 | }, 9 | "dependencies": { 10 | "zod": "^4.1.5" 11 | }, 12 | "devDependencies": { 13 | "@thedaviddias/config-typescript": "workspace:*", 14 | "@types/node": "24.3.0", 15 | "typescript": "^5.9.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/kit/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/color-picker.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Color Picker" 3 | description: "Select colors with visual feedback" 4 | icon: Palette 5 | status: draft 6 | --- 7 | 8 | 9 | This page is empty for now. Please help us by 10 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 11 | to add content. 12 | 13 | -------------------------------------------------------------------------------- /apps/gallery/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts 29 | -------------------------------------------------------------------------------- /apps/web/content/pattern-guide/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Pattern Guides", 3 | "description": "Decision guides for patterns", 4 | "icon": "Compass", 5 | "root": true, 6 | "pages": [ 7 | "---Introduction---", 8 | "index", 9 | "---Guides---", 10 | "choosing-input-types", 11 | "input-selection-guide", 12 | "pagination-vs-infinite-scroll", 13 | "modal-vs-popover-guide", 14 | "table-vs-list-vs-cards" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "AI Intelligence", 3 | "description": "AI-powered interface patterns", 4 | "icon": "Brain", 5 | "pages": [ 6 | "ai-chat", 7 | "ai-error-states", 8 | "ai-loading-states", 9 | "ai-suggestions", 10 | "context-window", 11 | "model-selector", 12 | "prompt-input", 13 | "response-feedback", 14 | "streaming-response", 15 | "token-counter" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Data Display", 3 | "description": "Data visualization and display components", 4 | "icon": "ChartBar", 5 | "pages": [ 6 | "calendar", 7 | "card-grid", 8 | "chart", 9 | "comparison-table", 10 | "dashboard", 11 | "filter-panel", 12 | "kanban-board", 13 | "list-view", 14 | "statistics", 15 | "table", 16 | "timeline", 17 | "tree-view" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /configs/typescript/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "plugins": [{ "name": "next" }], 7 | "module": "ESNext", 8 | "moduleResolution": "Bundler", 9 | "allowJs": true, 10 | "jsx": "preserve", 11 | "noEmit": true, 12 | "paths": { 13 | "@/*": ["./*"] 14 | } 15 | }, 16 | "exclude": ["node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you believe you have found a security vulnerability in Links Base, we encourage you to responsibly disclose this and not open a public issue. Please report it using [GitHub Security Advisory](https://github.com/thedaviddias/links-base/security/advisories/new) tool, to ensure confidentiality and security. 6 | 7 | We'll review it as soon as possible and publish a fix accordingly. 8 | -------------------------------------------------------------------------------- /apps/web/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { createFromSource } from "fumadocs-core/search/server"; 2 | import { source } from "@/lib/source"; 3 | 4 | // Use static search generation for better performance 5 | // This pre-generates the search index at build time 6 | export const revalidate = false; 7 | 8 | export const { staticGET: GET } = createFromSource(source, { 9 | // https://docs.orama.com/docs/orama-js/supported-languages 10 | language: "english", 11 | }); 12 | -------------------------------------------------------------------------------- /packages/tracking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ux-patterns/tracking", 3 | "version": "0.1.0", 4 | "description": "Unified tracking utilities for UX Patterns monorepo", 5 | "type": "module", 6 | "exports": { 7 | "./*": "./src/*.ts" 8 | }, 9 | "files": [ 10 | "src/**" 11 | ], 12 | "devDependencies": { 13 | "next-plausible": "^3.12.4", 14 | "typescript": "^5.9.2" 15 | }, 16 | "peerDependencies": { 17 | "next-plausible": "^3.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/examples/patterns/forms/text-field/index.ts: -------------------------------------------------------------------------------- 1 | export { basicTextFieldExample } from "./basic"; 2 | export { characterCounterTextFieldExample } from "./character-counter"; 3 | export { helperTextFieldExample } from "./helper-text"; 4 | export { inputTypesTextFieldExample } from "./input-types"; 5 | export { requiredTextFieldExample } from "./required"; 6 | export { validationTextFieldExample } from "./validation"; 7 | export { withIconsTextFieldExample } from "./with-icons"; 8 | -------------------------------------------------------------------------------- /packages/constants/src/links.tsx: -------------------------------------------------------------------------------- 1 | export const FOOTER_OPENSOURCE_LINKS = [ 2 | { 3 | path: "https://llmstxthub.com/", 4 | label: "LLMs.txt Hub", 5 | rel: "noopener noreferrer", 6 | }, 7 | { 8 | path: "https://frontendchecklist.io/", 9 | label: "Front-End Checklist", 10 | rel: "noopener noreferrer", 11 | }, 12 | { 13 | path: "https://github.com/thedaviddias/indie-dev-toolkit", 14 | label: "Indie Dev Toolkit", 15 | rel: "noopener noreferrer", 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /apps/kit/components/simple-icon.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from "react"; 2 | import type { SimpleIcon } from "simple-icons"; 3 | 4 | export const SimpleIconComponent: FC<{ 5 | icon?: SimpleIcon; 6 | className?: string; 7 | }> = ({ icon, className }) => ( 8 | 16 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/tag-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tag Input" 3 | summary: "Enter and format tags" 4 | description: "Create tag input components for dynamic keyword entry with validation and accessibility support." 5 | icon: Tags 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/time-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Time Input" 3 | summary: "Enter time in a structured format" 4 | description: "Build user-friendly time input fields with validation and accessibility features." 5 | icon: Clock 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/utils/get-patterns.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getPatternCategories, 3 | type PatternCategory, 4 | } from "./get-pattern-categories"; 5 | 6 | export async function getPatterns(): Promise { 7 | const categories = await getPatternCategories(); 8 | 9 | return categories.map((category) => ({ 10 | name: category.name, 11 | path: category.path, 12 | description: category.description, 13 | patterns: category.patterns.sort((a, b) => a.title.localeCompare(b.title)), 14 | })); 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/date-range.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Date Range" 3 | summary: "Select a range between two dates" 4 | description: "Build date range selection with calendar interfaces and validation features." 5 | icon: CalendarRange 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/signature-pad.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Signature Pad" 3 | summary: "Allow users to sign their name" 4 | description: "Build touch-enabled signature capture with canvas drawing and validation features." 5 | icon: Pen 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/date-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Date Input" 3 | summary: "Enter dates in a structured text format" 4 | description: "Build date input fields with validation, formatting, and localization features." 5 | icon: Calendar 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/phone-number.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Phone Number" 3 | summary: "Format and validate phone numbers" 4 | description: "Create phone number inputs with international format support and validation features." 5 | icon: Phone 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/rating-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rating Input" 3 | summary: "Rate something with a number of stars" 4 | description: "Build user-friendly rating components with star ratings and accessibility features." 5 | icon: Star 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/link.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Link" 3 | summary: "Create accessible and interactive links" 4 | description: "Build accessible links with proper styling, hover states, and keyboard navigation support." 5 | icon: Link 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /packages/registry/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/date-picker.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Date Picker" 3 | summary: "Select dates from a calendar interface" 4 | description: "Create user-friendly date pickers with calendar interfaces and keyboard navigation." 5 | icon: CalendarDays 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/rich-text-editor.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rich Text Editor" 3 | summary: "Edit and format text content" 4 | description: "Create accessible rich text editors with text formatting tools and keyboard shortcuts." 5 | icon: PenTool 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@thedaviddias/config-typescript/react-library.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "baseUrl": ".", 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "paths": { 9 | "@/*": ["./src/*"], 10 | "@ux-patterns/hooks/*": ["../hooks/src/*"], 11 | "@ux-patterns/ui/*": ["./src/*"], 12 | "@/ui/*": ["../registry/registry/default/ui/*"] 13 | } 14 | }, 15 | "include": ["src/**/*"], 16 | "exclude": ["node_modules", "dist"] 17 | } 18 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/hambuger-menu.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Hamburger Menu" 3 | summary: "Display a menu icon for mobile devices" 4 | description: "Create accessible mobile menus with smooth animations and touch-friendly interactions." 5 | icon: Menu 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/tabs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tabs" 3 | summary: "Switch between different views" 4 | description: "Create accessible tab interfaces with keyboard navigation, ARIA attributes, and responsive design patterns." 5 | icon: LayoutGrid 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/content-management/drag-and-drop.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Drag and Drop" 3 | summary: "Allow users to reorder items intuitively" 4 | description: "Build intuitive drag and drop functionality with accessibility and touch support." 5 | icon: Move 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/megamenu.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Megamenu" 3 | summary: "Display a large number of links in a single menu" 4 | description: "Build accessible and responsive megamenus with keyboard navigation and mobile-friendly adaptations." 5 | icon: Grid3x3 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/sidebar.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sidebar" 3 | summary: "Organize and structure site navigation" 4 | description: "Build responsive and accessible sidebar navigation with collapsible sections and keyboard navigation support." 5 | icon: PanelLeft 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@ux-patterns/ui/lib/utils", 16 | "ui": "@/components/shadcn", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/utils/generate-breadcrumb-schema.ts: -------------------------------------------------------------------------------- 1 | import { siteConfig } from "@/lib/site.config"; 2 | 3 | interface BreadcrumbItem { 4 | name: string; 5 | url: string; 6 | } 7 | 8 | export function generateBreadcrumbSchema(items: BreadcrumbItem[]) { 9 | return { 10 | "@context": "https://schema.org", 11 | "@type": "BreadcrumbList", 12 | itemListElement: items.map((item, index) => ({ 13 | "@type": "ListItem", 14 | position: index + 1, 15 | name: item.name, 16 | item: `${siteConfig.url}${item.url}`, 17 | })), 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Patterns", 3 | "description": "UI/UX patterns and components", 4 | "icon": "Layers", 5 | "root": true, 6 | "pages": [ 7 | "---Introduction---", 8 | "getting-started", 9 | "when-to-use-what", 10 | "books", 11 | "---Pattern Guide---", 12 | "forms", 13 | "navigation", 14 | "data-display", 15 | "content-management", 16 | "user-feedback", 17 | "authentication", 18 | "e-commerce", 19 | "media", 20 | "social", 21 | "ai-intelligence", 22 | "advanced" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ux-patterns/hooks", 3 | "version": "0.1.0", 4 | "description": "Shared React hooks for UX Patterns applications", 5 | "type": "module", 6 | "exports": { 7 | "./*": "./src/*" 8 | }, 9 | "scripts": { 10 | "check": "tsc --noEmit", 11 | "check:type": "tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "react": "^19.1.1" 15 | }, 16 | "devDependencies": { 17 | "@thedaviddias/config-typescript": "workspace:*", 18 | "@types/react": "^19.1.12", 19 | "typescript": "^5.9.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-soft.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-soft 4 | * @title Soft Button 5 | * @type registry:block 6 | * @description Soft button variant with subtle background 7 | * @categories ["buttons", "variants", "soft"] 8 | * @tags ["soft", "variant", "subtle", "background", "muted"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonSoft() { 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-default.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-default 4 | * @title Default Button 5 | * @type registry:block 6 | * @description Basic button with default styling and behavior 7 | * @categories ["buttons", "basic", "foundation"] 8 | * @tags ["default", "basic", "foundation", "simple"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonDefault() { 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /apps/gallery/lib/breadcrumb-schema.ts: -------------------------------------------------------------------------------- 1 | import type { BreadcrumbItem } from "@/lib/url-utils"; 2 | 3 | const BASE_URL = 4 | process.env.NEXT_PUBLIC_BASE_URL || "https://ux-patterns.com/gallery"; 5 | 6 | export function generateBreadcrumbSchema(items: BreadcrumbItem[]) { 7 | return { 8 | "@context": "https://schema.org", 9 | "@type": "BreadcrumbList", 10 | itemListElement: items.map((item, index) => ({ 11 | "@type": "ListItem", 12 | position: index + 1, 13 | name: item.label, 14 | item: `${BASE_URL}${item.href}`, 15 | })), 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/hooks/src/use-copy.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export function useCopy(duration = 1500) { 4 | const [copied, setCopied] = useState(false); 5 | 6 | const copy = async (text: string) => { 7 | try { 8 | await navigator.clipboard.writeText(text); 9 | setCopied(true); 10 | setTimeout(() => setCopied(false), duration); 11 | return true; 12 | } catch (err) { 13 | console.error("Failed to copy text: ", err); 14 | return false; 15 | } 16 | }; 17 | 18 | return { 19 | copied, 20 | copy, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /packages/seo/src/structured-data/base.ts: -------------------------------------------------------------------------------- 1 | import type { StructuredDataConfig } from "../types"; 2 | 3 | /** 4 | * Base class for creating structured data 5 | */ 6 | export class StructuredDataGenerator { 7 | protected config: StructuredDataConfig; 8 | 9 | constructor(config: StructuredDataConfig) { 10 | this.config = config; 11 | } 12 | 13 | protected absoluteUrl(url?: string): string | undefined { 14 | if (!url) return undefined; 15 | if (url.startsWith("http")) return url; 16 | return new URL(url, this.config.baseUrl).toString(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/use-copy.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export function useCopy(duration = 1500) { 4 | const [copied, setCopied] = useState(false); 5 | 6 | const copy = async (text: string) => { 7 | try { 8 | await navigator.clipboard.writeText(text); 9 | setCopied(true); 10 | setTimeout(() => setCopied(false), duration); 11 | return true; 12 | } catch (err) { 13 | console.error("Failed to copy text: ", err); 14 | return false; 15 | } 16 | }; 17 | 18 | return { 19 | copied, 20 | copy, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/components/glossary/alphabet-nav.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Letter } from "./letter"; 4 | 5 | interface AlphabetNavProps { 6 | availableLetters: string[]; 7 | } 8 | 9 | export function AlphabetNav({ availableLetters }: AlphabetNavProps) { 10 | return ( 11 |
12 | {Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ").map((letter) => ( 13 | 18 | ))} 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/toggle.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Toggle" 3 | summary: "Switch between two states" 4 | description: "Implement toggle switches for binary state control in your web applications. Learn best practices for toggle buttons, state management, and accessibility." 5 | icon: ToggleLeft 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/file-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "File Input" 3 | summary: "Upload and handle files" 4 | description: "Create user-friendly file upload components for your web applications. Learn best practices for file selection, drag-and-drop, progress indicators, and validation." 5 | icon: FileUp 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/glossary/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: UX Patterns Glossary 3 | description: A comprehensive glossary of UX patterns, design principles, and web development terminology. 4 | --- 5 | 6 | import { TermsListContainer } from "@/components/glossary/terms-list-container"; 7 | 8 | Welcome to our comprehensive glossary of UX patterns and web development terminology. This resource is designed to help developers and designers understand the key concepts, patterns, and principles discussed throughout our documentation. 9 | 10 | ## Browse by Letter 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/cookie-consent.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Cookie Consent" 3 | summary: "Inform users about the use of cookies" 4 | description: "Implement effective cookie consent banners in your web applications. Learn best practices for GDPR compliance, user privacy, and consent management." 5 | icon: Cookie 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/gallery/app/api/search-data/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { getUniquePatterns, loadEntries } from "@/lib/loadEntries"; 3 | 4 | export async function GET() { 5 | try { 6 | const entries = await loadEntries(); 7 | const patterns = getUniquePatterns(entries); 8 | 9 | return NextResponse.json({ 10 | entries, 11 | patterns, 12 | }); 13 | } catch (error) { 14 | console.error("Failed to load search data:", error); 15 | return NextResponse.json( 16 | { error: "Failed to load search data" }, 17 | { status: 500 }, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-ghost.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-ghost 4 | * @title Ghost Button 5 | * @type registry:block 6 | * @description Ghost button variant with subtle styling 7 | * @categories ["buttons", "variants", "subtle"] 8 | * @tags ["ghost", "variant", "subtle", "minimal", "transparent"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonGhost() { 14 | return ( 15 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-outline.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-outline 4 | * @title Outline Button 5 | * @type registry:block 6 | * @description Outline button variant with border styling 7 | * @categories ["buttons", "variants", "outline"] 8 | * @tags ["outline", "variant", "border", "secondary"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonOutline() { 14 | return ( 15 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/kit/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import { SitemapBuilder } from "@ux-patterns/seo/sitemap"; 2 | import { source } from "@/lib/source"; 3 | 4 | export default function sitemap() { 5 | const builder = new SitemapBuilder("https://kit.uxpatterns.dev"); 6 | 7 | // Add static pages 8 | builder.addStaticPages([ 9 | "", // Homepage 10 | "changelog", 11 | ]); 12 | 13 | // Add all documentation pages 14 | const docPages = source.getPages(); 15 | const docUrls = docPages.map((page) => page.url.substring(1)); // Remove leading slash 16 | builder.addStaticPages(docUrls); 17 | 18 | return builder.build(); 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/multi-select-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Multi-select Input" 3 | summary: "Choose multiple items from a list" 4 | description: "Implement multi-select components for multiple item selection in your web applications. Learn best practices for list management, keyboard navigation, and accessibility." 5 | icon: List 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-destructive.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-destructive 4 | * @title Destructive Button 5 | * @type registry:block 6 | * @description Destructive button variant for dangerous actions 7 | * @categories ["buttons", "destructive-actions", "danger"] 8 | * @tags ["destructive", "danger", "delete", "warning", "red"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonDestructive() { 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/components/pattern-comparison/comparison-hero.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | interface ComparisonHeroProps { 4 | title: string; 5 | description: string; 6 | } 7 | 8 | export function ComparisonHero({ title, description }: ComparisonHeroProps) { 9 | return ( 10 |
11 |

12 | {title} 13 |

14 |
15 | {description} 16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/empty-states.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Empty States" 3 | summary: "Guide users when no content is available" 4 | description: "Create effective empty state experiences in your web applications. Learn best practices for handling no-content scenarios with helpful messaging and clear actions." 5 | icon: CircleAlert 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/skeleton.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Skeleton" 3 | summary: "Show users that content is being loaded" 4 | description: "Build effective skeleton loading states for your web applications. Learn best practices for implementing content placeholders and loading animations with proper accessibility." 5 | icon: LoaderCircle 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/sentry.server.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the server. 2 | // The config you add here will be used whenever the server handles a request. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from "@sentry/nextjs"; 6 | 7 | Sentry.init({ 8 | dsn: "https://931a829b16ed3b014310f19f0a55c618@o515454.ingest.us.sentry.io/4508413880041472", 9 | 10 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 11 | debug: false, 12 | enabled: process.env.NODE_ENV !== "development", 13 | }); 14 | -------------------------------------------------------------------------------- /apps/gallery/lib/source.ts: -------------------------------------------------------------------------------- 1 | import { loader } from "fumadocs-core/source"; 2 | import { entries, pages } from "@/.source"; 3 | 4 | // See https://fumadocs.vercel.app/docs/headless/source-api for more info 5 | export const entriesSource = loader({ 6 | // it assigns a URL to your entries 7 | baseUrl: "/entries", 8 | source: entries.toFumadocsSource(), 9 | }); 10 | 11 | export const pagesSource = loader({ 12 | // it assigns a URL to your pages 13 | baseUrl: "/pages", 14 | source: pages.toFumadocsSource(), 15 | }); 16 | 17 | // For backward compatibility, export pagesSource as source 18 | export const source = pagesSource; 19 | -------------------------------------------------------------------------------- /apps/web/content/patterns/navigation/navigation-menu.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Navigation Menu" 3 | summary: "Organize and structure site navigation" 4 | description: "Build effective navigation menus for your website. Learn best practices for creating accessible, responsive navigation with proper keyboard support and mobile-friendly interactions." 5 | icon: LayoutDashboard 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/web/app/layout.client.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useParams } from "next/navigation"; 4 | import type { ReactNode } from "react"; 5 | import { cn } from "../lib/cn"; 6 | 7 | export function Body({ 8 | children, 9 | }: { 10 | children: ReactNode; 11 | }): React.ReactElement { 12 | const mode = useMode(); 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | } 20 | 21 | export function useMode(): string | undefined { 22 | const { slug } = useParams(); 23 | return Array.isArray(slug) && slug.length > 0 ? slug[0] : undefined; 24 | } 25 | -------------------------------------------------------------------------------- /apps/gallery/app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UX Patterns Gallery", 3 | "short_name": "UX Gallery", 4 | "description": "Browse 1000+ real UX patterns from top sites", 5 | "icons": [ 6 | { 7 | "src": "/web-app-manifest-192x192.png", 8 | "sizes": "192x192", 9 | "type": "image/png", 10 | "purpose": "maskable" 11 | }, 12 | { 13 | "src": "/web-app-manifest-512x512.png", 14 | "sizes": "512x512", 15 | "type": "image/png", 16 | "purpose": "maskable" 17 | } 18 | ], 19 | "theme_color": "#09090b", 20 | "background_color": "#09090b", 21 | "display": "standalone", 22 | "start_url": "/", 23 | "scope": "/" 24 | } 25 | -------------------------------------------------------------------------------- /apps/kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@thedaviddias/config-typescript/nextjs.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/.source": ["./.source/index.ts"], 7 | "@/*": ["./*"], 8 | "@/registry/*": ["../../packages/registry/registry/default/*"], 9 | "@/ui/*": ["../../packages/registry/registry/default/ui/*"] 10 | }, 11 | "moduleResolution": "bundler", 12 | "skipLibCheck": true 13 | }, 14 | "include": [ 15 | "next-env.d.ts", 16 | "types.d.ts", 17 | "**/*.ts", 18 | "**/*.tsx", 19 | ".next/types/**/*.ts", 20 | ".source/**/*.ts" 21 | ], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/textarea.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Textarea" 3 | summary: "Multi-line text input for longer content" 4 | description: "Learn how to implement accessible textarea components for collecting long-form content, comments, and detailed responses. Discover best practices for multi-line text input with proper validation and user experience." 5 | icon: TextSelect 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | -------------------------------------------------------------------------------- /apps/kit/app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UX Patterns Kit", 3 | "short_name": "UP Kit", 4 | "description": "Ship better UX than most design teams. Production-ready React components.", 5 | "icons": [ 6 | { 7 | "src": "/web-app-manifest-192x192.png", 8 | "sizes": "192x192", 9 | "type": "image/png", 10 | "purpose": "maskable" 11 | }, 12 | { 13 | "src": "/web-app-manifest-512x512.png", 14 | "sizes": "512x512", 15 | "type": "image/png", 16 | "purpose": "maskable" 17 | } 18 | ], 19 | "theme_color": "#09090b", 20 | "background_color": "#09090b", 21 | "display": "standalone", 22 | "start_url": "/", 23 | "scope": "/" 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/components/pattern-preview.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image from "next/image"; 4 | import { usePathname } from "next/navigation"; 5 | 6 | export const PatternPreview = ({ alt }: { alt: string }) => { 7 | const pathname = usePathname(); 8 | const segments = pathname.split("/"); 9 | const patternName = segments[segments.length - 1]; 10 | 11 | return ( 12 |
13 | {alt 21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/gallery/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | export function slugify(text: string): string { 9 | return text 10 | .toLowerCase() 11 | .replace(/\s+/g, "-") 12 | .replace(/[^a-z0-9-]/g, ""); 13 | } 14 | 15 | export function formatWebsite(website: string): string { 16 | return website.replace(/^(https?:\/\/)?(www\.)?/, "").replace(/\/$/, ""); 17 | } 18 | 19 | export function getPatternSlug(pattern: string): string { 20 | return pattern.toLowerCase().replace(/\s+/g, "-"); 21 | } 22 | -------------------------------------------------------------------------------- /apps/kit/app/llms.mdx/[[...slug]]/route.ts: -------------------------------------------------------------------------------- 1 | import { notFound } from "next/navigation"; 2 | import { type NextRequest, NextResponse } from "next/server"; 3 | import { getLLMText } from "@/lib/get-llm-text"; 4 | import { source } from "@/lib/source"; 5 | export const revalidate = false; 6 | export async function GET( 7 | _req: NextRequest, 8 | { params }: { params: Promise<{ slug?: string[] }> }, 9 | ) { 10 | const { slug } = await params; 11 | const page = source.getPage(slug); 12 | if (!page) notFound(); 13 | return new NextResponse(await getLLMText(page)); 14 | } 15 | export function generateStaticParams() { 16 | return source.generateParams(); 17 | } 18 | -------------------------------------------------------------------------------- /packages/constants/src/social.tsx: -------------------------------------------------------------------------------- 1 | import { SiDiscord, SiInstagram, SiX } from "@icons-pack/react-simple-icons"; 2 | import { AUTHOR, PROJECT } from "./author"; 3 | 4 | interface SocialLink { 5 | label: string; 6 | link: string; 7 | icon?: React.ReactNode; 8 | rel?: "me"; 9 | } 10 | 11 | export const SOCIAL_LINKS: SocialLink[] = [ 12 | { 13 | label: "Discord", 14 | link: PROJECT.discord, 15 | icon: , 16 | }, 17 | { 18 | label: "X", 19 | link: AUTHOR.social.twitterUrl, 20 | rel: "me" as const, 21 | icon: , 22 | }, 23 | { 24 | label: "Instagram", 25 | link: AUTHOR.social.instagramUrl, 26 | icon: , 27 | }, 28 | ]; 29 | -------------------------------------------------------------------------------- /apps/gallery/components/header/search-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { LargeSearchToggleBtn } from "@ux-patterns/ui/components/custom/search-button"; 4 | import { usePlausible } from "next-plausible"; 5 | import { useSearch } from "@/lib/search-context"; 6 | import { trackGallerySearch } from "@/lib/tracking"; 7 | 8 | export function SearchButton() { 9 | const { openSearch } = useSearch(); 10 | const plausible = usePlausible(); 11 | 12 | const handleSearchClick = () => { 13 | trackGallerySearch(plausible, "open"); 14 | openSearch(); 15 | }; 16 | 17 | return ( 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /configs/typescript/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "declaration": true, 6 | "declarationMap": true, 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "incremental": true, 10 | "tsBuildInfoFile": ".tsbuildinfo", 11 | "isolatedModules": true, 12 | "lib": ["es2022", "DOM", "DOM.Iterable"], 13 | "module": "NodeNext", 14 | "moduleDetection": "force", 15 | "moduleResolution": "NodeNext", 16 | "resolveJsonModule": true, 17 | "skipLibCheck": true, 18 | "strict": true, 19 | "target": "ES2022", 20 | "strictNullChecks": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/app/llms.mdx/[...slug]/route.ts: -------------------------------------------------------------------------------- 1 | import { notFound } from "next/navigation"; 2 | import { type NextRequest, NextResponse } from "next/server"; 3 | import { getLLMText } from "@/lib/get-llm-text"; 4 | import { source } from "@/lib/source"; 5 | 6 | export const revalidate = false; 7 | 8 | export async function GET( 9 | _req: NextRequest, 10 | { params }: { params: Promise<{ slug: string[] }> }, 11 | ) { 12 | const slug = (await params).slug; 13 | const page = source.getPage(slug); 14 | if (!page) notFound(); 15 | 16 | return new NextResponse(await getLLMText(page)); 17 | } 18 | 19 | export function generateStaticParams() { 20 | return source.generateParams(); 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/notification.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Notification" 3 | summary: "Inform users about important updates" 4 | description: "Implement effective notification systems in your web applications. Learn best practices for toast messages, alerts, and user notifications with proper timing and accessibility." 5 | icon: Bell 6 | status: draft 7 | --- 8 | 9 | 10 | This page is a work in progress. Don't consider it complete yet. 11 | 12 | 13 | **_(Also called toast)_** 14 | 15 | ## Overview 16 | 17 | ## Usage 18 | 19 | - To inform the user about a change in the application 20 | - To alert the user about an error or warning 21 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-link.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-link 4 | * @title Link Button 5 | * @type registry:block 6 | * @description Link-styled button variant. For navigation, prefer using asChild with an element to preserve link semantics and accessibility. 7 | * @categories ["buttons", "variants", "navigation"] 8 | * @tags ["link", "variant", "navigation", "text", "minimal"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonLink() { 14 | return ( 15 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/lib/is-active.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarTab } from "fumadocs-ui/utils/get-sidebar-tabs"; 2 | 3 | function normalize(url: string) { 4 | if (url.length > 1 && url.endsWith("/")) return url.slice(0, -1); 5 | return url; 6 | } 7 | 8 | export function isActive( 9 | url: string, 10 | pathname: string, 11 | nested = true, 12 | ): boolean { 13 | url = normalize(url); 14 | pathname = normalize(pathname); 15 | 16 | return url === pathname || (nested && pathname.startsWith(`${url}/`)); 17 | } 18 | 19 | export function isTabActive(tab: SidebarTab, pathname: string) { 20 | if (tab.urls) return tab.urls.has(normalize(pathname)); 21 | 22 | return isActive(tab.url, pathname, true); 23 | } 24 | -------------------------------------------------------------------------------- /packages/constants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ux-patterns/constants", 3 | "version": "0.1.0", 4 | "description": "Shared React constants for UX Patterns applications", 5 | "type": "module", 6 | "exports": { 7 | "./social": "./src/social.tsx", 8 | "./patterns": "./src/patterns.ts", 9 | "./author": "./src/author.ts" 10 | }, 11 | "scripts": { 12 | "check": "tsc --noEmit", 13 | "check:type": "tsc --noEmit" 14 | }, 15 | "dependencies": { 16 | "@icons-pack/react-simple-icons": "^13.7.0", 17 | "react": "^19.1.1" 18 | }, 19 | "devDependencies": { 20 | "@thedaviddias/config-typescript": "workspace:*", 21 | "@types/react": "^19.1.12", 22 | "typescript": "^5.9.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | format: 5 | glob: "*.{js,jsx,ts,tsx,json}" 6 | run: npx @biomejs/biome format --write {staged_files} 7 | lint: 8 | glob: "*.{js,jsx,ts,tsx}" 9 | run: npx @biomejs/biome lint {staged_files} 10 | organize-imports: 11 | glob: "*.{js,jsx,ts,tsx}" 12 | run: npx @biomejs/biome check --write {staged_files} 13 | sync-gallery-metadata: 14 | glob: "apps/gallery/content/entries/**/*.mdx" 15 | run: | 16 | cd apps/gallery && 17 | pnpm tsx scripts/sync-websites-metadata.ts && 18 | pnpm tsx scripts/extract-favicon-colors.ts && 19 | git add data/websites.json 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/custom/registry/component-source.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "@ux-patterns/ui/lib/utils"; 4 | import type * as React from "react"; 5 | import { CodeBlockWrapper } from "../code/code-block-wrapper"; 6 | 7 | interface ComponentSourceProps extends React.HTMLAttributes { 8 | src: string; 9 | } 10 | 11 | export function ComponentSource({ 12 | children, 13 | className, 14 | ...props 15 | }: ComponentSourceProps) { 16 | return ( 17 | 22 | {children} 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /apps/web/components/glossary/terms-list-container.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from "react"; 2 | import { getGlossaryTerms } from "@/utils/get-glossary-terms"; 3 | import { TermsList } from "./terms-list"; 4 | 5 | function LoadingTerms() { 6 | return
Loading glossary terms...
; 7 | } 8 | 9 | export function TermsListContainer() { 10 | return ( 11 | }> 12 | 13 | 14 | ); 15 | } 16 | 17 | async function TermsListContent() { 18 | const terms = await getGlossaryTerms(); 19 | 20 | if (!terms || terms.length === 0) { 21 | return

No glossary terms found.

; 22 | } 23 | 24 | return ; 25 | } 26 | -------------------------------------------------------------------------------- /apps/web/instrumentation-client.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the client. 2 | // The config you add here will be used whenever a users loads a page in their browser. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from "@sentry/nextjs"; 6 | 7 | Sentry.init({ 8 | dsn: "https://931a829b16ed3b014310f19f0a55c618@o515454.ingest.us.sentry.io/4508413880041472", 9 | 10 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 11 | debug: false, 12 | enabled: process.env.NODE_ENV !== "development", 13 | }); 14 | 15 | export const onRouterTransitionStart = Sentry.captureRouterTransitionStart; 16 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/currency-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Currency Input" 3 | summary: "Enter and format monetary values" 4 | description: "Create currency input fields with number formatting and international currency handling." 5 | icon: DollarSign 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Internationalization 16 | 17 | ### Internationalization Beyond Localization 18 | 19 | - **Currency Formatting** → Adapt for region-specific formats (e.g., `$1,299.99 USD` vs. `1.299,99 € EUR`). 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/progress-indicator.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Progress Indicator" 3 | summary: "Show completion status of an operation" 4 | description: "Create effective progress indicators for your web applications. Learn best practices for implementing progress bars, step indicators, and completion feedback with proper accessibility." 5 | icon: Gauge 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | Not to be confused with [loading indicator](/patterns/user-feedback/loading-indicator). 16 | -------------------------------------------------------------------------------- /apps/web/components/glossary/term-page.tsx: -------------------------------------------------------------------------------- 1 | import { GlossaryStructuredData } from "./structured-data"; 2 | 3 | interface TermPageProps { 4 | frontMatter: { 5 | title: string; 6 | description: string; 7 | category?: string[]; 8 | related_patterns?: string[]; 9 | synonyms?: string[]; 10 | }; 11 | url?: string; 12 | children: React.ReactNode; 13 | } 14 | 15 | export function TermPage({ frontMatter, url, children }: TermPageProps) { 16 | return ( 17 |
18 | 24 | {children} 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/examples/patterns/content-management/modal/native.ts: -------------------------------------------------------------------------------- 1 | export const nativeModalExample = ` 2 | 3 | 9 | 10 | 11 | 12 |

Modal Title

13 |

Modal content goes here...

14 | 15 | 25 |
26 | `; 27 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Forms", 3 | "description": "Form components and input patterns", 4 | "icon": "FileText", 5 | "pages": [ 6 | "autocomplete", 7 | "button", 8 | "checkbox", 9 | "code-confirmation", 10 | "color-picker", 11 | "currency-input", 12 | "date-input", 13 | "date-picker", 14 | "date-range", 15 | "file-input", 16 | "form-validation", 17 | "multi-select-input", 18 | "password", 19 | "phone-number", 20 | "radio", 21 | "rating-input", 22 | "rich-text-editor", 23 | "search-field", 24 | "selection-input", 25 | "signature-pad", 26 | "slider", 27 | "tag-input", 28 | "text-field", 29 | "textarea", 30 | "time-input", 31 | "toggle" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/seo/src/structured-data/breadcrumb.ts: -------------------------------------------------------------------------------- 1 | import type { BreadcrumbItem, SchemaOrgBase } from "../types"; 2 | import { StructuredDataGenerator } from "./base"; 3 | 4 | export class BreadcrumbSchema extends StructuredDataGenerator { 5 | /** 6 | * Generate BreadcrumbList schema 7 | */ 8 | breadcrumbs(items: BreadcrumbItem[]): SchemaOrgBase & Record { 9 | return { 10 | "@context": "https://schema.org", 11 | "@type": "BreadcrumbList", 12 | itemListElement: items.map((item, index) => ({ 13 | "@type": "ListItem", 14 | position: item.position || index + 1, 15 | name: item.name, 16 | ...(item.url && { 17 | item: this.absoluteUrl(item.url), 18 | }), 19 | })), 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/code-confirmation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Code Confirmation" 3 | summary: "Verify codes with segmented input" 4 | description: "Implement user-friendly code confirmation inputs for verification codes and OTPs. Learn best practices for segmented inputs, auto-focus behavior, and accessibility." 5 | icon: KeyRound 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ![2FA Code Confirmation](/patterns/code-confirmation/do/paypal.jpg) 16 | _Good example of 2FA code confirmation screen from PayPal_ 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Next.js: debug server-side", 6 | "type": "node-terminal", 7 | "request": "launch", 8 | "command": "pnpm dev" 9 | }, 10 | { 11 | "name": "Next.js: debug client-side", 12 | "request": "launch", 13 | "url": "http://localhost:3060" 14 | }, 15 | { 16 | "name": "Next.js: debug full stack", 17 | "type": "node-terminal", 18 | "request": "launch", 19 | "command": "pnpm dev", 20 | "serverReadyAction": { 21 | "pattern": "started server on .+, url: (https?://.+)", 22 | "uriFormat": "%s", 23 | "action": "debugWithChrome" 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 20 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /apps/gallery/components/common/empty-state.tsx: -------------------------------------------------------------------------------- 1 | interface EmptyStateProps { 2 | message: string; 3 | subMessage?: string; 4 | linkHref?: string; 5 | linkText?: string; 6 | } 7 | 8 | export function EmptyState({ 9 | message, 10 | subMessage, 11 | linkHref, 12 | linkText, 13 | }: EmptyStateProps) { 14 | return ( 15 |
16 |

{message}

17 | {subMessage && ( 18 |

{subMessage}

19 | )} 20 | {linkHref && linkText && ( 21 | 25 | {linkText} 26 | 27 | )} 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/slider.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Slider" 3 | summary: "Select values from a range" 4 | description: "Learn how to implement accessible range slider inputs in your web applications. Discover best practices for continuous value selection, touch interactions, and accessibility." 5 | icon: SlidersHorizontal 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Resources 16 | 17 | ### Articles 18 | 19 | - [Designing The Perfect Slider UX](https://www.smashingmagazine.com/2017/07/designing-perfect-slider/) by Smashing Magazine 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/social/like-button.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Like Button" 3 | summary: "Like and reaction buttons" 4 | description: "Learn how to implement like buttons and reactions. Discover best practices for engagement tracking, animations, and accessibility." 5 | icon: Heart 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - User comments: [Comment System](/patterns/social/comment-system) 18 | - Share actions: [Share Dialog](/patterns/social/share-dialog) 19 | - Toggle states: [Toggle](/patterns/forms/toggle) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/user-feedback/loading-indicator.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Loading Indicator" 3 | summary: "Show users that content is being loaded" 4 | description: "Build effective loading indicators for your web applications. Learn best practices for implementing loading states, spinners, and progress feedback with proper accessibility." 5 | icon: Loader 6 | status: draft 7 | --- 8 | 9 | 10 | This page is a work in progress. Don't consider it complete yet. 11 | 12 | 13 | **_(Also called loader, loading, spinner)_** 14 | 15 | Not to be confused with [progress indicator](/patterns/user-feedback/progress-indicator). 16 | 17 | ## Overview 18 | 19 | A **loading indicator** is a visual cue that indicates that a task is in progress. 20 | -------------------------------------------------------------------------------- /apps/web/sentry.edge.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on). 2 | // The config you add here will be used whenever one of the edge features is loaded. 3 | // Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally. 4 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 5 | 6 | import * as Sentry from "@sentry/nextjs"; 7 | 8 | Sentry.init({ 9 | dsn: "https://931a829b16ed3b014310f19f0a55c618@o515454.ingest.us.sentry.io/4508413880041472", 10 | 11 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 12 | debug: false, 13 | enabled: process.env.NODE_ENV !== "development", 14 | }); 15 | -------------------------------------------------------------------------------- /apps/web/content/patterns/media/image-upload.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Image Upload" 3 | summary: "Upload and preview images" 4 | description: "Learn how to implement image upload interfaces. Discover best practices for drag-and-drop, preview, and validation." 5 | icon: Upload 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - File uploads: [File Input](/patterns/forms/file-input) 18 | - Drag interactions: [Drag and Drop](/patterns/content-management/drag-and-drop) 19 | - Image display: [Image Gallery](/patterns/media/image-gallery) 20 | -------------------------------------------------------------------------------- /apps/gallery/components/common/keyboard-shortcuts.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | import { useSearch } from "@/lib/search-context"; 5 | 6 | export function KeyboardShortcuts() { 7 | const { openSearch } = useSearch(); 8 | 9 | useEffect(() => { 10 | const handleKeyDown = (event: KeyboardEvent) => { 11 | // Check for Command+K (macOS) or Ctrl+K (Windows/Linux) 12 | if ((event.metaKey || event.ctrlKey) && event.key === "k") { 13 | event.preventDefault(); 14 | openSearch(); 15 | } 16 | }; 17 | 18 | window.addEventListener("keydown", handleKeyDown); 19 | return () => { 20 | window.removeEventListener("keydown", handleKeyDown); 21 | }; 22 | }, [openSearch]); 23 | 24 | return null; // This component doesn't render anything 25 | } 26 | -------------------------------------------------------------------------------- /apps/web/content/patterns/media/image-gallery.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Image Gallery" 3 | summary: "Display and browse image collections" 4 | description: "Learn how to implement image galleries. Discover best practices for lightboxes, thumbnails, and image navigation." 5 | icon: Images 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Slideshow display: [Carousel](/patterns/content-management/carousel) 18 | - Image uploads: [Image Upload](/patterns/media/image-upload) 19 | - Fullscreen viewing: [Modal](/patterns/content-management/modal) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/social/share-dialog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Share Dialog" 3 | summary: "Social sharing functionality" 4 | description: "Learn how to implement share dialogs. Discover best practices for social media integration, copy links, and sharing analytics." 5 | icon: Share2 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Modal dialogs: [Modal](/patterns/content-management/modal) 18 | - Copy to clipboard: [Tooltip](/patterns/content-management/tooltip) 19 | - Social engagement: [Like Button](/patterns/social/like-button) 20 | -------------------------------------------------------------------------------- /packages/registry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@thedaviddias/config-typescript/react-library.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "types": ["vitest/globals", "@testing-library/jest-dom", "vitest-axe"], 8 | "paths": { 9 | "@": ["./registry/default"], 10 | "@/*": ["./registry/default/*"], 11 | "@/registry/*": ["./registry/default/*"], 12 | "@/components/ui/*": ["./registry/default/ui/*"], 13 | "@/ui/*": ["./registry/default/ui/*"], 14 | "@/lib/*": ["./registry/default/lib/*"] 15 | } 16 | }, 17 | "include": [ 18 | "registry/**/*", 19 | "scripts/**/*", 20 | ".generated/**/*", 21 | "tests/**/*", 22 | "test-setup.ts" 23 | ], 24 | "exclude": ["node_modules", "public"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/kit/app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as Sentry from "@sentry/nextjs"; 4 | import NextError from "next/error"; 5 | import { useEffect } from "react"; 6 | 7 | export default function GlobalError({ 8 | error, 9 | }: { 10 | error: Error & { digest?: string }; 11 | }) { 12 | useEffect(() => { 13 | Sentry.captureException(error); 14 | }, [error]); 15 | 16 | return ( 17 | 18 | 19 | {/* `NextError` is the default Next.js error page component. Its type 20 | definition requires a `statusCode` prop. However, since the App Router 21 | does not expose status codes for errors, we simply pass 0 to render a 22 | generic error message. */} 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as Sentry from "@sentry/nextjs"; 4 | import NextError from "next/error"; 5 | import { useEffect } from "react"; 6 | 7 | export default function GlobalError({ 8 | error, 9 | }: { 10 | error: Error & { digest?: string }; 11 | }) { 12 | useEffect(() => { 13 | Sentry.captureException(error); 14 | }, [error]); 15 | 16 | return ( 17 | 18 | 19 | {/* `NextError` is the default Next.js error page component. Its type 20 | definition requires a `statusCode` prop. However, since the App Router 21 | does not expose status codes for errors, we simply pass 0 to render a 22 | generic error message. */} 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/calendar.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Calendar View" 3 | summary: "Display dates and events in calendar format" 4 | description: "Learn how to implement calendar views. Discover best practices for date navigation, event display, and calendar interactions." 5 | icon: CalendarDays 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Date selection: [Date Picker](/patterns/forms/date-picker) 18 | - Event timeline: [Timeline](/patterns/data-display/timeline) 19 | - Date ranges: [Date Range](/patterns/forms/date-range) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/media/video-player.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Video Player" 3 | summary: "Video playback with controls" 4 | description: "Learn how to implement video players. Discover best practices for playback controls, captions, and responsive video." 5 | icon: Video 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Audio playback: [Video Player](/patterns/media/video-player) (similar controls) 18 | - Media galleries: [Image Gallery](/patterns/media/image-gallery) 19 | - Fullscreen mode: [Modal](/patterns/content-management/modal) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/social/comment-system.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Comment System" 3 | summary: "User comments and discussion threads" 4 | description: "Learn how to implement comment systems. Discover best practices for threaded discussions, moderation, and user interactions." 5 | icon: MessageSquare 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - User reactions: [Like Button](/patterns/social/like-button) 18 | - User mentions: [Tag Input](/patterns/forms/tag-input) 19 | - Activity updates: [Activity Feed](/patterns/social/activity-feed) 20 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "bundler", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "paths": { 19 | "@/.source": ["./.source/index.ts"], 20 | "@/*": ["./*"] 21 | }, 22 | "plugins": [ 23 | { 24 | "name": "next" 25 | } 26 | ] 27 | }, 28 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 29 | "exclude": ["node_modules"] 30 | } 31 | -------------------------------------------------------------------------------- /apps/gallery/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "bundler", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "paths": { 19 | "@/.source": ["./.source/index.ts"], 20 | "@/*": ["./*"] 21 | }, 22 | "plugins": [ 23 | { 24 | "name": "next" 25 | } 26 | ] 27 | }, 28 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 29 | "exclude": ["node_modules"] 30 | } 31 | -------------------------------------------------------------------------------- /apps/web/content/patterns/advanced/search-results.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Search Results" 3 | summary: "Display and filter search results" 4 | description: "Learn how to implement search results pages. Discover best practices for result ranking, filtering, and pagination." 5 | icon: FileSearch 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Search input: [Search Field](/patterns/forms/search-field) 18 | - Result filtering: [Filter Panel](/patterns/data-display/filter-panel) 19 | - Result pagination: [Pagination](/patterns/navigation/pagination) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/password-reset.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Password Reset" 3 | summary: "Password recovery and reset flows" 4 | description: "Learn how to implement secure password reset functionality. Discover best practices for recovery emails, token validation, and security considerations." 5 | icon: Key 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Sign in: [Login Form](/patterns/authentication/login) 18 | - Password fields: [Password](/patterns/forms/password) 19 | - Email validation: [Text Field](/patterns/forms/text-field) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/social-login.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Social Login" 3 | summary: "OAuth and social media authentication" 4 | description: "Learn how to implement social login. Discover best practices for OAuth flows, provider integration, and account linking." 5 | icon: LogIn 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Traditional login: [Login Form](/patterns/authentication/login) 18 | - New accounts: [Sign Up Flow](/patterns/authentication/signup) 19 | - Account linking: [User Profile](/patterns/authentication/user-profile) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/user-profile.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "User Profile" 3 | summary: "User profile and account management" 4 | description: "Learn how to implement user profile interfaces. Discover best practices for displaying user information, avatars, and account settings." 5 | icon: User 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Account settings: [Form Validation](/patterns/forms/form-validation) 18 | - Avatar upload: [File Input](/patterns/forms/file-input) 19 | - Profile editing: [Text Field](/patterns/forms/text-field) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/chart.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Charts & Graphs" 3 | summary: "Visualize data with graphs and charts" 4 | description: "Learn how to implement accessible charts and graphs. Discover best practices for data visualization, chart types, and responsive designs." 5 | icon: ChartBar 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Tabular data: [Data Table](/patterns/data-display/table) 18 | - Loading states: [Skeleton](/patterns/user-feedback/skeleton) 19 | - Empty data: [Empty States](/patterns/user-feedback/empty-states) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/filter-panel.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Filter Panel" 3 | summary: "Filter and refine data displays" 4 | description: "Learn how to implement filter panels for data refinement. Discover best practices for faceted search, filter UI, and dynamic filtering." 5 | icon: ListFilter 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Data display: [Data Table](/patterns/data-display/table) 18 | - Search functionality: [Search Field](/patterns/forms/search-field) 19 | - Multiple selections: [Checkbox](/patterns/forms/checkbox) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/signup.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sign Up Flow" 3 | summary: "User registration and account creation" 4 | description: "Learn how to implement effective sign-up flows. Discover best practices for user registration, form validation, and onboarding experiences." 5 | icon: UserPlus 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Existing users: [Login Form](/patterns/authentication/login) 18 | - Form validation: [Form Validation](/patterns/forms/form-validation) 19 | - Password creation: [Password](/patterns/forms/password) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/statistics.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Statistics Display" 3 | summary: "Display key metrics and statistics" 4 | description: "Learn how to implement statistics displays. Discover best practices for metric cards, KPI dashboards, and data visualization." 5 | icon: TrendingUp 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Data visualization: [Charts & Graphs](/patterns/data-display/chart) 18 | - Card layouts: [Card Grid](/patterns/data-display/card-grid) 19 | - Dashboards: [Dashboard Layout](/patterns/data-display/dashboard) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/e-commerce/product-card.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Product Card" 3 | summary: "Product display cards for e-commerce" 4 | description: "Learn how to implement effective product cards. Discover best practices for product images, pricing display, and quick actions." 5 | icon: Package 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Product grids: [Card Grid](/patterns/data-display/card-grid) 18 | - Cart management: [Shopping Cart](/patterns/e-commerce/shopping-cart) 19 | - Image display: [Carousel](/patterns/content-management/carousel) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/social/activity-feed.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Activity Feed" 3 | summary: "Social activity and updates stream" 4 | description: "Learn how to implement activity feeds. Discover best practices for real-time updates, infinite scrolling, and engagement tracking." 5 | icon: Activity 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Timeline view: [Timeline](/patterns/data-display/timeline) 18 | - Loading more: [Infinite Scroll](/patterns/navigation/infinite-scroll) 19 | - User interactions: [Comment System](/patterns/social/comment-system) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/advanced/command-palette.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Command Palette" 3 | summary: "Quick command execution interface" 4 | description: "Learn how to implement command palettes. Discover best practices for keyboard shortcuts, fuzzy search, and command discovery." 5 | icon: Terminal 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Search interface: [Search Field](/patterns/forms/search-field) 18 | - Keyboard navigation: [Navigation Menu](/patterns/navigation/navigation-menu) 19 | - Modal interface: [Modal](/patterns/content-management/modal) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/comparison-table.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Comparison Table" 3 | summary: "Compare features and options side-by-side" 4 | description: "Learn how to implement comparison tables. Discover best practices for feature comparisons, pricing tables, and decision matrices." 5 | icon: Table2 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Data tables: [Data Table](/patterns/data-display/table) 18 | - Pricing display: [Product Card](/patterns/e-commerce/product-card) 19 | - Feature lists: [List View](/patterns/data-display/list-view) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/e-commerce/checkout.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Checkout Flow" 3 | summary: "Multi-step checkout process" 4 | description: "Learn how to implement checkout flows. Discover best practices for payment forms, order review, and conversion optimization." 5 | icon: CreditCard 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Cart review: [Shopping Cart](/patterns/e-commerce/shopping-cart) 18 | - Payment forms: [Form Validation](/patterns/forms/form-validation) 19 | - Progress tracking: [Progress Indicator](/patterns/user-feedback/progress-indicator) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/e-commerce/shopping-cart.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Shopping Cart" 3 | summary: "Shopping cart and item management" 4 | description: "Learn how to implement shopping cart functionality. Discover best practices for cart management, item updates, and persistent storage." 5 | icon: ShoppingCart 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Product display: [Product Card](/patterns/e-commerce/product-card) 18 | - Next step: [Checkout Flow](/patterns/e-commerce/checkout) 19 | - Quantity selection: [Number Input](/patterns/forms/text-field) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/advanced/wizard.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Wizard / Stepper" 3 | summary: "Multi-step forms and processes" 4 | description: "Learn how to implement wizards and steppers. Discover best practices for multi-step forms, progress tracking, and step validation." 5 | icon: GitBranch 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Progress tracking: [Progress Indicator](/patterns/user-feedback/progress-indicator) 18 | - Form validation: [Form Validation](/patterns/forms/form-validation) 19 | - Navigation: [Breadcrumb](/patterns/navigation/breadcrumb) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/prompt-input.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Prompt Input" 3 | summary: "Enhanced text inputs for AI prompts" 4 | description: "Learn how to implement prompt input interfaces. Discover best practices for multiline inputs, prompt templates, and input enhancements." 5 | icon: Pen 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Basic input: [Textarea](/patterns/forms/textarea) 18 | - Token limits: [Token Counter](/patterns/ai-intelligence/token-counter) 19 | - AI suggestions: [AI Suggestions](/patterns/ai-intelligence/ai-suggestions) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/login.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Login Form" 3 | summary: "User authentication and sign-in forms" 4 | description: "Learn how to implement secure and user-friendly login forms. Discover best practices for authentication, password fields, and remember me functionality." 5 | icon: LogIn 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - New users: [Sign Up](/patterns/authentication/signup) 18 | - Password fields: [Password](/patterns/forms/password) 19 | - Password recovery: [Password Reset](/patterns/authentication/password-reset) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/dashboard.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Dashboard Layout" 3 | summary: "Comprehensive dashboard layouts" 4 | description: "Learn how to implement dashboard layouts. Discover best practices for widget organization, responsive grids, and data visualization." 5 | icon: ChartBar 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Metrics display: [Statistics Display](/patterns/data-display/statistics) 18 | - Data visualization: [Charts & Graphs](/patterns/data-display/chart) 19 | - Grid layouts: [Card Grid](/patterns/data-display/card-grid) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/tree-view.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tree View" 3 | summary: "Display hierarchical data structures" 4 | description: "Learn how to implement tree views for hierarchical data. Discover best practices for expandable nodes, selection states, and keyboard navigation." 5 | icon: GitBranch 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Expandable content: [Accordion](/patterns/content-management/accordion) 18 | - Navigation hierarchy: [Sidebar](/patterns/navigation/sidebar) 19 | - Selection controls: [Checkbox](/patterns/forms/checkbox) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/model-selector.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Model Selector" 3 | summary: "AI model selection interface" 4 | description: "Learn how to implement model selectors. Discover best practices for model comparison, capability display, and settings management." 5 | icon: Cpu 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Selection UI: [Selection Input](/patterns/forms/selection-input) 18 | - Settings: [Account Settings](/patterns/authentication/account-settings) 19 | - Comparison: [Comparison Table](/patterns/data-display/comparison-table) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/token-counter.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Token Counter" 3 | summary: "Display token usage and limits" 4 | description: "Learn how to implement token counters. Discover best practices for usage indicators, limit warnings, and cost estimation." 5 | icon: Hash 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Progress display: [Progress Indicator](/patterns/user-feedback/progress-indicator) 18 | - Input limits: [Prompt Input](/patterns/ai-intelligence/prompt-input) 19 | - Usage stats: [Statistics Display](/patterns/data-display/statistics) 20 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-sizes.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-sizes 4 | * @title Button Sizes 5 | * @type registry:block 6 | * @description Button component showcasing different size variants 7 | * @categories ["buttons", "sizes", "variants"] 8 | * @tags ["sizes", "variants", "xs", "sm", "md", "lg", "responsive"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonSizes() { 14 | return ( 15 |
16 | 17 | 18 | 19 | 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/ai-error-states.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "AI Error States" 3 | summary: "Handling AI-specific errors" 4 | description: "Learn how to implement AI error states. Discover best practices for rate limits, model errors, and graceful degradation." 5 | icon: CircleAlert 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - General errors: [Notification](/patterns/user-feedback/notification) 18 | - Empty states: [Empty States](/patterns/user-feedback/empty-states) 19 | - Response retry: [Response Feedback](/patterns/ai-intelligence/response-feedback) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/response-feedback.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Response Feedback" 3 | summary: "Feedback mechanisms for AI responses" 4 | description: "Learn how to implement response feedback. Discover best practices for ratings, regeneration, and improvement signals." 5 | icon: ThumbsUp 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Rating interface: [Rating Input](/patterns/forms/rating-input) 18 | - User reactions: [Like Button](/patterns/social/like-button) 19 | - Response display: [Streaming Response](/patterns/ai-intelligence/streaming-response) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/context-window.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Context Window" 3 | summary: "Managing AI conversation context" 4 | description: "Learn how to implement context window management. Discover best practices for conversation history, context limits, and memory management." 5 | icon: Archive 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Chat interface: [AI Chat Interface](/patterns/ai-intelligence/ai-chat) 18 | - Token limits: [Token Counter](/patterns/ai-intelligence/token-counter) 19 | - History display: [Timeline](/patterns/data-display/timeline) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/kanban-board.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Kanban Board" 3 | summary: "Organize tasks in columns and swimlanes" 4 | description: "Learn how to implement kanban boards. Discover best practices for drag-and-drop, task management, and workflow visualization." 5 | icon: Trello 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Drag interactions: [Drag and Drop](/patterns/content-management/drag-and-drop) 18 | - Task cards: [Card Grid](/patterns/data-display/card-grid) 19 | - Progress tracking: [Progress Indicator](/patterns/user-feedback/progress-indicator) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/timeline.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Timeline" 3 | summary: "Display chronological events and activities" 4 | description: "Learn how to implement timelines for showing chronological data. Discover best practices for activity feeds, history logs, and event sequences." 5 | icon: Clock 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Activity updates: [Activity Feed](/patterns/social/activity-feed) 18 | - Progress tracking: [Progress Indicator](/patterns/user-feedback/progress-indicator) 19 | - Loading more: [Load More](/patterns/navigation/load-more) 20 | -------------------------------------------------------------------------------- /apps/web/components/glossary/structured-data.tsx: -------------------------------------------------------------------------------- 1 | import { JsonLd } from "@/components/json-ld"; 2 | 3 | interface GlossaryStructuredDataProps { 4 | term: string; 5 | definition: string; 6 | category: string[]; 7 | url?: string; 8 | } 9 | 10 | export function GlossaryStructuredData({ 11 | term, 12 | definition, 13 | category, 14 | url, 15 | }: GlossaryStructuredDataProps) { 16 | const structuredData = { 17 | "@context": "https://schema.org", 18 | "@type": "DefinedTerm", 19 | name: term, 20 | description: definition, 21 | url: url, 22 | inDefinedTermSet: { 23 | "@type": "DefinedTermSet", 24 | name: "UX Patterns Glossary", 25 | url: "https://uxpatterns.dev/glossary", 26 | }, 27 | ...(category.length > 0 && { termCode: category.join(",") }), 28 | }; 29 | 30 | return ; 31 | } 32 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/two-factor.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Two-Factor Authentication" 3 | summary: "Two-factor authentication setup and verification" 4 | description: "Learn how to implement two-factor authentication. Discover best practices for TOTP, SMS verification, and security keys." 5 | icon: Shield 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Login security: [Login Form](/patterns/authentication/login) 18 | - Code entry: [Code Confirmation](/patterns/forms/code-confirmation) 19 | - Account security: [Account Settings](/patterns/authentication/account-settings) 20 | -------------------------------------------------------------------------------- /apps/gallery/components/common/platform-badge.tsx: -------------------------------------------------------------------------------- 1 | import { Monitor, Smartphone } from "lucide-react"; 2 | import Link from "next/link"; 3 | import { cn } from "@/lib/utils"; 4 | 5 | interface PlatformBadgeProps { 6 | platform: string; 7 | className?: string; 8 | } 9 | 10 | export function PlatformBadge({ platform, className }: PlatformBadgeProps) { 11 | const Icon = platform === "web" ? Monitor : Smartphone; 12 | const href = `/${platform}`; 13 | 14 | return ( 15 | 22 | 23 | {platform} 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /apps/web/components/decision-flow/types.ts: -------------------------------------------------------------------------------- 1 | import type { Edge, Node } from "@xyflow/react"; 2 | 3 | export type NodeData = { 4 | label: string; 5 | description?: string; 6 | patternLink?: string; 7 | }; 8 | 9 | export type DecisionNodeType = 10 | | "question" // Decision points 11 | | "pattern" // Pattern recommendations 12 | | "category" // Pattern categories 13 | | "consideration" // Important factors to consider 14 | | "comparison"; // Compare multiple patterns 15 | 16 | export type DecisionNode = Node & { 17 | type: DecisionNodeType; 18 | }; 19 | 20 | export type DecisionEdge = Edge & { 21 | label?: string; 22 | }; 23 | 24 | export interface DecisionFlowProps { 25 | nodes: DecisionNode[]; 26 | edges: DecisionEdge[]; 27 | className?: string; 28 | title?: string; // Optional title for the download filename 29 | } 30 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/ai-chat.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "AI Chat Interface" 3 | summary: "Conversational AI chat interfaces" 4 | description: "Learn how to implement AI chat interfaces. Discover best practices for message threading, context management, and conversational UX." 5 | icon: Bot 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Message input: [Prompt Input](/patterns/ai-intelligence/prompt-input) 18 | - Response display: [Streaming Response](/patterns/ai-intelligence/streaming-response) 19 | - Chat history: [Context Window](/patterns/ai-intelligence/context-window) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/ai-suggestions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "AI Suggestions" 3 | summary: "AI-powered autocomplete and suggestions" 4 | description: "Learn how to implement AI suggestions. Discover best practices for predictive text, smart completions, and recommendation interfaces." 5 | icon: Sparkles 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Basic autocomplete: [Autocomplete](/patterns/forms/autocomplete) 18 | - Prompt enhancement: [Prompt Input](/patterns/ai-intelligence/prompt-input) 19 | - Selection interface: [Selection Input](/patterns/forms/selection-input) 20 | -------------------------------------------------------------------------------- /apps/web/app/llms-full.txt/route.ts: -------------------------------------------------------------------------------- 1 | import { getLLMText } from "@/lib/get-llm-text"; 2 | import { source } from "@/lib/source"; 3 | 4 | // Cache for 24 hours (86400 seconds) 5 | export const revalidate = 86400; 6 | 7 | export async function GET() { 8 | const scan = source 9 | .getPages() 10 | .filter((file) => file.slugs[0] !== "pages") 11 | .filter((file) => file.slugs[0] !== "blog") 12 | .map(getLLMText); 13 | const scanned = await Promise.all(scan); 14 | 15 | const content = scanned.join("\n\n"); 16 | 17 | return new Response(content, { 18 | headers: { 19 | "Content-Type": "text/plain; charset=utf-8", 20 | "Cache-Control": 21 | "public, max-age=86400, s-maxage=86400, stale-while-revalidate=604800", 22 | "CDN-Cache-Control": "max-age=86400", 23 | "Vercel-CDN-Cache-Control": "max-age=86400", 24 | }, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /apps/web/content/patterns/authentication/account-settings.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Account Settings" 3 | summary: "User account configuration and preferences" 4 | description: "Learn how to implement account settings. Discover best practices for preference management, privacy controls, and account configuration." 5 | icon: Settings 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Profile management: [User Profile](/patterns/authentication/user-profile) 18 | - Security settings: [Two-Factor Authentication](/patterns/authentication/two-factor) 19 | - Preference toggles: [Toggle](/patterns/forms/toggle) 20 | -------------------------------------------------------------------------------- /apps/web/content/patterns/forms/radio.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Radio" 3 | summary: "Select a single option from a group" 4 | description: "Implement accessible radio button groups in your web applications. Learn best practices for single-choice selection controls, keyboard navigation, and ARIA attributes." 5 | icon: CircleDot 6 | status: draft 7 | --- 8 | 9 | import { GuidesBanner } from "@/components/guides-banner"; 10 | 11 | 12 | This page is empty for now. Please help us by 13 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 14 | to add content. 15 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /packages/hooks/src/use-copy-to-clipboard.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | 5 | export function useCopyToClipboard({ 6 | timeout = 2000, 7 | onCopy, 8 | }: { 9 | timeout?: number; 10 | onCopy?: () => void; 11 | } = {}) { 12 | const [isCopied, setIsCopied] = React.useState(false); 13 | 14 | const copyToClipboard = (value: string) => { 15 | if (typeof window === "undefined" || !navigator.clipboard.writeText) { 16 | return; 17 | } 18 | 19 | if (!value) return; 20 | 21 | navigator.clipboard.writeText(value).then(() => { 22 | setIsCopied(true); 23 | 24 | if (onCopy) { 25 | onCopy(); 26 | } 27 | 28 | if (timeout !== 0) { 29 | setTimeout(() => { 30 | setIsCopied(false); 31 | }, timeout); 32 | } 33 | }, console.error); 34 | }; 35 | 36 | return { isCopied, copyToClipboard }; 37 | } 38 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-with-keyboard-shortcut.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-with-keyboard-shortcut 4 | * @title Keyboard Shortcut Button 5 | * @type registry:block 6 | * @description Button with keyboard shortcut display 7 | * @categories ["buttons", "keyboard", "shortcuts"] 8 | * @tags ["keyboard", "shortcuts", "kbd", "accessibility", "power-user", "mac"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonWithKeyboardShortcut() { 14 | return ( 15 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/gallery/components/filters/filter-chip.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { X } from "lucide-react"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | interface FilterChipProps { 7 | label: string; 8 | onRemove: () => void; 9 | className?: string; 10 | } 11 | 12 | export function FilterChip({ label, onRemove, className }: FilterChipProps) { 13 | return ( 14 |
20 | {label} 21 | 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /apps/kit/lib/get-llm-text.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | import type { InferPageType } from "fumadocs-core/source"; 3 | import { remarkInclude } from "fumadocs-mdx/config"; 4 | import { remark } from "remark"; 5 | import remarkGfm from "remark-gfm"; 6 | import remarkMdx from "remark-mdx"; 7 | import type { source } from "@/lib/source"; 8 | 9 | const processor = remark() 10 | .use(remarkMdx) 11 | // needed for Fumadocs MDX 12 | .use(remarkInclude) 13 | .use(remarkGfm); 14 | 15 | export async function getLLMText(page: InferPageType) { 16 | const processed = await processor.process({ 17 | path: page.absolutePath, 18 | value: await fs.readFile(page.absolutePath), 19 | }); 20 | 21 | // note: it doesn't escape frontmatter, it's up to you. 22 | return `# ${page.data.title} 23 | URL: ${page.url} 24 | 25 | ${processed.value}`; 26 | } 27 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/ai-loading-states.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "AI Loading States" 3 | summary: "Loading states for AI operations" 4 | description: "Learn how to implement AI-specific loading states. Discover best practices for thinking animations, progress indicators, and stream initialization." 5 | icon: Loader 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Basic loading: [Loading Indicator](/patterns/user-feedback/loading-indicator) 18 | - Stream display: [Streaming Response](/patterns/ai-intelligence/streaming-response) 19 | - Skeleton states: [Skeleton](/patterns/user-feedback/skeleton) 20 | -------------------------------------------------------------------------------- /apps/web/components/feedback-wrapper.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { trackDocsFeedback } from "@ux-patterns/tracking/helpers"; 4 | import { usePlausible } from "next-plausible"; 5 | import { handleFeedbackRate } from "@/lib/feedback-actions"; 6 | import type { ActionResponse, Feedback as FeedbackType } from "./feedback"; 7 | import { Feedback } from "./feedback"; 8 | 9 | export function FeedbackWrapper() { 10 | const plausible = usePlausible(); 11 | 12 | const handleRateAction = async ( 13 | url: string, 14 | feedback: FeedbackType, 15 | ): Promise => { 16 | // Track the feedback event on the client side 17 | trackDocsFeedback(plausible, feedback.opinion, url, feedback.message); 18 | 19 | // Call the server action 20 | return handleFeedbackRate(url, feedback); 21 | }; 22 | 23 | return ; 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/content/patterns/ai-intelligence/streaming-response.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Streaming Response" 3 | summary: "Real-time AI response streaming" 4 | description: "Learn how to implement streaming AI responses. Discover best practices for progressive rendering, markdown formatting, and stream interruption." 5 | icon: Zap 6 | status: draft 7 | --- 8 | 9 | 10 | This page is empty for now. Please help us by 11 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 12 | to add content. 13 | 14 | 15 | ## Related patterns 16 | 17 | - Loading states: [AI Loading States](/patterns/ai-intelligence/ai-loading-states) 18 | - Response actions: [Response Feedback](/patterns/ai-intelligence/response-feedback) 19 | - Error handling: [AI Error States](/patterns/ai-intelligence/ai-error-states) 20 | -------------------------------------------------------------------------------- /lychee.toml: -------------------------------------------------------------------------------- 1 | ############################# Display ############################# 2 | verbose = "info" 3 | output = "./lychee-out.md" 4 | 5 | ############################# Cache ############################### 6 | cache = true 7 | max_cache_age = "2d" 8 | 9 | ############################# Runtime ############################# 10 | max_concurrency = 14 11 | 12 | ############################# Requests ############################ 13 | user_agent = "curl/7.83.1" 14 | timeout = 20 15 | accept = ["200", "429", "403"] 16 | scheme = ["https"] 17 | 18 | ############################# Exclusions ########################## 19 | base = "https://github.com/thedaviddias/ux-patterns-for-developers/" 20 | exclude = [ 21 | "https://caniuse.com", 22 | "../../../public/*", 23 | "/patterns/*", 24 | "https://github.com/public/*", 25 | " https://github.com/patterns/*" 26 | ] 27 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-with-sound.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-with-sound 4 | * @title Sound Effects Buttons 5 | * @type registry:block 6 | * @description Button with audio feedback and sound effects 7 | * @categories ["buttons", "sound", "audio"] 8 | * @tags ["sound", "audio", "feedback", "effects", "interactive", "accessibility"] 9 | * @registryDependencies ["button"] 10 | */ 11 | "use client"; 12 | 13 | import { Button } from "@/ui/button"; 14 | 15 | export default function ButtonWithSound() { 16 | return ( 17 |
18 | 21 | 24 | 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/link-checker.yml: -------------------------------------------------------------------------------- 1 | name: Links Checker 2 | 3 | on: 4 | repository_dispatch: 5 | workflow_dispatch: 6 | 7 | env: 8 | HUSKY: 0 9 | 10 | jobs: 11 | links-checker: 12 | runs-on: ubuntu-latest 13 | name: Links Checker 14 | permissions: 15 | issues: write 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Link Checker 22 | id: lychee 23 | uses: lycheeverse/lychee-action@v2.2.0 24 | with: 25 | args: --config ./lychee.toml --verbose --no-progress . 26 | fail: true 27 | 28 | - name: Create Issue From File 29 | if: env.exit_code != 0 30 | uses: peter-evans/create-issue-from-file@v5 31 | with: 32 | title: Link Checker Report 33 | content-filepath: ./lychee-out.md 34 | labels: report, automated issue 35 | -------------------------------------------------------------------------------- /apps/web/components/blog/mobile-toc.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | Drawer, 5 | DrawerContent, 6 | DrawerHeader, 7 | DrawerTrigger, 8 | } from "@ux-patterns/ui/components/shadcn/drawer"; 9 | import { List } from "lucide-react"; 10 | import { TableOfContents } from "@/components/blog/table-of-contents"; 11 | 12 | export function MobileTableOfContents() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

Table of Contents

22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-with-haptics.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-with-haptics 4 | * @title Haptic Feedback Buttons 5 | * @type registry:block 6 | * @description Button with haptic feedback for mobile devices 7 | * @categories ["buttons", "haptics", "mobile"] 8 | * @tags ["haptics", "mobile", "feedback", "touch", "vibration", "interactive"] 9 | * @registryDependencies ["button"] 10 | */ 11 | "use client"; 12 | 13 | import { Button } from "@/ui/button"; 14 | 15 | export default function ButtonWithHaptics() { 16 | return ( 17 |
18 | 19 | 20 | 21 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/kit/constants/footer.ts: -------------------------------------------------------------------------------- 1 | export const FOOTER_GENERAL_LINKS = [ 2 | { 3 | path: "/", 4 | label: "Home", 5 | }, 6 | { 7 | path: "/docs", 8 | label: "Docs", 9 | }, 10 | { 11 | path: "/docs/components", 12 | label: "Components", 13 | }, 14 | ]; 15 | 16 | export const FOOTER_RESOURCES_LINKS = [ 17 | { 18 | path: "/llms.txt", 19 | label: "LLMs", 20 | }, 21 | { 22 | path: "/llms-full.txt", 23 | label: "LLMs Full", 24 | }, 25 | ]; 26 | 27 | export const FOOTER_OPENSOURCE_LINKS = [ 28 | { 29 | path: "https://llmstxthub.com/", 30 | label: "LLMs.txt Hub", 31 | rel: "noopener noreferrer", 32 | }, 33 | { 34 | path: "https://frontendchecklist.io/", 35 | label: "Front-End Checklist", 36 | rel: "noopener noreferrer", 37 | }, 38 | { 39 | path: "https://github.com/thedaviddias/indie-dev-toolkit", 40 | label: "Indie Dev Toolkit", 41 | rel: "noopener noreferrer", 42 | }, 43 | ]; 44 | -------------------------------------------------------------------------------- /apps/gallery/lib/image-utils.ts: -------------------------------------------------------------------------------- 1 | import type { Entry } from "./types"; 2 | 3 | /** 4 | * Constructs the image path based on the entry's website 5 | * Path structure: /website/[website-name]/[filename] 6 | */ 7 | export function getImagePath(entry: Entry): string { 8 | // If the media.src is already a full URL or data URL, return as is 9 | if ( 10 | entry.media.src.startsWith("http") || 11 | entry.media.src.startsWith("data:") 12 | ) { 13 | return entry.media.src; 14 | } 15 | 16 | // If the media.src is already a full path starting with /, return as is 17 | if (entry.media.src.startsWith("/")) { 18 | return entry.media.src; 19 | } 20 | 21 | // Construct the path based on website 22 | const website = entry.website; // e.g., "klingai.com" 23 | const filename = entry.media.src; // e.g., "001.png" 24 | 25 | // Build the path: /web/[website-name]/[filename] 26 | return `/web/${website}/${filename}`; 27 | } 28 | -------------------------------------------------------------------------------- /apps/gallery/components/common/pattern-badge.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { getCategoryForPattern } from "@/lib/url-utils"; 3 | import { cn } from "@/lib/utils"; 4 | 5 | interface PatternBadgeProps { 6 | pattern: string; 7 | platform: string; 8 | className?: string; 9 | } 10 | 11 | export function PatternBadge({ 12 | pattern, 13 | platform, 14 | className, 15 | }: PatternBadgeProps) { 16 | const normalizedPattern = pattern.toLowerCase().replace(/\s+/g, "-"); 17 | const category = getCategoryForPattern(normalizedPattern) || "navigation"; 18 | const href = `/${platform}/${category}/${normalizedPattern}`; 19 | 20 | return ( 21 | 28 | {pattern} 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /apps/web/components/pattern-comparison/decision-flow-section.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { LazyDecisionFlow } from "@/components/decision-flow/lazy-decision-flow"; 4 | import type { 5 | DecisionEdge, 6 | DecisionNode, 7 | } from "@/components/decision-flow/types"; 8 | 9 | interface DecisionFlowSectionProps { 10 | title: string; 11 | nodes: DecisionNode[]; 12 | edges: DecisionEdge[]; 13 | } 14 | 15 | export function DecisionFlowSection({ 16 | title, 17 | nodes, 18 | edges, 19 | }: DecisionFlowSectionProps) { 20 | return ( 21 |
22 |

Interactive Decision Flow

23 |
24 | 30 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /apps/gallery/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface Entry { 2 | id: string; 3 | title: string; 4 | pattern: string; 5 | platform: "web" | "mobile"; 6 | type: "do" | "dont"; 7 | website: string; 8 | media: { 9 | type: "image" | "video"; 10 | src: string; 11 | }; 12 | tags?: string[]; 13 | content: string; 14 | source?: { 15 | url: string; 16 | capturedAt: string; 17 | }; 18 | // Internal fields for content processing 19 | slug: string; 20 | filePath: string; 21 | body?: any; // MDX compiled body for rendering 22 | } 23 | 24 | export interface SearchIndex { 25 | id: string; 26 | title: string; 27 | pattern: string; 28 | platform: string; 29 | type: string; 30 | website: string; 31 | tags: string[]; 32 | content: string; 33 | slug: string; 34 | } 35 | 36 | export type FilterState = { 37 | platform: "all" | "web" | "mobile"; 38 | type: "all" | "do" | "dont"; 39 | pattern: string | null; 40 | search: string; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/hooks/src/use-config.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | type Config = { 4 | packageManager: "npm" | "yarn" | "pnpm" | "bun"; 5 | }; 6 | 7 | const defaultConfig: Config = { 8 | packageManager: "pnpm", 9 | }; 10 | 11 | export function useConfig() { 12 | const [config, setConfig] = useState(defaultConfig); 13 | 14 | // Load config from localStorage on mount 15 | useEffect(() => { 16 | const stored = localStorage.getItem("config"); 17 | if (stored) { 18 | try { 19 | const parsedConfig = JSON.parse(stored); 20 | setConfig(parsedConfig); 21 | } catch { 22 | // If parsing fails, use default config 23 | setConfig(defaultConfig); 24 | } 25 | } 26 | }, []); 27 | 28 | // Save config to localStorage whenever it changes 29 | useEffect(() => { 30 | localStorage.setItem("config", JSON.stringify(config)); 31 | }, [config]); 32 | 33 | return [config, setConfig] as const; 34 | } 35 | -------------------------------------------------------------------------------- /packages/ui/src/components/shadcn/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "../../lib/utils"; 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ); 21 | }, 22 | ); 23 | Input.displayName = "Input"; 24 | 25 | export { Input }; 26 | -------------------------------------------------------------------------------- /apps/web/components/blog/author-card.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@ux-patterns/ui/lib/utils"; 2 | 3 | interface Author { 4 | name: string; 5 | position: string; 6 | avatar: string; 7 | } 8 | 9 | interface AuthorCardProps { 10 | author: Author; 11 | className?: string; 12 | } 13 | 14 | export function AuthorCard({ author, className }: AuthorCardProps) { 15 | return ( 16 |
17 | {/** biome-ignore lint/performance/noImgElement: no need to optimize */} 18 | {author.name} 23 |
24 |

25 | {author.name} 26 |

27 |

28 | {author.position} 29 |

30 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /apps/web/components/blog/hash-scroll-handler.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | 5 | export function HashScrollHandler() { 6 | useEffect(() => { 7 | const handleHashScroll = () => { 8 | const hash = window.location.hash; 9 | if (hash) { 10 | const element = document.getElementById(hash.slice(1)); 11 | if (element) { 12 | const offset = 80; 13 | const elementPosition = element.getBoundingClientRect().top; 14 | const offsetPosition = elementPosition + window.pageYOffset - offset; 15 | window.scrollTo({ 16 | top: offsetPosition, 17 | behavior: "smooth", 18 | }); 19 | } 20 | } 21 | }; 22 | if (window.location.hash) { 23 | setTimeout(handleHashScroll, 100); 24 | } 25 | window.addEventListener("hashchange", handleHashScroll); 26 | 27 | return () => { 28 | window.removeEventListener("hashchange", handleHashScroll); 29 | }; 30 | }, []); 31 | 32 | return null; 33 | } 34 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-disabled.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-disabled 4 | * @title Disabled Button States 5 | * @type registry:block 6 | * @description Button in disabled states with different approaches 7 | * @categories ["buttons", "states", "accessibility"] 8 | * @tags ["disabled", "states", "accessibility", "aria", "interactive"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonDisabled() { 14 | return ( 15 |
16 | 17 | 20 | 21 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/ui/src/components/custom/pattern-badge.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@ux-patterns/ui/lib/utils"; 2 | import type { ReactNode } from "react"; 3 | 4 | export interface PatternBadgeProps { 5 | variant: "new" | "updated"; 6 | className?: string; 7 | children?: ReactNode; 8 | } 9 | 10 | export function PatternBadge({ 11 | variant, 12 | className, 13 | children, 14 | }: PatternBadgeProps) { 15 | const variantStyles = { 16 | new: "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400", 17 | updated: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400", 18 | }; 19 | 20 | const label = children || (variant === "new" ? "New" : "Updated"); 21 | 22 | return ( 23 | 31 | {label} 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /apps/web/components/component-preview-wrapper.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | interface PageMetadata { 6 | title?: string; 7 | description?: string; 8 | [key: string]: unknown; 9 | } 10 | 11 | // Create a context to pass metadata to components 12 | const MetadataContext = React.createContext(null); 13 | 14 | // Enhanced ComponentPreview that uses metadata context 15 | export const ComponentPreviewWithMetadata = ( 16 | props: React.ComponentProps, 17 | ) => { 18 | const _metadata = React.useContext(MetadataContext); 19 | return <>{props.children}; 20 | }; 21 | 22 | // Provider component to wrap MDX content with metadata 23 | export const MetadataProvider = ({ 24 | children, 25 | metadata, 26 | }: { 27 | children: React.ReactNode; 28 | metadata: PageMetadata; 29 | }) => { 30 | return ( 31 | 32 | {children} 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /apps/kit/components/footer/footer-copyright.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { usePlausible } from "next-plausible"; 4 | import { trackFooterClick } from "@/lib/tracking"; 5 | 6 | export const FooterCopyright = () => { 7 | const plausible = usePlausible(); 8 | 9 | const handleAuthorClick = () => { 10 | trackFooterClick(plausible, "social", "David Dias"); 11 | }; 12 | 13 | return ( 14 |
15 |

© {new Date().getFullYear()} UX Patterns Kit

16 |

17 | Made with{" "} 18 | 19 | ♥ 20 | {" "} 21 | by{" "} 22 | 29 | David Dias 30 | {" "} 31 | for the Open-Source Community. 32 |

33 |
34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /apps/web/components/glossary/term-card.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import defaultMdxComponents from "fumadocs-ui/mdx"; 4 | 5 | interface TermCardProps { 6 | title: string; 7 | description: string; 8 | category: string[]; 9 | url: string; 10 | } 11 | 12 | export function TermCard({ title, description, category, url }: TermCardProps) { 13 | const Card = defaultMdxComponents.Card; 14 | 15 | return ( 16 | 17 |
18 |

19 | {description} 20 |

21 | {category.length > 0 && ( 22 |
23 | {category.map((cat) => ( 24 | 28 | {cat} 29 | 30 | ))} 31 |
32 | )} 33 |
34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /packages/hooks/src/use-in-view.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | 3 | interface UseInViewOptions { 4 | threshold?: number; 5 | rootMargin?: string; 6 | triggerOnce?: boolean; 7 | } 8 | 9 | export function useInView(options: UseInViewOptions = {}) { 10 | const { threshold = 0, rootMargin = "0px", triggerOnce = false } = options; 11 | const [isInView, setIsInView] = useState(false); 12 | const ref = useRef(null); 13 | 14 | useEffect(() => { 15 | const element = ref.current; 16 | if (!element) return; 17 | 18 | const observer = new IntersectionObserver( 19 | ([entry]) => { 20 | setIsInView(entry.isIntersecting); 21 | if (entry.isIntersecting && triggerOnce) { 22 | observer.disconnect(); 23 | } 24 | }, 25 | { 26 | threshold, 27 | rootMargin, 28 | }, 29 | ); 30 | 31 | observer.observe(element); 32 | 33 | return () => { 34 | observer.disconnect(); 35 | }; 36 | }, [threshold, rootMargin, triggerOnce]); 37 | 38 | return { ref, isInView }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-icon-left.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-icon-left 4 | * @title Button with Left Icons 5 | * @type registry:block 6 | * @description Button with icons positioned on the left side 7 | * @categories ["buttons", "icons", "layout"] 8 | * @tags ["icons", "left", "layout", "lucide", "positioning"] 9 | * @dependencies ["lucide-react"] 10 | * @registryDependencies ["button"] 11 | */ 12 | import { Mail, Save, Upload } from "lucide-react"; 13 | import { Button } from "@/ui/button"; 14 | 15 | export default function ButtonIconLeft() { 16 | return ( 17 |
18 | 22 | 26 | 30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /apps/gallery/components/footer/footer-copyright.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AUTHOR } from "@ux-patterns/constants/author"; 4 | import { usePlausible } from "next-plausible"; 5 | import { trackFooterClick } from "@/lib/tracking"; 6 | 7 | export const FooterCopyright = () => { 8 | const plausible = usePlausible(); 9 | 10 | const handleAuthorClick = () => { 11 | trackFooterClick(plausible, "social", AUTHOR.name); 12 | }; 13 | 14 | return ( 15 |
16 |

© {new Date().getFullYear()} UX Patterns for Devs

17 |

18 | Made with{" "} 19 | 20 | ♥ 21 | {" "} 22 | by{" "} 23 | 30 | {AUTHOR.name} 31 | {" "} 32 | for the Open-Source Community. 33 |

34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /apps/gallery/content/entries/carousel/klingai/carousel-auto-play.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: "kling002" 3 | title: "Auto-playing carousel without pause controls" 4 | pattern: "Carousel" 5 | platform: "web" 6 | type: "dont" 7 | website: "klingai.com" 8 | media: 9 | type: "image" 10 | src: "002.png" 11 | tags: ["accessibility", "carousel", "autoplay", "controls"] 12 | source: 13 | url: "https://klingai.com" 14 | capturedAt: "2024-12-01" 15 | --- 16 | 17 | This carousel automatically advances without any pause or stop controls, violating WCAG 2.2.2 (Pause, Stop, Hide) and preventing users from reading content at their own pace. 18 | 19 | ## The issue 20 | 21 | Users cannot control the carousel's auto-rotation, making it impossible for those with reading difficulties or cognitive disabilities to consume the content properly. 22 | 23 | ## Learn more 24 | 25 | See the [Carousel pattern documentation](/patterns/content-management/carousel#auto-play-without-controls) for why auto-play without controls is problematic and how to implement accessible carousel controls. -------------------------------------------------------------------------------- /apps/kit/next.config.mjs: -------------------------------------------------------------------------------- 1 | import { createMDX } from "fumadocs-mdx/next"; 2 | import { withPlausibleProxy } from "next-plausible"; 3 | 4 | const withMDX = createMDX(); 5 | 6 | /** @type {import('next').NextConfig} */ 7 | const config = { 8 | reactStrictMode: true, 9 | images: { 10 | remotePatterns: [ 11 | { 12 | protocol: "https", 13 | hostname: "www.google.com", 14 | pathname: "/s2/favicons", 15 | }, 16 | { 17 | protocol: "https", 18 | hostname: "images.unsplash.com", 19 | pathname: "/**", 20 | }, 21 | ], 22 | }, 23 | async rewrites() { 24 | return [ 25 | { 26 | source: "/docs/:path*.mdx", 27 | destination: "/llms.mdx/:path*", 28 | }, 29 | ]; 30 | }, 31 | webpack: (config) => { 32 | // Ignore OpenTelemetry instrumentation warnings 33 | config.ignoreWarnings = [ 34 | { 35 | module: /@opentelemetry\/instrumentation/, 36 | message: /Critical dependency/, 37 | }, 38 | ]; 39 | return config; 40 | }, 41 | }; 42 | 43 | export default withPlausibleProxy()(withMDX(config)); 44 | -------------------------------------------------------------------------------- /apps/web/components/footer/footer-copyright.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AUTHOR } from "@ux-patterns/constants/author"; 4 | import { usePlausible } from "next-plausible"; 5 | import { trackFooterClick } from "@/lib/tracking"; 6 | 7 | export const FooterCopyright = () => { 8 | const plausible = usePlausible(); 9 | 10 | const handleAuthorClick = () => { 11 | trackFooterClick(plausible, "social", AUTHOR.name); 12 | }; 13 | 14 | return ( 15 |
16 |

© {new Date().getFullYear()} UX Patterns for Devs

17 |

18 | Made with{" "} 19 | 20 | ♥ 21 | {" "} 22 | by{" "} 23 | 30 | {AUTHOR.name} 31 | {" "} 32 | for the Open-Source Community. 33 |

34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /apps/web/utils/get-glossary-terms.ts: -------------------------------------------------------------------------------- 1 | import { source } from "@/lib/source"; 2 | 3 | export interface GlossaryTerm { 4 | title: string; 5 | description: string; 6 | category: string[]; 7 | slug: string; 8 | url: string; 9 | } 10 | 11 | export async function getGlossaryTerms(): Promise { 12 | try { 13 | const allPages = source.getPages(); 14 | 15 | // Filter for glossary pages (excluding index) 16 | const glossaryPages = allPages.filter( 17 | (page) => page.slugs[0] === "glossary" && page.slugs.length > 1, 18 | ); 19 | 20 | const terms = glossaryPages.map((page) => { 21 | return { 22 | title: page.data.title || page.slugs[page.slugs.length - 1], 23 | description: page.data.description || "", 24 | category: Array.isArray(page.data.category) ? page.data.category : [], 25 | slug: page.slugs[page.slugs.length - 1], 26 | url: page.url, 27 | }; 28 | }); 29 | 30 | return terms; 31 | } catch (error) { 32 | console.error("Error fetching glossary terms:", error); 33 | return []; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/web/components/search.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useDocsSearch } from "fumadocs-core/search/client"; 3 | import { 4 | SearchDialog, 5 | SearchDialogClose, 6 | SearchDialogContent, 7 | SearchDialogHeader, 8 | SearchDialogIcon, 9 | SearchDialogInput, 10 | SearchDialogList, 11 | SearchDialogOverlay, 12 | type SharedProps, 13 | } from "fumadocs-ui/components/dialog/search"; 14 | 15 | export const DefaultSearchDialog = (props: SharedProps) => { 16 | const { search, setSearch, query } = useDocsSearch({ 17 | type: "static", 18 | }); 19 | 20 | return ( 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/list-view.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "List View" 3 | summary: "Display data in vertical lists" 4 | description: "Learn how to implement list views for data display. Discover best practices for list layouts, item selection, and virtual scrolling." 5 | icon: ListOrdered 6 | status: draft 7 | --- 8 | 9 | import { GuidesBanner } from "@/components/guides-banner"; 10 | 11 | 12 | This page is empty for now. Please help us by 13 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 14 | to add content. 15 | 16 | 17 | 22 | 23 | ## Related patterns 24 | 25 | - Grid layout: [Card Grid](/patterns/data-display/card-grid) 26 | - Table display: [Data Table](/patterns/data-display/table) 27 | - Scrolling: [Infinite Scroll](/patterns/navigation/infinite-scroll) 28 | -------------------------------------------------------------------------------- /packages/tracking/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript types for tracking functionality 3 | */ 4 | 5 | import type { SharedEventName } from "./events"; 6 | 7 | // Plausible tracking function type 8 | export type PlausibleTracker = ( 9 | event: SharedEventName | string, 10 | options?: { props?: Record }, 11 | ) => void; 12 | 13 | // Common tracking prop types 14 | export interface FooterLinkProps { 15 | link_type: "general" | "resource" | "social"; 16 | link_label: string; 17 | } 18 | 19 | export interface NavigationProps { 20 | url: string; 21 | label?: string; 22 | } 23 | 24 | export interface ComponentProps { 25 | component_name: string; 26 | } 27 | 28 | export interface NewsletterProps { 29 | variant?: "default" | "inline"; 30 | } 31 | 32 | // Helper function parameter types 33 | export type FooterLinkType = "general" | "resource" | "social"; 34 | export type NavigationType = "github" | "main_site" | "pattern"; 35 | export type NewsletterEventType = "success" | "error"; 36 | export type NewsletterVariant = "default" | "inline"; 37 | -------------------------------------------------------------------------------- /apps/web/components/blog/authors.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from "react"; 2 | import { formatDate } from "@/utils/date"; 3 | import { LinkCustom } from "../link-custom"; 4 | 5 | type TopContentProps = { 6 | title: string; 7 | date: string; 8 | authors: { 9 | name: string; 10 | link: string; 11 | }[]; 12 | }; 13 | 14 | export const TopContent: FC = async ({ 15 | title, 16 | date, 17 | authors, 18 | }) => { 19 | const dateObj = new Date(date); 20 | 21 | const dateObjFormatted = formatDate(dateObj); 22 | 23 | return ( 24 | <> 25 |

{title}

26 |
27 | {"by"}{" "} 28 | {authors.map((author) => ( 29 | 30 | 34 | {author.name} 35 | 36 | 37 | ))} 38 |
39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/2.2.3/schema.json", 3 | "vcs": { 4 | "enabled": true, 5 | "clientKind": "git", 6 | "useIgnoreFile": true 7 | }, 8 | "files": { 9 | "ignoreUnknown": true, 10 | "includes": [ 11 | "**", 12 | "!**/public/_pagefind", 13 | "!**/public/r", 14 | "!**/.next", 15 | "!**/dist", 16 | "!**/build", 17 | "!**/node_modules", 18 | "!**/.source", 19 | "!**/packages/registry/.generated", 20 | "!**/apps/kit/public/r/registry.json", 21 | "!**/packages/registry/registry", 22 | "!**/apps/kit/public/r/", 23 | "!**/coverage", 24 | "!**/.turbo", 25 | "!**/*.css" 26 | ] 27 | }, 28 | "linter": { 29 | "enabled": true, 30 | "rules": { 31 | "recommended": true, 32 | "suspicious": { 33 | "noExplicitAny": "off" 34 | }, 35 | "security": { 36 | "noDangerouslySetInnerHtml": "off" 37 | } 38 | } 39 | }, 40 | "formatter": { 41 | "enabled": true, 42 | "indentStyle": "tab" 43 | }, 44 | "css": { 45 | "linter": { 46 | "enabled": false 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-full-width.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-full-width 4 | * @title Full-Width Buttons 5 | * @type registry:block 6 | * @description Button with full width styling for prominent actions 7 | * @categories ["buttons", "layout", "full-width"] 8 | * @tags ["full-width", "wide", "layout", "prominent", "mobile"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonFullWidth() { 14 | return ( 15 |
16 | 17 | 20 | 23 |
24 | 25 | 28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /apps/web/content/patterns/data-display/card-grid.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Card Grid" 3 | summary: "Display content in a responsive card layout" 4 | description: "Learn how to implement card grids for content display. Discover best practices for responsive layouts, card designs, and grid systems." 5 | icon: Grid3x3 6 | status: draft 7 | --- 8 | 9 | import { GuidesBanner } from "@/components/guides-banner"; 10 | 11 | 12 | This page is empty for now. Please help us by 13 | [contributing](https://github.com/thedaviddias/ux-patterns-for-developers/blob/main/.github/CONTRIBUTING.md) 14 | to add content. 15 | 16 | 17 | 22 | 23 | ## Related patterns 24 | 25 | - Single cards: [Modal](/patterns/content-management/modal) 26 | - Loading cards: [Skeleton](/patterns/user-feedback/skeleton) 27 | - Carousel display: [Carousel](/patterns/content-management/carousel) 28 | -------------------------------------------------------------------------------- /apps/gallery/content/entries/popover/klingai/popover-auto-open.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | id: popover-auto-open-001 3 | title: "Auto-opening popover with poor keyboard support" 4 | pattern: Popover 5 | platform: web 6 | type: dont 7 | website: klingai.com 8 | media: 9 | type: image 10 | src: "001.png" 11 | tags: [accessibility, keyboard-navigation, dismiss-behavior] 12 | source: 13 | url: "https://app.klingai.com" 14 | capturedAt: "2025-09-11" 15 | --- 16 | 17 | This popover automatically opens on page load, lacks ESC key dismissal, and prevents keyboard navigation. 18 | 19 | ## The issue 20 | 21 | Violates accessibility best practices by forcing users to dismiss the popover before accessing content. Missing required ARIA attributes and proper focus management. 22 | 23 | ## Learn more 24 | 25 | See the [Popover pattern documentation](/patterns/content-management/popover#best-practices) for proper trigger behavior, [ARIA attributes](/patterns/content-management/popover#aria-attributes), and [keyboard interaction patterns](/patterns/content-management/popover#keyboard-interaction-pattern). 26 | -------------------------------------------------------------------------------- /apps/web/components/sections/home-cta.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ArrowRight } from "lucide-react"; 4 | import { TRACKING_CLASSES } from "@/lib/tracking"; 5 | import { LinkCustom } from "../link-custom"; 6 | 7 | export const HomeCTA = () => { 8 | return ( 9 |
10 | 16 | Get Started 17 | 18 | 19 | 25 | Browse Patterns 26 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-icon-right.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-icon-right 4 | * @title Button with Right Icons 5 | * @type registry:block 6 | * @description Button with icons positioned on the right side 7 | * @categories ["buttons", "icons", "layout"] 8 | * @tags ["icons", "right", "layout", "lucide", "positioning", "navigation"] 9 | * @dependencies ["lucide-react"] 10 | * @registryDependencies ["button"] 11 | */ 12 | import { ArrowRight, ChevronRight, ExternalLink } from "lucide-react"; 13 | import { Button } from "@/ui/button"; 14 | 15 | export default function ButtonIconRight() { 16 | return ( 17 |
18 | 22 | 26 | 30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /apps/web/components/pattern-guide-list.tsx: -------------------------------------------------------------------------------- 1 | import { getPageTreePeers } from "fumadocs-core/server"; 2 | import defaultMdxComponents from "fumadocs-ui/mdx"; 3 | import { source } from "@/lib/source"; 4 | 5 | export function PatternGuideList() { 6 | // Get all pages in the pattern-guide directory 7 | const peerPages = getPageTreePeers(source.pageTree, "/pattern-guide"); 8 | 9 | // Filter out the index page and separator items 10 | const guidePages = peerPages.filter( 11 | (page) => 12 | page.url !== "/pattern-guide" && 13 | typeof page.name === "string" && 14 | !page.name.startsWith("---") && 15 | page.url.startsWith("/pattern-guide/"), 16 | ); 17 | 18 | const Cards = defaultMdxComponents.Cards; 19 | const Card = defaultMdxComponents.Card; 20 | 21 | return ( 22 | 23 | {guidePages.map((page) => ( 24 | 25 | {page.description || 26 | "Compare similar patterns and make informed decisions based on your specific use case and requirements."} 27 | 28 | ))} 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /apps/web/examples/patterns/content-management/expandable-text/basic.ts: -------------------------------------------------------------------------------- 1 | export const basicExpandableTextExample = ` 2 |
3 |

4 | This is the visible content... 5 | 8 |

9 | 12 |
13 | 14 | 30 | `; 31 | -------------------------------------------------------------------------------- /apps/kit/components/footer/footer-link.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Link from "next/link"; 4 | import { usePlausible } from "next-plausible"; 5 | import { trackFooterClick } from "@/lib/tracking"; 6 | 7 | interface FooterLinkProps { 8 | label: string; 9 | path?: string; 10 | shortlink?: string; 11 | rel?: string; 12 | linkType: "general" | "resource" | "social"; 13 | } 14 | 15 | export const FooterLink = ({ 16 | label, 17 | path, 18 | shortlink, 19 | rel, 20 | linkType, 21 | }: FooterLinkProps) => { 22 | const plausible = usePlausible(); 23 | 24 | const handleClick = () => { 25 | trackFooterClick(plausible, linkType, label); 26 | }; 27 | 28 | // Merge rel attribute to preserve security tokens for external links 29 | const mergedRel = rel ? `${rel} noopener noreferrer` : undefined; 30 | 31 | return ( 32 | 38 | {label} 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /apps/gallery/lib/search-context.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { createContext, type ReactNode, useContext, useState } from "react"; 4 | 5 | interface SearchContextType { 6 | isSearchOpen: boolean; 7 | openSearch: () => void; 8 | closeSearch: () => void; 9 | } 10 | 11 | const SearchContext = createContext(undefined); 12 | 13 | interface SearchProviderProps { 14 | children: ReactNode; 15 | } 16 | 17 | export function SearchProvider({ children }: SearchProviderProps) { 18 | const [isSearchOpen, setIsSearchOpen] = useState(false); 19 | 20 | const openSearch = () => { 21 | setIsSearchOpen(true); 22 | }; 23 | 24 | const closeSearch = () => { 25 | setIsSearchOpen(false); 26 | }; 27 | 28 | return ( 29 | 30 | {children} 31 | 32 | ); 33 | } 34 | 35 | export function useSearch() { 36 | const context = useContext(SearchContext); 37 | if (context === undefined) { 38 | throw new Error("useSearch must be used within a SearchProvider"); 39 | } 40 | return context; 41 | } 42 | -------------------------------------------------------------------------------- /apps/kit/app/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { DocsLayout } from "@ux-patterns/ui/components/custom/layout-notebook"; 2 | import { 3 | TrackedLargeSearchToggle, 4 | TrackedSearchToggle, 5 | } from "@ux-patterns/ui/components/custom/tracked-search-toggle"; 6 | import { baseOptions, linkItems } from "@/lib/layout.shared"; 7 | import { source } from "@/lib/source"; 8 | import { TRACKING_EVENTS } from "@/lib/tracking"; 9 | 10 | export default function Layout({ children }: LayoutProps<"/docs">) { 11 | const { nav, ...base } = baseOptions(); 12 | 13 | return ( 14 | 28 | ), 29 | sm: ( 30 | 31 | ), 32 | }, 33 | }} 34 | > 35 | {children} 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /apps/web/components/glossary/letter.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "@/lib/cn"; 4 | 5 | interface LetterProps { 6 | letter: string; 7 | hasContent?: boolean; 8 | } 9 | 10 | export function Letter({ letter, hasContent = true }: LetterProps) { 11 | if (!hasContent) { 12 | return ( 13 | 19 | {letter} 20 | 21 | ); 22 | } 23 | 24 | const handleClick = (e: React.MouseEvent) => { 25 | e.preventDefault(); 26 | const element = document.getElementById(letter.toLowerCase()); 27 | if (element) { 28 | element.scrollIntoView({ 29 | behavior: "smooth", 30 | block: "start", 31 | }); 32 | } 33 | }; 34 | 35 | return ( 36 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /packages/registry/registry/default/blocks/button/button-variants.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @registry 3 | * @name button-variants 4 | * @title Button Variants Showcase 5 | * @type registry:block 6 | * @description Button component showcasing all available style variants 7 | * @categories ["buttons", "variants", "showcase"] 8 | * @tags ["variants", "showcase", "solid", "outline", "ghost", "link", "danger", "success", "warning"] 9 | * @registryDependencies ["button"] 10 | */ 11 | import { Button } from "@/ui/button"; 12 | 13 | export default function ButtonVariants() { 14 | return ( 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /apps/kit/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import { cva, type VariantProps } from "class-variance-authority"; 2 | 3 | const variants = { 4 | primary: "bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80", 5 | outline: "border hover:bg-fd-accent hover:text-fd-accent-foreground", 6 | ghost: "hover:bg-fd-accent hover:text-fd-accent-foreground", 7 | secondary: 8 | "border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground", 9 | } as const; 10 | 11 | export const buttonVariants = cva( 12 | "inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none", 13 | { 14 | variants: { 15 | variant: variants, 16 | // fumadocs use `color` instead of `variant` 17 | color: variants, 18 | size: { 19 | sm: "gap-1 px-2 py-1.5 text-xs", 20 | icon: "p-1.5 [&_svg]:size-5", 21 | "icon-sm": "p-1.5 [&_svg]:size-4.5", 22 | "icon-xs": "p-1 [&_svg]:size-4", 23 | }, 24 | }, 25 | }, 26 | ); 27 | 28 | export type ButtonProps = VariantProps; 29 | -------------------------------------------------------------------------------- /apps/web/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import { cva, type VariantProps } from "class-variance-authority"; 2 | 3 | const variants = { 4 | primary: "bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80", 5 | outline: "border hover:bg-fd-accent hover:text-fd-accent-foreground", 6 | ghost: "hover:bg-fd-accent hover:text-fd-accent-foreground", 7 | secondary: 8 | "border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground", 9 | } as const; 10 | 11 | export const buttonVariants = cva( 12 | "inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none", 13 | { 14 | variants: { 15 | variant: variants, 16 | // fumadocs use `color` instead of `variant` 17 | color: variants, 18 | size: { 19 | sm: "gap-1 px-2 py-1.5 text-xs", 20 | icon: "p-1.5 [&_svg]:size-5", 21 | "icon-sm": "p-1.5 [&_svg]:size-4.5", 22 | "icon-xs": "p-1 [&_svg]:size-4", 23 | }, 24 | }, 25 | }, 26 | ); 27 | 28 | export type ButtonProps = VariantProps; 29 | -------------------------------------------------------------------------------- /apps/web/components/footer/footer-link.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { usePlausible } from "next-plausible"; 4 | import { trackFooterClick } from "@/lib/tracking"; 5 | import { LinkCustom } from "../link-custom"; 6 | 7 | interface FooterLinkProps { 8 | label: string; 9 | path?: string; 10 | shortlink?: string; 11 | rel?: string; 12 | linkType: "general" | "resource" | "social"; 13 | } 14 | 15 | export const FooterLink = ({ 16 | label, 17 | path, 18 | shortlink, 19 | rel, 20 | linkType, 21 | }: FooterLinkProps) => { 22 | const plausible = usePlausible(); 23 | 24 | const handleClick = () => { 25 | trackFooterClick(plausible, linkType, label); 26 | }; 27 | 28 | // Merge rel attribute to preserve security tokens for external links 29 | const mergedRel = rel ? `${rel} noopener noreferrer` : undefined; 30 | 31 | return ( 32 | 38 | {label} 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /.github/actions/install/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup project dependencies 2 | 3 | description: "Setup project dependencies" 4 | 5 | runs: 6 | using: "composite" 7 | steps: 8 | - name: Install pnpm 9 | uses: pnpm/action-setup@v4.0.0 10 | id: pnpm-install 11 | with: 12 | run_install: false 13 | 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v5 16 | with: 17 | node-version-file: ".nvmrc" 18 | registry-url: "https://registry.npmjs.org" 19 | cache: "pnpm" 20 | 21 | - name: Cache dependencies 22 | id: cache_dependencies 23 | uses: actions/cache@v4 24 | with: 25 | path: | 26 | ~/.pnpm 27 | ${{ github.workspace }}/apps/web/.next/cache 28 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} 29 | restore-keys: | 30 | ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}- 31 | 32 | - name: Install dependencies 33 | shell: bash 34 | # if: steps.cache_dependencies.outputs.cache-hit != 'true' 35 | run: pnpm install 36 | -------------------------------------------------------------------------------- /apps/gallery/components/sections/entries-grid.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from "react"; 2 | import type { Entry } from "@/lib/types"; 3 | import { EmptyState } from "../common/empty-state"; 4 | import { GalleryClient } from "../common/gallery-client"; 5 | 6 | type EntriesGridProps = { 7 | filteredEntries: Entry[]; 8 | }; 9 | 10 | export default function EntriesGrid({ filteredEntries }: EntriesGridProps) { 11 | return ( 12 |
13 | {filteredEntries.length === 0 ? ( 14 | 18 | ) : ( 19 | <> 20 |

21 | {filteredEntries.length}{" "} 22 | {filteredEntries.length === 1 ? "example" : "examples"} 23 |

24 | 25 | Loading gallery...
28 | } 29 | > 30 | 31 | 32 | 33 | )} 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /apps/kit/source.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | defineDocs, 4 | frontmatterSchema, 5 | metaSchema, 6 | } from "fumadocs-mdx/config"; 7 | import { createGenerator, remarkAutoTypeTable } from "fumadocs-typescript"; 8 | import { z } from "zod"; 9 | 10 | const generator = createGenerator({ 11 | cache: false, // Disable caching to avoid ENOENT errors in CI 12 | }); 13 | 14 | // You can customise Zod schemas for frontmatter and `meta.json` here 15 | // see https://fumadocs.dev/docs/mdx/collections#define-docs 16 | export const docs: ReturnType = defineDocs({ 17 | docs: { 18 | schema: frontmatterSchema.extend({ 19 | date: z.string().optional(), 20 | tags: z.array(z.string()).optional(), 21 | version: z.string().optional(), 22 | }), 23 | }, 24 | meta: { 25 | schema: metaSchema, 26 | }, 27 | }); 28 | 29 | // Explicitly type the export to avoid type inference issues 30 | export type Docs = typeof docs; 31 | 32 | export default defineConfig({ 33 | lastModifiedTime: "git", 34 | mdxOptions: { 35 | remarkPlugins: [[remarkAutoTypeTable, { generator }]], 36 | providerImportSource: "@/mdx-components", 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /apps/gallery/components/common/disclaimer-banner.tsx: -------------------------------------------------------------------------------- 1 | import { Info } from "lucide-react"; 2 | import Link from "next/link"; 3 | 4 | interface DisclaimerBannerProps { 5 | className?: string; 6 | } 7 | 8 | export function DisclaimerBanner({ className = "" }: DisclaimerBannerProps) { 9 | return ( 10 |
13 |
14 |
15 | 16 |
17 |
18 |

19 | Educational Purpose:{" "} 20 | These examples are shown for learning UX patterns, not to criticize 21 | companies or teams.{" "} 22 | 26 | Read our disclaimer 27 | {" "} 28 | for more information. 29 |

30 |
31 |
32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/registry/test-setup.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | import "vitest-axe/extend-expect"; 3 | import { vi } from "vitest"; 4 | 5 | // Mock window.matchMedia 6 | Object.defineProperty(window, "matchMedia", { 7 | writable: true, 8 | value: vi.fn().mockImplementation((query) => ({ 9 | matches: false, 10 | media: query, 11 | onchange: null, 12 | addListener: vi.fn(), 13 | removeListener: vi.fn(), 14 | addEventListener: vi.fn(), 15 | removeEventListener: vi.fn(), 16 | dispatchEvent: vi.fn(), 17 | })), 18 | }); 19 | 20 | // Mock IntersectionObserver 21 | class MockIntersectionObserver implements IntersectionObserver { 22 | readonly root = null; 23 | readonly rootMargin = ""; 24 | readonly thresholds = []; 25 | observe = vi.fn(); 26 | unobserve = vi.fn(); 27 | disconnect = vi.fn(); 28 | takeRecords = vi.fn(() => []); 29 | } 30 | vi.stubGlobal("IntersectionObserver", MockIntersectionObserver as any); 31 | 32 | // Mock ResizeObserver 33 | class MockResizeObserver implements ResizeObserver { 34 | observe = vi.fn(); 35 | unobserve = vi.fn(); 36 | disconnect = vi.fn(); 37 | } 38 | vi.stubGlobal("ResizeObserver", MockResizeObserver as any); 39 | -------------------------------------------------------------------------------- /apps/web/components/suggest-pattern.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { PROJECT } from "@ux-patterns/constants/author"; 4 | import { useId } from "react"; 5 | 6 | import { TRACKING_CLASSES } from "@/lib/tracking"; 7 | import { LinkCustom } from "./link-custom"; 8 | 9 | export const SuggestPattern = () => { 10 | const suggestPatternTitleId = useId(); 11 | 12 | return ( 13 |
17 |

21 | Got a pattern request? 22 |

23 |

24 | Let us know, and we'll add it! 25 |

26 | 32 | Send Suggestion 33 | 34 |
35 | ); 36 | }; 37 | --------------------------------------------------------------------------------