{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "icons", "type": "registry:item", "title": "aesthetic Icon", "description": "Aesthetic Icon primitive, v0.25.0. Installs ae-icon, ae-mode, ae-moon, ae-sun without the full stylesheet. Recipes: mode.js.", "registryDependencies": [ "https://aesthetic.mistystep.io/r/aesthetic-base.json" ], "meta": { "version": "0.25.0", "tier": "primitive", "route": "icons", "classes": ["ae-icon", "ae-mode", "ae-moon", "ae-sun"], "attributes": [], "tokens": [ "--ae-surface", "--ae-wash", "--ae-ink", "--ae-ink-faint", "--ae-accent", "--ae-surface-dark", "--ae-wash-dark", "--ae-ink-dark", "--ae-ink-faint-dark", "--ae-accent-dark", "--ae-font", "--ae-font-mono", "--ae-w-regular", "--ae-ease", "--ae-quick", "--ae-ink-muted", "--ae-ink-muted-dark" ], "recipes": ["mode.js"] }, "files": [ { "path": "registry/icons/aesthetic.icons.css", "type": "registry:file", "target": "~/aesthetic.icons.css", "content": "/* aesthetic icons v0.25.0 — generated by scripts/build-registry.mjs. */\n@import './aesthetic.base.css';\n\n:root {\n --ae-ink-muted: #737373;\n --ae-ink-muted-dark: #8f8f8f;\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not(.light):not([data-ae-mode='light']) {\n --ae-ink-muted: var(--ae-ink-muted-dark);\n }\n}\n\n.light,\n[data-ae-mode='light'] {\n color-scheme: light;\n}\n\n:root.dark,\n:root[data-ae-mode='dark'] {\n color-scheme: dark;\n --ae-ink-muted: var(--ae-ink-muted-dark);\n}\n\n.ae-icon {\nwidth: 1.2em;\n height: 1.2em;\n stroke: currentColor;\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n fill: none;\n vertical-align: -0.2em;\n}\n\n.ae-mode {\nposition: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.2em;\n height: 2.2em;\n background: transparent;\n border: 0;\n padding: 0;\n cursor: pointer;\n color: var(--ae-ink-muted);\n transition: color var(--ae-quick) var(--ae-ease);\n}\n\n.ae-mode::before {\ncontent: '\\200b';\n}\n\n.ae-mode:hover {\ncolor: var(--ae-ink);\n}\n\n.ae-mode .ae-icon {\nposition: absolute;\n inset: 0;\n margin: auto;\n width: 1.25em;\n height: 1.25em;\n transition:\n opacity var(--ae-quick) var(--ae-ease),\n transform var(--ae-quick) var(--ae-ease);\n}\n\n.ae-mode .ae-sun {\nopacity: 1;\n transform: rotate(0) scale(1);\n}\n\n.ae-mode .ae-moon {\nopacity: 0;\n transform: rotate(-90deg) scale(0.6);\n}\n\n.dark .ae-mode .ae-sun,\n[data-ae-mode='dark'] .ae-mode .ae-sun {\nopacity: 0;\n transform: rotate(90deg) scale(0.6);\n}\n\n.dark .ae-mode .ae-moon,\n[data-ae-mode='dark'] .ae-mode .ae-moon {\nopacity: 1;\n transform: rotate(0) scale(1);\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not(.light):not([data-ae-mode='light']) .ae-mode .ae-sun {\n opacity: 0;\n transform: rotate(90deg) scale(0.6);\n }\n\n :root:not(.light):not([data-ae-mode='light']) .ae-mode .ae-moon {\n opacity: 1;\n transform: rotate(0) scale(1);\n }\n}\n" }, { "path": "recipes/mode.js", "type": "registry:file", "target": "~/aesthetic.mode.js", "content": "/* the mode: light and dark, defaulting to the system, with a toggle.\n\n The boot — read the persisted choice before first paint so the page\n never flashes the wrong mode — must be inlined in :\n\n \n\n The toggle below pins the opposite of the effective scheme. Pinning\n color-scheme keeps UA widgets (scrollbars, form controls) on the\n pinned side even when the OS disagrees. The change itself is quick\n and interruptible: a new click cuts any in-flight transition and\n applies the newest mode immediately. Unsupported view transitions\n get the same quick color ease; reduced motion is instant.\n next-themes consumers: keep attribute=\"class\" and skip this file —\n the classes match. */\n(() => {\n const root = document.documentElement;\n let activeTransition = null;\n let easingTimer = 0;\n let runId = 0;\n let targetDark = null;\n\n const isDark = () =>\n root.classList.contains('dark')\n ? true\n : root.classList.contains('light')\n ? false\n : matchMedia('(prefers-color-scheme: dark)').matches;\n\n const reducedMode = matchMedia('(prefers-reduced-motion: reduce)');\n\n const clearAnimation = () => {\n if (activeTransition && activeTransition.skipTransition) {\n activeTransition.skipTransition();\n }\n activeTransition = null;\n if (easingTimer) {\n clearTimeout(easingTimer);\n easingTimer = 0;\n }\n root.classList.remove('ae-vt-mode', 'ae-mode-easing');\n };\n\n const applyMode = (dark) => {\n root.classList.toggle('dark', dark);\n root.classList.toggle('light', !dark);\n root.style.colorScheme = dark ? 'dark' : 'light';\n try {\n localStorage.setItem('ae-mode', dark ? 'dark' : 'light');\n } catch (e) {}\n };\n\n document.querySelectorAll('.ae-mode').forEach((btn) => {\n btn.addEventListener('click', () => {\n const nextDark = !(targetDark ?? isDark());\n const id = ++runId;\n targetDark = nextDark;\n const flip = () => {\n if (id !== runId) return;\n applyMode(nextDark);\n };\n clearAnimation();\n if (reducedMode.matches) {\n flip();\n } else if (document.startViewTransition) {\n root.classList.add('ae-vt-mode');\n activeTransition = document.startViewTransition(flip);\n easingTimer = setTimeout(() => {\n if (id !== runId) return;\n root.classList.remove('ae-vt-mode');\n easingTimer = 0;\n }, 180);\n activeTransition.finished.finally(() => {\n if (id !== runId) return;\n root.classList.remove('ae-vt-mode');\n activeTransition = null;\n if (easingTimer) {\n clearTimeout(easingTimer);\n easingTimer = 0;\n }\n });\n } else {\n root.classList.add('ae-mode-easing');\n flip();\n easingTimer = setTimeout(() => {\n if (id !== runId) return;\n root.classList.remove('ae-mode-easing');\n easingTimer = 0;\n }, 180);\n }\n });\n });\n})();\n" } ] }