Coder Social home page Coder Social logo

buildbreakdo / style-it Goto Github PK

View Code? Open in Web Editor NEW
159.0 4.0 8.0 2.11 MB

Component for writing plaintext CSS in React apps -- isomorphic, scoped, FOUC-free, fully featured, CSS-in-JS

License: MIT License

JavaScript 100.00%
css-in-js reactjs isomorphic cssinjs react

style-it's Introduction

Style It

Style It is a component for writing plaintext CSS in JavaScript apps. Use the same familiar CSS syntax you already know and love -- now inside of your components.

Coverage Status

Question, issue, or request? Open an issue or reach out on gitter.

Feature Style It Inline Style
Nothing new to learn With Style It, you use the same familiar CSS syntax you already know and love -- now inside of your components. Just wrap your component with Style It and start writing CSS.
Fully featured There is no abstraction to go through -- Style It is just CSS. Use any CSS feature available.
Scoped selectors Move your CSS out of the global name space. Style It automatically scopes selectors for you to the component's root element and acts as a polyfill for sub-tree scoping (learn more on MDN).
Layout styling Style your layout without using an external CSS file. Style It makes keeping all of your CSS in the App component hierarchy easy -- from global layout to component specific styles.

SEE MORE »

Installation

Install with npm.

npm install style-it --save

NPM Stats

Alternatively, if you are not using npm as your package manager the files can be downloaded directly from the npm content delivery network.

Usage

Style It takes advantage of ES2015 / ES6 specification for Template Literals (previously "template strings") to write plaintext CSS in JavaScript. Use of Template Literals enables multi-line strings, string interpolation, and use of curley brackets / braces to declare CSS rules.

Template Literals are enclosed by the back-tick (` `) (grave accent) character instead of double or single quotes. Learn more about Template Literals on MDN.

Functional syntax

In:

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return Style.it(`
      .intro {
        font-size: 40px;
      }
    `,
      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    );
  }
}

export default Intro;

Out:

<p class="intro _scoped-1">
  <style type="text/css">
    ._scoped-1.intro {
      font-size: 40px;
    }
  </style>

  CSS-in-JS made simple -- just Style It.
</p>

JSX syntax

In:

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return (
      <Style>
        {`
          .intro {
            font-size: 40px;
          }
        `}

        <p className="intro">CSS-in-JS made simple -- just Style It.</p>
      </Style>
    );
  }
}

export default Intro;

Out:

<p class="intro _scoped-1">
  <style type="text/css">
    ._scoped-1.intro {
      font-size: 40px;
    }
  </style>

  CSS-in-JS made simple -- just Style It.
</p>

Component with Component Root Node

Let's say we have two components ParentComponent and ChildComponent defined as follows (note the root node in the ParentComponent is another component):

...
class ParentComponent extends Component {
    render() {
        return (
            <ChildComponent>
                <p> Some stuff </p>
            </ChildComponent>
        )
    }
}
...
...
class ChildComponent extends Component {
    render() {
        return (
            <div>
                {this.props.children}
            </div>
        )
    }
}
...

The child component is a typical passthrough component, that takes the props (in this case <p> Some stuff </p>) and renders them.

A core feature of Style It are scopes, you do not have to worry about global CSS name collisions because each CSS rule is scoped to it's own sub-tree (poly-like fill for scoped attribute). To achieve this, a scoping className is added. This works automagically when component root nodes are types like <div>, <span>, <p>, etc. When the root node of a component is another component, this becomes a much more tricky problem because it is not known how deep the component root node nesting will go (e.g., multiple passthrough components composed inside of one another).

For this reason, components which serve as root nodes for other components must have their className attribute set to this.props.className to have the CSS scoping class name properly set (without the scope class name CSS styles will not apply). So the ChildComponent (since it is used as a root node in the ParentComponent) becomes:

...
class ChildComponent extends Component {
    render() {
        return (
            <div className={this.props.className}>
                {this.props.children}
            </div>
        )
    }
}
...

So all we added was an explicit className assignment from props (this snippet className={this.props.className}). Would argue that this is best practices anyway for a component that can be a root node; likely should use the spread operator (e.g., {...this.props}) to cover all your bases and include events a would be user of your component may attach.

If you would like to play with this scenario online, you can open this example in JSFIDDLE.

Additional Usage

JavaScript variables in your CSS

In:

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    const fontSize = 13;

    return Style.it(`
      .intro {
        font-size: ${ fontSize }px;  // ES2015 & ES6 Template Literal string interpolation
      }
      .package {
        color: blue;
      }
      .package:hover {
        color: aqua;
      }
    `,
      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    );
  }
}

