GithubHelp home page GithubHelp logo

kodyl / stilr Goto Github PK

View Code? Open in Web Editor NEW
236.0 9.0 16.0 913 KB

Encapsulated styling for your javascript components with all the power of javascript and CSS combined.

License: MIT License

Makefile 4.69% JavaScript 95.31%

stilr's Introduction

Stilr Build Status npm version

Encapsulated styling for your javascript components with all the power of javascript and CSS combined.

  • Unique class names (Content Hash Based)
  • Useable on the server
  • Allows nested pseudo selectors
  • Allows nested media queries
  • No namespacing / Class name collisions.
  • Plays nicely with React Hot Loader and autoprefixer.

...oh, and did I mention you get duplicate style elimination for free?

Note: This library fits really nice with React but should work with other libraries like Angular 2 or Deku

API

object StyleSheet.create(object spec)

Stilr extracts the styles from the style object and returns an object with the same keys mapped to class names.

Example

import StyleSheet from 'stilr';

const palm = '@media screen and (max-width:600px)';

const styles = StyleSheet.create({
  container: {
    color: '#fff',
    ':hover': {                 // Pseudo Selectors are allowed
      color: '#000'
    },
    [palm]: {                   // Media Queries are allowed
      fontSize: 16,
      ':hover': {
        color: 'blue'  		// Pseudo selectors inside media queries.
      }
    }
  }
});

console.log(styles.container);  // => '_xsrhhm' -- (The class name for this style.)

string StyleSheet.render()

Stilr outputs the contents of its internal stylesheet as a string of css

Example

import StyleSheet from 'stilr';

StyleSheet.create({
  container: {
    color: '#fff'
  }
});

const CSS = StyleSheet.render();

console.log(CSS);             // => '._yiw79c{color:#fff;}'

bool StyleSheet.clear()

Clear Stilr internal stylesheet

Example

import StyleSheet from 'stilr';

const styles = StyleSheet.create({
  container: {
    color: '#fff'
  }
});

StyleSheet.clear();

const CSS = StyleSheet.render();

console.log(CSS);             // => ''

Examples

Basic Button Component Example.

Let's start of by creating our styles. If you have ever used React Native, this will be familiar to you:

import StyleSheet from 'stilr';
import { palm } from './breakpoints';
import { color, font } from './theme';

const styles = StyleSheet.create({
  base: {
    transition: 'background-color .25s',
    borderRadius: 2,
    textAlign: 'center',
    fontSize: 20,
    padding: 6,
    color: '#fff',
    border: `${ color.border } 1px solid`,
    [palm]: {
      fontSize: 18
    }
  },
  primary: {
    backgroundColor: color.primary,
    ':hover': {
      color: 'tomato'
    }
  },
  secondary: {
    backgroundColor: 'tomato',
    color: '#eee'
  }
});

Stilr will now generate a set of class names based on the content of your styles and return an object with the same keys mapped to those classes.

Note that you're able to use pseudo selectors and media queries. Pseudo selectors are written like you normally would in CSS, e.g.: :hover, :active, :before etc. Media queries are the same, e.g. palm in the example is just a string: @media screen and (max-width:600px). Any valid media query is allowed.

Since we just have a bunch of class names now, we can use these in our React Component.

import React, { PropTypes } from 'react';

class Button extends React.Component {
  static propTypes = {
    type: PropTypes.oneOf(['primary', 'secondary'])
  }

