{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "search", "type": "registry:ui", "title": "Search", "description": "A meaningful microanimated Search icon.", "files": [ { "path": "ui/icons/search.tsx", "type": "registry:ui", "content": "import { forwardRef, type SVGProps } from \"react\";\n\nexport interface SearchProps extends Omit, \"children\"> {\n size?: number | string;\n title?: string;\n}\n\nconst styles = \"/* Framework-neutral motion trigger. Import once in the consuming app. */\\n:root {\\n --mi-time: 1;\\n --mi-ease: cubic-bezier(0.32, 0.72, 0.24, 1);\\n --mi-ease-spring: linear(0, 0.032 2%, 0.12 4%, 0.31 7%, 0.56 10.5%, 0.76 13.5%, 0.9 16.5%, 0.988 19.5%, 1.042 22.5%, 1.07 26%, 1.078 30%, 1.062 36%, 1.03 44%, 1.008 52%, 0.997 62%, 0.998 74%, 1);\\n --mi-ease-pop: linear(0, 0.04 2%, 0.15 4.5%, 0.4 8%, 0.68 11.5%, 0.9 14.5%, 1.06 17.5%, 1.15 21%, 1.185 25%, 1.17 29.5%, 1.11 35.5%, 1.04 42%, 0.985 49%, 0.962 55.5%, 0.965 62%, 0.985 70%, 1.002 82%, 1);\\n --mi-ease-bounce: linear(0, 0.2 5%, 0.65 11%, 1.05 17%, 1.35 22%, 1.52 26%, 1.55 29%, 1.44 34%, 1.2 41%, 1 48%, 0.88 55%, 0.86 59%, 0.92 66%, 1 74%, 1.04 81%, 1.01 90%, 1);\\n}\\n\\n@media (hover: hover) {\\n :where(button:not(:disabled), [role=\\\"button\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"menuitem\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"tab\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"option\\\"]:not([aria-disabled=\\\"true\\\"]), a:not([aria-disabled=\\\"true\\\"]), summary:not([aria-disabled=\\\"true\\\"]), label:not([aria-disabled=\\\"true\\\"]), .mi-trigger:not([aria-disabled=\\\"true\\\"])):hover .mi {\\n --mi-progress: 1;\\n }\\n}\\n\\n:where(button:not(:disabled), [role=\\\"button\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"menuitem\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"tab\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"option\\\"]:not([aria-disabled=\\\"true\\\"]), a:not([aria-disabled=\\\"true\\\"]), summary:not([aria-disabled=\\\"true\\\"]), label:not([aria-disabled=\\\"true\\\"]), .mi-trigger:not([aria-disabled=\\\"true\\\"])):focus-visible .mi {\\n --mi-progress: 1;\\n}\\n\\n:where(button, [role=\\\"button\\\"], [role=\\\"menuitem\\\"], [role=\\\"tab\\\"], [role=\\\"option\\\"], a, summary, label, .mi-trigger):not(:hover):not(:focus-visible) .mi,\\n:where(button:disabled, [aria-disabled=\\\"true\\\"]) .mi {\\n --mi-progress: 0;\\n}\\n\\n.mi-play .mi,\\n.mi.mi-play { --mi-progress: 1; }\\n.mi { overflow: visible; }\\n\\n\\n@media (prefers-reduced-motion: no-preference) {\\n .mi-search .mi-search-glass {\\n transform-box: view-box;\\n transform-origin: 11px 11px;\\n transform: rotate(calc(var(--mi-progress, 0) * var(--mi-search-tilt, -17deg)))\\n scale(calc(1 + var(--mi-progress, 0) * (var(--mi-search-zoom, 1.08) - 1)));\\n transition: transform calc(300ms * var(--mi-time, 1))\\n var(--mi-ease-spring, ease-out);\\n }\\n /* One-shot scan. The 100% frame equals the base hover pose exactly, so the\\n keyframe hands off invisibly and hover-out is a plain reverse transition. */\\n @keyframes mi-search-scan {\\n 0% {\\n transform: rotate(0deg) scale(1);\\n }\\n 38% {\\n transform: rotate(calc(var(--mi-search-tilt, -17deg) * 1.35))\\n scale(calc(1 + (var(--mi-search-zoom, 1.08) - 1) * 1.3));\\n }\\n 64% {\\n transform: rotate(calc(var(--mi-search-tilt, -17deg) * 0.5))\\n scale(var(--mi-search-zoom, 1.08));\\n }\\n 100% {\\n transform: rotate(var(--mi-search-tilt, -17deg))\\n scale(var(--mi-search-zoom, 1.08));\\n }\\n }\\n @media (hover: hover) {\\n :where(button:not(:disabled), [role=\\\"button\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"menuitem\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"tab\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"option\\\"]:not([aria-disabled=\\\"true\\\"]), a:not([aria-disabled=\\\"true\\\"]), summary:not([aria-disabled=\\\"true\\\"]), label:not([aria-disabled=\\\"true\\\"]), .mi-trigger:not([aria-disabled=\\\"true\\\"])):where(:hover) .mi-search .mi-search-glass {\\n animation: mi-search-scan calc(300ms * var(--mi-time, 1))\\n ease-in-out 1;\\n }\\n }\\n :where(button:not(:disabled), [role=\\\"button\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"menuitem\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"tab\\\"]:not([aria-disabled=\\\"true\\\"]), [role=\\\"option\\\"]:not([aria-disabled=\\\"true\\\"]), a:not([aria-disabled=\\\"true\\\"]), summary:not([aria-disabled=\\\"true\\\"]), label:not([aria-disabled=\\\"true\\\"]), .mi-trigger:not([aria-disabled=\\\"true\\\"])):where(:focus-visible) .mi-search .mi-search-glass {\\n animation: mi-search-scan calc(300ms * var(--mi-time, 1))\\n ease-in-out 1;\\n }\\n}\";\n\nexport const Search = forwardRef(function Search(\n { size = 24, strokeWidth = 2, className, title, \"aria-label\": ariaLabel, ...props },\n ref,\n) {\n const accessibleTitle = title ?? ariaLabel;\n\n return (\n <>\n \n ${escapeText(title)}` : \"\"}`,\n }}\n {...props}\n />\n \n );\n});\n\nSearch.displayName = \"Search\";\n\nfunction escapeText(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\");\n}\n\nexport default Search;\n" } ] }