export default Intro;

Out:

<p class="intro _scoped-1">
  <style type="text/css">
    ._scoped-1.intro {
      font-size: 13px;
    }
    ._scoped-1 .package {
      color: blue;
    }
    ._scoped-1 .package:hover {
      color: aqua;
    }
  </style>

  CSS-in-JS made simple -- just Style It.
</p>

Layout styling

Style your layout without using an external CSS file. Style It makes keeping all of your CSS in the App component hierarchy easy -- from global layout to component specific styles (uses JSX syntax).

In:

import React from 'react';
import Style from 'style-it';

class App extends React.Component {
  render() {
    return (
      <div>
        <Style>
          {`
            body {
              font-size: small;
              font-family: arial;
            }
            h1 {
              font-size: large;
            }
            h2 {
              font-size: medium;
            }
            h3 {
              font-size: small;
            }
            a {
              color: blue;
            }
            a:hover {
              color: aqua;
            }
         `}
        </Style>

        <header />
        <main />
        <footer />
      </div>
    );
  }
}

export default App;

Out:

<div>
  <style type="text/css">
    body {
      font-size: small;
      font-family: arial;
    }
    h1 {
      font-size: large;
    }
    h2 {
      font-size: medium;
    }
    h3 {
      font-size: small;
    }
    a {
      color: blue;
    }
    a:hover {
      color: aqua;
    }
  </style>

  <header></header>
  <main></main>
  <footer></footer>
</div>

More Features

Feature Style It Inline Style
Increase cohesion Self contained and complete components. With Style It there is no need to fragment your component by creating an external CSS file when you need to use pseudo-classes, pseudo-selectors, at-media rules, or one of Reacts unsupported vendor prefixes (like flexbox or cursor grab).
Write less code Use the right tool for the right job. With Style It the full power of CSS is available so we can stop reimplementing CSS language features like :hover and nth-of-type in JavaScript.
Faster build times Remove the preprocessor middle man -- this is just CSS. Style It brings back the simplicity of writing CSS and removes the need for managing and executing a build step.
Increase readability Maintain the simplicity, symmetry, and beauty of HTML's open and close syntax. With Style It you can achieve cohesion and clean separation while still bringing component concerns together as a single self-contained unit of code.
Isomorphic Style It is completely isomorphic and renders the same markup on server and client.
Increase new hire productivity On boarding of new developers is quick and easy with Style It -- there is nothing new to learn. Get your new developers styling today!
Better workflow Better workflow, a classic workflow. Not often we are able to say that, because Style It requires no transformations to your CSS you can make adjustments in your browser and simply copy and paste the style rules into your component.
Easily portable By simply being CSS Style It makes porting styles elsewhere easy with no special syntax or quirks to transform.
Share styles Easily share styles across multiple components by leveraging JavaScript import notation you already know.
Small size Style It is tiny at only 1.84kB gzipped.
Better composition More easily evaluate when to break down a component. CSS complexity is an oft forgotten input when evaluating component complexity (+selectors, +pseudo states). Unifying HTML-CSS-JS in one place ensures CSS complexity is a part of your -- when should I decompose -- equation.

Behind The Scenes (bonus)

How scopes work

To isolate styles Style It iterates over child component prop data and generates a hash that is used as a scoping class. Preference would be to use Math.random() however HTML rendered on the server would not be identical to the HTML rendered on the client. Use of Math.random() would create a server-client mismatch and benefits of the serverside render would be lost. Working within these constraints, Style It collects child component props and hashes them to generate a -- unique as possible -- identifier on client and server. Relative to how iterating through children is historically done, by going to the DOM and reading values, React allows us to perform this operation incredibly fast by leveraging the component hierarchy already held in memory. The hash of the prop data is then used to create a class name (e.g., _scope-472614893) which is automatically prefixed to selectors for you.

Optimization

Firm believer in Don Knuth's Literate Programmer movement: 'the most important function of computer code is to communicate the programmer's intent to a human reader' and that we should not prematurely optimize; optimizations tend to vary and be scenario specific, making code less idiomatic and obtuse, bad for readability and maintainability.

Point being: You probably do not need to worry about this section.

That said, every usage scenario cannot be predicted, so an escape hatch is built into the library. If performance is sluggish this is potentially due to class name scope thrashing. Prop data used to create the class name scope is changing quickly causing the DOM sub-tree to rerender (e.g., live data). To tell Style It to ignore this prop during hash generation mark the prop as internal by prefixing _ to the prop name (e.g, _someProp).