  render() {
    const { type, children } = this.props;
    const buttonStyles = [
      styles.base,
      styles[ type ]
    ].join(' ');

    return (
      <button className={ buttonStyles }>
        { children }
      </button>
    );
}

Next up, let's render our css and mount our app:

import React from 'react';
import Button from './button';
import StyleSheet from 'stilr';

React.render(
  <Button type='primary' />,
  document.getElementById('root')
);

document.getElementById('stylesheet').textContent = StyleSheet.render();

This step could also have been done on the server. Since StyleSheet.render just returns a string of css. In our case, it would look something like this in a prettified version:

@media screen and (max-width:600px) {
  ._82uwp6 {
    font-size: 18px;
  }
}

._82uwp6 {
  transition: background-color .25s;
  border-radius: 2px;
  text-align: center;
  font-size: 20px;
  padding: 6px;
  color: #fff;
  border: #fff 1px solid;
}

._11jt6vs:hover {
  color: tomato;
}

._11jt6vs {
  background-color: red;
}

._1f4wq27 {
  background-color: tomato;
  color: #eee;
}

In case you were wondering: Yes, this would would be an ideal place to add something like autoprefixer, minification etc.

Duplicate Style Elimation

import StyleSheet from 'stilr';

const styles = StyleSheet.create({
  same: {
    fontSize: 18,
    color: '#000'
  },
  sameSame: {
    fontSize: 18,
    color: '#000'
  }
});

console.log( styles.same );        => '_1v3qejj'
console.log( styles.sameSame );    => '_1v3qejj'

...magic.

Under the hood, stilr creates class names based on a content hash of your style object which means that when the content is the same, the same hash will always be returned.

Extracting your styles.

Server

If you do serverside rendering, you should be able to extract your styles right after you load your app.

import React from 'react';
import StyleSheet from 'stilr';
import App from '../your-app.js';

const css = StyleSheet.render();
const html = React.renderToStaticMarkup(<App />);

// Extract css to a file or insert it in a file at the top of your html document

Apply autoprefixer here, or other preprocess goodness here. If you're really fancy, you only do the required autoprefixes based on the user agent.

Development

When working with Stilr in development, the preferred way to extract styles would be the following way, just before you initialize your app.

import App from '../app';
import React from 'react';
import StyleSheet from 'stilr';

let stylesheet = document.createElement('style');
stylesheet.textContent = StyleSheet.render();
document.head.appendChild(stylesheet);

React.render(<App />, document.getElementById('root'));

React Hot Loader + Autoprefixer

If you're using React Hot Loader. Use the following approach in development to get hot loading styles and autoprefixer awesomeness.

import React from 'react';
import StyleSheet from 'stilr';
import autoprefixer from 'autoprefixer';
import postcss from 'postcss';


// Make sure you have a style element with the ID: 'stylesheet' in your html.
const stylesheet = document.getElementById('stylesheet');

class App extends React.Component {
   render() {
     if (process.env !== 'production') {
       const prefixedCSS = postcss(autoprefixer()).process( StyleSheet.render() ).css;
       stylesheet.textContent = prefixedCSS;
     }
     
     return (
        ...snip...
     );
   }
}

If you're using Webpack, you have to add node: { fs: 'empty' } to your config file. Otherwise autoprefixer will throw an error when trying to use it on the client.

Production

Makefile

Add this as a build step in your makefile.

extract-styles:
	@node -p "require('babel/register')({ignore: false}); var s = require('stilr'); require('./your-app.js'); s.render()" > ./bundle.css

The following snippet can also be executed anywhere to extract the styles. Remember to replace ./your-app.js with the entry file of your app. node -p "require('babel/register')({ignore: false}); var s = require('stilr'); require('./your-app.js')

TODO:

