{"_id":"twirlwind","_rev":"3-4a1b49ca99cf0e2976247ea319e01ae3","name":"twirlwind","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"twirlwind","version":"0.1.0","keywords":["converter","css","css-in-js","css-to-tailwind","style-object","styles","tailwind","tailwind-v4","tailwindcss","utility-classes"],"license":"MIT","_id":"twirlwind@0.1.0","maintainers":[{"name":"dannote","email":"dannotemail@gmail.com"}],"homepage":"https://github.com/open-pencil/twirlwind#readme","bugs":{"url":"https://github.com/open-pencil/twirlwind/issues"},"dist":{"shasum":"3e040cdf9b974acef109aeb1b462f0ad9efffe6d","tarball":"https://registry.npmjs.org/twirlwind/-/twirlwind-0.1.0.tgz","fileCount":5,"integrity":"sha512-6XYhof3Gzh13dIYJ7FSNffVUex4wHmQfAlk9vtYpBvFWz0jaRur2ZkcIQ0o8NQ+YJQwGU+DPgTpNHU/MxGfbOA==","signatures":[{"sig":"MEYCIQC4qOzrxWD9Uk9/ZBoqYM6dMHYYlV7wnKXf35nw2BYFtgIhAL7XKz8iuXrMbL3My4LG1ZYac8L7EKFRWHsH92PsGaMQ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":116083},"main":"dist/index.mjs","type":"module","types":"dist/index.d.mts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"337170d6d20384f0e1e0066f24622a7c3867d07e","scripts":{"lint":"oxlint","test":"bun test --timeout 20000","build":"tsdown src/index.ts --format esm --dts","format":"oxfmt --write .","typecheck":"tsgo --noEmit","format:check":"oxfmt --check .","prepublishOnly":"bun run lint && bun run format:check && bun test && bun run typecheck && bun run build","coverage:report":"bun scripts/report-coverage.ts"},"_npmUser":{"name":"dannote","email":"dannotemail@gmail.com"},"repository":{"url":"git+https://github.com/open-pencil/twirlwind.git","type":"git"},"_npmVersion":"11.6.2","description":"Tailwind v4-first CSS style object to utility class serializer with lossless arbitrary-property fallback.","directories":{},"sideEffects":false,"_nodeVersion":"25.2.1","_hasShrinkwrap":false,"devDependencies":{"oxfmt":"^0.49.0","oxlint":"^1.64.0","tsdown":"^0.22.0","postcss":"^8.5.14","mdn-data":"^2.28.1","@types/bun":"latest","typescript":"^5.9.3","tailwindcss":"^4.3.0","@tailwindcss/cli":"^4.3.0","@typescript/native-preview":"^7.0.0-dev.20260512.1"},"_npmOperationalInternal":{"tmp":"tmp/twirlwind_0.1.0_1778943315539_0.4465900435535304","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"twirlwind","version":"0.2.0","keywords":["converter","css","css-in-js","css-to-tailwind","style-object","styles","tailwind","tailwind-v4","tailwindcss","utility-classes"],"license":"MIT","_id":"twirlwind@0.2.0","maintainers":[{"name":"dannote","email":"dannotemail@gmail.com"}],"homepage":"https://github.com/open-pencil/twirlwind#readme","bugs":{"url":"https://github.com/open-pencil/twirlwind/issues"},"dist":{"shasum":"b14c7ba87f9137ef5b100e9bbbaffc8ce31b8164","tarball":"https://registry.npmjs.org/twirlwind/-/twirlwind-0.2.0.tgz","fileCount":5,"integrity":"sha512-7eKlngOSP7oSeFASGuGjhK2WX7oQxdZ1re33E56MHzSilKWzlq53ZPa8YEH2hfAk9hEplKpKMNwEGwhHnZxoUQ==","signatures":[{"sig":"MEYCIQCPNdUrkNZLLw8PvpsHuqDRqu84f33efJJiW804ryOdUgIhAJhC78J8Qbg6yFDTsZGX0dqlufdfopbTNEJUonUDT8Fk","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":112634},"main":"dist/index.mjs","type":"module","types":"dist/index.d.mts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"503a4e0133080daae27fbebcc35712f07efd2f1d","scripts":{"lint":"oxlint","test":"bun test --timeout 20000","build":"tsdown src/index.ts --format esm --dts","format":"oxfmt --write .","typecheck":"tsgo --noEmit","format:check":"oxfmt --check .","prepublishOnly":"bun run lint && bun run format:check && bun test && bun run typecheck && bun run build","coverage:report":"bun scripts/report-coverage.ts"},"_npmUser":{"name":"dannote","email":"dannotemail@gmail.com"},"repository":{"url":"git+https://github.com/open-pencil/twirlwind.git","type":"git"},"_npmVersion":"11.6.2","description":"Tailwind v4-first CSS style object to utility class serializer with lossless arbitrary-property fallback.","directories":{},"sideEffects":false,"_nodeVersion":"25.2.1","_hasShrinkwrap":false,"devDependencies":{"oxfmt":"^0.49.0","oxlint":"^1.64.0","tsdown":"^0.22.0","postcss":"^8.5.14","mdn-data":"^2.28.1","@types/bun":"latest","typescript":"^5.9.3","tailwindcss":"^4.3.0","@tailwindcss/cli":"^4.3.0","@typescript/native-preview":"^7.0.0-dev.20260512.1"},"_npmOperationalInternal":{"tmp":"tmp/twirlwind_0.2.0_1778944687486_0.6206951094070026","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"twirlwind","version":"0.3.0","description":"Tailwind v4-first CSS style object to utility class serializer with lossless arbitrary-property fallback.","keywords":["converter","css","css-in-js","css-to-tailwind","style-object","styles","tailwind","tailwind-v4","tailwindcss","utility-classes"],"homepage":"https://github.com/open-pencil/twirlwind#readme","bugs":{"url":"https://github.com/open-pencil/twirlwind/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/open-pencil/twirlwind.git"},"type":"module","sideEffects":false,"main":"dist/index.mjs","module":"dist/index.mjs","types":"dist/index.d.mts","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"scripts":{"test":"bun test --timeout 20000","typecheck":"tsgo --noEmit","lint":"oxlint","format":"oxfmt --write .","format:check":"oxfmt --check .","build":"tsdown src/index.ts --format esm --dts","prepublishOnly":"bun run lint && bun run format:check && bun test && bun run typecheck && bun run build","coverage:report":"bun scripts/report-coverage.ts"},"devDependencies":{"@tailwindcss/cli":"^4.3.0","@types/bun":"latest","@typescript/native-preview":"^7.0.0-dev.20260512.1","mdn-data":"^2.28.1","oxfmt":"^0.49.0","oxlint":"^1.64.0","postcss":"^8.5.14","tailwindcss":"^4.3.0","tsdown":"^0.22.0","typescript":"^5.9.3"},"gitHead":"fd7980de086e5e0ad64b8ae263d9a35a1449f034","_id":"twirlwind@0.3.0","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-+pFwkOQG1euPgEmuR3lTQXwkr/2aK91ycf4p7dV11qYM1LbEnYUb1c4zewpimV52sSj7AfgBVmsoHp30ZRygLA==","shasum":"6c2be3fe003f9c56d578085d27994379a044fb3b","tarball":"https://registry.npmjs.org/twirlwind/-/twirlwind-0.3.0.tgz","fileCount":5,"unpackedSize":119337,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDQs/ajqZQEuAHMwadY5yI2krZj7ZO6B6SWkXzzfXPt9QIhAJAH04T2dGa0pz4HJheKh6enMqanqjYzCRUKLqsa7Vrn"}]},"_npmUser":{"name":"dannote","email":"dannotemail@gmail.com"},"directories":{},"maintainers":[{"name":"dannote","email":"dannotemail@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/twirlwind_0.3.0_1778946583878_0.8601698813950276"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-16T14:55:15.538Z","modified":"2026-05-16T15:49:44.132Z","0.1.0":"2026-05-16T14:55:15.702Z","0.2.0":"2026-05-16T15:18:07.626Z","0.3.0":"2026-05-16T15:49:44.020Z"},"bugs":{"url":"https://github.com/open-pencil/twirlwind/issues"},"license":"MIT","homepage":"https://github.com/open-pencil/twirlwind#readme","keywords":["converter","css","css-in-js","css-to-tailwind","style-object","styles","tailwind","tailwind-v4","tailwindcss","utility-classes"],"repository":{"type":"git","url":"git+https://github.com/open-pencil/twirlwind.git"},"description":"Tailwind v4-first CSS style object to utility class serializer with lossless arbitrary-property fallback.","maintainers":[{"name":"dannote","email":"dannotemail@gmail.com"}],"readme":"# Twirlwind\n\nTailwind v4-first CSS-to-utility-class serializer for JavaScript/TypeScript.\n\nConverts style objects, CSS declaration strings, and `CSSStyleDeclaration` values into clean Tailwind utility classes. Prefers canonical utilities, falls back to arbitrary values, then arbitrary properties — every CSS property produces valid output.\n\n## Install\n\n```sh\nnpm install twirlwind\n```\n\n## Usage\n\n```ts\nimport { twirl } from 'twirlwind'\n\ntwirl({ display: 'flex', padding: '16px 8px', color: '#ef4444' })\n// → \"flex py-4 px-2 text-red-500\"\n```\n\n### Inputs\n\n`twirl()` accepts any of these and returns a class string:\n\n```ts\n// Style object\ntwirl({ backgroundColor: 'white', fontSize: '16px' })\n\n// CSS string\ntwirl('display: flex; padding: 16px')\n\n// CSSStyleDeclaration (browser)\ntwirl(element.style)\n\n// Computed styles\ntwirl(getComputedStyle(element))\n```\n\n### Detailed result\n\nUse `twirl.convert()` when you need metadata:\n\n```ts\nconst result = twirl.convert({ display: 'flex', width: '37px' })\n\nresult.className // \"flex w-[37px]\"\nresult.classes // [\"flex\", \"w-[37px]\"]\nresult.exact // exact utility matches\nresult.arbitrary // arbitrary value/property fallbacks\nresult.unmatched // declarations that couldn't be converted\n```\n\n## Features\n\n### Color matching\n\nMatches across formats — OKLCH, hex, `rgb()`, keywords, opacity modifiers.\n\n```ts\ntwirl({ color: '#ef4444' }) // \"text-red-500\"\ntwirl({ color: 'rgb(59 130 246)' }) // \"text-blue-500\"\ntwirl({ color: 'oklch(62.3% 0.214 259.815 / 50%)' }) // \"text-blue-500/50\"\ntwirl({ color: 'currentColor' }) // \"text-current\"\n```\n\n### Shorthand expansion\n\nCSS shorthands decompose into Tailwind longhands.\n\n```ts\ntwirl({ border: '2px solid #ef4444' }) // \"border-2 border-red-500 border-solid\"\ntwirl({ font: 'bold 16px/1.5 sans-serif' }) // \"font-bold text-base leading-normal font-sans\"\ntwirl({ background: 'white center no-repeat' }) // \"bg-white bg-center bg-no-repeat\"\n```\n\n### Multi-value parsing\n\nCompound `transform` and `filter` declarations decompose into individual classes.\n\n```ts\ntwirl({ transform: 'translateX(8px) rotate(45deg)' }) // \"rotate-45 translate-x-2\"\ntwirl({ filter: 'blur(8px) brightness(0.75)' }) // \"blur brightness-75\"\ntwirl({ scrollSnapType: 'x mandatory' }) // \"snap-x snap-mandatory\"\n```\n\n### Compression\n\nExpanded longhands compress to shorthand utilities.\n\n```ts\ntwirl({ margin: '8px' }) // \"m-2\"\ntwirl({ inset: '0' }) // \"inset-0\"\ntwirl({ padding: '8px 16px' }) // \"py-2 px-4\"\ntwirl({ borderRadius: '8px' }) // \"rounded-lg\"\ntwirl({ gap: '12px 12px' }) // \"gap-3\"\n```\n\n### Variants\n\nNested objects map to Tailwind variants.\n\n```ts\ntwirl({\n  color: 'white',\n  ':hover': { color: '#3b82f6' },\n  '@media (min-width: 768px)': { display: 'grid' },\n  '@media (prefers-color-scheme: dark)': { backgroundColor: 'black' },\n  '@container (min-width: 512px)': { display: 'flex' }\n})\n// → \"text-white hover:text-blue-500 md:grid dark:bg-black @lg:flex\"\n```\n\n### Arbitrary fallback\n\nEvery CSS property produces valid output.\n\n```ts\ntwirl({ scrollTimelineName: '--main' }) // \"[scroll-timeline-name:--main]\"\ntwirl({ width: '37px' }) // \"w-[37px]\"\n```\n\n## Options\n\n```ts\ntwirl(input, {\n  allowArbitraryValues: true, // default: true\n  allowArbitraryProperties: true, // default: true\n  compression: 'safe', // \"none\" | \"safe\" | \"aggressive\"\n  sort: 'grouped', // \"input\" | \"tailwind\" | \"grouped\"\n  colorMatch: 'exact', // \"exact\" | \"nearest\" | \"none\"\n  numericMultipliers: 'integer', // \"all\" | \"integer\" | \"never\"\n  theme: {\n    colors: { brand: '#ff6600' },\n    spacing: { '18': '4.5rem' }\n  }\n})\n```\n\n## How it works\n\n1. **Normalize** — camelCase → kebab-case, numeric → px, vendor prefixes, `!important`\n2. **Expand** — `margin`, `border`, `font`, `background`, `transition`, `overflow`, `gap`, etc.\n3. **Convert** — exact utility → value alias → spacing token → color match → arbitrary value → arbitrary property\n4. **Compress** — merge longhands back to shorthand utilities\n5. **Sort** — deterministic output ordering\n\n## License\n\nMIT\n","readmeFilename":"README.md"}