{"id":50902949,"url":"https://github.com/stacksjs/ts-css","last_synced_at":"2026-06-16T04:31:29.652Z","repository":{"id":354651382,"uuid":"1223921055","full_name":"stacksjs/ts-css","owner":"stacksjs","description":"Read, walk, query, transform, or minify CSS. Performant.","archived":false,"fork":false,"pushed_at":"2026-06-13T20:05:25.000Z","size":541,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T22:08:43.475Z","etag":null,"topics":["css","minify","query","read","transform","typescript","walk"],"latest_commit_sha":null,"homepage":"","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/stacksjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","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},"funding":{"github":["stacksjs","chrisbbreuer"],"open_collective":"stacksjs"}},"created_at":"2026-04-28T19:35:26.000Z","updated_at":"2026-06-10T11:52:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stacksjs/ts-css","commit_stats":null,"previous_names":["stacksjs/ts-css"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/stacksjs/ts-css","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fts-css","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fts-css/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fts-css/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fts-css/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stacksjs","download_url":"https://codeload.github.com/stacksjs/ts-css/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fts-css/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34391702,"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-16T02:00:06.860Z","response_time":126,"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","minify","query","read","transform","typescript","walk"],"created_at":"2026-06-16T04:31:28.923Z","updated_at":"2026-06-16T04:31:29.644Z","avatar_url":"https://github.com/stacksjs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/stacksjs","https://github.com/sponsors/chrisbbreuer","https://opencollective.com/stacksjs"],"categories":[],"sub_categories":[],"readme":"# @stacksjs/ts-css\n\n\u003e Pure-TypeScript CSS toolkit for Bun \u0026 Node. Zero runtime dependencies.\n\n`ts-css` is a CSS toolkit for build tools, linters, design-system pipelines,\nSSR templating, runtime style transforms — anything that needs to read,\nwalk, query, transform, or minify CSS in a JS/TS environment. Written from\nscratch in TypeScript with strict types and the CSS Syntax Module Level 3\ntokenizer.\n\nIt bundles every common CSS pipeline primitive — tokenizer, AST parser,\nvisitor walker, generator, selector parser, selector matcher, and minifier —\ninto a single zero-dep package. The shape of each sub-API mirrors the\nde-facto community standard for that capability (`css-tree`-style AST and\nwalker, `css-what`-style selector tokens, the `css-select` adapter contract,\n`csso`-style minify result), so adopting `ts-css` rarely requires call-site\nchanges — but the project stands on its own and isn't pitched as \"the\nreplacement for X.\"\n\n## Highlights\n\n- **Zero runtime deps.** Only ts-css's own code. Ships ~4.5k lines of TS.\n- **One install for the whole CSS pipeline.** Tokenizer, parser, walker,\n  generator, selector parser/matcher, and minifier — all behind sub-module\n  imports at `@stacksjs/ts-css/parse`, `/what`, `/select`, `/optimize`.\n- **Familiar API shapes.** Visitor/walker callbacks, AST node types, and\n  the `css-select` adapter contract follow the conventions established by\n  the popular community libraries, so most existing call sites work\n  unchanged.\n- **Bun-first.** Built and tested on Bun, works on Node ≥18.\n- **Strict TypeScript.** Strict mode, isolatedDeclarations, verbatimModuleSyntax.\n\n## Install\n\n```bash\nbun add @stacksjs/ts-css\n# or\nnpm i @stacksjs/ts-css\n```\n\n## Quick start\n\n### Parse, walk, generate\n\n```ts\nimport { generate, parse, walk } from '@stacksjs/ts-css'\n\nconst ast = parse(`\n  .foo, .bar \u003e .baz {\n    color: red;\n    background: url(\"/x.png\") no-repeat;\n  }\n`)\n\nwalk(ast, {\n  visit: 'Declaration',\n  enter(decl) {\n    console.log(decl.property, '→', generate(decl.value))\n  },\n})\n// color → red\n// background → url(\"/x.png\") no-repeat\n\nconsole.log(generate(ast))\n// .foo,.bar\u003e.baz{color:red;background:url(\"/x.png\") no-repeat}\n```\n\n### Match selectors against any tree\n\n```ts\nimport { selectAll } from '@stacksjs/ts-css'\n\nconst adapter = {\n  isTag: (n) =\u003e n?.type === 'el',\n  getName: (e) =\u003e e.name,\n  getAttributeValue: (e, n) =\u003e e.attrs[n],\n  // …other adapter methods (see docs/api/select.md)\n}\n\nselectAll('p \u003e span.foo:not(.disabled)', root, { adapter, xmlMode: true })\n```\n\nThe matcher is decoupled from any DOM implementation: pass an `Adapter`\ndescribing your tree (HTML, XML, an SVG AST, your design-system component\ngraph, anything with `parent`/`children`/`name`/`attrs`-like access) and\nts-css will match standard CSS selectors against it.\n\n### Minify CSS\n\n```ts\nimport { minify } from '@stacksjs/ts-css'\n\nminify(`\n  .a {\n    margin: 0px 0.5em 10.000px;\n    color: #aabbcc;\n    color: blue; /* later wins */\n  }\n`).css\n// → \".a{margin:0 .5em 10px;color:blue}\"\n```\n\n### Compute selector specificity\n\n```ts\nimport { parse, syntax } from '@stacksjs/ts-css'\n\nconst sel = parse('#a.b div', { context: 'selector' })\nsyntax.specificity(sel) // [1, 1, 1]\n```\n\n## API surface\n\n`ts-css` exposes a complete CSS pipeline. The named exports below cover the\nendpoints most build/lint/transform tools reach for:\n\n| Capability                       | Export                       |\n| -------------------------------- | ---------------------------- |\n| Parse a stylesheet to an AST (`stylesheet`/`rule`/`declaration`/`selectorList`/etc. contexts) | `parse(source, opts?)` |\n| Walk the AST with `enter`/`leave` callbacks, `visit` filtering, and a `walk.skip` sentinel | `walk(ast, fn \\| visitor)` |\n| Stringify any AST node back to CSS source | `generate(node)` |\n| Deep-clone an AST subtree | `clone(node)` |\n| Doubly-linked children container with cursor-safe `forEach(data, item, list)` mid-walk mutation | `List` / `ListItem` |\n| Tokenize a selector string into segments | `cssWhat.parse(selector)` |\n| Detect combinator/traversal segments | `cssWhat.isTraversal(seg)` |\n| Match a selector against an arbitrary tree via the `Adapter` contract | `selectAll`, `selectOne`, `is`, `compile` |\n| Minify with declaration/value compression and whitespace collapse | `minify`, `minifyBlock` |\n| Compute selector specificity tuple | `syntax.specificity(node)` |\n\nA namespace export (`csstree`, `cssWhat`, `cssSelect`, `csso`) is also\navailable so existing code that imports from those packages can typically\nswitch by just changing the import path.\n\n## CLI\n\n```bash\nts-css minify input.css \u003e output.css\nts-css parse  input.css                # AST as JSON on stdout\nts-css format input.css                # round-trip through parser/generator\n```\n\n## What's not (yet) in scope\n\nThis is a pragmatic toolkit — full surface area where it's load-bearing,\ntrimmed where edge cases would force in spec data or rarely-used passes:\n\n- ❌ **No CSS-spec-aware value lexer.** Validating that `red` is a valid\n  `\u003ccolor\u003e`, or that `1px` is the right syntax for a given property, is\n  thousands of lines of spec grammar that most pipelines don't need. The\n  AST preserves enough information to layer that on top if you do.\n- ❌ **No cross-rule restructuring pass.** Reordering rules across the\n  whole stylesheet to fold compatible declarations is complex and gives\n  marginal gains beyond gzip. Declaration-level minification (numbers,\n  colors, dedup, longhand collapse) ships fully.\n\nEverything else round-trips byte-equivalent.\n\n## Project layout\n\n```\nsrc/\n  parse/     # tokenizer, parser, AST types, walker, generator, clone, List\n  what/      # selector parser/stringifier\n  select/    # selectAll/selectOne/is engine + pseudo-class compilers\n  optimize/  # minifier, value compressors, specificity\n```\n\nEach sub-module is exported as a separate entry point so you can import\njust what you need:\n\n```ts\nimport { parse, walk } from '@stacksjs/ts-css/parse'\nimport { selectAll } from '@stacksjs/ts-css/select'\nimport { minify } from '@stacksjs/ts-css/optimize'\n```\n\n## Performance\n\nEnd-to-end (parse → minify → generate) on a 6 KB representative fixture\n(Apple M3 Pro, Bun 1.3):\n\n| Pipeline                    | Time/iter |\n| --------------------------- | --------- |\n| `ts-css.minify(source)`     | **~270 µs** |\n| `csso.minify(source)`       | ~890 µs    |\n\n→ **~3.3× faster end-to-end** with zero runtime deps. Per-API breakdown\nin [`docs/benchmarks.md`](./docs/benchmarks.md). Run them yourself with\n`bun run bench`.\n\n## Documentation\n\nFull docs: see [`./docs`](./docs) or run `bun run dev:docs` to start the\nlocal docs site.\n\n- [Getting started](./docs/intro.md)\n- [Parser API](./docs/api/parse.md)\n- [Selector parser API](./docs/api/what.md)\n- [Selector matcher API](./docs/api/select.md)\n- [Optimizer API](./docs/api/optimize.md)\n- [Migration guide](./docs/migration.md)\n- [Benchmarks](./docs/benchmarks.md)\n\n## License\n\nMIT — see [`LICENSE.md`](./LICENSE.md).\n\n`ts-css` is an independent implementation. The visitor / linked-list shape\nin `src/parse`, the selector tokenizer model in `src/what`, the adapter\npattern in `src/select`, and the minification plan in `src/optimize` all\nfollow conventions established by the long-standing community libraries in\neach area, so call sites that already use those conventions tend to drop in\nwithout changes. All such projects are MIT-licensed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacksjs%2Fts-css","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstacksjs%2Fts-css","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacksjs%2Fts-css/lists"}