  • Remove React as a dependency
  • More examples

stilr's People

Contributors

alex3165 avatar c089 avatar danieljuhl avatar karlhorky avatar michelebertoli avatar tmcw avatar webpapaya avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stilr's Issues

Vendor prefixing?

Hi, loving Stilr so far. Can your recommend an approach to vendor prefix values in development / hot-load mode? Autoprefixer works great on the server side but it seems to have compatibility issues being imported into Webpack.

Stilr & webpack - dynamic assets URLs

Hello.

I am using webpack & stilr. I need to use autoprefixer for my stylesheets.
The problem is that URLs for my assets are not known at build time - they are only known at run time. I am using __webpack_public_path__ parameter to handle this situation.

That is what makes it impossible to render stilr stylesheets at build time - URLs to assets are incomplete (e.g. in backgroundImage css attributes), and stilr generates different classnames than at run time.

I am okay with having styles rendered at run time.

But now I am trying to apply autoprefixer. Using it increases the size of my bundle by almost 2 MB which is unacceptable.

Is there any way around this? Or maybe am I doing something wrong?

neccesary nesting?

Stilr supports pseudo elements by nesting them:

StyleSheet.create({
  x: {
    color: 'red',
    ':hover': {
      color: 'green'
    }
  }
})

However, it doesn't work if you don't nest the pseudo selectors:

StyleSheet.create({
  x: {
    color: 'red',
  },
  'x:hover': {
    color: 'green'
  }
})

This, creates two keys, with none of them with a :hover pseudo selector.

Is this on purpose? If not, perhaps it should be documented. (I'll do a pull request, just need your answer first)

Multiple values using arrays?

There is a similar project, called JSS (https://github.com/jsstyles/jss)
It has this great feature for dealing with the limitation of JS objects with unique keys:
https://github.com/jsstyles/jss#multiple-declarations-with-identical-property-names

I can't think of many situations where this would be useful when already using autoprefixr, but I think it would be a good feature to have to be feature complete.

Perhaps using JSS would make this simpler, but word of warning, JSS doesn't support features such as nested pseudo selectors and media queries etc.

test two levels deep

sometimes when using both a media query and a pseudo-selector, it may be important to change the styles with the pseudo selector within a media query.

For this, it is important to support styles that are nested more that one level deep.

Allow style keys to be strings

In general it works, however some values are incorrectly post-fixed with "px".

const style = Stilr.create({
    header: {
        'z-index': 9999
    }
});

This style will be converted to:

.header{z-index:9999px}

v1.2.2 not working on internet explorer

Currently in version 1.2.2, dist/util.js Number.isInteger is used as is.

It should be used with babel, so it output something like var _Number$isInteger = require('babel-runtime/core-js/number/is-integer')['default'];

Currently, it's failing on internet explorer because of that.

Allow prefix for tags

I'd like all my tags to have a prefix in front of them:

#app .xyz {}

Would be nice to have an option on render to prefix.

Actually even better would be just being able to control the tag completely, something like:

stilr(styles, (name, hash) => {
  return '#app ' + name + '.' + hash 
})

Why use className as interface

This seems like the strictly the wrong way to inject styles.

  1. Now to apply styles you must set a class attribute
  2. Your class attributes must not consider they don't conflict with your "style class" attributes

I'd prefer the react-style method, where they shim in a new property (styles) and you pass them in. Or alternatively, why not just have an interface where I can pass them directly into style={}? Or is that already possible?

What about debugging?

The project seems super-interesting, I can't wait to try! But one question came to my mind - what about debugging? Did you consider sourcemaps generation? Or conditional (process.env.NODE_ENV !== 'production) expanding class names to readable form (like _xy132as_redButton). Do you plan anything like that?

Pretification

Just opening to track this, may make a PR for it soon.

Trouble importing Stilr via require()

Not sure if this is a Stilr bug or a transpiler bug, but as of 1.2.0 I cannot require Stilr via:

var StyleSheet = require("Stilr");

Instead I have to work-around the default export:

var StyleSheet = require("Stilr").default;

`content=""` is not rendered properly

Input

Stylesheet.create({
  heading: {
    ':before': {  content: "" }
  }
});

Output

._3TCwqu:before{content:;}

Expected Output

._3TCwqu:before{content:"";}

I don't know if we can generalize this concept for empty strings that they're rendered as "" or if content should have a special behaviour?

Webpack plugin

Thanks for this, might i suggest a webpack plugin, so that not only i don't need to:

 if (process.env !== 'production') {
       const prefixedCSS = postcss(autoprefixer()).process( StyleSheet.render() ).css;
       stylesheet.textContent = prefixedCSS;
   } 

but also to ether on development or production, to leverage the webpack build process

In hot-load mode, what if <App> doesn't re-render but a child of its does?

Your suggestion for making Stilr work in a hot-loaded environment is to include some code in the root component of the entire app that re-renders the content of <style>.

This idea works well, except when: the root component doesn't re-render but a child of its does. Such a case happens, for example, when react-router is used.

How do you work around this issue? An obvious workaround is to include the code updating <style> in every style-changing component. But this is tedious.

Am I missing something?

A better workaround is to make Stilr an EventEmitter. If this idea makes sense to you, I can submit a PR.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.