Props like value on <input /> and <textarea /> are automatically ignored, as are props internal to React (they are not idempotent and change from server to client).

License

MIT. Copyright © 2016-present Joshua Robinson.

style-it's People

Contributors

buildbreakdo avatar dependabot[bot] avatar npmcdn-to-unpkg-bot avatar w33ble 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

style-it's Issues

problem with generate CSS style for child component

hello i want set Style to sub child but css not generate correctly
my code

    return Style.it(`.ant-table-tbody tr td {color: red}`,
        <div className='sample'>
          <Table
            columns={columns}
            size="small"
            dataSource={this.props.config.selectedAtomicQueryValue.sheets[0].data}
            pagination={this.renderPagination(this.props.config)}
          /></div>);

and generated css

<style type="text/css">.s1287016723 .ant-table-tbody tr td {
.s1287016723 color: red}
</style>

the problem is here .s1287016723 color: red}

Investigate converting to stateless function for performance boost

See https://facebook.github.io/react/docs/reusable-components.html

Stateless Functions
You may also define your React classes as a plain JavaScript function. For example using the stateless function syntax:

function HelloMessage(props) {
return

Hello {props.name}
;
}
ReactDOM.render(, mountNode);
Or using the new ES6 arrow syntax:

const HelloMessage = (props) =>

Hello {props.name}
;
ReactDOM.render(, mountNode);
This simplified component API is intended for components that are pure functions of their props. These components must not retain internal state, do not have backing instances, and do not have the component lifecycle methods. They are pure functional transforms of their input, with zero boilerplate. However, you may still specify .propTypes and .defaultProps by setting them as properties on the function, just as you would set them on an ES6 class.

NOTE:
Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component. Normally this isn't an issue, since stateless functions do not provide an imperative API. Without an imperative API, there isn't much you could do with an instance anyway. However, if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.
NOTE:
In React v0.14, stateless functional components were not permitted to return null or false (a workaround is to return a instead). This was fixed in React v15, and stateless functional components are now permitted to return null.
In an ideal world, most of your components would be stateless functions because in the future we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. This is the recommended pattern, when possible.

Dependency react-lib-adler32 is missing license

We've been using style-it in the canvas feature in Kibana, but it was flagged during an open source license audit because of a dependency on react-lib-adler32.

https://github.com/buildbreakdo/style-it/blob/master/package.json#L129

The react-lib-adler32 dependency was flagged as having no license file. From the source it appears to be a redistribution of code that was under a BSD 3-clause license, but it doesn't include a BSD license itself.

Since Kibana builds include all the dependencies including transitive ones, we're looking at ways to either correct react-lib-adler32 or work around it so we can continue using style-it.

I wanted to see if it would be possible to either:

  • update react-lib-adler32 ( @buildbreakdo it appears you are the author ?) to add the BSD-3 clause license.
  • Have style-it vendor in the adler32 code, carrying forward the required information to satisfy the terms of the BSD-3 clause license.

Add Options to memoize first style calculation after component mounted?

I suppose this is a feature request that I'd like to get your opinion on. I use the library to inject a static string of CSS styles, like so:

import Style from 'style-it';
import styleSheet from './index.scss';

class MyComp extends React.Component {
  /* OTHER CLASS STUFF */
  render() {
    return Style.it(styleSheet, <div className="foobar">Some Content</div>);
  }
}

Since I'm passing a static style string, the stylesheet does not need to be rebuilt each time render is run, only the first time. It would be nice it style.if (or the <Style> component) accepted a third options argument, with something like an onlyGenerateOnce parameter to indicate that the stylesheet never needs to be rebuilt after it is originally created.

Scope for lists of things are not unique

Look at leaves of react create element tree to create scope pepper, optionally user could pass down the key value used and add this value to the CSS passed into to add to the pepper; generally this should not be necessary because we can just look at the outer branches of the tree where there is likely to be unique data.

Style not generated when using classname composition

When using the reactive syntax and a composed classname to set the styles in a component, the Style it styles don't seem to be generated. Example:

<Style>
.${styles.toggler}:
   background: red;
}
.${styles.activeToggler} {
   background: blue;
}
</Style>

The first rule is correctly applied, but when the second classname is given to the element, nothing happens (the background is still red). The css for the components above has this pattern:

:local(.toggler) {
   some_css_rule: ...
}
:local(.activeToggler) {
   composes: toggler;
   some_other_css_rule: ...
}

Removing the line composes: toggler will make the Style css rules work.

For reference:
Style-it: 2.0.0
React: 16.4.1

Union root selectors in single statement are scoped multiple times

In:
...

