{"id":50521924,"url":"https://github.com/open-pencil/twirlwind","last_synced_at":"2026-06-12T01:30:31.909Z","repository":{"id":358284450,"uuid":"1237158769","full_name":"open-pencil/twirlwind","owner":"open-pencil","description":"Tailwind v4-first CSS-to-utility-class serializer","archived":false,"fork":false,"pushed_at":"2026-05-16T15:51:57.000Z","size":69,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-16T17:36:47.890Z","etag":null,"topics":["css","css-in-js","css-to-tailwind","style-object","tailwind","tailwindcss","typescript","utility-classes"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/twirlwind","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/open-pencil.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-12T23:48:33.000Z","updated_at":"2026-05-16T16:16:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/open-pencil/twirlwind","commit_stats":null,"previous_names":["open-pencil/twirlwind"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/open-pencil/twirlwind","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-pencil%2Ftwirlwind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-pencil%2Ftwirlwind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-pencil%2Ftwirlwind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-pencil%2Ftwirlwind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/open-pencil","download_url":"https://codeload.github.com/open-pencil/twirlwind/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-pencil%2Ftwirlwind/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34225350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","css-in-js","css-to-tailwind","style-object","tailwind","tailwindcss","typescript","utility-classes"],"created_at":"2026-06-03T05:00:34.616Z","updated_at":"2026-06-12T01:30:31.854Z","avatar_url":"https://github.com/open-pencil.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Twirlwind\n\n[![npm](https://img.shields.io/npm/v/twirlwind)](https://www.npmjs.com/package/twirlwind)\n[![CI](https://github.com/open-pencil/twirlwind/actions/workflows/ci.yml/badge.svg)](https://github.com/open-pencil/twirlwind/actions/workflows/ci.yml)\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---\n\nMaintained as part of [OpenPencil](https://github.com/open-pencil) for the [OpenPencil editor](https://github.com/open-pencil/open-pencil).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-pencil%2Ftwirlwind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopen-pencil%2Ftwirlwind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-pencil%2Ftwirlwind/lists"}