light – weight css preprocessor

Overview

STYLIS

stylis

A Light–weight CSS Preprocessor.

Coverage Size Licence NPM

Installation

  • Use a Direct Download: <script src=stylis.js></script>
  • Use a CDN: <script src=unpkg.com/stylis></script>
  • Use NPM: npm install stylis --save

Features

  • nesting a { &:hover {} }
  • selector namespacing
  • vendor prefixing (flex-box, etc...)
  • minification
  • esm module compatible
  • tree-shaking-able

Abstract Syntax Structure

const declaration = {
	value: 'color:red;',
	type: 'decl',
	props: 'color',
	children: 'red',
	line: 1, column: 1
}

const comment = {
	value: '/*@noflip*/',
	type: 'comm',
	props: '/',
	children: '@noflip',
	line: 1, column: 1
}

const ruleset = {
	value: 'h1,h2',
	type: 'rule',
	props: ['h1', 'h2'],
	children: [/* ... */],
	line: 1, column: 1
}

const atruleset = {
	value: '@media (max-width:100), (min-width:100)',
	type: '@media',
	props: ['(max-width:100)', '(min-width:100)'],
	children: [/* ... */],
	line: 1, column: 1
}

Example:

import {compile, serialize, stringify} from 'stylis'

serialize(compile(`h1{all:unset}`), stringify)

Compile

compile('h1{all:unset}') === [{value: 'h1', type: 'rule', props: ['h1'], children: [/* ... */]}]
compile('--foo:unset;') === [{value: '--foo:unset;', type: 'decl', props: '--foo', children: 'unset'}]

Tokenize

tokenize('h1 h2 h3 [h4 h5] fn(args) "a b c"') === ['h1', 'h2', 'h3', '[h4 h5]', 'fn', '(args)', '"a b c"']

Serialize

serialize(compile('h1{all:unset}'), stringify)

Middleware

The 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.

Elements 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).

Traversal

serialize(compile('h1{all:unset}'), middleware([(element, index, children) => {
	assert(children === element.root.children && children[index] === element.children)
}, stringify])) === 'h1{all:unset;}'

The abstract syntax tree also includes an additional return property for more niche uses.

Prefixing

serialize(compile('h1{all:unset}'), middleware([(element, index, children, callback) => {
	if (element.type === 'decl' && element.props === 'all' && element.children === 'unset')
		element.return = 'color:red;' + element.value
}, stringify])) === 'h1{color:red;all:unset;}'
serialize(compile('h1{all:unset}'), middleware([(element, index, children, callback) => {
	if (element.type === 'rule' && element.props.indexOf('h1') > -1)
		return serialize([{...element, props: ['h2', 'h3']}], callback)
}, stringify])) === 'h2,h3{all:unset;}h1{all:unset;}'

Reading

serialize(compile('h1{all:unset}'), middleware([stringify, (element, index, children) => {
	assert(element.return === 'h1{all:unset;}')
}])) === 'h1{all:unset;color:red;}'

The middlewares in 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.

Variables

CSS variables are supported but a note should be made about the exotic use of css variables. The css spec mentions the following

The allowed syntax for custom properties is extremely permissive. The production matches any sequence of one or more tokens, so long as the sequence does not contain , , unmatched <)-token>, <]-token>, or <}-token>, or top-level tokens or tokens with a value of "!".