<Style> {` .someRootSelector.otherRootSelector { color: red; } `}
</Style>

...

Out, double scoping breaks selector:
...

<style type="text/css"> ._scoped-1.someRootSelector._scoped-1.otherRootSelector { color: red; } </style>

...

Solution:
Combine root selectors into a single regex statement separated by | and only replace first match (e.g., no global flag).

Remove support for node < 6

Node 4 is out of support, and even Node 6 only has ~4 months left, and users running older versions of Node can probably get by with older versions of this library. I only bring this up because I notice that CI is only failing on versions < 6, because of other dependencies dropping support.

screenshot 2019-01-03 11 35 16

Also, consider adding Node 8 and 10 to the testing matrix. "Stable" is tracking version 11.

Proposal for handling an object as first argument of Style.it

Hello, thanks for your lib..

An idea...
Instead of writing this code:

  render() {
    return Style.it(`
      .intro {
        font-size: 40px;
      }
    `,
      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    );
  }

It should be possible to write something like that:

  render() {
    const myCSS = {
      '.intro': {
        fontSize: '40px'
      }
    };

    return Style.it(myCSS, <p className="intro">CSS-in-JS made simple -- just Style It.</p>);
  }

And in this case, Style.it() will convert the myCSS object to the string. It should be easy to implement.

Keyframe offset 'from' and 'to' are incorrectly being prefixed with scopes

Input

        @keyframes MOVE-BG {
          from {
            transform: translateX(0);
          }
          to {
            transform: translateX(-550px);
          }
        }

Output

        @keyframes MOVE-BG {
          ._scoped-1 from {
            transform: translateX(0);
          }
          ._scoped-1 to {
            transform: translateX(-550px);
          }
        }

Expected

        @keyframes MOVE-BG {
          from {
            transform: translateX(0);
          }
          to {
            transform: translateX(-550px);
          }
        }

Keyframe offset % are being prefixed with scopes

@keyframes NAME-YOUR-ANIMATION {
              0%   { opacity: 0; }
              100% { opacity: 1; }
}

is incorrectly turning into

@keyframes NAME-YOUR-ANIMATION {
             ._scoped-1 0%   { opacity: 0; }
             ._scoped-1 100% { opacity: 1; }
}

Error out when root element is void type

From MDN:

A void element is an element whose content model never allows it to have contents under any circumstances. Void elements can have attributes.

The following is a complete list of the void elements in HTML:

area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr

Void element types cannot be the root element because there is no place to nest a style element. Solution = error out with good warning.

Appended comment at EOL breaks CSS processing for that snippet

@media print {
*,
*::before,
*::after {
  background: transparent !important;
  color: #000 !important; /* Black prints faster: http://www.sanbeiji.com/archives/953 */
  box-shadow: none !important;
  text-shadow: none !important;
}

Comment causes line to be incorrectly processed. Discovered for while creating a layout style (global -- no scope), need to check if true in scoped also.

support for fragments

The following will fail with error:

TypeError: Cannot convert a Symbol value to a string
const cmp = () =>  {
  return (<Style>
    {styles}
    <>
    <label htmlFor={name} className="label">{label}</label>
    <input ref={ref} id={name} name={name} className="text-input"/>
    <button onClick={onButtonClick} className="button">{buttonText}</button>
    </>
  </Style>)
}

The error comes from this line https://github.com/buildbreakdo/style-it/blob/master/src/index.js#L303 and it's because React.Fragment is a symbol

I realise the approach for this may have to deviate from the current approach (e.g. it'll probably require prefixing the generated scope class string to all classes on elements within a fragment) - but it would also be handy if it "Just Worked" and fits well with the philosophy of optimizing for user consumption.

Also this may or may not relate to #25

Add support for JS style comments

JS style comments build but break inside of reactive-style. JS style comments should be allowed as inputs but stripped during output.

...
render() (
  <Style>
    {`
       // JS style comment
       .foo { color: red; }
    `}

     <div className="foo" />
)
...

Layout thrashing when props pass through and scope hashes are regenerated

Reactive Style avoids flash of unstyled content (FOUC) by including style elements in the DOM tree on the initial server side render. This works well to avoid FOUC however in combination with generating scopes as unique as possible by hashing component tree props, too much DOM thrashing occurs. Because styles are included in the DOM tree addition and removal of styles occurs, potentially causing FOUC.

To avoid this, move style management after the initial render into the head of the document. On componentDidMount clone styles into head. This adds overhead to the initial render to avoid FOUC but alleviates layout thrashing over the long term.

After initial mount styles should be managed in the head instead of in the DOM.

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.