UNPKG

22.8 kBMarkdownView Raw
1# styled-jsx
2
3[![Build Status](https://travis-ci.org/zeit/styled-jsx.svg?branch=master)](https://travis-ci.org/zeit/styled-jsx)
4[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
5[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
6[![Slack Channel](http://zeit-slackin.now.sh/badge.svg)](https://zeit.chat)
7
8Full, scoped and component-friendly CSS support for JSX (rendered on the server or the client).
9
10
11Code and docs are for v2 which we highly recommend you to try. Looking for styled-jsx v1? Switch to the [v1 branch](https://github.com/zeit/styled-jsx/tree/v1).
12
13For an overview about the **features** and **tradeoffs** of styled-jsx you may want to take a look at [this presentation](https://speakerdeck.com/giuseppe/styled-jsx).
14
15- [Getting started](#getting-started)
16- [Configuration options](#configuration-options)
17 * [`optimizeForSpeed`](#optimizeforspeed)
18 * [`sourceMaps`](#sourcemaps)
19 * [`vendorPrefixes`](#vendorprefixes)
20- [Features](#features)
21- [How It Works](#how-it-works)
22 * [Why It Works Like This](#why-it-works-like-this)
23- [Targeting The Root](#targeting-the-root)
24- [Keeping CSS in separate files](#keeping-css-in-separate-files)
25- [Styles outside of components](#styles-outside-of-components)
26- [Global styles](#global-styles)
27 * [One-off global selectors](#one-off-global-selectors)
28- [Dynamic styles](#dynamic-styles)
29 * [Via interpolated dynamic props](#via-interpolated-dynamic-props)
30 * [Via `className` toggling](#via-classname-toggling)
31 * [Via inline `style`](#via-inline-style)
32- [Constants](#constants)
33- [Server-Side Rendering](#server-side-rendering)
34 * [`styled-jsx/server`](#-styled-jsx-server)
35- [CSS Preprocessing via Plugins](#css-preprocessing-via-plugins)
36 * [Plugin options](#plugin-options)
37 * [Example plugins](#example-plugins)
38- [FAQ](#faq)
39 * [Warning: unknown `jsx` prop on <style> tag](#warning-unknown-jsx-prop-on-style-tag)
40 * [Can I return an array of components when using React 16?](#can-i-return-an-array-of-components-when-using-react-16)
41 * [Styling third parties / child components from the parent](#styling-third-parties--child-components-from-the-parent)
42 * [Some styles are missing in production](https://github.com/zeit/styled-jsx/issues/319#issuecomment-349239326)
43- [Syntax Highlighting](#syntax-highlighting)
44
45## Getting started
46
47Firstly, install the package:
48
49```bash
50npm install --save styled-jsx
51```
52
53Next, add `styled-jsx/babel` to `plugins` in your babel configuration:
54
55```json
56{
57 "plugins": [
58 "styled-jsx/babel"
59 ]
60}
61```
62
63Now add `<style jsx>` to your code and fill it with CSS:
64
65```jsx
66export default () => (
67 <div>
68 <p>only this paragraph will get the style :)</p>
69
70 { /* you can include <Component />s here that include
71 other <p>s that don't get unexpected styles! */ }
72
73 <style jsx>{`
74 p {
75 color: red;
76 }
77 `}</style>
78 </div>
79)
80```
81
82## Configuration options
83
84The following are optional settings for the babel plugin.
85
86#### `optimizeForSpeed`
87
88Blazing fast and optimized CSS rules injection system based on the CSSOM APIs.
89
90```
91{
92 "plugins": [
93 ["styled-jsx/babel", { "optimizeForSpeed": true }]
94 ]
95}
96```
97When in production\* this mode is automatically enabled.<br>
98Beware that when using this option source maps cannot be generated and styles cannot be edited via the devtools.
99
100\* `process.env.NODE_ENV === 'production'`
101
102
103#### `sourceMaps`
104
105Generates source maps (default: `false`)
106
107#### `vendorPrefixes`
108
109Turn on/off automatic vendor prefixing (default: `true`)
110
111## Features
112
113- Full CSS support, no tradeoffs in power
114- Runtime size of just **3kb** (gzipped, from 12kb)
115- Complete isolation: Selectors, animations, keyframes
116- Built-in CSS vendor prefixing
117- Very fast, minimal and efficient transpilation (see below)
118- High-performance runtime-CSS-injection when not server-rendering
119- Future-proof: Equivalent to server-renderable "Shadow CSS"
120- Source maps support
121- Dynamic styles and themes support \***new**
122- CSS Preprocessing via Plugins \***new**
123
124## How It Works
125
126The example above transpiles to the following:
127
128```jsx
129import _JSXStyle from 'styled-jsx/style'
130
131export default () => (
132 <div className='jsx-123'>
133 <p className='jsx-123'>only this paragraph will get the style :)</p>
134 <_JSXStyle styleId='123' css={`p.jsx-123 {color: red;}`} />
135 </div>
136)
137```
138
139### Why It Works Like This
140
141Unique classnames give us style encapsulation and `_JSXStyle` is heavily optimized for:
142
143- Injecting styles upon render
144- Only injecting a certain component's style once (even if the component is included multiple times)
145- Removing unused styles
146- Keeping track of styles for server-side rendering
147
148### Targeting The Root
149
150Notice that the outer `<div>` from the example above also gets a `jsx-123` classname. We do this so that
151you can target the "root" element, in the same manner that
152[`:host`](https://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/#toc-style-host) works with Shadow DOM.
153
154If you want to target _only_ the host, we suggest you use a class:
155
156```jsx
157export default () => (
158 <div className="root">
159 <style jsx>{`
160 .root {
161 color: green;
162 }
163 `}</style>
164 </div>
165)
166```
167
168### Keeping CSS in separate files
169
170Styles can be defined in separate JavaScript modules by tagging with `css` any template literal that contain CSS.
171
172`css` must be imported from `styled-jsx/css`:
173
174```js
175/* styles.js */
176import css from 'styled-jsx/css'
177
178export const button = css`button { color: hotpink; }`
179export default css`div { color: green; }`
180```
181
182imported as regular strings:
183
184```jsx
185import styles, { button } from './styles'
186
187export default () => (
188 <div>
189 <button>styled-jsx</button>
190 <style jsx>{styles}</style>
191 <style jsx>{button}</style>
192 </div>
193)
194```
195
196Styles are automatically scoped but you can also be consumed as [globals](#global-styles).
197
198N.B. Dynamic styles cannot be used in external styles.
199
200We also support CommonJS exports but you can only export one string per module:
201
202```js
203module.exports = css`div { color: green; }`
204
205// the following won't work
206// module.exports = { styles: css`div { color: green; }` }
207```
208
209### Styles outside of components
210
211The `css` tag from `styled-jsx/css` can be also used to define styles in your components files but outside of the component itself. This might help with keeping `render` methods smaller.
212
213```jsx
214import css from 'styled-jsx/css'
215
216export default () => (
217 <div>
218 <button>styled-jsx</button>
219 <style jsx>{button}</style>
220 </div>
221)
222
223const button = css`button { color: hotpink; }`
224```
225
226Like in externals styles `css` doesn't work with dynamic styles. If you have dynamic parts you might want to place them inline inside of your component using a regular `<style jsx>` element.
227
228### Global styles
229
230To skip scoping entirely, you can make the global-ness of your styles
231explicit by adding _global_.
232
233```jsx
234export default () => (
235 <div>
236 <style jsx global>{`
237 body {
238 background: red
239 }
240 `}</style>
241 </div>
242)
243```
244
245The advantage of using this over `<style>` is twofold: no need
246to use `dangerouslySetInnerHTML` to avoid escaping issues with CSS
247and take advantage of `styled-jsx`'s de-duping system to avoid
248the global styles being inserted multiple times.
249
250### One-off global selectors
251
252Sometimes it's useful to skip selectors scoping. In order to get a one-off global selector we support `:global()`, inspired by [css-modules](https://github.com/css-modules/css-modules).
253
254This is very useful in order to, for example, generate a *global class* that
255you can pass to 3rd-party components. For example, to style
256`react-select` which supports passing a custom class via `optionClassName`:
257
258```jsx
259import Select from 'react-select'
260export default () => (
261 <div>
262 <Select optionClassName="react-select" />
263
264 <style jsx>{`
265 /* "div" will be prefixed, but ".react-select" won't */
266
267 div :global(.react-select) {
268 color: red
269 }
270 `}</style>
271 </div>
272)
273```
274
275### Dynamic styles
276
277To make a component's visual representation customizable from the outside world there are three options.
278
279#### Via interpolated dynamic props
280
281Any value that comes from the component's `render` method scope is treated as dynamic. This makes it possible to use `props` and `state` for example.
282
283```jsx
284const Button = (props) => (
285 <button>
286 { props.children }
287 <style jsx>{`
288 button {
289 padding: ${ 'large' in props ? '50' : '20' }px;
290 background: ${props.theme.background};
291 color: #999;
292 display: inline-block;
293 font-size: 1em;
294 }
295 `}</style>
296 </button>
297)
298```
299
300New styles' injection is optimized to perform well at runtime.
301
302That said when your CSS is mostly static we recommend to split it up in static and dynamic styles and use two separate `style` tags so that, when changing, only the dynamic parts are recomputed/rendered.
303
304```jsx
305const Button = (props) => (
306 <button>
307 { props.children }
308 <style jsx>{`
309 button {
310 color: #999;
311 display: inline-block;
312 font-size: 2em;
313 }
314 `}</style>
315 <style jsx>{`
316 button {
317 padding: ${ 'large' in props ? '50' : '20' }px;
318 background: ${props.theme.background};
319 }
320 `}</style>
321 </button>
322)
323```
324
325#### Via `className` toggling
326
327The second option is to pass properties that toggle class names.
328
329```jsx
330const Button = (props) => (
331 <button className={ 'large' in props && 'large' }>
332 { props.children }
333 <style jsx>{`
334 button {
335 padding: 20px;
336 background: #eee;
337 color: #999
338 }
339 .large {
340 padding: 50px
341 }
342 `}</style>
343 </button>
344)
345```
346
347Then you would use this component as either `<Button>Hi</Button>` or `<Button large>Big</Button>`.
348
349#### Via inline `style`
350
351\***best for animations**
352
353Imagine that you wanted to make the padding in the button above completely customizable. You can override the CSS you configure via inline-styles:
354
355```jsx
356const Button = ({ padding, children }) => (
357 <button style={{ padding }}>
358 { children }
359 <style jsx>{`
360 button {
361 padding: 20px;
362 background: #eee;
363 color: #999
364 }
365 `}</style>
366 </button>
367)
368```
369
370In this example, the padding defaults to the one set in `<style>` (`20`), but the user can pass a custom one via `<Button padding={30}>`.
371
372### Constants
373
374It is possible to use constants like so:
375
376```jsx
377import { colors, spacing } from '../theme'
378import { invertColor } from '../theme/utils'
379
380const Button = ({ children }) => (
381 <button>
382 { children }
383 <style jsx>{`
384 button {
385 padding: ${ spacing.medium };
386 background: ${ colors.primary };
387 color: ${ invertColor(colors.primary) };
388 }
389 `}</style>
390 </button>
391)
392```
393
394Please keep in mind that constants defined outside of the component scope are treated as static styles.
395
396## Server-Side Rendering
397
398### `styled-jsx/server`
399
400The main export flushes your styles to an array of `React.Element`:
401
402```jsx
403import React from 'react'
404import ReactDOM from 'react-dom/server'
405import flush from 'styled-jsx/server'
406import App from './app'
407
408export default (req, res) => {
409 const app = ReactDOM.renderToString(<App />)
410 const styles = flush()
411 const html = ReactDOM.renderToStaticMarkup(<html>
412 <head>{ styles }</head>
413 <body>
414 <div id="root" dangerouslySetInnerHTML={{__html: app}} />
415 </body>
416 </html>)
417 res.end('<!doctype html>' + html)
418}
419```
420
421We also expose `flushToHTML` to return generated HTML:
422
423```jsx
424import React from 'react'
425import ReactDOM from 'react-dom/server'
426import { flushToHTML } from 'styled-jsx/server'
427import App from './app'
428
429export default (req, res) => {
430 const app = ReactDOM.renderToString(<App />)
431 const styles = flushToHTML()
432 const html = `<!doctype html>
433 <html>
434 <head>${styles}</head>
435 <body>
436 <div id="root">${app}</div>
437 </body>
438 </html>`
439 res.end(html)
440}
441```
442
443It's **paramount** that you use one of these two functions so that
444the generated styles can be diffed when the client loads and
445duplicate styles are avoided.
446
447## CSS Preprocessing via Plugins
448
449Styles can be preprocessed via plugins.
450
451Plugins are regular JavaScript modules that export a simple function with the following signature:
452
453```js
454(css: string, options: Object) => string
455```
456
457Basically they accept a CSS string in input, optionally modify it and finally return it.
458
459Plugins make it possible to use popular preprocessors like SASS, Less, Stylus, PostCSS or apply custom transformations to the styles at **compile time**.
460
461To register a plugin add an option `plugins` for `styled-jsx/babel` to your `.babelrc`. `plugins` must be an array of module names or *full* paths for local plugins.
462
463```json
464{
465 "plugins": [
466 [
467 "styled-jsx/babel",
468 { "plugins": ["my-styled-jsx-plugin-package", "/full/path/to/local/plugin"] }
469 ]
470 ]
471}
472```
473
474<details>
475 <summary>Instructions to integrate with Next.js</summary>
476 In order to register styled-jsx plugins in a Next.js app you need to create a custom .babelrc file:
477
478 ```json
479 {
480 "presets": [
481 [
482 "next/babel",
483 {
484 "styled-jsx": {
485 "plugins": [
486 "styled-jsx-plugin-postcss"
487 ]
488 }
489 }
490 ]
491 ]
492 }
493 ```
494
495 This is a fairly new feature so make sure that you using a version of Next.js that supports passing options to `styled-jsx`.
496</details>
497<br>
498
499Plugins are applied in definition order left to right before styles are scoped.
500
501In order to resolve local plugins paths you can use NodeJS' [require.resolve](https://nodejs.org/api/globals.html#globals_require_resolve).
502
503N.B. when applying the plugins styled-jsx replaces template literals expressions with placeholders because otherwise CSS parsers would get invalid CSS E.g.
504
505```css
506/* `ExprNumber` is a number */
507%%styled-jsx-placeholder-ExprNumber%%
508```
509
510**Plugins won't transform expressions** (eg. dynamic styles).
511
512When publishing a plugin you may want to add the keywords: `styled-jsx` and `styled-jsx-plugin`.
513We also encourage you to use the following naming convention for your plugins:
514
515```
516styled-jsx-plugin-<your-plugin-name>
517```
518
519#### Plugin options
520
521Users can set plugin options by registering a plugin as an array that contains
522the plugin path and an options object.
523
524```json
525{
526 "plugins": [
527 [
528 "styled-jsx/babel",
529 {
530 "plugins": [
531 ["my-styled-jsx-plugin-package", { "exampleOption": true }]
532 ],
533 "sourceMaps": true
534 }
535 ]
536 ]
537}
538```
539
540Each plugin receives a `options` object as second argument which contains
541the babel and user options:
542
543```js
544(css, options) => { /* ... */ }
545```
546
547The `options` object has the following shape:
548
549```js
550{
551 // user options go here
552 // eg. exampleOption: true
553
554 // babel options
555 babel: {
556 sourceMaps: boolean,
557 vendorPrefixes: boolean,
558 isGlobal: boolean,
559 filename: ?string, // defined only when the filename option is passed to Babel, such as when using Babel CLI or Webpack
560 location: { // the original location of the CSS block in the JavaScript file
561 start: {
562 line: number,
563 column: number,
564 },
565 end: {
566 line: number,
567 column: number,
568 }
569 }
570 }
571}
572```
573
574#### Example plugins
575
576The following plugins are proof of concepts/sample:
577
578* [styled-jsx-plugin-sass](https://github.com/giuseppeg/styled-jsx-plugin-sass)
579* [styled-jsx-plugin-postcss](https://github.com/giuseppeg/styled-jsx-plugin-postcss)
580* [styled-jsx-plugin-stylelint](https://github.com/giuseppeg/styled-jsx-plugin-stylelint)
581* [styled-jsx-plugin-less](https://github.com/erasmo-marin/styled-jsx-plugin-less)
582* [styled-jsx-plugin-stylus](https://github.com/omardelarosa/styled-jsx-plugin-stylus)
583
584## FAQ
585
586### Warning: unknown `jsx` prop on &lt;style&gt; tag
587
588If you get this warning it means that your styles were not compiled by styled-jsx.
589
590Please take a look at your setup and make sure that everything is correct and that the styled-jsx transformation is ran by Babel.
591
592### Can I return an array of components when using React 16?
593
594No, this feature is not supported. However we support React Fragments, which are available in React `16.2.0` and above.
595
596```jsx
597const StyledImage = ({ src, alt = '' }) => (
598 <React.Fragment>
599 <img src={src} alt={alt} />
600 <style jsx>{`img { max-width: 100% }`}</style>
601 </React.Fragment>
602)
603```
604
605### Styling third parties / child components from the parent
606
607When the component accepts a `className` (or ad-hoc) prop that you can use to style those components then you can generate scoped styles locally in the parent component and resolve them to get a `className` and the actual scoped styles like so:
608
609```jsx
610import Link from 'react-router-dom' // component to style
611
612// Generate a `scope` fragment and resolve it
613const scoped = resolveScopedStyles(
614 <scope>
615 <style jsx>{'.link { color: green }'}</style>
616 </scope>
617)
618
619// Your component that uses Link
620export default ({ children }) => (
621 <div>
622 {children}
623
624 {/* use the scoped className */}
625 <Link to="/about" className={`link ${scoped.className}`}>
626 About
627 </Link>
628
629 {/* apply the scoped styles */}
630 {scoped.styles}
631 </div>
632)
633```
634
635`resolveScopedStyles` looks like this:
636
637```jsx
638function resolveScopedStyles(scope) {
639 return {
640 className: scope.props.className,
641 styles: scope.props.children
642 }
643}
644```
645
646When the component doesn't accept any `className` or doesn't expose any API to customize the component, then you only option is to use `:global()` styles:
647
648```jsx
649export default () => (
650 <div>
651 <ExternalComponent />
652
653 <style jsx>{`
654 /* "div" will be prefixed, but ".nested-element" won't */
655
656 div > :global(.nested-element) {
657 color: red
658 }
659 `}</style>
660 </div>
661)
662```
663
664Please keep in mind that `:global()` styles will affect the entire subtree, so in many cases you may want to be careful and use the children (direct descendant) selector `>`.
665
666## Syntax Highlighting
667
668When working with template literals a common drawback is missing syntax highlighting. The following editors currently have support for highlighting CSS inside `<style jsx>` elements.
669
670 _If you have a solution for an editor not on the list_ __please [open a PR](https://github.com/zeit/styled-jsx/pull/new/master)__ _and let us now._
671
672### Atom
673
674The [`language-babel`](https://github.com/gandm/language-babel) package for the [Atom editor](https://atom.io/) has an option to [extend the grammar for JavaScript tagged template literals](https://github.com/gandm/language-babel#javascript-tagged-template-literal-grammar-extensions).
675
676After [installing the package](https://github.com/gandm/language-babel#installation) add the code below to the appropriate settings entry. In a few moments you should be blessed with proper CSS syntax highlighting. ([source](https://github.com/gandm/language-babel/issues/324))
677
678```
679"(?<=<style jsx>{)|(?<=<style jsx global>{)":source.css.styled
680```
681
682![babel-language settings entry](https://cloud.githubusercontent.com/assets/2313237/22627258/6c97cb68-ebb7-11e6-82e1-60205f8b31e7.png)
683
684### Webstorm/Idea
685
686The IDE let you inject any language in place with _Inject language or reference_ in an _Intention Actions_ (default _alt+enter_).
687Simply perform the action in the string template and select CSS.
688You get full CSS highlighting and autocompletion and it will last until you close the IDE.
689
690Additionally you can use language injection comments to enable all the IDE language features indefinitely using the language comment style:
691
692```jsx
693import { colors, spacing } from '../theme'
694import { invertColor } from '../theme/utils'
695
696const Button = ({ children }) => (
697 <button>
698 { children }
699
700 { /*language=CSS*/ }
701 <style jsx>{`
702 button {
703 padding: ${ spacing.medium };
704 background: ${ colors.primary };
705 color: ${ invertColor(colors.primary) };
706 }
707 `}</style>
708 </button>
709)
710```
711
712### Emmet
713
714 If you're using Emmet you can add the following snippet to `~/emmet/snippets-styledjsx.json` This will allow you to expand `style-jsx` to a styled-jsx block.
715
716 ```json
717 {
718 "html": {
719 "snippets": {
720 "style-jsx": "<style jsx>{`\n\t$1\n`}</style>"
721 }
722 }
723}
724```
725
726### Syntax Highlighting [Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=blanu.vscode-styled-jsx)
727Launch VS Code Quick Open (⌘+P), paste the following command, and press enter.
728```
729ext install vscode-styled-jsx
730```
731
732### Autocomplete [Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=AndrewRazumovsky.vscode-styled-jsx-languageserver)
733Launch VS Code Quick Open (⌘+P), paste the following command, and press enter.
734```
735ext install vscode-styled-jsx-languageserver
736```
737
738### Vim
739
740Install [vim-styled-jsx](https://github.com/alampros/vim-styled-jsx) with your plugin manager of choice.
741
742## ESLint
743If you're using `eslint-plugin-import`, the `css` import will generate errors, being that it's a "magic" import (not listed in package.json). To avoid these, simply add the following line to your eslint configuration:
744
745```
746"settings": {"import/core-modules": ["styled-jsx/css"] }
747```
748
749## Credits
750
751- **Pedram Emrouznejad** ([rijs](https://github.com/rijs/fullstack)) suggested attribute selectors over my initial class prefixing idea.
752- **Sunil Pai** ([glamor](https://github.com/threepointone/glamor)) inspired the use of `murmurhash2` (minimal and fast hashing) and an efficient style injection logic.
753- **Sultan Tarimo** built [stylis.js](https://github.com/thysultan), a super fast and tiny CSS parser and compiler.
754- **Max Stoiber** ([styled-components](https://github.com/styled-components)) proved the value of retaining the familiarity of CSS syntax and pointed me to the very efficient [stylis](https://github.com/thysultan/stylis.js) compiler (which we forked to very efficiently append attribute selectors to the user's css)
755- **Yehuda Katz** ([ember](https://github.com/emberjs)) convinced me on Twitter to transpile CSS as an alternative to CSS-in-JS.
756- **Evan You** ([vuejs](https://github.com/vuejs)) discussed his Vue.js CSS transformation with me.
757- **Henry Zhu** ([babel](https://github.com/babel)) helpfully pointed me to some important areas of the babel plugin API.
758
759## Authors
760
761- Guillermo Rauch ([@rauchg](https://twitter.com/rauchg)) - [▲ZEIT](https://zeit.co)
762- Naoyuki Kanezawa ([@nkzawa](https://twitter.com/nkzawa)) - [▲ZEIT](https://zeit.co)
763- Giuseppe Gurgone ([@giuseppegurgone](https://twitter.com/giuseppegurgone))