Stylis
- ~2Kb minified+gzipped
- ~5kb minified
Stylis is a small css compiler that turns this
stylis('#user', styles);
Where styles
is the following css
~~foo: 20px;
font-size: 2em;
font-family: sans-serif;
width: var(~~foo);
:host {
color: red;
}
:host(.fancy) {
color: red;
}
:host-context(body) {
color: red;
}
.name {
transform: rotate(30deg);
}
@global {
body {
background: yellow;
}
}
span, h1, :global(h2) {
color:red;
}
& {
animation: slidein 3s ease infinite;
display: flex;
flex: 1;
user-select: none;
}
&:before {
animation: slidein 3s ease infinite;
}
@keyframes slidein {
from { transform: translate(10px); }
to { transform: translate(200px); }
}
@media (max-width: 600px) {
display: block;
&, h1 {
appearance: none;
}
}
h1 {
color: red;
h2 {
display: block;
h3, &:hover {
color: blue;
}
}
font-size: 12px;
}
@mixin large-text {
font-size: 20px;
}
@mixin linx (link, visit, hover, active) {
a {
color: var(~~link);
&:hover {
color: var(~~hover);
}
}
}
& {
@include large-text;
}
@include linx(white, blue, green, red);
into this (minus the whitespace)
#user {
font-size: 2em;
font-family: sans-serif;
width: 20px;
}
#user {
color: red;
}
#user.fancy {
color: red;
}
body #user {
color: red;
}
#user .name {
-webkit-transform: rotate(30deg);
-ms-transform: rotate(30deg);
transform: rotate(30deg);
}
body {
background: yellow;
}
#user span,
#user h1,
h2 {
color: red;
}
#user {
display: -webkit-flex;
display: flex;
-webkit-flex: 1;
-moz-flex: 1;
flex: 1;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-animation: userslidein 3s ease infinite;
animation: userslidein 3s ease infinite;
}
#user:before {
-webkit-animation: userslidein 3s ease infinite;
animation: userslidein 3s ease infinite;
}
@-webkit-keyframes userslidein {
from {
-webkit-transform: translate(10px);
-ms-transform: translate(10px);
transform: translate(10px);
}
to {
-webkit-transform: translate(200px);
-ms-transform: translate(200px);
transform: translate(200px);
}
}
@keyframes userslidein {
from {
-webkit-transform: translate(10px);
-ms-transform: translate(10px);
transform: translate(10px);
}
to {
-webkit-transform: translate(200px);
-ms-transform: translate(200px);
transform: translate(200px);
}
}
@media (max-width: 600px) {
#user, #user h1 {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
#user {
display: block;
}
}
#user h1 {
color: red;
font-size: 12px;
}
#user h1 h2 {
display: block;
}
#user h1 h2 h3,
#user h1 h2 h2:hover {
color: blue;
}
#user {
font-size: 20px;
}
#user a {
color: white;
&:hover {
color: green;
}
}
Supports
- Edge
- IE 9+
- Chrome
- Firefox
- Safari
- Node.js
Installation
direct download
<script src=stylis.min.js></script>
CDN
<script src=https://unpkg.com/stylis@0.10.0/stylis.min.js></script>
npm
npm install stylis --save
Features
- variables via
~~foo
and var(~~foo)
; - web component emulation of
:host
, :host()
and :host-context()
- inline global injection via
:global(selector)
- block level global injection via
@global {}
- nesting
a { &:hover {} }
- static and function mixins via
@mixin ...
and @include
- prefixer
- namespacing
- flat css
color: red; h1 { color: red; }
- middleware support
- keyframes and animation namespacing (with the option to disable)
API
stylis(
selector: {string},
styles: {string},
animations: {boolean=}
compact: {boolean=}
middleware: {function=}
);
Middleware
The optional middleware function accepts four arguments ctx, str, line, column
, the middleware is executed at 4 stages.
- at every selector declaration
ctx = 0
i.e .class
/ .foo, .bar
- at every property declaration
ctx = 1
i.e color: red;
- before a block of compiled css is added to the output string
ctx = 2
, i.e .class {color:red;}
- when flat css is appened to the output bundle
ctx = 3
, i.e .prefix { color:red; }
- When an
import
statement is found ctx = 4
, i.e import 'foo'
If you wanted to you could parse import statements in the middleware and return the imported file, stylis will then insert the content of it into the css that it later parse/compile. The str value on import context is the file name i.e foo
or foo.scss
or multiple files foo, bar
.
For any stage if the middleware returns a non-falsey value the token or block of css will be replaced with the return value. For example we can add a feature random()
that when used prints a random number.
stylis(``, `h1 { width: calc(random()*10); }`, false, function (ctx, str, line, column) {
switch (ctx) {
case 1: return str.replace(/random\(\)/g, Math.random());
}
});
Will replace all instances of random()
with a random number
Intergration
You can use stylis to build an abstraction ontop of, for example imagine we want to build an abstract that makes the following React Component possible
class Heading extends React.Component {
stylesheet(){
return `
&{
color: blue
}
`;
}
render() {
return (
React.createElement('h1', 'Hello World')
);
}
}
We could simply extend the Component class as follows
React.Component.prototype.stylis = function (self) {
var namespace = this.displayName;
return function () {
stylis(namespace, self.stylesheet(), document.head);
mounted = true;
this.setAttribute(namespace);
}
}
Then use it in the following way
class Heading extends React.Component {
stylesheet(){
return `
&{
color: blue
}
`;
}
render() {
return (
React.createElement('h1', {ref: this.stylis(self)}, 'Hello World')
);
}
}
When the first instance of the component is mounted the function assigned to the ref will get executed adding a style element with the compiled output of stylesheet()
where as only the namespace attribute is added to any subsequent instances.
You can of course do this another way
class Heading extends React.Component {
constructor (props) {
super(props);
this.style = React.createElement('style', {id: this.displayName}, this.stylesheet());
}
stylesheet(){
return `
&{
color: blue
}
`;
}
render() {
return (
React.createElement('h1', null, 'Hello World', this.style)
);
}
}
One will add it to the head another will render it in place with the component.
If you want a better picture into what can be done, there is an abstraction i created
for dio.js that does away with the above boilerplate entirely http://jsbin.com/mozefe/1/edit?js,output