{"id":13451999,"url":"https://github.com/thysultan/stylis","last_synced_at":"2025-05-14T05:10:50.418Z","repository":{"id":40639648,"uuid":"74873203","full_name":"thysultan/stylis","owner":"thysultan","description":"light – weight css preprocessor","archived":false,"fork":false,"pushed_at":"2025-02-06T10:46:49.000Z","size":1136,"stargazers_count":1749,"open_issues_count":8,"forks_count":86,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-05-13T06:03:55.290Z","etag":null,"topics":["css","preprocessor"],"latest_commit_sha":null,"homepage":"https://stylis.js.org","language":"JavaScript","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/thysultan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":null,"patreon":null,"open_collective":"stylis","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2016-11-27T07:15:27.000Z","updated_at":"2025-05-12T13:24:17.000Z","dependencies_parsed_at":"2025-02-27T09:15:07.265Z","dependency_job_id":null,"html_url":"https://github.com/thysultan/stylis","commit_stats":{"total_commits":588,"total_committers":37,"mean_commits":"15.891891891891891","dds":0.3248299319727891,"last_synced_commit":"f7bbabecb4801659bf6c3223b52d5dc7460ca6f3"},"previous_names":["thysultan/stylis.js"],"tags_count":143,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thysultan%2Fstylis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thysultan%2Fstylis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thysultan%2Fstylis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thysultan%2Fstylis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thysultan","download_url":"https://codeload.github.com/thysultan/stylis/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254076850,"owners_count":22010611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","preprocessor"],"created_at":"2024-07-31T07:01:09.661Z","updated_at":"2025-05-14T05:10:50.373Z","avatar_url":"https://github.com/thysultan.png","language":"JavaScript","readme":"# STYLIS\n\n[![stylis](https://stylis.js.org/assets/logo.svg)](https://github.com/thysultan/stylis.js)\n\nA Light–weight CSS Preprocessor.\n\n[![Coverage](https://coveralls.io/repos/github/thysultan/stylis.js/badge.svg?branch=master)](https://coveralls.io/github/thysultan/stylis.js)\n[![Size](https://badgen.net/bundlephobia/minzip/stylis)](https://bundlephobia.com/result?p=stylis)\n[![Licence](https://badgen.net/badge/license/MIT/blue)](https://github.com/thysultan/stylis.js/blob/master/LICENSE)\n[![NPM](https://badgen.net/npm/v/stylis)](https://www.npmjs.com/package/stylis)\n\n## Installation\n\n* Use a Direct Download: `\u003cscript src=stylis.js\u003e\u003c/script\u003e`\n* Use a CDN: `\u003cscript src=unpkg.com/stylis\u003e\u003c/script\u003e`\n* Use NPM: `npm install stylis --save`\n\n## Features\n\n- nesting `a { \u0026:hover {} }`\n- selector namespacing\n- vendor prefixing (flex-box, etc...)\n- minification\n- esm module compatible\n- tree-shaking-able\n\n## Abstract Syntax Structure\n\n```js\nconst declaration = {\n\tvalue: 'color:red;',\n\ttype: 'decl',\n\tprops: 'color',\n\tchildren: 'red',\n\tline: 1, column: 1\n}\n\nconst comment = {\n\tvalue: '/*@noflip*/',\n\ttype: 'comm',\n\tprops: '/',\n\tchildren: '@noflip',\n\tline: 1, column: 1\n}\n\nconst ruleset = {\n\tvalue: 'h1,h2',\n\ttype: 'rule',\n\tprops: ['h1', 'h2'],\n\tchildren: [/* ... */],\n\tline: 1, column: 1\n}\n\nconst atruleset = {\n\tvalue: '@media (max-width:100), (min-width:100)',\n\ttype: '@media',\n\tprops: ['(max-width:100)', '(min-width:100)'],\n\tchildren: [/* ... */],\n\tline: 1, column: 1\n}\n```\n\n## Example:\n\n```js\nimport {compile, serialize, stringify} from 'stylis'\n\nserialize(compile(`h1{all:unset}`), stringify)\n```\n\n### Compile\n\n```js\ncompile('h1{all:unset}') === [{value: 'h1', type: 'rule', props: ['h1'], children: [/* ... */]}]\ncompile('--foo:unset;') === [{value: '--foo:unset;', type: 'decl', props: '--foo', children: 'unset'}]\n```\n\n### Tokenize\n\n```js\ntokenize('h1 h2 h3 [h4 h5] fn(args) \"a b c\"') === ['h1', 'h2', 'h3', '[h4 h5]', 'fn', '(args)', '\"a b c\"']\n```\n\n### Serialize\n\n```js\nserialize(compile('h1{all:unset}'), stringify)\n```\n\n### Vendor Prefixing\n\n```js\nimport {compile, serialize, stringify, middleware, prefixer } from 'stylis';\n\nserialize(compile('div{display:flex;}'), middleware([prefixer, stringify]))\n```\n\n\n## Middleware\n\nThe middleware helper is a convenient helper utility, that for all intents and purposes you can do without if you intend to implement your own traversal logic. The `stringify` middleware is one such middleware that can be used in conjunction with it.\n\nElements passed to middlewares have a `root` property that is the immediate root/parent of the current element **in the compiled output**, so it references the parent in the already expanded CSS-like structure. Elements have also `parent` property that is the immediate parent of the current element **from the input structure** (structure representing the input string).\n\n### Traversal\n\n```js\nserialize(compile('h1{all:unset}'), middleware([(element, index, children) =\u003e {\n\tassert(children === element.root.children \u0026\u0026 children[index] === element.children)\n}, stringify])) === 'h1{all:unset;}'\n```\n\nThe abstract syntax tree also includes an additional `return` property for more niche uses.\n\n### Prefixing\n\n```js\nserialize(compile('h1{all:unset}'), middleware([(element, index, children, callback) =\u003e {\n\tif (element.type === 'decl' \u0026\u0026 element.props === 'all' \u0026\u0026 element.children === 'unset')\n\t\telement.return = 'color:red;' + element.value\n}, stringify])) === 'h1{color:red;all:unset;}'\n```\n\n```js\nserialize(compile('h1{all:unset}'), middleware([(element, index, children, callback) =\u003e {\n\tif (element.type === 'rule' \u0026\u0026 element.props.indexOf('h1') \u003e -1)\n\t\treturn serialize([{...element, props: ['h2', 'h3']}], callback)\n}, stringify])) === 'h2,h3{all:unset;}h1{all:unset;}'\n```\n\n### Reading\n\n```js\nserialize(compile('h1{all:unset}'), middleware([stringify, (element, index, children) =\u003e {\n\tassert(element.return === 'h1{all:unset;}')\n}])) === 'h1{all:unset;color:red;}'\n```\n\nThe middlewares in [src/Middleware.js](src/Middleware.js) dive into tangible examples of how you might implement a middleware, alternatively you could also create your own middleware system as `compile` returns all the nessessary structure to fork from.\n\n## Variables\n\nCSS variables are supported but a note should be made about the exotic use of css variables. The css spec mentions the following\n\n\u003eThe allowed syntax for custom properties is extremely permissive. The \u003cdeclaration-value\u003e production matches any sequence of one or more tokens, so long as the sequence does not contain \u003cbad-string-token\u003e, \u003cbad-url-token\u003e, unmatched \u003c)-token\u003e, \u003c]-token\u003e, or \u003c}-token\u003e, or top-level \u003csemicolon-token\u003e tokens or \u003cdelim-token\u003e tokens with a value of \"!\".\n\nThat is to say css variables according to the spec allows: `--foo: if(x \u003e 5) this.width = 10;` and while this value is obviously useless as a variable, and would be invalid in any normal property, it still might be read and acted on by JavaScript and this is supported by Stylis, however things become slightly undefined when we start to include the `{` and `}` productions in our use of exotic css variables. \n\nFor example consider the following: `--foo: {};`\n\nWhile this is valid CSS and supported. It is unclear what should happen when the rule collides with the implicit block termination rule that allows i.e `h1{color:red}`(notice the omitted semicolon) to also be a valid CSS production. This results in the following contradiction in: `h1{--example: {}` is it to be treated as `h1{--foo:{;}` or `h1{--foo:{}` the later of which is an unterminated block or in the following: `h1{--foo:{} h1{color:red;}` should it be `h1 {--foo:{}h1{color:red;};` where `{}h1{color:red;` is part of the css variable `--foo` and not a new rule or should it be something else? \n\nNevertheless Stylis still supports the exotic forms highlighted in the spec, however you should consider it as a general rule to delimit such exotic uses of variables in strings or parentheses i.e: `h1{--foo:'{'}` or `h1{--foo:({)}`. \n\n## Benchmark\n\nStylis is at-least 2X faster than its predecesor.\n\n### License\n\nStylis is [MIT licensed](./LICENSE).\n","funding_links":["https://opencollective.com/stylis"],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthysultan%2Fstylis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthysultan%2Fstylis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthysultan%2Fstylis/lists"}