That is to say css variables according to the spec allows: --foo: if(x > 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.

For example consider the following: --foo: {};

While 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?

Never the less 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:({)}.

Benchmark

Stylis is at-least 2X faster than its predecesor.

License

Stylis is MIT licensed.

Comments
  • autoprefixer for grid in IE11?

    autoprefixer for grid in IE11?

    A year has gone by since this was last discussed, (https://github.com/thysultan/stylis.js/issues/51). Since then Autoprefixer has implemented a decent support for css grid in IE11. See for example https://css-tricks.com/css-grid-in-ie-css-grid-and-the-new-autoprefixer/. It would have been fantastic if stylis had the same support.

    feature 
    opened by jonasbjorkberg 38
  • Better JSDoc types

    Better JSDoc types

    I work almost exclusively in Typescript and not having type support is a show stopper for what is otherwise a lovely project. I looked at #208 and while I think it has good intentions the types are vague. That's not the PR author's fault - tsc just extracts the types from JSDoc. The JSDoc typings could be better.

    I went through and tried to fix it up. I removed all the instances of object and function with their real types. There are a few errors and maybe you can correct me on some assumptions I had to make about what is a possible data type but I think these are good types - specifically the use of Middleware as a type to explain that a function is middleware (or returns middleware in the case of rulesheet()).

    I arbitrarily picked Middleware.js to write the JSDoc @typedef but they could be anywhere - maybe index.js would be a better place?

    I'll leave comments in the PR code to explain what I'm trying to do

    opened by heyheyhello 23
  • disabled cascade and specificity

    disabled cascade and specificity

    We realized that disabling cascade and scoping every selector breaks specificity eg.

    Say we have:

    div > div > span // specificity [0,0,0,3]
    .clickTest // specificity [0,0,1,0]
    

    When each selector is scoped the specificity becomes:

    [0,0,3,3]
    [0,0,2,0]
    

    Now the first selector is more specific than the second - it wasn't the case with the original selectors.

    To solve this issue I think that we could just scope the last selector:

    div > div > span.jsx-123 // specificity [0,0,1,3]
    .clickTest.jsx-123 // specificity [0,0,2,0] <- this still wins
    

    @thysultan would it be hard to make this change and maybe release a major version?

    opened by giuseppeg 23
  • Update package.json to exports itself

    Update package.json to exports itself

    This change is needed to access the package.json for external bundlers as documented here and discussed here. I also did the same for another npm package here.

    opened by Graphmaxer 21
  • Ship es modules to npm

    Ship es modules to npm

    Would like to continue the issue mentioned in https://github.com/thysultan/stylis.js/issues/79

    That's actually another topic that I would like to tackle here - I think stylis should ship es & cjs bundles (maybe event closure compiled?) to npm, so libraries like emotion could consume it gracefully without having to copy its source.

    The whole thing of shipping es modules, cjs modules and umd files is pretty easy. Stylis structure is somewhat special though and its the reason why I havent yet send PR for this. I would like to discuss what we can do to tackle this.

    What is the problem? Stylis references the factory closure, so it can act both as constructor and as a singleton. While I really am impressed by this cleverness (it took me few moments to see what's going on) it doesn't play well with existing tooling. Normally the umd factory is just a factory that creates a module and is not referenced from the inner closure.

    In my opinion it would be better to just export something like:

    export function Stylis() { /* ... */ }
    export default /*#__PURE__*/new Stylis()
    

    That way the default export could even be removed by UglifyJS (but that's really just a single line of code and a single function call) if the consumer would be interested solely in the constructor style.

    I'm also thinking that both use and set functions are technically tree-shakeable, but with the current approach of mixed singleton/constructors I have no idea how to support tree-shaking of those in a sane manner.

    Let me know what do you think, I'm ready to invest time to refactor in direction that we agree upon.

    opened by Andarist 18
  • feat(#119): add grid prefix support

    feat(#119): add grid prefix support

    When finished, this should close https://github.com/thysultan/stylis/issues/119 once and for all.

    Note: ~~It is still WIP~~ Although the feature is implemented, it hasn't been optimized AT ALL, so suggestions, optimizations, discussions are welcome!

    opened by SukkaW 16
  • Automatic d.ts generation from jsdoc

    Automatic d.ts generation from jsdoc

    Adds automatic d.ts generation to make the package usable in typescript.

    • Adds types/ to .gitignore
    • Adds tsconfig.json to generate d.ts from js files
    • Adds "types": "types/" in package.json
    • Adds typescript devDependency
    • Adds "tsc &&" before rollup

    Tested by linking the package in a typescript project.

    Thank you :)

    opened by SalvatorePreviti 15
  • Remove 2009 flex box spec

    Remove 2009 flex box spec

    The spec is ancient and the prefixes are hurting performance for any browser that is ie11 and above.

    New syntax support is: Chrome 21+, Safari 6.1+, Firefox 22+, Opera (& Opera Mobile) 12.1+, IE 11+, and Blackberry 10+.

    (https://css-tricks.com/old-flexbox-and-new-flexbox/)

    Summary: Old is 2.3x slower than new.

    https://developers.google.com/web/updates/2013/10/Flexbox-layout-isn-t-slow https://developers.google.com/web/tools/lighthouse/audits/old-flexbox

    Regardless of how modern browsers are using the older properties, we are sending down unnecessary properties when using SSR.

    pasted_image_at_2017_10_10_05_57_pm_360

    I think we should remove this for the benefit of all end users. I'm willing to help with the removal if you are open to the idea of removing the old spec.

    opened by tkh44 15
  • .cjs does not work with React Native and/or Expo

    .cjs does not work with React Native and/or Expo

    Since the latest version, which changed the file extension to .cjs, it does not work anymore with React Native. I'm getting the following error:

    Failed building JavaScript bundle.
    While trying to resolve module `stylis` from file `/Users/.../node_modules/@emotion/cache/dist/cache.browser.cjs.js`, the package `/Users/.../node_modules/stylis/package.json` was successfully found. However, this package itself specifies a `main` module field that could not be resolved (`/Users/.../node_modules/stylis/dist/stylis.cjs`. Indeed, none of these files exist:
    
      * /Users/.../node_modules/stylis/dist/stylis.cjs(.native|.ios.expo.ts|.native.expo.ts|.expo.ts|.ios.expo.tsx|.native.expo.tsx|.expo.tsx|.ios.expo.js|.native.expo.js|.expo.js|.ios.expo.jsx|.native.expo.jsx|.expo.jsx|.ios.ts|.native.ts|.ts|.ios.tsx|.native.tsx|.tsx|.ios.js|.native.js|.js|.ios.jsx|.native.jsx|.jsx|.ios.json|.native.json|.json|.ios.wasm|.native.wasm|.wasm)
      * /Users/.../node_modules/stylis/dist/stylis.cjs/index(.native|.ios.expo.ts|.native.expo.ts|.expo.ts|.ios.expo.tsx|.native.expo.tsx|.expo.tsx|.ios.expo.js|.native.expo.js|.expo.js|.ios.expo.jsx|.native.expo.jsx|.expo.jsx|.ios.ts|.native.ts|.ts|.ios.tsx|.native.tsx|.tsx|.ios.js|.native.js|.js|.ios.jsx|.native.jsx|.jsx|.ios.json|.native.json|.json|.ios.wasm|.native.wasm|.wasm)
    
    opened by efoken 14
  • v3 => v4 migration guide

    v3 => v4 migration guide

    Nice work shipping v4 🎉

    Just wondering if there's a changelog or migration guide i can follow? Looking at the README.md, i'm not entirely sure how to achieve the same functionality as i was using for v3 🤔

    Is it as simple as going from:

     stylis(selector, styles);
    

    to

    import {compile, serialize, stringify} from 'stylis'
    
    serialize(compile(`${selector}{${styles}}`), stringify)
    
    opened by danieldelcore 14
  • Syntax error in deep nesting

    Syntax error in deep nesting

    Hi, I have detected in the received syntax as output the following example:

    Input :

    .component{
    	&.--state-a{	
    		color:black;
    		&.--state-a{	
    			color:black;
    		}
    	}
    }
    

    Output received:

    [namespace] .component {
    &.--state-a {
    	color: black;
    &.--state-a {
    	color: black;
    }
    

    Expected output:

    [namespace] .component.--state-a {
    	color: black;
    }
    
    [namespace] .component.--state-a.--state-a {
    	color: black;
    }
    

    I'm reading the code, to be able to contribute to stylis.

    opened by UpperCod 12
  • fix: webkit mask-composite values

    fix: webkit mask-composite values

    Fix for mask-composite prop values for webkit

    Here are the changes:

    'add' → 'source-over'
    'substract' → 'source-out'
    'intersect' → 'source-in'
    'exclude' → 'xor'
    

    Example for element.return:

    before fix (not working in webkit)
    '-webkit-mask-composite: add,intersect;mask-composite: add,intersect;'  
    
    after fix
    '-webkit-mask-composite: source-over,source-in;mask-composite: add,intersect;'
    
    opened by Olivia-SY129 0
  • minification does not happen for gradients

    minification does not happen for gradients

    Versions

    [email protected]

    Problem

    Stylis does minification and it works great:

    .foo { color: red; }
    .bar {
      color: blue;
      background: orange;
    }
    /* ⬇️⬇️⬇️ */
    .foo{color:red;}.bar{color:blue;background:orange;}
    

    The problem happens with radial-gradient& repeating-linear-gradient:

    .foo {
      background: repeating-linear-gradient(
        -45deg,
        red,
        red 5px,
        blue 5px,
        blue 10px
      );
    }
    .bar {
      color: blue;
      background: orange;
    }
    .baz {
      background-image:radial-gradient(
        ellipse 50rem 50rem,
        #8e91e6 0%, 
        #403f5a 25%,
        transparent 46%
      )
    }
    /* ⬇️⬇️⬇️ */
     .foo{background:repeating-linear-gradient(
        -45deg,
        red,
        red 5px,
        blue 5px,
        blue 10px
      );}.bar{color:blue;background:orange;}.baz{background-image:radial-gradient(
        ellipse 50rem 50rem,
        #8e91e6 0%, 
        #403f5a 25%,
        transparent 46%
      );}
    

    👆 notice that .foo & .baz are not minified.

    Reproduction

    https://codesandbox.io/s/awesome-rgb-wf0j3e

    opened by layershifter 0
  • Please, help me to to make a plugin

    Please, help me to to make a plugin

    Hi. I want to make simple stylis plugin

    1. It search through declarations, when find one with gap prop, it replaced by
    --fgp-gap: ${value} !important;
    margin-top: calc(-1 * var(--fgp-gap));
    margin-right: calc(-1 * var(--fgp-gap));
    
    1. Next to rule with declaration from step 1 we need to add a new rule
    ${declaration selector} > * {
       margin-top: var(--fgp-gap);
       margin-right: var(--fgp-gap);
    }
    

    Please help me. I want to make a polyfill for projects which use emotion 11 solutions with styles with vendors, that has not support Safari <14.1, for example in this case with gap property on flex elements. (For example popular ui lib mantine).

    I tried like this.

    import type { StylisPlugin } from '@emotion/cache';
    import { copy } from 'stylis';
    
    const gapPolyfillPlugin: StylisPlugin = (element, index, children, callback) => {
      // rule | decl | @media
      if (element.type === 'decl') {
        if (element.props === 'gap') {
          element.return = `
          --fgp-gap: ${element.children} !important;
          margin-top: calc(-1 * var(--fgp-gap));
          margin-right: calc(-1 * var(--fgp-gap));
          `;
    
          // code below not working
          if (element.parent && element.parent.type === 'rule') {
            const appendedRule = copy(element.parent.value, element.parent, element.parent.type);
            appendedRule.return = `${element.parent.return} ${element.parent.value} > * {
              margin-top: var(--fgp-gap);
              margin-right: var(--fgp-gap);
            }`;
          }
        }
    
        // prefix(element.value, element.length);
      }
    };
    
    export { gapPolyfillPlugin };
    
    

    Anyone!

    opened by 7iomka 0
  • Down-Transpile Media Query Range Syntax to min- and max-?

    Down-Transpile Media Query Range Syntax to min- and max-?

    Hi there, first of all, thanks for the continued work on Stylis! Very essential to the CSS-in-JS ecosystem.

    Just learned about the Media Query Range Syntax, looks great:

    /* This...
    @media (min-width: 400px) {}
    /* ...can now be replaced by this: */
    @media (width >= 400px) {}
    

    Since Chrome 104 and Firefox 63 have support for this, it would be nice to be able to use the new syntax out of the box with no plugins (eg. in Emotion) while still supporting older browsers.

    Currently, Stylis leaves these media queries untouched, which means they will only work in these (limited) supporting browsers.

    Maybe Stylis could parse these new type of media queries and emit the old min- and max- syntax?

    Alternatives Considered

    Leave it to the community to create a Stylis plugin for this (is there a centralized place where Stylis plugins can be found / requested? there wasn't much coming up in my searches...)

    Prior art

    The PostCSS ecosystem has a plugin for this over here: https://github.com/postcss/postcss-media-minmax

    cc @Andarist

    opened by karlhorky 1
  • Fix: #292

    Fix: #292

    Fixes #292

    Use djb2 to hash the entire css property. A test case using known-css-properties has been added to make sure there is no collision.

    The bundle size before the change:

    index.js → dist/umd/stylis.js...
     stylis.js ⏤  4.11 kB
    
    created dist/umd/stylis.js in 385ms
    
    index.js → dist/stylis.mjs...
     stylis.mjs ⏤  3.97 kB
    

    The bundle size after the change:

    index.js → dist/umd/stylis.js...
     stylis.js ⏤  4.54 kB
    
    created dist/umd/stylis.js in 378ms
    
    index.js → dist/stylis.mjs...
     stylis.mjs ⏤  4.41 kB
    created dist/stylis.mjs in 388ms
    

    It is 500 bytes larger (after gzip).

    Mark the PR as a draft for further discussion.

    Potential optimization: use toString(36) or a self-implemented toString(62)?

    opened by SukkaW 1
A simple, template literals-based preprocessor for WGSL shaders

WGSL Preprocessor This library provides a dirt-simple way of adding simple preprocessor macros to your WGSL shaders via tagged templates. It supports:

Brandon Jones 19 Nov 17, 2022
A light-weight, customizable lightbox plugin for jQuery

About Colorbox: A customizable lightbox plugin for jQuery. See the project page for documentation and a demonstration, and the FAQ for solutions and e

Jack Moore 4.8k Dec 29, 2022
A light-weight, no-dependency, vanilla JavaScript engine to drive the user's focus across the page

Driver.js Powerful, highly customizable vanilla JavaScript engine to drive the user's focus across the page No external dependencies, supports all maj

Kamran Ahmed 14.3k Jan 4, 2023
A light-weight module that brings the Fetch API to Node.js

A light-weight module that brings Fetch API to Node.js. Consider supporting us on our Open Collective: Motivation Features Difference from client-side

Node Fetch 8.1k Jan 4, 2023
Mosha-vue-toastify - A light weight and fun Vue 3 toast or notification or snack bar or however you wanna call it library.

Mosha Vue Toastify A lightweight and fun Vue 3 toast or notification or snack bar or however you wanna call it library. English | 简体中文 Talk is cheap,

Baidi Liu 187 Jan 2, 2023
JQuery-TableToExcel - Light weight jQuery plugin for export HTML table to excel file

tableToExcel Light weight jQuery plugin for export table to excel file Demos Website and demo here: http://tanvirpro.com/all_project/jQueryTableToExce

Tanvir Sarker 4 May 8, 2022
Light-weight react-like maximum-optimistic library for building user interfaces.

wili Wili is a 2kb Light-weight react-like maximum-optimistic library for building user interfaces. Usage Welcome componenet: class Welcome extends Wi

Amir Fo 3 Feb 16, 2022
Light-weight Linked Open Data native cataloguing and crowdsourcing platform

id name brief-description type release-date release-number work-package keywords licence release link demo running-instance credits clef CLEF, Crowdso

Polifonia H2020 10 Apr 26, 2022
StashQL is a light-weight, open-source npm package that improves the speed of your GraphQL queries in your application.

Table of Contents What is StashQL? Install Getting Started Queries Mutations refillCache clearRelatedFields Logging The Team What is StashQL? StashQL

OSLabs Beta 67 Sep 30, 2022
🪶 An light weight image host built using typescript.

Feather; an next-gen image uploader built to be used with sharex. Built using typescript, expressjs. To get started ( IN ORDER ) ~ Hosting ~ npm i ~ n

Saige 5 Jun 14, 2022
A light-weight, simple, and straightforward learning tool for your Kubernetes cluster

Introducing Neptune Light-weight, simple, and straightforward learning tool for your Kubernetes cluster Neptune is an approachable learning tool, ligh

OSLabs Beta 109 Jan 4, 2023
A robust and light-weight inventory management application designed to help businesses maintain perfect control over every unit of stock.

Inventory Buddy Access inventory anytime on web, tablet or mobile. Inventory Buddy is a robust and light-weight inventory management application desig

Brynn Smith 7 Nov 5, 2022
A light-weight user's step-by-step guide for your website using Vanilla JS.

WebTour JS A light-weight user's step-by-step guide for your website using Vanilla JS. Features User's walkthrough - can be used to guide user's to yo

JM de Leon 23 Nov 21, 2022
This is a Pokemon cards website. There are 800+ pokemon sprites with their name, moves height, weight, and picture

Hi ?? , I'm Rayan Hossain A passionate Full Stack developer with expertise in WordPress ?? I’m currently working on codes_tips ?? I regularly write ar

Rayan Hossain 2 Oct 5, 2022
BMI Calculator can give us the bmi result of our bmi on the basis of our corresponding height and weight.

BMI means body mass index. Body Mass Index (BMI) is a person's weight in kilograms divided by the square of height in meters.

Bipronath Saha 1 Jan 20, 2022
A lightweight (the actual ship is heavy though), performat & powerful sharder for Discord.JS. Indomitable uses cluster module to evenly spread her weight (load) across your cores

Indomitable A lightweight (the actual ship is heavy though), performat & powerful sharder for Discord.JS. Indomitable uses cluster module to evenly sp

Saya 17 Nov 29, 2022
A ✨light✨ and magical Svelte component for CSS media queries🐹

Svelte CSS media queries ?? Demo - Svelte REPL Lightweight, comfortable, like Svelte ?? how to install npm i svelte-media-queries What can I do? quer

Nikita Fedorov 9 Dec 26, 2022
A JavaScript library to apply light-dark theme in web pages with the help of css variables

Theme Changer The simplest JavaScript library to apply light - dark theme in your website. First Check Out One Example How to use Files index.html sty

Abinash Karmakar 8 Dec 20, 2022
jQuery based scrolling Bar, for PC and Smartphones (touch events). It is modern slim, easy to integrate, easy to use. Tested on Firefox/Chrome/Maxthon/iPhone/Android. Very light <7ko min.js and <1Ko min.css.

Nice-Scrollbar Responsive jQuery based scrolling Bar, for PC and Smartphones (touch events). It is modern slim, easy to integrate, easy to use. Tested

Renan LAVAREC 2 Jan 18, 2022