{"_id":"@dennisl0731/oh-my-ui","_rev":"8-f28454abd70af943d1ac7aef22933b39","name":"@dennisl0731/oh-my-ui","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@dennisl0731/oh-my-ui","version":"0.1.0","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.1.0","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"517872c4e17a22ec04f28a5b5a897e5db0888cd3","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.1.0.tgz","fileCount":5,"integrity":"sha512-1wvJokf05PoBeVwsBW/T+CcOHu+VdpQRiel6ueU+qQZfG5M11q0NkpIo1bgGravA82IDjCZbGiEBxSDNYpXqKw==","signatures":[{"sig":"MEUCICqKJQ/piTjEDnJ7ET/gd2hAqQfSRmdlrYOmguuX5Z8zAiEAk5RhqPMIoMlAdNsAtGbsOjbQC3mktjbz/gNJ7y3IWdU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":52413},"exports":{"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset for the oh-my-* family. Warm-paper aesthetic, sage-green accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"tailwindcss":"^3.4.0","lucide-react":"^1.0.0"},"peerDependenciesMeta":{"tailwindcss":{"optional":true},"lucide-react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.1.0_1779957113199_0.2533431922625491","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@dennisl0731/oh-my-ui","version":"0.2.0","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.2.0","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"fe5e857d27cc208bc3ed17bd6042292eda73f134","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.2.0.tgz","fileCount":5,"integrity":"sha512-AEAqDmWklVlt03OyFIJaA3YHKpo74L99mkOwQ2LrdwxFyi31FZ7w+IfK6J9xVkfEPuciS731CZP8yb+X3JBHCg==","signatures":[{"sig":"MEQCIHsJuPfoXtQ5nk0VI3VhN0AXXAtqhe9e5v1/Yqy0fDBVAiAGB0xgM5zroM/TEz6kHJAZn6eUXb+auCrS6sTSIT5XJA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":52629},"exports":{"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset for the oh-my-* family. Warm-paper aesthetic, sage-green accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"tailwindcss":"^3.4.0","lucide-react":"^1.0.0"},"peerDependenciesMeta":{"tailwindcss":{"optional":true},"lucide-react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.2.0_1779958857582_0.326957221953724","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@dennisl0731/oh-my-ui","version":"0.2.1","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.2.1","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"721b7cba6f12ca2320edf1526f1ae6d381c81906","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.2.1.tgz","fileCount":6,"integrity":"sha512-My72t+RhBXsyqyKknWC782B0BXXHnPik+9EMfr77N5DW47GxRHCbAbVAyn0FNEH3GvQHNMNvZrLswTnC0TAUZw==","signatures":[{"sig":"MEUCIEtlXNLptFnyZLGH3S9euZV3PqVYBDqHeWBYnSba1zjQAiEAg5ICPQws5ITPN4PiyaLQWK52eKuHX2XZm7wO8WiD6hw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":79959},"exports":{"./icons":{"import":"./icons.mjs","default":"./icons.mjs"},"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","scripts":{"test":"npm run test:tokens && npm run test:visual","test:serve":"node ./scripts/serve-examples.mjs","test:tokens":"playwright test tests/tokens.spec.js","test:visual":"playwright test tests/visual.spec.js","test:update-snapshots":"playwright test tests/visual.spec.js --update-snapshots"},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset + lucide icon re-export for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"lucide-react":"^1.16.0","@tailwindcss/forms":"^0.5.10","@tailwindcss/typography":"^0.5.16","@tailwindcss/aspect-ratio":"^0.4.2","@tailwindcss/container-queries":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@playwright/test":"^1.54.1"},"peerDependencies":{"react":"^18.0.0","tailwindcss":"^3.4.0"},"peerDependenciesMeta":{"react":{"optional":true},"tailwindcss":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.2.1_1780364675863_0.8234499804335453","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@dennisl0731/oh-my-ui","version":"0.2.2","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.2.2","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"9a8730c0843caacd5d82c8089a79d46a97a9f8ae","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.2.2.tgz","fileCount":6,"integrity":"sha512-MhbxHiKgzkWWWBFkN1FWaD31ZZtDGRQ/1+INTc2Tpv9KAaOZbNHHR6QH3EuNshb97g80si26lU1n3NpWkI43UA==","signatures":[{"sig":"MEQCIFdfzBREHfVt2lRvsniiw7TJg6er/hDDGhtHfg7GcPy8AiBJwAIOVD0ZcsBF0zxoXIzGpeudurJBJOQEQxISYQ5TWw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":89286},"exports":{"./icons":{"import":"./icons.mjs","default":"./icons.mjs"},"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","scripts":{"test":"npm run test:tokens && npm run test:visual","test:serve":"node ./scripts/serve-examples.mjs","test:tokens":"playwright test tests/tokens.spec.js","test:visual":"playwright test tests/visual.spec.js","test:update-snapshots":"playwright test tests/visual.spec.js --update-snapshots"},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset + lucide icon re-export for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"lucide-react":"^1.16.0","@tailwindcss/forms":"^0.5.10","@tailwindcss/typography":"^0.5.16","@tailwindcss/aspect-ratio":"^0.4.2","@tailwindcss/container-queries":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@playwright/test":"^1.54.1"},"peerDependencies":{"react":"^18.0.0","tailwindcss":"^3.4.0"},"peerDependenciesMeta":{"react":{"optional":true},"tailwindcss":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.2.2_1780369088597_0.7323713281159141","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@dennisl0731/oh-my-ui","version":"0.2.3","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.2.3","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"5359db77aef60c77a89351d05135c64c3cb1da55","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.2.3.tgz","fileCount":7,"integrity":"sha512-tzkhIOdgbrobo7gTZuyp1LgcvpK0Z3s/9pyPQtxnjRgy/JboUZvcoprisCUdGLNqZ/uvJvhRf5eQfJAF9RUkkA==","signatures":[{"sig":"MEUCIHe2sm0cxxGHKzf9nuoGoVBaV5GzUBov8IDy2uVPcoqdAiEAsMQy0ocRKUm6EqU0oSWAV5+bmhgpe1wgcLIbRzaSKOk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":123773},"exports":{"./icons":{"import":"./icons.mjs","default":"./icons.mjs"},"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./icons.svg":"./icons.svg","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","scripts":{"test":"npm run test:tokens && npm run test:visual","test:serve":"node ./scripts/serve-examples.mjs","test:tokens":"playwright test tests/tokens.spec.js","test:visual":"playwright test tests/visual.spec.js","test:update-snapshots":"playwright test tests/visual.spec.js --update-snapshots"},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset + lucide icons (React + vanilla SVG sprite) + nav/toast/copy helpers for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"lucide-react":"^1.16.0","@tailwindcss/forms":"^0.5.10","@tailwindcss/typography":"^0.5.16","@tailwindcss/aspect-ratio":"^0.4.2","@tailwindcss/container-queries":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@playwright/test":"^1.54.1"},"peerDependencies":{"react":"^18.0.0","tailwindcss":"^3.4.0"},"peerDependenciesMeta":{"react":{"optional":true},"tailwindcss":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.2.3_1780374137186_0.44374959752381615","host":"s3://npm-registry-packages-npm-production"}},"0.2.4":{"name":"@dennisl0731/oh-my-ui","version":"0.2.4","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.2.4","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"6d631845f519fd77d078910244c6beeb8d842cb1","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.2.4.tgz","fileCount":7,"integrity":"sha512-/XJMQRJI5SbkHL++1NPrdtLPsb1Opw19yhJHip2t6InChip2Kcm7Fr7NeDL6wF98gPMNgS4N/H9Tva3E4hWn/A==","signatures":[{"sig":"MEYCIQDgAYyKDMtk5hzk5wXBh/So89I6QVo8MzauR7tcxePtswIhAKrIomPh9hW3D+ArvEwxr25pd92G2tuNO+WyIZVgbv7T","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":139794},"exports":{"./icons":{"import":"./icons.mjs","default":"./icons.mjs"},"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./icons.svg":"./icons.svg","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","scripts":{"test":"npm run test:tokens && npm run test:visual","test:serve":"node ./scripts/serve-examples.mjs","test:tokens":"playwright test tests/tokens.spec.js","test:visual":"playwright test tests/visual.spec.js","test:update-snapshots":"playwright test tests/visual.spec.js --update-snapshots"},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset + lucide icons (React + vanilla SVG sprite) + nav/toast/copy helpers for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"lucide-react":"^1.16.0","@tailwindcss/forms":"^0.5.10","@tailwindcss/typography":"^0.5.16","@tailwindcss/aspect-ratio":"^0.4.2","@tailwindcss/container-queries":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@playwright/test":"^1.54.1"},"peerDependencies":{"react":"^18.0.0","tailwindcss":"^3.4.0"},"peerDependenciesMeta":{"react":{"optional":true},"tailwindcss":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.2.4_1781891902389_0.6468563217453536","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@dennisl0731/oh-my-ui","version":"0.3.0","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","_id":"@dennisl0731/oh-my-ui@0.3.0","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"homepage":"https://github.com/oh-my/ui#readme","bugs":{"url":"https://github.com/oh-my/ui/issues"},"dist":{"shasum":"5164eeff8be4d4f29f25ac6cfd2168a1cbd08017","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.3.0.tgz","fileCount":7,"integrity":"sha512-shiVod+v+82/NVG5104p8hpM/XaIquR8/5OiZidZfGpVZI/mC2TnZJvWGP7DAv/L1lm6MjwgXn7yGEcfw0rpLg==","signatures":[{"sig":"MEYCIQDl9d2FJK4RZJc3jBgc21gPU/gcJsSGCzFS4yoPFehHlQIhAIkLc29dr9fG3ogohGG+kDggPqENVoC4sF7nXx7YQQmO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":163639},"exports":{"./icons":{"import":"./icons.mjs","default":"./icons.mjs"},"./nav.js":"./js/nav.js","./omi.css":"./css/omi.css","./icons.svg":"./icons.svg","./tailwind-preset":{"import":"./tailwind-preset.js","default":"./tailwind-preset.js","require":"./tailwind-preset.js"}},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","scripts":{"test":"npm run test:tokens && npm run test:visual","test:serve":"node ./scripts/serve-examples.mjs","test:tokens":"playwright test tests/tokens.spec.js","test:visual":"playwright test tests/visual.spec.js","test:update-snapshots":"playwright test tests/visual.spec.js --update-snapshots"},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"repository":{"url":"git+https://github.com/oh-my/ui.git","type":"git"},"_npmVersion":"11.11.0","description":"Shared CSS + Tailwind preset + lucide icons (React + vanilla SVG sprite) + nav/toast/copy helpers for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"lucide-react":"^1.16.0","@tailwindcss/forms":"^0.5.10","@tailwindcss/typography":"^0.5.16","@tailwindcss/aspect-ratio":"^0.4.2","@tailwindcss/container-queries":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@playwright/test":"^1.54.1"},"peerDependencies":{"react":"^18.0.0","tailwindcss":"^3.4.0"},"peerDependenciesMeta":{"react":{"optional":true},"tailwindcss":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/oh-my-ui_0.3.0_1782174876848_0.2742286914702956","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@dennisl0731/oh-my-ui","version":"0.4.0","description":"Shared CSS + Tailwind preset + lucide icons (React + vanilla SVG sprite) + nav/toast/copy helpers for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"license":"MIT","author":{"name":"Dennis L","email":"dennisl@udel.edu"},"repository":{"type":"git","url":"git+https://github.com/oh-my/ui.git"},"scripts":{"test":"npm run test:tokens && npm run test:visual","test:serve":"node ./scripts/serve-examples.mjs","test:tokens":"playwright test tests/tokens.spec.js","test:visual":"playwright test tests/visual.spec.js","test:update-snapshots":"playwright test tests/visual.spec.js --update-snapshots"},"exports":{"./omi.css":"./css/omi.css","./nav.js":"./js/nav.js","./icons.svg":"./icons.svg","./tailwind-preset":{"require":"./tailwind-preset.js","import":"./tailwind-preset.js","default":"./tailwind-preset.js"},"./icons":{"import":"./icons.mjs","default":"./icons.mjs"}},"dependencies":{"@tailwindcss/aspect-ratio":"^0.4.2","@tailwindcss/container-queries":"^0.1.1","@tailwindcss/forms":"^0.5.10","@tailwindcss/typography":"^0.5.16","lucide-react":"^1.16.0"},"devDependencies":{"@playwright/test":"^1.54.1"},"peerDependencies":{"tailwindcss":"^3.4.0","react":"^18.0.0"},"peerDependenciesMeta":{"tailwindcss":{"optional":true},"react":{"optional":true}},"publishConfig":{"access":"public"},"gitHead":"f7c3d9e320cd724417a159d18a82fba5f37e8963","_id":"@dennisl0731/oh-my-ui@0.4.0","bugs":{"url":"https://github.com/oh-my/ui/issues"},"homepage":"https://github.com/oh-my/ui#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-B3A6ANdU60u4EvmCP9pik40/6sC2u6V+Y/BEbvbpU76qlA1mu3cIyvOE/EVo72jvKYv8gpnJS2BUu94Td7OasQ==","shasum":"72a899283461fdfc1e3cb6d4836526a0b48925a7","tarball":"https://registry.npmjs.org/@dennisl0731/oh-my-ui/-/oh-my-ui-0.4.0.tgz","fileCount":7,"unpackedSize":168731,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDyuJ0vivFHOxjnqVhr3DYr4tOG4PrU5tI5WNYhANdWcgIgMI+Iu0veVzPNgsa3X0xuEMTsALYkd2j0vqC1QfF9PFc="}]},"_npmUser":{"name":"dennislx","email":"dennisl@udel.edu"},"directories":{},"maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/oh-my-ui_0.4.0_1783186200849_0.6878426993246016"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-28T08:31:53.058Z","modified":"2026-07-04T17:30:01.084Z","0.1.0":"2026-05-28T08:31:53.336Z","0.2.0":"2026-05-28T09:00:57.732Z","0.2.1":"2026-06-02T01:44:36.026Z","0.2.2":"2026-06-02T02:58:08.741Z","0.2.3":"2026-06-02T04:22:17.501Z","0.2.4":"2026-06-19T17:58:22.575Z","0.3.0":"2026-06-23T00:34:36.986Z","0.4.0":"2026-07-04T17:30:00.979Z"},"bugs":{"url":"https://github.com/oh-my/ui/issues"},"author":{"name":"Dennis L","email":"dennisl@udel.edu"},"license":"MIT","homepage":"https://github.com/oh-my/ui#readme","keywords":["oh-my","tailwindcss","tailwind-preset","design-system","css"],"repository":{"type":"git","url":"git+https://github.com/oh-my/ui.git"},"description":"Shared CSS + Tailwind preset + lucide icons (React + vanilla SVG sprite) + nav/toast/copy helpers for the oh-my-* family. Warm-paper aesthetic, earthy-brown accents, per-service colorways.","maintainers":[{"name":"dennislx","email":"dennisl@udel.edu"}],"readme":"# @dennisl0731/oh-my-ui\n\nShared CSS + Tailwind preset for the `oh-my-*` family.\n\nThis package is intentionally a design foundation, not a locked UI kit. It gives you:\n\n- design tokens via CSS variables\n- a Tailwind preset wired to those tokens\n- a small nav/theme bootstrap script that enhances existing markup\n- Lucide icon re-exports (React) **and a vanilla `icons.svg` sprite** for server-rendered apps\n- JS helpers for toast + clipboard copy (works on plain HTTP)\n\nIt does not force page structure, copy, layout, or product-specific components.\n\n## What's new in 0.4.0\n\n- **Apps menu + host-derived cross-app links** in `nav.js`. Supply a registry\n  via `window.__OMI_APPS__` or `data-apps=\"<url.json>\"`; `.omi-appsdrop` is\n  filled with links built from the current host (`window.omiOrigin(sub)`), so\n  they survive reverse-proxy base/port changes. `.omi-nav` entries now accept\n  `sub` too. No project data baked into the package. See the *Apps menu* section.\n\n## What's new in 0.2.4\n\n- **New CSS primitives** promoted from the oh-my-* apps (were re-implemented in\n  several app `<style>` blocks):\n  - `.omi-switch` — toggle switch (`.sm` variant). Wrap a checkbox + `.track`.\n  - `.omi-meter` — progress / capacity bar (`.lg`/`.sm` sizes; `.fill.warn`,\n    `.fill.full`; `.indeterminate`).\n  - `.omi-details` — collapsible disclosure (`<summary>` + `.omi-chevron` +\n    `.omi-summary-note`; body in `.omi-details-body`).\n  - `.omi-mini-player` — fixed bottom media bar (`.mp-ico`/`.mp-title`/`.mp-audio`).\n  - `.omi-log-console` — floating streaming-log panel (`.lc-head`/`.lc-chip`\n    `running|done|error` / `.lc-body` / `.lc-line` `step|ok|warn|err`).\n  - `.omi-usage-tip` — provider-status popover (`.dot.ok/.bad`, `.nm`).\n  - `.omi-hint` text + pill variants `.omi-pill.info`, `.omi-pill.accent`,\n    `.omi-pill.lg`.\n- Gallery: new `controls` page in `examples/visual/` showcasing the above.\n\n## What's new in 0.2.3\n\n- **JS helpers** in `nav.js`: `window.omiToast(...)`, `window.omiCopy(text)`, and\n  a declarative `<button data-omi-copy=\"…\">` auto-wire (with HTTPS-fallback so\n  copy works on plain-HTTP dev servers).\n- **`icons.svg`** — vanilla SVG `<symbol>` sprite for non-React consumers\n  (Flask, Jinja, any server-rendered app). Reference via\n  `<svg><use href=\"…/icons.svg#i-clipboard\"/></svg>`.\n- **New CSS primitives:** `.omi-dropzone`, `.omi-asset-card` (+ `.omi-asset-grid`),\n  `.omi-ai-badge`, `.omi-theme-picker.compact`, `.omi-scroll-clean`.\n- **Press feedback:** `.omi-btn.pressed` animation + `.confirmed::after`\n  floating \"✓ copied\" chip — automatically applied by `data-omi-copy`.\n- **Behavior change:** `.omi-panel-body` no longer auto-scrolls. Short static\n  panels in flex column layouts no longer get surprise scrollbars. Add\n  the `.scroll` modifier on dashboard panels that genuinely need overflow.\n\n## Install\n\n```sh\nnpm install @dennisl0731/oh-my-ui\n```\n\nConsumer app peer deps:\n\n```sh\nnpm install tailwindcss react\n```\n\n`lucide-react` and the common Tailwind plugins used by the preset are bundled through this package.\n\n## What You Get\n\n### 1. CSS tokens and shared primitives\n\nImport once:\n\n```css\n@import '@dennisl0731/oh-my-ui/omi.css';\n\n@tailwind components;\n@tailwind utilities;\n```\n\nSkip `@tailwind base`. `omi.css` already ships its own base/reset styles.\n\nThe stylesheet includes:\n\n- warm layered surfaces\n- shared accent/border/text tokens\n- button, input, panel, dialog, card, pill, log, markdown, and editor primitives\n- stat cards, alerts, toolbars, tables, skeletons, menu/popover/drawer/toast shells\n- code and document block wrappers\n- service-specific accent overrides such as `body.service-image` and `body.service-slide`\n\nDefault service accent is the earthy tan palette. `body.service-sage` restores the older sage accent.\n\n### 2. Built-in themes\n\nTheme switching is driven by `data-omi-theme` on `<html>`.\n\nBuilt-in themes:\n\n- `paper`\n- `forest`\n- `ocean`\n- `sunset`\n- `grape`\n- `graphite`\n- `rose`\n- `dark` — dark, GitHub-style\n- `light` — light, GitHub-style\n- `catppuccin` — dark, Catppuccin Mocha\n- `nord` — dark, Nord\n\nExample:\n\n```html\n<html data-omi-theme=\"ocean\">\n```\n\nThese themes change the global surface/text/accent tokens. Service classes like `service-image` still work on top by only shifting the accent family.\n\nThe seven warm-paper themes (`paper`…`rose`) are light. The four ported\nthemes add dark colorways (`dark`, `catppuccin`, `nord`) plus a neutral\n`light`. Dark themes re-skin the bundled components by overriding the\nneutral surface channels (`--omi-surface`, `--omi-surface-2`,\n`--omi-surface-3`, `--omi-shadow-rgb`, `--omi-scrim-rgb`); small semantic\nchips (status pills, thinking/tool blocks) keep their accent tints.\n\n### 3. Tailwind preset\n\nUse the preset in `tailwind.config.js`:\n\n```js\nimport omiPreset from '@dennisl0731/oh-my-ui/tailwind-preset';\n\n/** @type {import('tailwindcss').Config} */\nexport default {\n  presets: [omiPreset],\n  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],\n};\n```\n\nThe preset adds:\n\n- token-backed colors such as `bg-surface-1`, `text-fg`, `bg-accent`, `border-edge`\n- token-backed fonts and radii\n- `shadow-soft` and `shadow-lift`\n- centered container defaults\n- `prose-omi` typography theme\n- `@tailwindcss/forms` with `strategy: 'class'`\n- `@tailwindcss/typography`\n- `@tailwindcss/aspect-ratio`\n- `@tailwindcss/container-queries`\n\nThis is the closest thing to an \"industrial standard\" baseline for app work: forms, prose, media ratio handling, responsive container logic, and tokenized colors all work out of the box.\n\n### 4. Icons\n\n**React consumers:**\n\n```tsx\nimport { Check, Plus, Trash2 } from '@dennisl0731/oh-my-ui/icons';\n```\n\nEverything from `lucide-react` is re-exported unchanged.\n\n**Vanilla / server-rendered consumers:**\n\nThe package ships an SVG `<symbol>` sprite at `icons.svg`. Inline it once\nper page (or serve it as a static file and reference via `<use>`):\n\n```html\n{# Jinja partial: inline once #}\n{% include '_icons.html' %}                       <!-- or copy icons.svg verbatim -->\n\n<svg class=\"icon\" aria-hidden=\"true\">\n  <use href=\"#i-clipboard\"/>\n</svg>\n```\n\nSprite IDs are `i-<lucide-icon-name>` — e.g. `i-clipboard`, `i-image`,\n`i-pen-line`, `i-settings`, `i-refresh-cw`, `i-trash-2`, `i-sparkles`,\n`i-bug`, `i-check`, `i-x`, `i-chevron-right/down/left/up`, `i-arrow-left/right`,\n`i-search`, `i-eye`, `i-copy`, `i-file`, `i-folder`, `i-download`, `i-upload`,\n`i-presentation`, `i-brain`, `i-info`, `i-alert-circle`, `i-check-circle`,\n`i-x-circle`, `i-more-horizontal`, `i-menu`, `i-sun`, `i-moon`, `i-plus`,\n`i-edit`, `i-scan-text`.\n\nRecommended icon CSS:\n\n```css\n.icon { width: 1em; height: 1em; vertical-align: -0.15em; flex-shrink: 0; display: inline-block; }\n```\n\nIcons use `stroke=\"currentColor\"` so they inherit the surrounding text color.\n\n### 5. JS helpers (`nav.js`)\n\n`nav.js` exposes three globals:\n\n```js\n// Toast — uses .omi-toast / .omi-toast-stack from omi.css. Creates the\n// stack container on first call if not already in the DOM.\nwindow.omiToast({ title: 'Saved', body: 'Theme updated.', kind: 'success' });\nwindow.omiToast('Quick title', 'optional body', 'error');   // legacy positional\n\n// Clipboard write with HTTPS-context fallback. Always returns Promise<boolean>.\nconst ok = await window.omiCopy('text to copy');\n\n// Re-scan the DOM for [data-omi-copy] after dynamic inserts.\nwindow.omiWireCopy(rootElement?);\n```\n\nThe declarative form auto-wires on initial page load:\n\n```html\n<button data-omi-copy=\"ssh user@host\"\n        data-omi-copy-label=\"ssh command\">\n  copy\n</button>\n```\n\nOn click: press animation → clipboard write (with fallback) → ✓ floater on\nsuccess → toast (`title: 'Copied'` or `'Copy failed'`, body = the label).\nButtons that already opt out via `data-omi-copy-wired` are skipped on rescan,\nso calling `omiWireCopy()` repeatedly is safe.\n\n## Theme Switching\n\n`nav.js` no longer injects product UI. It only enhances markup you already render.\n\n### Minimal example\n\n```html\n<div class=\"omi-topnav\">\n  <div class=\"omi-brand\">oh-my-ui</div>\n  <nav class=\"omi-nav\"></nav>\n  <label class=\"omi-theme-picker\">\n    <span>Theme</span>\n    <select data-omi-theme-switch></select>\n  </label>\n</div>\n\n<script>\n  window.__OMI_NAV__ = [\n    { id: 'image', label: 'image', href: 'http://localhost:5006/' },\n    { id: 'slide', label: 'slide', href: 'http://localhost:5008/' },\n    { id: 'skill', label: 'skill', href: 'http://localhost:5009/' }\n  ];\n\n  window.__OMI_THEME__ = {\n    defaultTheme: 'paper'\n  };\n</script>\n<script src=\"/node_modules/@dennisl0731/oh-my-ui/js/nav.js\" data-service=\"image\"></script>\n```\n\n### Behavior\n\n- if `window.__OMI_NAV__` exists, `.omi-nav` will be filled from that config\n- if a `select[data-omi-theme-switch]` exists, it will be wired to theme state\n- if the select has no options, built-in themes are injected automatically\n- selected theme is persisted to `localStorage` under `omi-theme`\n- theme changes emit `document` event `omi:themechange`\n\n### Optional theme config\n\n```html\n<script>\n  window.__OMI_THEME__ = {\n    defaultTheme: 'forest',\n    storageKey: 'my-app-theme',\n    themes: [\n      { id: 'paper', label: 'Paper' },\n      { id: 'forest', label: 'Forest' },\n      { id: 'ocean', label: 'Ocean' }\n    ]\n  };\n</script>\n```\n\n### Apps menu (`.omi-appsdrop`) — host-derived cross-app links\n\nThe package ships **no app list** (that's project data). Supply your own and\n`nav.js` fills any `.omi-appsdrop` container. Two ways:\n\n```html\n<!-- inline -->\n<script>\n  window.__OMI_APPS__ = [\n    { id: 'clipboard', sub: 'clipboard', path: '/',        label: 'clipboard', icon: 'clipboard' },\n    { id: 'podcast',   sub: 'rss',       path: '/podcast', label: 'podcast',   icon: '<svg …>' }\n  ];\n</script>\n<script src=\"…/nav.js\" data-service=\"clipboard\"></script>\n\n<!-- or a settings file, fetched once -->\n<script src=\"…/nav.js\" data-service=\"clipboard\" data-apps=\"/omi/apps.json\"></script>\n```\n\nEach entry: `{id, sub, path, label, icon}`. `icon` is either a sprite id\n(`clipboard` → `icons.svg#i-clipboard`) or a raw `<svg …>` string. No list →\nthe container is left untouched (so hardcoded markup still works).\n\nHrefs are built by `window.omiOrigin(sub)`, which swaps only the leading host\nlabel and keeps the current scheme/apex/port — so links keep working when the\nreverse proxy changes the base domain or port. `.omi-nav` entries accept the\nsame `sub` (+ optional `path`) instead of a hardcoded `href`/`port`. Bare-IP /\n`localhost` / single-label hosts fall back to same-origin.\n\n## Tailwind Utilities\n\nCommon aliases:\n\n| Class | Token |\n| --- | --- |\n| `bg-surface-{1,2,3}` | `--tw-bg-{1,2,3}` |\n| `text-fg` | `--tw-fg` |\n| `text-fg-soft` | `--tw-fg-soft` |\n| `text-muted` | `--tw-muted` |\n| `bg-accent`, `bg-accent-deep` | `--tw-accent*` |\n| `border-edge`, `border-edge2` | `--omi-border*` |\n| `font-sans`, `font-heading`, `font-mono` | `--omi-font-*` |\n| `rounded`, `rounded-lg`, `rounded-full` | `--omi-radius*` |\n\nOpacity modifiers work:\n\n```html\n<div class=\"bg-fg/5 text-fg border border-edge\"></div>\n```\n\n## Recommended Next Components\n\nThese primitives are now available as styling building blocks:\n\n- tables: `.omi-table-wrap`, `.omi-table`, `.omi-table-actions`, `.omi-table-empty`\n- alerts: `.omi-alert.info|success|warn|error`\n- stat cards: `.omi-stat-grid`, `.omi-stat-card`, `.omi-stat-value`, `.omi-stat-delta`\n- toolbars: `.omi-toolbar`, `.omi-toolbar-group`, `.omi-segmented`\n- list rows: `.omi-list`, `.omi-list-row`, `.omi-list-row-meta`, `.omi-list-row-actions`\n- loading states: `.omi-skeleton.line|title|block`\n- overlays: `.omi-menu`, `.omi-popover`, `.omi-drawer`, `.omi-toast-stack`, `.omi-toast`\n- code/docs: `.omi-codeblock`, `.omi-doc-block`\n- ingestion: `.omi-dropzone` *(0.2.3)*\n- gallery: `.omi-asset-grid`, `.omi-asset-card` (+ `.omi-asset-card-thumb|meta|name|submeta|actions|check`) *(0.2.3)*\n- status: `.omi-ai-badge.ok|bad` *(0.2.3)*\n- chrome: `.omi-theme-picker.compact`, `.omi-scroll-clean` *(0.2.3)*\n- feedback animations: `.omi-btn.pressed`, `.omi-btn.confirmed` *(0.2.3, auto-applied by `data-omi-copy`)*\n\nThey are intentionally presentational. Behavior such as keyboard navigation, focus management, escape handling, portal mounting, and toast lifecycles should stay in the consumer app or a later headless helper layer.\n\n## Primitive Examples\n\n### Table\n\n```html\n<div class=\"omi-table-wrap\">\n  <table class=\"omi-table\">\n    <thead>\n      <tr>\n        <th>Name</th>\n        <th>Status</th>\n        <th></th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>Experiment A</td>\n        <td><span class=\"omi-pill ok\">Ready</span></td>\n        <td>\n          <div class=\"omi-table-actions\">\n            <button class=\"omi-btn secondary sm\">Open</button>\n          </div>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n```\n\n### Alert\n\n```html\n<div class=\"omi-alert warn\">\n  <div class=\"omi-alert-body\">\n    <strong>Review needed</strong>\n    Config changed and requires confirmation before publish.\n  </div>\n</div>\n```\n\n### Toolbar\n\n```html\n<div class=\"omi-toolbar\">\n  <div class=\"omi-toolbar-group\">\n    <input class=\"omi-input\" placeholder=\"Search\">\n    <div class=\"omi-segmented\">\n      <button class=\"active\">All</button>\n      <button>Open</button>\n      <button>Closed</button>\n    </div>\n  </div>\n  <div class=\"omi-toolbar-spacer\"></div>\n  <div class=\"omi-toolbar-group\">\n    <button class=\"omi-btn secondary\">Filter</button>\n    <button class=\"omi-btn primary\">New</button>\n  </div>\n</div>\n```\n\n### Stat Cards\n\n```html\n<div class=\"omi-stat-grid\">\n  <div class=\"omi-stat-card\">\n    <div class=\"omi-stat-label\">Success Rate</div>\n    <div class=\"omi-stat-value\">93.2%</div>\n    <div class=\"omi-stat-meta\">\n      <span class=\"omi-stat-delta up\">+4.1%</span>\n      vs last week\n    </div>\n  </div>\n</div>\n```\n\n### Skeleton\n\n```html\n<div class=\"omi-card\">\n  <div class=\"omi-card-body\">\n    <div class=\"omi-skeleton title\"></div>\n    <div style=\"height:10px\"></div>\n    <div class=\"omi-skeleton line\"></div>\n    <div style=\"height:8px\"></div>\n    <div class=\"omi-skeleton line\"></div>\n  </div>\n</div>\n```\n\n### Drawer and Toast\n\n```html\n<aside class=\"omi-drawer open\">\n  <div class=\"omi-drawer-header\">\n    <div class=\"omi-drawer-title\">Details</div>\n  </div>\n  <div class=\"omi-drawer-body\">...</div>\n</aside>\n<div class=\"omi-drawer-backdrop\"></div>\n\n<div class=\"omi-toast-stack\">\n  <div class=\"omi-toast success\">\n    <div class=\"omi-toast-title\">Saved</div>\n    <div class=\"omi-toast-body\">Theme settings were updated.</div>\n  </div>\n</div>\n```\n\nToasts are usually spawned from JS — see `window.omiToast(...)` above.\n\n### Drop zone (`.omi-dropzone`)\n\nPaste / drag-drop / click-to-pick file ingestion:\n\n```html\n<div class=\"omi-dropzone\" id=\"dz\" tabindex=\"0\" role=\"button\">\n  <span class=\"omi-dropzone-icon\">\n    <svg class=\"icon\"><use href=\"#i-clipboard\"/></svg>\n  </span>\n  <div class=\"omi-dropzone-title\">\n    Paste an image with <kbd>Ctrl</kbd>+<kbd>V</kbd>\n  </div>\n  <div class=\"omi-dropzone-hint\">\n    drag &amp; drop a file<span class=\"sep\">·</span>or click\n  </div>\n  <input type=\"file\" hidden>\n</div>\n```\n\nThe app owns the JS — wire `paste` / `dragover` / `drop` / `click` handlers\nand toggle the `.active` class on dragover for the highlight pulse. Idle\nstate has a subtle lifted shadow; hover gets an accent ring; active gets\nsolid border + accent fill.\n\n### Asset card (`.omi-asset-card` + `.omi-asset-grid`)\n\nTile-based gallery for screenshots, images, or any thumbnailable asset:\n\n```html\n<div class=\"omi-asset-grid\">\n  <div class=\"omi-asset-card\">\n    <img class=\"omi-asset-card-thumb\" src=\"/thumb/1\" alt=\"\">\n    <div class=\"omi-asset-card-meta\">\n      <div class=\"omi-asset-card-name\">screenshot_2026-06-02.png</div>\n      <div class=\"omi-asset-card-submeta\">\n        <span>77 KB</span>\n        <span class=\"sep\">·</span>\n        <span>screenshots</span>\n      </div>\n    </div>\n    <div class=\"omi-asset-card-actions\">\n      <button class=\"omi-icon-btn\" data-omi-copy=\"/path/to/file\"\n              title=\"Copy path\">\n        <svg class=\"icon\"><use href=\"#i-copy\"/></svg>\n      </button>\n      <button class=\"omi-icon-btn\" title=\"Edit\">\n        <svg class=\"icon\"><use href=\"#i-pen-line\"/></svg>\n      </button>\n      <button class=\"omi-icon-btn danger\" title=\"Delete\">\n        <svg class=\"icon\"><use href=\"#i-trash-2\"/></svg>\n      </button>\n    </div>\n    <div class=\"omi-asset-card-check\">\n      <svg class=\"icon\"><use href=\"#i-check\"/></svg>\n    </div>\n  </div>\n</div>\n```\n\nApply `.selected` to a card to mark it; apply `.selecting` to the grid to\nalways-show the check overlay during multi-select. Without those, the check\nappears only on hover.\n\n### AI status badge (`.omi-ai-badge`)\n\n```html\n<span class=\"omi-ai-badge ok\"  title=\"Claude AI available\"></span>\n<span class=\"omi-ai-badge bad\" title=\"AI not configured\"></span>\n<span class=\"omi-ai-badge\"     title=\"AI status unknown\"></span>\n```\n\nPure CSS: the dot is a `::before` pseudo-element. Toggle the `.ok` / `.bad`\nclass from JS based on your AI/service health check.\n\n### Compact theme picker (`.omi-theme-picker.compact`)\n\n```html\n<label class=\"omi-theme-picker compact\" title=\"Choose theme\">\n  <span>theme</span>     <!-- hidden in compact mode; kept for a11y -->\n  <select data-omi-theme-switch></select>\n</label>\n```\n\nThe `compact` modifier hides the label, drops the background/padding, and\ngives the `<select>` a borderless-pill style that fits cleanly in a top nav.\n\n### Node graphs (`.omi-flow` — React Flow + elkjs)\n\nA **theme-only, opt-in** restyle of [`@xyflow/react`](https://reactflow.dev)\nto the warm-paper palette. No JS is bundled — install the libs in your app:\n\n```sh\nnpm install @xyflow/react elkjs\n```\n\n```jsx\nimport '@xyflow/react/dist/style.css';   // 1. React Flow base styles\nimport '@dennisl0731/oh-my-ui/omi.css';  // 2. then this theme\n\n<ReactFlow className=\"omi-flow\" nodes={nodes} edges={edges}>\n  <Background />\n  <Controls />\n  <MiniMap />\n</ReactFlow>\n```\n\nAdding `className=\"omi-flow\"` themes nodes, edges, handles, controls,\nminimap, and the background pattern — mostly via React Flow's own\n`--xy-*` CSS variables, so per-service colorways (`data-omi-theme`) and\ndark mode flow through automatically.\n\n`elkjs` only computes node `x/y` positions, so it needs no styling. Three\noptional accent classes are available for nodes returned from an ELK\nlayout (set on a node's `className`): `omi-flow__node--root`,\n`omi-flow__node--accent`, `omi-flow__node--muted`.\n\n## Design Direction Ideas\n\nThe package can support more than one visual attitude while keeping the same API:\n\n- editorial workspace: denser typography, stronger document styling, better table and footnote treatments\n- builder console: more structured panels, status colors, logs, inspectors, split panes\n- gallery mode: richer cards, media-first layouts, asset metadata chips\n- research notebook: citations, callouts, figure wrappers, code-result pair layouts\n\nThat is the right level for this repo: shared language plus shared primitives, not fixed page compositions.\n\n## CSS-only Usage\n\n```html\n<link rel=\"stylesheet\" href=\"/node_modules/@dennisl0731/oh-my-ui/css/omi.css\">\n<script src=\"/node_modules/@dennisl0731/oh-my-ui/js/nav.js\"></script>\n```\n\nIf you only want theme behavior, render your own select with `data-omi-theme-switch`.\n\n## Testing\n\nThis repo now includes a minimal Playwright visual regression harness.\n\nFiles:\n\n- `examples/visual/index.html`: static preview shell\n- `examples/visual/preview.js`: deterministic scenes for each primitive group\n- `tests/tokens.spec.js`: CSS token contract checks\n- `tests/visual.spec.js`: screenshot regression checks\n- `playwright.config.js`: local static server + browser config\n\nInstall test deps:\n\n```sh\nnpm install\nnpx playwright install\n```\n\nRun token checks:\n\n```sh\nnpm run test:tokens\n```\n\nGenerate or refresh screenshots:\n\n```sh\nnpm run test:update-snapshots\n```\n\nRun visual regression against committed baselines:\n\n```sh\nnpm run test:visual\n```\n\nRecommended workflow:\n\n- add or change a primitive in `omi.css`\n- update the matching scene in `examples/visual/preview.js`\n- regenerate snapshots intentionally\n- review image diffs before committing\n- commit snapshot baselines together with the style change\n\nThe important rule is that screenshot baselines are part of the contract. If a visual change is intended, update them in the same PR. If the change is not intended, the diff should fail review.\n\n## Publishing\n\n```sh\nnpm publish --access public\n```\n","readmeFilename":"README.md"}