├── .gitignore
├── examples
├── shared
│ ├── utils
│ │ └── text
│ │ │ ├── index.js
│ │ │ ├── text.js
│ │ │ └── text.spec.js
│ ├── assets
│ │ ├── more.svg
│ │ ├── like.svg
│ │ ├── reply.svg
│ │ └── retweet.svg
│ ├── index.html
│ └── data
│ │ └── 755481795206971392.json
├── css
│ ├── src
│ │ ├── components
│ │ │ ├── app
│ │ │ │ ├── index.js
│ │ │ │ └── app.js
│ │ │ ├── footer
│ │ │ │ ├── index.js
│ │ │ │ └── footer.js
│ │ │ ├── header
│ │ │ │ ├── index.js
│ │ │ │ └── header.js
│ │ │ ├── tweet
│ │ │ │ ├── index.js
│ │ │ │ └── tweet.js
│ │ │ └── content
│ │ │ │ ├── index.js
│ │ │ │ └── content.js
│ │ ├── index.js
│ │ └── styles
│ │ │ └── main.css
│ ├── package.json
│ └── webpack.custom.babel.js
├── jss
│ ├── src
│ │ ├── components
│ │ │ ├── app
│ │ │ │ ├── index.js
│ │ │ │ └── app.js
│ │ │ ├── footer
│ │ │ │ ├── index.js
│ │ │ │ └── footer.js
│ │ │ ├── header
│ │ │ │ ├── index.js
│ │ │ │ └── header.js
│ │ │ ├── tweet
│ │ │ │ ├── index.js
│ │ │ │ └── tweet.js
│ │ │ ├── content
│ │ │ │ ├── index.js
│ │ │ │ └── content.js
│ │ │ └── global-styles
│ │ │ │ ├── index.js
│ │ │ │ └── global-styles.js
│ │ └── index.js
│ ├── package.json
│ └── README.md
├── aphrodite
│ ├── src
│ │ ├── components
│ │ │ ├── app
│ │ │ │ ├── index.js
│ │ │ │ └── app.js
│ │ │ ├── tweet
│ │ │ │ ├── index.js
│ │ │ │ └── tweet.js
│ │ │ ├── content
│ │ │ │ ├── index.js
│ │ │ │ └── content.js
│ │ │ ├── footer
│ │ │ │ ├── index.js
│ │ │ │ └── footer.js
│ │ │ └── header
│ │ │ │ ├── index.js
│ │ │ │ └── header.js
│ │ ├── index.js
│ │ └── global.css
│ ├── webpack.custom.babel.js
│ ├── package.json
│ └── README.md
├── radium
│ ├── src
│ │ ├── components
│ │ │ ├── app
│ │ │ │ ├── index.js
│ │ │ │ └── app.js
│ │ │ ├── content
│ │ │ │ ├── index.js
│ │ │ │ └── content.js
│ │ │ ├── footer
│ │ │ │ ├── index.js
│ │ │ │ └── footer.js
│ │ │ ├── header
│ │ │ │ ├── index.js
│ │ │ │ └── header.js
│ │ │ └── tweet
│ │ │ │ ├── index.js
│ │ │ │ └── tweet.js
│ │ └── index.js
│ ├── package.json
│ └── README.md
├── css-modules
│ ├── src
│ │ ├── components
│ │ │ ├── app
│ │ │ │ ├── index.js
│ │ │ │ ├── app.js
│ │ │ │ └── app.css
│ │ │ ├── content
│ │ │ │ ├── index.js
│ │ │ │ ├── content.css
│ │ │ │ └── content.js
│ │ │ ├── footer
│ │ │ │ ├── index.js
│ │ │ │ ├── footer.css
│ │ │ │ └── footer.js
│ │ │ ├── header
│ │ │ │ ├── index.js
│ │ │ │ ├── header.css
│ │ │ │ └── header.js
│ │ │ └── tweet
│ │ │ │ ├── index.js
│ │ │ │ ├── tweet.css
│ │ │ │ └── tweet.js
│ │ ├── styles
│ │ │ └── colors.css
│ │ └── index.js
│ ├── webpack.custom.babel.js
│ ├── package.json
│ └── README.md
├── styled-components
│ ├── src
│ │ ├── components
│ │ │ ├── app
│ │ │ │ ├── index.js
│ │ │ │ └── app.js
│ │ │ ├── footer
│ │ │ │ ├── index.js
│ │ │ │ └── footer.js
│ │ │ ├── header
│ │ │ │ ├── index.js
│ │ │ │ └── header.js
│ │ │ ├── tweet
│ │ │ │ ├── index.js
│ │ │ │ └── tweet.js
│ │ │ └── content
│ │ │ │ ├── index.js
│ │ │ │ └── content.js
│ │ └── index.js
│ ├── package.json
│ └── README.md
├── .babelrc
├── .eslintrc
├── README.md
├── package.json
└── webpack.base.babel.js
├── site
├── favicon.ico
├── src
│ ├── index.css
│ ├── index.js
│ ├── App.css
│ ├── App.js
│ └── logo.svg
├── package.json
├── index.html
└── README.md
├── CONTRIBUTING.md
├── LICENSE.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/examples/shared/utils/text/index.js:
--------------------------------------------------------------------------------
1 | export * from './text';
2 |
--------------------------------------------------------------------------------
/examples/css/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './app';
2 |
--------------------------------------------------------------------------------
/examples/jss/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './app';
2 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './app';
2 |
--------------------------------------------------------------------------------
/examples/css/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './footer';
2 |
--------------------------------------------------------------------------------
/examples/css/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './header';
2 |
--------------------------------------------------------------------------------
/examples/css/src/components/tweet/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './tweet';
2 |
--------------------------------------------------------------------------------
/examples/jss/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './footer';
2 |
--------------------------------------------------------------------------------
/examples/jss/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './header';
2 |
--------------------------------------------------------------------------------
/examples/jss/src/components/tweet/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './tweet';
2 |
--------------------------------------------------------------------------------
/examples/radium/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './app';
2 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/tweet/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './tweet';
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './app';
2 |
--------------------------------------------------------------------------------
/examples/css/src/components/content/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './content';
2 |
--------------------------------------------------------------------------------
/examples/jss/src/components/content/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './content';
2 |
--------------------------------------------------------------------------------
/examples/radium/src/components/content/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './content';
2 |
--------------------------------------------------------------------------------
/examples/radium/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './footer';
2 |
--------------------------------------------------------------------------------
/examples/radium/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './header';
2 |
--------------------------------------------------------------------------------
/examples/radium/src/components/tweet/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './tweet';
2 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/content/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './content';
2 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './footer';
2 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './header';
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/content/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './content';
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './footer';
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './header';
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/tweet/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './tweet';
2 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/app/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './app';
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/tweet/tweet.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0 .6rem;
3 | }
4 |
--------------------------------------------------------------------------------
/examples/jss/src/components/global-styles/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './global-styles';
2 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './footer';
2 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/header/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './header';
2 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/tweet/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './tweet';
2 |
--------------------------------------------------------------------------------
/site/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/styled-components/comparison/HEAD/site/favicon.ico
--------------------------------------------------------------------------------
/examples/styled-components/src/components/content/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './content';
2 |
--------------------------------------------------------------------------------
/site/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"],
3 | "plugins": ["transform-class-properties"]
4 | }
5 |
--------------------------------------------------------------------------------
/examples/shared/assets/more.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/css-modules/src/styles/colors.css:
--------------------------------------------------------------------------------
1 | @value accent: #1da1f2;
2 | @value animation: #e81c4f;
3 | @value border: #e1e8ed;
4 | @value primary: #292f33;
5 | @value secondary: #8899a6;
6 |
--------------------------------------------------------------------------------
/examples/css/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import App from './components/app';
5 |
6 | render( , document.getElementById('example-root'));
7 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import App from './components/app';
5 |
6 | render( , document.getElementById('example-root'));
7 |
--------------------------------------------------------------------------------
/examples/css-modules/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import App from './components/app';
5 |
6 | render( , document.getElementById('example-root'));
7 |
--------------------------------------------------------------------------------
/examples/styled-components/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import App from './components/app';
5 |
6 | render( , document.getElementById('example-root'));
7 |
--------------------------------------------------------------------------------
/examples/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "airbnb",
4 | "rules": {
5 | "react/prop-types": 0
6 | },
7 | "env": {
8 | "browser": true,
9 | "mocha": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/site/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import './index.css';
5 |
6 | ReactDOM.render(
7 | ,
8 | document.getElementById('root')
9 | );
10 |
--------------------------------------------------------------------------------
/examples/jss/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import GlobalStyles from './components/global-styles';
5 | import App from './components/app';
6 |
7 | render( , document.getElementById('example-root'));
8 |
--------------------------------------------------------------------------------
/examples/shared/assets/like.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/radium/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { StyleRoot } from 'radium';
4 |
5 | import App from './components/app';
6 |
7 | render(
8 |
9 |
10 | ,
11 | document.getElementById('example-root')
12 | );
13 |
--------------------------------------------------------------------------------
/examples/css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "css",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "../node_modules/.bin/webpack-dev-server --config webpack.custom.babel.js"
8 | },
9 | "author": "Max Stoiber",
10 | "license": "MIT"
11 | }
12 |
--------------------------------------------------------------------------------
/examples/css/webpack.custom.babel.js:
--------------------------------------------------------------------------------
1 | import { extendBaseConfig } from '../webpack.base.babel';
2 |
3 | export default extendBaseConfig({
4 | module: {
5 | loaders: [
6 | {
7 | test: /\.css$/,
8 | exclude: /node_modules/,
9 | loader: 'style!css',
10 | },
11 | ],
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/examples/aphrodite/webpack.custom.babel.js:
--------------------------------------------------------------------------------
1 | import { extendBaseConfig } from '../webpack.base.babel';
2 |
3 | export default extendBaseConfig({
4 | module: {
5 | loaders: [
6 | {
7 | test: /\.css$/,
8 | exclude: /node_modules/,
9 | loader: 'style!css',
10 | },
11 | ],
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/examples/shared/assets/reply.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/css/src/components/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Tweet from '../tweet';
4 | import data from '../../../../shared/data/755481795206971392.json';
5 |
6 | import 'normalize.css';
7 | import '../../styles/main.css';
8 |
9 | const App = () => (
10 |
11 |
12 |
13 | );
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Tweet from '../tweet';
4 | import data from '../../../../shared/data/755481795206971392.json';
5 |
6 | import 'normalize.css';
7 | import styles from './app.css';
8 |
9 | const App = () => (
10 |
11 |
12 |
13 | );
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "site",
3 | "version": "0.0.1",
4 | "private": true,
5 | "devDependencies": {
6 | "react-scripts": "0.1.0"
7 | },
8 | "dependencies": {
9 | "react": "^15.2.1",
10 | "react-dom": "^15.2.1"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "eject": "react-scripts eject"
16 | }
17 | }
--------------------------------------------------------------------------------
/examples/styled-components/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "styled-components-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "../node_modules/.bin/webpack-dev-server --config ../webpack.base.babel.js --content-base build"
8 | },
9 | "author": "Max Stoiber",
10 | "license": "MIT",
11 | "dependencies": {
12 | "styled-components": "^0.2.5"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/site/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | @keyframes spin {
22 | from { transform: rotate(0deg); }
23 | to { transform: rotate(360deg); }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/css-modules/webpack.custom.babel.js:
--------------------------------------------------------------------------------
1 | import autoprefixer from 'autoprefixer';
2 | import values from 'postcss-modules-values';
3 | import { extendBaseConfig } from '../webpack.base.babel';
4 |
5 | export default extendBaseConfig({
6 | module: {
7 | loaders: [
8 | {
9 | test: /\.css$/,
10 | exclude: /node_modules/,
11 | loader: 'style!css?modules!postcss',
12 | },
13 | ],
14 | },
15 | postcss: () => [autoprefixer, values],
16 | });
17 |
--------------------------------------------------------------------------------
/examples/jss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jss-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "../node_modules/.bin/webpack-dev-server --config ../webpack.base.babel.js --content-base build"
8 | },
9 | "author": "Max Stoiber",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "css-loader": "0.25.0",
13 | "react-jss": "4.0.3",
14 | "style-loader": "0.13.1"
15 | },
16 | "dependencies": {}
17 | }
18 |
--------------------------------------------------------------------------------
/examples/radium/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "radium-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "../node_modules/.bin/webpack-dev-server --config ../webpack.base.babel.js --content-base build"
8 | },
9 | "author": "Max Stoiber",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "style-loader": "0.13.1",
13 | "css-loader": "0.25.0"
14 | },
15 | "dependencies": {
16 | "radium": "0.18.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/aphrodite/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aphrodite-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "../node_modules/.bin/webpack-dev-server --config webpack.custom.babel.js --content-base build"
8 | },
9 | "author": "Max Stoiber",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "style-loader": "0.13.1",
13 | "css-loader": "0.25.0"
14 | },
15 | "dependencies": {
16 | "aphrodite": "0.5.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/global.css:
--------------------------------------------------------------------------------
1 | html {
2 | color: #292f33;
3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
4 | font-size: 14px;
5 | line-height: 1.3125;
6 | }
7 |
8 | a {
9 | text-decoration: none;
10 | color: #1da1f2;
11 | }
12 |
13 | svg {
14 | fill: currentColor;
15 | height: 1.25em;
16 | }
17 |
18 | @media screen and (min-width: 360px) {
19 | html {
20 | font-size: 15px;
21 | }
22 | }
23 |
24 | @media screen and (min-width: 600px) {
25 | html {
26 | font-size: 16px;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/css/src/components/content/content.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | const Content = ({ text, media }) => (
4 |
10 | );
11 |
12 | Content.propTypes = {
13 | media: PropTypes.object,
14 | text: PropTypes.string,
15 | };
16 |
17 | export default Content;
18 |
--------------------------------------------------------------------------------
/examples/shared/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Max Stoiber on Twitter: "👏 Love love love this article by @chantastic. CSS-in-JS isn’t a campaign against CSS! https://t.co/P3QdkX88rs https://t.co/vV3dJ4fens"
6 |
7 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/site/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import logo from './logo.svg';
3 | import './App.css';
4 |
5 | class App extends Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
Welcome to React
12 |
13 |
14 | To get started, edit src/App.js and save to reload.
15 |
16 |
17 | );
18 | }
19 | }
20 |
21 | export default App;
22 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/content/content.css:
--------------------------------------------------------------------------------
1 | @value accent, border from '../../styles/colors.css';
2 |
3 | .text {
4 | font-size: 1.25rem;
5 | font-weight: 300;
6 | line-height: 1.5em;
7 | margin: 0;
8 | padding: .65625rem 0 .98438rem;
9 | }
10 |
11 | .text a {
12 | color: accent;
13 | }
14 |
15 | .text a:hover {
16 | text-decoration: underline;
17 | }
18 |
19 | .media {
20 | border-radius: .35rem;
21 | border: 1px solid border;
22 | display: block;
23 | margin: .65625rem 0 1.3125rem;
24 | }
25 |
26 | .image {
27 | display: block;
28 | max-width: 100%;
29 | }
30 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/content/content.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | import styles from './content.css';
4 |
5 | const Content = ({ text, media }) => (
6 |
12 | );
13 |
14 | Content.propTypes = {
15 | media: PropTypes.object,
16 | text: PropTypes.string,
17 | };
18 |
19 | export default Content;
20 |
--------------------------------------------------------------------------------
/examples/shared/assets/retweet.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | ## Examples
2 |
3 | ### Running an example locally
4 |
5 | - Run `npm install` in this folder (only have to ever do that once)
6 | - Run `npm install` in the folder of the example you want to run
7 | - Run `npm start` in the example folder
8 |
9 | ### Quick links
10 |
11 | Here are quick links to all the existing examples:
12 |
13 | - [Vanilla CSS](./css)
14 | - [Aphrodite](./aphrodite)
15 | - [CSS Modules](./css-modules)
16 | - [JSS](./jss)
17 | - [Radium](./radium)
18 | - [`styled-components`](./styled-components)
19 |
20 | The shared code is inside the `shared/` folder – feel free to check it out too, though it's not very interesting.
21 |
--------------------------------------------------------------------------------
/examples/css-modules/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "css-modules",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "start": "../node_modules/.bin/webpack-dev-server --config webpack.custom.babel.js --content-base build"
8 | },
9 | "author": "Michele Bertoli",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "autoprefixer": "6.3.7",
13 | "css-loader": "webpack/css-loader#9d2941b2aa8fe7d49338cab459ff678d16cdc5b6",
14 | "postcss-loader": "0.9.1",
15 | "postcss-modules-values": "^1.1.3",
16 | "style-loader": "0.13.1"
17 | },
18 | "dependencies": {
19 | "normalize.css": "4.2.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/jss/src/components/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import injectSheet from 'react-jss';
3 |
4 | import Tweet from '../tweet';
5 | import data from '../../../../shared/data/755481795206971392.json';
6 |
7 | const styles = {
8 | container: {
9 | margin: '0 auto',
10 | width: '100%',
11 | '@media screen and (min-width: 360px)': {
12 | maxWidth: '400px',
13 | },
14 | '@media screen and (min-width: 600px)': {
15 | maxWidth: '600px',
16 | },
17 | },
18 | };
19 |
20 | const App = ({ sheet: { classes } }) => (
21 |
22 |
23 |
24 | );
25 |
26 | export default injectSheet(styles)(App);
27 |
--------------------------------------------------------------------------------
/site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React App
7 |
8 |
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for wanting to contribute! We're currently trying to figure most things out before starting the real work, so follow the Issues and let us know if you want a framework to be included!
4 |
5 | ## Publishing the site
6 |
7 | The site is in the `site` folder. It is deployed via Github Pages, and new changes can be deployed with PRs.
8 |
9 | ### Deployment
10 |
11 | 1. Run `npm run build` in the `site` directory
12 | 2. `git checkout -B gh-pages`
13 | 2. `git add -f build`
14 | 3. `git commit -m "Rebuild website"`
15 | 4. `git push origin :gh-pages`
16 | 5. Go back to the root directory and run `git subtree push --prefix site/build origin gh-pages`
17 | 6. Run `git checkout -`
18 |
19 | Done!
20 |
--------------------------------------------------------------------------------
/examples/shared/utils/text/text.js:
--------------------------------------------------------------------------------
1 | export const transform = data => {
2 | let text = data.text;
3 |
4 | data.entities.user_mentions.forEach(userMention => {
5 | text = text.replace(
6 | `@${userMention.screen_name}`,
7 | `@${userMention.screen_name} `
8 | );
9 | });
10 |
11 | data.entities.urls.forEach(url => {
12 | text = text.replace(
13 | url.url,
14 | `${url.display_url} `
15 | );
16 | });
17 |
18 | data.entities.media.forEach(media => {
19 | text = text.replace(
20 | media.url,
21 | ''
22 | );
23 | });
24 |
25 | return text.trim();
26 | };
27 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, css } from 'aphrodite';
3 |
4 | import Tweet from '../tweet';
5 | import data from '../../../../shared/data/755481795206971392.json';
6 |
7 | import 'normalize.css';
8 | import '../../global.css';
9 |
10 | const styles = StyleSheet.create({
11 | container: {
12 | margin: '0 auto',
13 | width: '100%',
14 | '@media screen and (min-width: 360px)': {
15 | maxWidth: '400px',
16 | },
17 | '@media screen and (min-width: 600px)': {
18 | maxWidth: '600px',
19 | },
20 | },
21 | });
22 |
23 | const App = () => (
24 |
25 |
26 |
27 | );
28 |
29 | export default App;
30 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/app/app.css:
--------------------------------------------------------------------------------
1 | @value primary from '../../styles/colors.css';
2 |
3 | html {
4 | color: primary;
5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6 | font-size: 14px;
7 | line-height: 1.3125;
8 | }
9 |
10 | a {
11 | text-decoration: none;
12 | }
13 |
14 | svg {
15 | fill: currentColor;
16 | height: 1.25em;
17 | }
18 |
19 | .container {
20 | margin: 0 auto;
21 | width: 100%;
22 | }
23 |
24 | @media screen and (min-width: 360px) {
25 | html {
26 | font-size: 15px;
27 | }
28 |
29 | .container {
30 | max-width: 400px;
31 | }
32 | }
33 |
34 | @media screen and (min-width: 600px) {
35 | html {
36 | font-size: 16px;
37 | }
38 |
39 | .container {
40 | max-width: 600px;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/header/header.css:
--------------------------------------------------------------------------------
1 | @value primary, secondary from '../../styles/colors.css';
2 |
3 | .header {
4 | display: flex;
5 | padding: 1rem 0 .65625rem;
6 | }
7 |
8 | .profile {
9 | flex: 1 0 0;
10 | margin: 0 .3rem;
11 | }
12 |
13 | .image {
14 | border-radius: .35rem;
15 | display: block;
16 | width: 100%;
17 | }
18 |
19 | .user {
20 | flex: 7 0 0;
21 | margin: 0 .3rem;
22 | }
23 |
24 | .url {
25 | display: inline-block;
26 | margin-top: -.15rem;
27 | }
28 |
29 | .url:hover .name {
30 | text-decoration: underline;
31 | }
32 |
33 | .name {
34 | color: primary;
35 | font-weight: 700;
36 | }
37 |
38 | .screenName {
39 | color: secondary;
40 | }
41 |
42 | .screenName:before {
43 | content: '\a';
44 | white-space: pre;
45 | }
46 |
--------------------------------------------------------------------------------
/examples/jss/src/components/global-styles/global-styles.js:
--------------------------------------------------------------------------------
1 | import injectSheet from 'react-jss';
2 | import 'normalize.css';
3 |
4 | // Global styles
5 | const styles = {
6 | html: {
7 | color: '#292f33',
8 | fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
9 | fontSize: 14,
10 | lineHeight: '1.3125',
11 | },
12 | a: {
13 | textDecoration: 'none',
14 | color: '#1da1f2',
15 | },
16 | svg: {
17 | fill: 'currentColor',
18 | height: '1.25em',
19 | },
20 | '@media screen and (min-width: 360px)': {
21 | html: {
22 | fontSize: 15,
23 | },
24 | },
25 | '@media screen and (min-width: 600px)': {
26 | html: {
27 | fontSize: 16,
28 | },
29 | },
30 | };
31 |
32 | export default injectSheet(styles, { named: false })();
33 |
--------------------------------------------------------------------------------
/examples/css/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | const Header = ({ name, profileImageUrl, screenName, url }) => (
4 |
17 | );
18 |
19 | Header.propTypes = {
20 | name: PropTypes.string,
21 | profileImageUrl: PropTypes.string,
22 | screenName: PropTypes.string,
23 | url: PropTypes.string,
24 | };
25 |
26 | export default Header;
27 |
--------------------------------------------------------------------------------
/examples/css/src/components/tweet/tweet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | import Header from '../header';
4 | import Content from '../content';
5 | import Footer from '../footer';
6 | import { transform } from '../../../../shared/utils/text';
7 |
8 | const Tweet = ({ data }) => (
9 |
10 |
16 |
20 |
25 |
26 | );
27 |
28 | Tweet.propTypes = {
29 | data: PropTypes.object,
30 | };
31 |
32 | export default Tweet;
33 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | import styles from './header.css';
4 |
5 | const Header = ({ name, profileImageUrl, screenName, url }) => (
6 |
19 | );
20 |
21 | Header.propTypes = {
22 | name: PropTypes.string,
23 | profileImageUrl: PropTypes.string,
24 | screenName: PropTypes.string,
25 | url: PropTypes.string,
26 | };
27 |
28 | export default Header;
29 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/tweet/tweet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | import Header from '../header';
4 | import Content from '../content';
5 | import Footer from '../footer';
6 | import { transform } from '../../../../shared/utils/text';
7 |
8 | import styles from './tweet.css';
9 |
10 | const Tweet = ({ data }) => (
11 |
12 |
18 |
22 |
27 |
28 | );
29 |
30 | Tweet.propTypes = {
31 | data: PropTypes.object,
32 | };
33 |
34 | export default Tweet;
35 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/tweet/tweet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import styled from 'styled-components';
3 |
4 | import Header from '../header';
5 | import Content from '../content';
6 | import Footer from '../footer';
7 | import { transform } from '../../../../shared/utils/text';
8 |
9 | const Wrapper = styled.div`
10 | padding: 0 .6rem;
11 | `;
12 |
13 | const Tweet = ({ data }) => (
14 |
15 |
21 |
25 |
30 |
31 | );
32 |
33 | Tweet.propTypes = {
34 | data: PropTypes.object,
35 | };
36 |
37 | export default Tweet;
38 |
--------------------------------------------------------------------------------
/examples/shared/data/755481795206971392.json:
--------------------------------------------------------------------------------
1 | {
2 | "created_at": "Tue Jul 19 19:17:50 +0000 2016",
3 | "text": "\ud83d\udc4f Love love love this article by @chantastic. CSS-in-JS isn\u2019t a campaign against CSS! https:\/\/t.co\/P3QdkX88rs https:\/\/t.co\/vV3dJ4fens",
4 | "entities": {
5 | "user_mentions": [{
6 | "screen_name": "chantastic"
7 | }],
8 | "urls": [{
9 | "url": "https:\/\/t.co\/P3QdkX88rs",
10 | "display_url": "medium.com\/learnreact\/sca\u2026"
11 | }],
12 | "media": [{
13 | "media_url_https": "https:\/\/pbs.twimg.com\/media\/CnwCr-nW8AAcQeZ.jpg",
14 | "url": "https:\/\/t.co\/vV3dJ4fens",
15 | "expanded_url": "http:\/\/twitter.com\/mxstbr\/status\/755481795206971392\/photo\/1"
16 | }]
17 | },
18 | "user": {
19 | "name": "Max Stoiber",
20 | "screen_name": "mxstbr",
21 | "url": "https:\/\/twitter.com\/mxstbr",
22 | "profile_image_url_https": "https:\/\/mxstbr.com\/headshot.jpeg"
23 | },
24 | "retweet_count": 32,
25 | "favorite_count": 79
26 | }
27 |
--------------------------------------------------------------------------------
/examples/radium/src/components/tweet/tweet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import radium from 'radium';
3 |
4 | import Header from '../header';
5 | import Content from '../content';
6 | import Footer from '../footer';
7 | import { transform } from '../../../../shared/utils/text';
8 |
9 | const styles = {
10 | container: {
11 | padding: '0 .6rem',
12 | },
13 | };
14 |
15 | const Tweet = ({ data }) => (
16 |
17 |
23 |
27 |
32 |
33 | );
34 |
35 | Tweet.propTypes = {
36 | data: PropTypes.object,
37 | };
38 |
39 | export default radium(Tweet);
40 |
--------------------------------------------------------------------------------
/examples/radium/src/components/content/content.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import radium from 'radium';
3 |
4 | const styles = {
5 | text: {
6 | fontSize: '1.25rem',
7 | fontWeight: '300',
8 | lineHeight: '1.5em',
9 | margin: '0',
10 | padding: '.65625rem 0 .98438rem',
11 | },
12 | media: {
13 | borderRadius: '.35rem',
14 | border: '1px solid #e1e8ed',
15 | color: '#1da1f2',
16 | display: 'block',
17 | margin: '.65625rem 0 1.3125rem',
18 | },
19 | image: {
20 | display: 'block',
21 | maxWidth: '100%',
22 | },
23 | };
24 |
25 | const Content = ({ text, media }) => (
26 |
32 | );
33 |
34 | Content.propTypes = {
35 | media: PropTypes.object,
36 | text: PropTypes.string,
37 | };
38 |
39 | export default radium(Content);
40 |
--------------------------------------------------------------------------------
/examples/jss/src/components/tweet/tweet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import injectSheet from 'react-jss';
3 |
4 | import Header from '../header';
5 | import Content from '../content';
6 | import Footer from '../footer';
7 | import { transform } from '../../../../shared/utils/text';
8 |
9 | const styles = {
10 | container: {
11 | padding: '0 .6rem',
12 | },
13 | };
14 |
15 | const Tweet = ({ data, sheet }) => (
16 |
17 |
23 |
27 |
32 |
33 | );
34 |
35 | Tweet.propTypes = {
36 | data: PropTypes.object,
37 | };
38 |
39 | export default injectSheet(styles)(Tweet);
40 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/tweet/tweet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { StyleSheet, css } from 'aphrodite';
3 |
4 | import Header from '../header';
5 | import Content from '../content';
6 | import Footer from '../footer';
7 | import { transform } from '../../../../shared/utils/text';
8 |
9 | const styles = StyleSheet.create({
10 | container: {
11 | padding: '0 .6rem',
12 | },
13 | });
14 |
15 | const Tweet = ({ data }) => (
16 |
17 |
23 |
27 |
32 |
33 | );
34 |
35 | Tweet.propTypes = {
36 | data: PropTypes.object,
37 | };
38 |
39 | export default Tweet;
40 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/content/content.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Text = styled.p`
5 | font-size: 1.25rem;
6 | font-weight: 300;
7 | line-height: 1.5em;
8 | margin: 0;
9 | padding: .65625rem 0 .98438rem;
10 |
11 | > a {
12 | color: #1da1f2;
13 | }
14 |
15 | > a:hover {
16 | text-decoration: underline;
17 | }
18 | `;
19 |
20 | const Media = styled.a`
21 | border-radius: .35rem;
22 | border: 1px solid #e1e8ed;
23 | display: block;
24 | margin: .65625rem 0 1.3125rem;
25 | `;
26 |
27 | const Image = styled.img`
28 | display: block;
29 | max-width: 100%;
30 | `;
31 |
32 | const Content = ({ text, media }) => (
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 |
41 | Content.propTypes = {
42 | media: PropTypes.object,
43 | text: PropTypes.string,
44 | };
45 |
46 | export default Content;
47 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016-present Maximilian Stoiber
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/content/content.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { StyleSheet, css } from 'aphrodite';
3 |
4 | const styles = StyleSheet.create({
5 | text: {
6 | fontSize: '1.25rem',
7 | fontWeight: '300',
8 | lineHeight: '1.5em',
9 | margin: '0',
10 | padding: '.65625rem 0 .98438rem',
11 | },
12 | media: {
13 | borderRadius: '.35rem',
14 | border: '1px solid #e1e8ed',
15 | color: '#1da1f2',
16 | display: 'block',
17 | margin: '.65625rem 0 1.3125rem',
18 | },
19 | image: {
20 | display: 'block',
21 | maxWidth: '100%',
22 | },
23 | });
24 |
25 | const Content = ({ text, media }) => (
26 |
32 | );
33 |
34 | Content.propTypes = {
35 | media: PropTypes.object,
36 | text: PropTypes.string,
37 | };
38 |
39 | export default Content;
40 |
--------------------------------------------------------------------------------
/examples/shared/utils/text/text.spec.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 |
3 | import { transform } from './text';
4 |
5 | describe('text', () => {
6 | describe('transform', () => {
7 | it('transforms text using entities', () => {
8 | const data = {
9 | text: '\ud83d\udc4f Love love love this article by @chantastic. CSS-in-JS isn\u2019t a campaign against CSS! https://t.co/P3QdkX88rs https://t.co/vV3dJ4fens',
10 | entities: {
11 | user_mentions: [{
12 | screen_name: 'chantastic',
13 | }],
14 | urls: [{
15 | url: 'https://t.co/P3QdkX88rs',
16 | display_url: 'medium.com/learnreact/sca\u2026',
17 | }],
18 | media: [{
19 | url: 'https://t.co/vV3dJ4fens',
20 | }],
21 | },
22 | };
23 |
24 | const expected = '\ud83d\udc4f Love love love this article by @chantastic . CSS-in-JS isn\u2019t a campaign against CSS! medium.com/learnreact/sca… ';
25 |
26 | assert.equal(transform(data), expected);
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/examples/jss/src/components/content/content.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import injectSheet from 'react-jss';
3 |
4 | const styles = {
5 | text: {
6 | fontSize: '1.25rem',
7 | fontWeight: 300,
8 | lineHeight: '1.5em',
9 | margin: 0,
10 | padding: '.65625rem 0 .98438rem',
11 | '& a': {
12 | color: '#1da1f2',
13 | },
14 | '& a:hover': {
15 | textDecoration: 'underline',
16 | },
17 | },
18 | media: {
19 | borderRadius: '.35rem',
20 | border: '1px solid #e1e8ed',
21 | color: '#1da1f2',
22 | display: 'block',
23 | margin: '.65625rem 0 1.3125rem',
24 | },
25 | image: {
26 | display: 'block',
27 | maxWidth: '100%',
28 | },
29 | };
30 |
31 | const Content = ({ text, media, sheet: { classes } }) => (
32 |
38 | );
39 |
40 | Content.propTypes = {
41 | media: PropTypes.object,
42 | text: PropTypes.string,
43 | };
44 |
45 | export default injectSheet(styles)(Content);
46 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled, { injectGlobal } from 'styled-components';
3 |
4 | import Tweet from '../tweet';
5 | import data from '../../../../shared/data/755481795206971392.json';
6 |
7 | import 'normalize.css';
8 |
9 | // eslint-disable-next-line no-unused-expressions
10 | injectGlobal`
11 | html {
12 | color: #292f33;
13 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
14 | font-size: 14px;
15 | line-height: 1.3125;
16 | }
17 |
18 | a {
19 | text-decoration: none;
20 | }
21 |
22 | svg {
23 | fill: currentColor;
24 | height: 1.25em;
25 | }
26 |
27 | @media screen and (min-width: 360px) {
28 | html {
29 | font-size: 15px;
30 | }
31 | }
32 |
33 | @media screen and (min-width: 600px) {
34 | html {
35 | font-size: 16px;
36 | }
37 | }
38 | `;
39 |
40 | const Container = styled.div`
41 | margin: 0 auto;
42 | width: 100%;
43 |
44 | @media screen and (min-width: 360px) {
45 | max-width: 400px;
46 | }
47 |
48 | @media screen and (min-width: 600px) {
49 | max-width: 600px;
50 | }
51 | `;
52 |
53 | const App = () => (
54 |
55 |
56 |
57 | );
58 |
59 | export default App;
60 |
--------------------------------------------------------------------------------
/examples/styled-components/README.md:
--------------------------------------------------------------------------------
1 | # `styled-components`
2 |
3 | *[Repo](https://github.com/styled-components/styled-components) – [Docs](https://github.com/styled-components/styled-components)*
4 |
5 | ```JSX
6 | // app.js
7 | import styled from 'styled-components';
8 |
9 | const Container = styled.div`
10 | margin: 0 auto;
11 | width: 100%;
12 |
13 | @media screen and (min-width: 360px) {
14 | max-width: 400px;
15 | }
16 |
17 | @media screen and (min-width: 600px) {
18 | max-width: 600px;
19 | }
20 | `;
21 |
22 | const App = () => (
23 |
24 |
25 |
26 | );
27 | ```
28 |
29 | ## Checklist
30 |
31 | - ✅ No build requirements
32 | - ✅ Small and lightweight
33 | - ✅ Supports global CSS
34 | - ✅ Supports entirety of CSS
35 | - ✅ Colocated
36 | - ✅ Isolated
37 | - ✅ Doesn’t break inline styles
38 | - ✅ Easy to override
39 | - ✅ Theming
40 | - ✅ Server side rendering
41 | - ❌ No wrapper components
42 | - ✅ ReactNative support
43 |
44 | Legend: ✅ = Yes, ❌ = No, 😕 = Kinda, refer to notes or parentheses
45 |
46 | #### Notes
47 |
48 | - Enforces best practices (small, focussed components; split smart and dumb) by removing the mapping between styles and components
49 | - Let's you write actual CSS in your JavaScript
50 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/footer/footer.css:
--------------------------------------------------------------------------------
1 | @value animation, border, secondary from '../../styles/colors.css';
2 |
3 | .date {
4 | padding-bottom: .98438rem;
5 | color: secondary;
6 | }
7 |
8 | .counters {
9 | border-top: 1px solid border;
10 | padding: .98438rem 0;
11 | text-transform: uppercase;
12 | }
13 |
14 | .value {
15 | font-weight: 700;
16 | }
17 |
18 | .label {
19 | color: secondary;
20 | font-size: .85rem;
21 | }
22 |
23 | .favorite {
24 | display: inline-block;
25 | margin-left: 1.96875rem;
26 | }
27 |
28 | .actions {
29 | align-items: center;
30 | border-bottom: 1px solid border;
31 | border-top: 1px solid border;
32 | color: secondary;
33 | display: flex;
34 | font-size: 1.5rem;
35 | height: 3.28125rem;
36 | width: 100%;
37 | }
38 |
39 | .icon {
40 | display: flex;
41 | flex-grow: 1;
42 | justify-content: center;
43 | text-align: center;
44 | }
45 |
46 | .button {
47 | composes: icon;
48 |
49 | background: none;
50 | border: none;
51 | color: inherit;
52 | cursor: pointer;
53 | font-size: inherit;
54 | outline: none;
55 | }
56 |
57 | .liked {
58 | animation: liked .25s;
59 | color: animation;
60 | }
61 |
62 | @keyframes liked {
63 | 50% {
64 | transform: scale(1.2);
65 | }
66 | 100% {
67 | transform: scale(1);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/examples/radium/src/components/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import radium, { Style } from 'radium';
3 |
4 | import Tweet from '../tweet';
5 | import data from '../../../../shared/data/755481795206971392.json';
6 |
7 | import 'normalize.css';
8 |
9 | const globalStyles = {
10 | html: {
11 | color: '#292f33',
12 | fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
13 | fontSize: 14,
14 | lineHeight: 1.3125,
15 | },
16 | a: {
17 | textDecoration: 'none',
18 | color: '#1da1f2',
19 | },
20 | svg: {
21 | fill: 'currentColor',
22 | height: '1.25em',
23 | },
24 | '@media screen and (min-width: 360px)': {
25 | html: {
26 | fontSize: 15,
27 | },
28 | },
29 | '@media screen and (min-width: 600px)': {
30 | html: {
31 | fontSize: 16,
32 | },
33 | },
34 | };
35 |
36 | const styles = {
37 | container: {
38 | margin: '0 auto',
39 | width: '100%',
40 | '@media screen and (min-width: 360px)': {
41 | maxWidth: '400px',
42 | },
43 | '@media screen and (min-width: 600px)': {
44 | maxWidth: '600px',
45 | },
46 | },
47 | };
48 |
49 | const App = () => (
50 |
51 |
52 |
53 |
54 | );
55 |
56 | export default radium(App);
57 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "examples",
3 | "version": "1.0.0",
4 | "description": "The examples",
5 | "main": "index.js",
6 | "scripts": {
7 | "lint": "eslint \"./{**/src,shared}/**/*.js\" --ignore-path ../.gitignore",
8 | "test": "mocha --compilers js:babel-register \"./shared/**/*.spec.js\""
9 | },
10 | "author": "Max Stoiber (http://mxstbr.com)",
11 | "license": "MIT",
12 | "dependencies": {
13 | "react": "15.2.1",
14 | "react-dom": "15.2.1"
15 | },
16 | "devDependencies": {
17 | "babel-core": "6.11.4",
18 | "babel-eslint": "^6.1.2",
19 | "babel-loader": "6.2.4",
20 | "babel-plugin-transform-class-properties": "^6.11.5",
21 | "babel-preset-es2015": "6.9.0",
22 | "babel-preset-react": "6.11.1",
23 | "babel-register": "6.11.6",
24 | "css-loader": "^0.25.0",
25 | "eslint": "3.1.1",
26 | "eslint-config-airbnb": "9.0.1",
27 | "eslint-plugin-import": "1.12.0",
28 | "eslint-plugin-jsx-a11y": "2.0.1",
29 | "eslint-plugin-react": "5.2.2",
30 | "html-webpack-plugin": "2.22.0",
31 | "json-loader": "0.5.4",
32 | "lodash": "^4.15.0",
33 | "mocha": "3.0.0",
34 | "normalize.css": "^4.2.0",
35 | "style-loader": "^0.13.1",
36 | "svg-react-loader": "0.3.6",
37 | "webpack": "1.13.1",
38 | "webpack-dev-server": "1.14.1"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/css-modules/README.md:
--------------------------------------------------------------------------------
1 | # CSS Modules
2 |
3 | *[Repo](https://github.com/css-modules/css-modules) – [Docs](https://github.com/css-modules/css-modules)*
4 |
5 | ```CSS
6 | /* app.css */
7 | .container {
8 | margin: 0 auto;
9 | width: 100%;
10 | }
11 |
12 | @media screen and (min-width: 360px) {
13 | .container {
14 | max-width: 400px;
15 | }
16 | }
17 | ```
18 |
19 | ```JS
20 | // app.js
21 |
22 | import styles from './app.css';
23 |
24 | const App = () => (
25 |
26 |
27 |
28 | );
29 | ```
30 |
31 | ## Checklist
32 |
33 | - ❌ No build requirements
34 | - ✅ Small and lightweight
35 | - ✅ Supports global CSS
36 | - ✅ Supports entirety of CSS
37 | - 😕 Colocated (_has_ to be in a separate file)
38 | - ✅ Isolated
39 | - ✅ Doesn’t break inline styles
40 | - ✅ Easy to override
41 | - 😕 Theming (it's CSS, so it works, but it's custom for each implementation, i.e. no standard way of doing it)
42 | - ✅ Pre-build
43 | - 😕 Server side rendering (requires running webpack on the server)
44 | - ✅ No wrapper components
45 | - ❌ ReactNative support
46 |
47 | Legend: ✅ = Yes, ❌ = No, 😕 = Kinda, refer to notes or parentheses
48 |
49 | #### Notes
50 |
51 | - Requires webpack and the `css-loader` (or PostCSS, but that's more work)
52 | - Adds composition of class names to CSS, which helps with code reuse
53 |
--------------------------------------------------------------------------------
/examples/jss/README.md:
--------------------------------------------------------------------------------
1 | # JSS
2 |
3 | *[Repo](https://github.com/cssinjs/jss) – [Docs](https://github.com/cssinjs/jss/tree/master/docs)*
4 |
5 | ```JS
6 | // app.js
7 | import injectSheet from 'react-jss';
8 |
9 | const styles = {
10 | container: {
11 | margin: '0 auto',
12 | width: '100%',
13 | '@media screen and (min-width: 360px)': {
14 | maxWidth: '400px',
15 | },
16 | '@media screen and (min-width: 600px)': {
17 | maxWidth: '600px',
18 | },
19 | },
20 | };
21 |
22 | const App = (props) => (
23 |
24 |
25 |
26 | );
27 |
28 | export default injectSheet(styles)(App);
29 | ```
30 |
31 | ## Checklist
32 |
33 | - ✅ No build requirements
34 | - ✅ Small and lightweight
35 | - 😕 Supports global CSS (requires separate setup)
36 | - ✅ Supports entirety of CSS
37 | - ✅ Colocated
38 | - ✅ Isolated
39 | - ✅ Doesn’t break inline styles
40 | - 😕 Easy to override (inline styles, but no standard mechanism)
41 | - ❌ Theming
42 | - ❌ Pre-build
43 | - ✅ Server side rendering
44 | - ❌ No wrapper components
45 | - ❌ ReactNative support
46 |
47 | Legend: ✅ = Yes, ❌ = No, 😕 = Kinda, refer to notes or parentheses
48 |
49 | #### Notes
50 |
51 | - Has plugin system
52 | - Supports all selectors (nesting, children, siblings,…), _requires_ custom `'&selector'` notation though (e.g. `'&:hover'`, `& div`)
53 |
--------------------------------------------------------------------------------
/examples/radium/README.md:
--------------------------------------------------------------------------------
1 | # Radium
2 |
3 | *[Repo](https://github.com/FormidableLabs/radium) – [Docs](https://formidable.com/open-source/radium/)*
4 |
5 | ```JS
6 | // app.js
7 | import radium from 'radium';
8 |
9 | const styles = {
10 | container: {
11 | margin: '0 auto',
12 | width: '100%',
13 | '@media screen and (min-width: 360px)': {
14 | maxWidth: '400px',
15 | },
16 | '@media screen and (min-width: 600px)': {
17 | maxWidth: '600px',
18 | },
19 | },
20 | };
21 |
22 | const App = () => (
23 |
24 |
25 |
26 | );
27 |
28 | export default radium(App);
29 | ```
30 |
31 | ## Checklist
32 |
33 | - ✅ No build requirements
34 | - ✅ Small and lightweight
35 | - ✅ Supports global CSS
36 | - ❌ Supports entirety of CSS
37 | - ✅ Colocated
38 | - ✅ Isolated
39 | - ✅ Doesn’t break inline styles (it _is_ inline styles)
40 | - ✅ Easy to override
41 | - ✅ Theming
42 | - ❌ Pre-build
43 | - ✅ Server side rendering
44 | - ❌ No wrapper components
45 | - ❌ ReactNative support
46 |
47 | Legend: ✅ = Yes, ❌ = No, 😕 = Kinda, refer to notes or parentheses
48 |
49 | #### Notes
50 |
51 | - Doesn't support pseudo selectors (`:checked`, `:last`, `:before`, or `:after`)
52 | - To use keyframes or media queries you have to wrap entire app in `StyleRoot` component
53 | - Has a helper for keyframes, `radium.keyframes`. `animation` shorthand property kinda works, but you _have_ to use `animationName` too and pass in the returned value from `radium.keyframes`. You cannot use `animation` on its own
54 | - Has good support for modifiers
55 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Wrapper = styled.div`
5 | display: flex;
6 | padding: 1rem 0 .65625rem;
7 | `;
8 |
9 | const Profile = styled.div`
10 | flex: 1 0 0;
11 | margin: 0 .3rem;
12 | `;
13 |
14 | const Image = styled.img`
15 | border-radius: .35rem;
16 | display: block;
17 | width: 100%;
18 | `;
19 |
20 | const User = styled.div`
21 | flex: 7 0 0;
22 | margin: 0 .3rem;
23 | `;
24 |
25 | const UserAnchor = styled.a`
26 | display: inline-block;
27 | margin-top: -.15rem;
28 |
29 | &:hover > #name {
30 | text-decoration: underline;
31 | }
32 | `;
33 |
34 | const Name = styled.span`
35 | color: #292f33;
36 | font-weight: 700;
37 | `;
38 |
39 | const ScreenName = styled.span`
40 | color: #8899a6;
41 |
42 | &:before {
43 | content: '\\a';
44 | white-space: pre;
45 | }
46 | `;
47 |
48 | const Header = ({ name, profileImageUrl, screenName, url }) => (
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {name}
58 | @{screenName}
59 |
60 |
61 |
62 | );
63 |
64 | Header.propTypes = {
65 | name: PropTypes.string,
66 | profileImageUrl: PropTypes.string,
67 | screenName: PropTypes.string,
68 | url: PropTypes.string,
69 | };
70 |
71 | export default Header;
72 |
--------------------------------------------------------------------------------
/examples/radium/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import radium from 'radium';
3 |
4 | const styles = {
5 | header: {
6 | display: 'flex',
7 | padding: '1rem 0 .65625rem',
8 | },
9 | profile: {
10 | flex: '1 0 0',
11 | margin: '0 .3rem',
12 | },
13 | image: {
14 | borderRadius: '.35rem',
15 | display: 'block',
16 | width: '100%',
17 | },
18 | user: {
19 | flex: '7 0 0',
20 | margin: '0 .3rem',
21 | },
22 | url: {
23 | display: 'inline-block',
24 | marginTop: '-.15rem',
25 | },
26 | name: {
27 | color: '#292f33',
28 | fontWeight: '700',
29 | ':hover': {
30 | textDecoration: 'underline',
31 | },
32 | },
33 | screenName: {
34 | color: '#8899a6',
35 | },
36 | screenNameBefore: {
37 | display: 'block',
38 | whiteSpace: 'pre',
39 | },
40 | };
41 |
42 | const Header = ({ name, profileImageUrl, screenName, url }) => (
43 |
57 | );
58 |
59 | Header.propTypes = {
60 | name: PropTypes.string,
61 | profileImageUrl: PropTypes.string,
62 | screenName: PropTypes.string,
63 | url: PropTypes.string,
64 | };
65 |
66 | export default radium(Header);
67 |
--------------------------------------------------------------------------------
/examples/aphrodite/README.md:
--------------------------------------------------------------------------------
1 | # Aphrodite
2 |
3 | *[Repo](https://github.com/khan/aphrodite) – [Docs](https://github.com/khan/aphrodite)*
4 |
5 | ```JS
6 | // app.js
7 | import { StyleSheet, css } from 'aphrodite';
8 |
9 | const styles = StyleSheet.create({
10 | container: {
11 | margin: '0 auto',
12 | width: '100%',
13 | '@media screen and (min-width: 360px)': {
14 | maxWidth: '400px',
15 | },
16 | '@media screen and (min-width: 600px)': {
17 | maxWidth: '600px',
18 | },
19 | },
20 | });
21 |
22 | const App = () => (
23 |
24 |
25 |
26 | );
27 | ```
28 |
29 | ## Checklist
30 |
31 | - ✅ No build requirements
32 | - ✅ Small and lightweight
33 | - ❌ Supports global CSS
34 | - ❌ Supports entirety of CSS
35 | - ✅ Colocated
36 | - ✅ Isolated
37 | - 😕 Doesn’t break inline styles (have to use `aphrodite/no-important`)
38 | - 😕 Easy to override (have to use `aphrodite/no-important`)
39 | - ❌ Theming
40 | - ❌ Pre-build
41 | - ✅ Server side rendering
42 | - ✅ No wrapper components
43 | - ❌ ReactNative support
44 |
45 | Legend: ✅ = Yes, ❌ = No, 😕 = Kinda, refer to notes or parentheses
46 |
47 | #### Notes
48 |
49 | - Use `aphrodite/no-important`, it un-breaks inline styles and makes things easier to override
50 | - Doesn't support nesting at all (i.e. no `.element a`)
51 | - Keyframes don't exist, instead you pass an object to `animationName`. The `animation` shorthand property doesn't work (which in turn means no animation chaining), you have to use `animationName` and `animationDuration`, `animationIterationCount`,… separately
52 | - `content` property requires double quotes (e.g. `content: '"Some content"'`)
53 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { StyleSheet, css } from 'aphrodite';
3 |
4 | const styles = StyleSheet.create({
5 | header: {
6 | display: 'flex',
7 | padding: '1rem 0 .65625rem',
8 | },
9 | profile: {
10 | flex: '1 0 0',
11 | margin: '0 .3rem',
12 | },
13 | image: {
14 | borderRadius: '.35rem',
15 | display: 'block',
16 | width: '100%',
17 | },
18 | user: {
19 | flex: '7 0 0',
20 | margin: '0 .3rem',
21 | },
22 | url: {
23 | display: 'inline-block',
24 | marginTop: '-.15rem',
25 | },
26 | name: {
27 | color: '#292f33',
28 | fontWeight: '700',
29 | ':hover': {
30 | textDecoration: 'underline',
31 | },
32 | },
33 | screenName: {
34 | color: '#8899a6',
35 | ':before': {
36 | content: '"\\a"',
37 | whiteSpace: 'pre',
38 | },
39 | },
40 | });
41 |
42 | const Header = ({ name, profileImageUrl, screenName, url }) => (
43 |
56 | );
57 |
58 | Header.propTypes = {
59 | name: PropTypes.string,
60 | profileImageUrl: PropTypes.string,
61 | screenName: PropTypes.string,
62 | url: PropTypes.string,
63 | };
64 |
65 | export default Header;
66 |
--------------------------------------------------------------------------------
/examples/jss/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import injectSheet from 'react-jss';
3 |
4 | const styles = {
5 | header: {
6 | display: 'flex',
7 | padding: '1rem 0 .65625rem',
8 | },
9 | profile: {
10 | flex: '1 0 0',
11 | margin: '0 .3rem',
12 | },
13 | image: {
14 | borderRadius: '.35rem',
15 | display: 'block',
16 | width: '100%',
17 | },
18 | user: {
19 | flex: '7 0 0',
20 | margin: '0 .3rem',
21 | },
22 | url: {
23 | display: 'inline-block',
24 | marginTop: '-.15rem',
25 | },
26 | name: {
27 | color: '#292f33',
28 | fontWeight: 700,
29 | '&:hover': {
30 | textDecoration: 'underline',
31 | },
32 | },
33 | screenName: {
34 | color: '#8899a6',
35 | '&:before': {
36 | content: '"\\a"',
37 | whiteSpace: 'pre',
38 | },
39 | },
40 | };
41 |
42 | const Header = ({ name, profileImageUrl, screenName, url, sheet }) => (
43 |
56 | );
57 |
58 | Header.propTypes = {
59 | name: PropTypes.string,
60 | profileImageUrl: PropTypes.string,
61 | screenName: PropTypes.string,
62 | url: PropTypes.string,
63 | };
64 |
65 | export default injectSheet(styles)(Header);
66 |
--------------------------------------------------------------------------------
/examples/webpack.base.babel.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { mergeWith, isArray } from 'lodash';
3 | import HtmlWebpackPlugin from 'html-webpack-plugin';
4 |
5 | /**
6 | * Concatenate custom webpack config with our base config.
7 | * Custom configs that have arrays will be concatenated with
8 | * the base arrays instead of replacing them.
9 | *
10 | * Usage:
11 | *
12 | * // example/webpack.custom.babel.js
13 | *
14 | * import { extend } from '../webpack.base.babel.js';
15 | *
16 | * export default extend({
17 | * custom: webpack,
18 | * config: {
19 | * here: true,
20 | * },
21 | * });
22 | */
23 |
24 | const config = {
25 | entry: path.resolve(process.cwd(), './src'),
26 | output: {
27 | path: path.resolve(process.cwd(), './build'),
28 | filename: 'bundle.js',
29 | publicPath: '/',
30 | },
31 | module: {
32 | loaders: [
33 | {
34 | test: /\.js$/,
35 | exclude: /node_modules/,
36 | loader: 'babel',
37 | },
38 | {
39 | test: /\.css$/,
40 | include: /node_modules/,
41 | loader: 'style!css',
42 | },
43 | {
44 | test: /\.svg$/,
45 | loader: 'babel!svg-react',
46 | },
47 | {
48 | test: /\.json$/,
49 | loader: 'json',
50 | },
51 | ],
52 | },
53 | plugins: [
54 | new HtmlWebpackPlugin({
55 | template: path.resolve(__dirname, './shared/index.html'),
56 | }),
57 | ],
58 | };
59 |
60 | export const extendBaseConfig = (customConfig) => {
61 | // eslint-disable-next-line consistent-return
62 | mergeWith(config, customConfig, (objValue, srcValue) => {
63 | // Merge array values, e.g. custom plugins or loaders
64 | if (isArray(objValue)) {
65 | return objValue.concat(srcValue);
66 | }
67 | });
68 |
69 | return config;
70 | };
71 |
72 | export default config;
73 |
--------------------------------------------------------------------------------
/examples/css/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 |
3 | import ReplyIcon from '../../../../shared/assets/reply.svg';
4 | import RetweetIcon from '../../../../shared/assets/retweet.svg';
5 | import LikeIcon from '../../../../shared/assets/like.svg';
6 | import MoreIcon from '../../../../shared/assets/more.svg';
7 |
8 | class Footer extends Component {
9 |
10 | constructor(props) {
11 | super(props);
12 |
13 | this.state = {
14 | liked: false,
15 | };
16 | }
17 |
18 | handleClick = () => {
19 | this.setState({
20 | liked: !this.state.liked,
21 | });
22 | }
23 |
24 | render() {
25 | const { createdAt, favoriteCount, retweetCount } = this.props;
26 | const { liked } = this.state;
27 |
28 | return (
29 |
30 |
{createdAt}
31 |
32 |
33 | {retweetCount}
34 | Retweets
35 |
36 |
37 |
38 | {liked ? favoriteCount + 1 : favoriteCount}
39 |
40 | Likes
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | }
62 |
63 | Footer.propTypes = {
64 | createdAt: PropTypes.string,
65 | favoriteCount: PropTypes.number,
66 | retweetCount: PropTypes.number,
67 | };
68 |
69 | export default Footer;
70 |
--------------------------------------------------------------------------------
/examples/css-modules/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 |
3 | import ReplyIcon from '../../../../shared/assets/reply.svg';
4 | import RetweetIcon from '../../../../shared/assets/retweet.svg';
5 | import LikeIcon from '../../../../shared/assets/like.svg';
6 | import MoreIcon from '../../../../shared/assets/more.svg';
7 |
8 | import styles from './footer.css';
9 |
10 | class Footer extends Component {
11 |
12 | constructor(props) {
13 | super(props);
14 |
15 | this.state = {
16 | liked: false,
17 | };
18 | }
19 |
20 | handleClick = () => {
21 | this.setState({
22 | liked: !this.state.liked,
23 | });
24 | }
25 |
26 | render() {
27 | const { createdAt, favoriteCount, retweetCount } = this.props;
28 | const { liked } = this.state;
29 |
30 | return (
31 |
32 |
{createdAt}
33 |
34 |
35 | {retweetCount}
36 | Retweets
37 |
38 |
39 |
40 | {liked ? favoriteCount + 1 : favoriteCount}
41 |
42 | Likes
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 |
63 | }
64 |
65 | Footer.propTypes = {
66 | createdAt: PropTypes.string,
67 | favoriteCount: PropTypes.number,
68 | retweetCount: PropTypes.number,
69 | };
70 |
71 | export default Footer;
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⚠️ WARNING: This repository is completely out of date, it was made in 2016. Do not rely on any of this information! ⚠️
2 |
3 |
4 |
5 |
6 |
7 |
8 | Click here if you really want to see the old, out of date readme
9 |
10 | # Comparison
11 |
12 | A deeper comparison between a selected few libraries for styling react applications and component libraries.
13 |
14 | ## Scope
15 |
16 | Our goal here is to make the same component using various frameworks to compare features and implementations. While the component is small, we built in certain constraints you might have in a real world app (e.g. `dangerouslySetInnerHTML`) to help compare the libraries. We do a deeper dive with a selected number of frameworks to see how they scale and where the boundaries are.
17 |
18 | This repo is set up so all common code is shared – inside the examples themselves, you'll _only see what's different_ to the other methods. That's true even for the build process and dependencies, you'll only see the changes you need to make to the build process and the dependencies you need to add for a certain method, not all of it.
19 |
20 | ## Examples
21 |
22 | You can start an example by running `npm install` in the `examples/` folder (only have to do that once), then running `npm install` inside the folder of the example you want to run and then running `npm start`.
23 |
24 | Here are quick links to all the existing examples: (or just check out the `examples/` folder)
25 |
26 | - [Vanilla CSS](./examples/css)
27 | - [Aphrodite](./examples/aphrodite)
28 | - [CSS Modules](./examples/css-modules)
29 | - [JSS](./examples/jss)
30 | - [Radium](./examples/radium)
31 | - [`styled-components`](./examples/styled-components)
32 |
33 | We have issues open for the frameworks we're planning on implementing; if you think there's a strong case for adding one that's not on our list please open an issue to let us know!
34 |
35 | *For a complete reference with less deep-div of all the available frameworks see [MicheleBertoli/css-in-js](https://github.com/MicheleBertoli/css-in-js)*
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ## License
44 |
45 | Licensed under the MIT license, copyright (c) 2016-present Maximilian Stoiber, Jed Watson and Joss Mackison
46 |
--------------------------------------------------------------------------------
/site/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import styled, { keyframes, css } from 'styled-components';
3 |
4 | import ReplyIcon from '../../../../shared/assets/reply.svg';
5 | import RetweetIcon from '../../../../shared/assets/retweet.svg';
6 | import LikeIcon from '../../../../shared/assets/like.svg';
7 | import MoreIcon from '../../../../shared/assets/more.svg';
8 |
9 | const likedAnimation = keyframes`
10 | 50% {
11 | transform: scale(1.2);
12 | }
13 | 100% {
14 | transform: scale(1);
15 | }
16 | `;
17 |
18 | const CreationDate = styled.div`
19 | padding-bottom: .98438rem;
20 | color: #8899a6;
21 | `;
22 |
23 | const Counters = styled.div`
24 | border-top: 1px solid #e1e8ed;
25 | padding: .98438rem 0;
26 | text-transform: uppercase;
27 | `;
28 |
29 | const Value = styled.span`
30 | font-weight: 700;
31 | `;
32 |
33 | const Label = styled.span`
34 | color: #8899a6;
35 | font-size: .85rem;
36 | `;
37 |
38 | const Favorite = styled.span`
39 | display: inline-block;
40 | margin-left: 1.96875rem;
41 | `;
42 |
43 | const Actions = styled.div`
44 | align-items: center;
45 | border-bottom: 1px solid #e1e8ed;
46 | border-top: 1px solid #e1e8ed;
47 | color: #8899a6;
48 | display: flex;
49 | font-size: 1.5rem;
50 | height: 3.28125rem;
51 | width: 100%;
52 | `;
53 |
54 | const Icon = styled.div`
55 | display: flex;
56 | flex-grow: 1;
57 | justify-content: center;
58 | text-align: center;
59 | `;
60 |
61 | const Button = styled.button`
62 | display: flex;
63 | flex-grow: 1;
64 | justify-content: center;
65 | text-align: center;
66 | background: none;
67 | border: none;
68 | color: inherit;
69 | cursor: pointer;
70 | font-size: inherit;
71 | outline: none;
72 |
73 | > svg {
74 | margin: 0 auto;
75 | }
76 | `;
77 |
78 | const Like = styled(LikeIcon)`
79 | ${(props) => {
80 | if (props.liked) {
81 | return css`
82 | color: #e81c4f;
83 | animation: ${likedAnimation} .25s;
84 | `;
85 | }
86 |
87 | return '';
88 | }}
89 | `;
90 |
91 | class Footer extends Component {
92 |
93 | constructor(props) {
94 | super(props);
95 |
96 | this.state = {
97 | liked: false,
98 | };
99 | }
100 |
101 | handleClick = () => {
102 | this.setState({
103 | liked: !this.state.liked,
104 | });
105 | }
106 |
107 | render() {
108 | const { createdAt, favoriteCount, retweetCount } = this.props;
109 | const { liked } = this.state;
110 |
111 | return (
112 |
113 |
{createdAt}
114 |
115 |
116 | {retweetCount}
117 | Retweets
118 |
119 |
120 |
121 | {liked ? favoriteCount + 1 : favoriteCount}
122 |
123 | Likes
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | );
136 | }
137 |
138 | }
139 |
140 | Footer.propTypes = {
141 | createdAt: PropTypes.string,
142 | favoriteCount: PropTypes.number,
143 | retweetCount: PropTypes.number,
144 | };
145 |
146 | export default Footer;
147 |
--------------------------------------------------------------------------------
/examples/css/src/styles/main.css:
--------------------------------------------------------------------------------
1 | /**
2 | * General
3 | */
4 |
5 | html {
6 | color: #292f33;
7 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
8 | font-size: 14px;
9 | line-height: 1.3125;
10 | }
11 |
12 | a {
13 | text-decoration: none;
14 | }
15 |
16 | svg {
17 | fill: currentColor;
18 | height: 1.25em;
19 | }
20 |
21 | .container {
22 | margin: 0 auto;
23 | width: 100%;
24 | }
25 |
26 | @media screen and (min-width: 360px) {
27 | html {
28 | font-size: 15px;
29 | }
30 |
31 | .container {
32 | max-width: 400px;
33 | }
34 | }
35 |
36 | @media screen and (min-width: 600px) {
37 | html {
38 | font-size: 16px;
39 | }
40 |
41 | .container {
42 | max-width: 600px;
43 | }
44 | }
45 |
46 | /**
47 | * Header
48 | */
49 |
50 | .header {
51 | display: flex;
52 | padding: 1rem 0 .65625rem;
53 | }
54 |
55 | .header__profile {
56 | flex: 1 0 0;
57 | margin: 0 .3rem;
58 | }
59 |
60 | .header__image {
61 | border-radius: .35rem;
62 | display: block;
63 | width: 100%;
64 | }
65 |
66 | .header__user {
67 | flex: 7 0 0;
68 | margin: 0 .3rem;
69 | }
70 |
71 | .header__user-url {
72 | display: inline-block;
73 | margin-top: -.15rem;
74 | }
75 |
76 | .header__user-url:hover .header__user-name {
77 | text-decoration: underline;
78 | }
79 |
80 | .header__user-name {
81 | color: #292f33;
82 | font-weight: 700;
83 | }
84 |
85 | .header__user-screenName {
86 | color: #8899a6;
87 | }
88 |
89 | .header__user-screenName:before {
90 | content: '\a';
91 | white-space: pre;
92 | }
93 |
94 | /**
95 | * Tweet
96 | */
97 |
98 | .tweet {
99 | padding: 0 .6rem;
100 | }
101 |
102 | .tweet__text {
103 | font-size: 1.25rem;
104 | font-weight: 300;
105 | line-height: 1.5em;
106 | margin: 0;
107 | padding: .65625rem 0 .98438rem;
108 | }
109 |
110 | .tweet__text a {
111 | color: #1da1f2;
112 | }
113 |
114 | .tweet__text a:hover {
115 | text-decoration: underline;
116 | }
117 |
118 | .tweet__media {
119 | border-radius: .35rem;
120 | border: 1px solid #e1e8ed;
121 | display: block;
122 | margin: .65625rem 0 1.3125rem;
123 | }
124 |
125 | .tweet__image {
126 | display: block;
127 | max-width: 100%;
128 | }
129 |
130 | /**
131 | * Footer
132 | */
133 |
134 | .footer__date {
135 | padding-bottom: .98438rem;
136 | color: #8899a6;
137 | }
138 |
139 | .footer__counters {
140 | border-top: 1px solid #e1e8ed;
141 | padding: .98438rem 0;
142 | text-transform: uppercase;
143 | }
144 |
145 | .footer__value {
146 | font-weight: 700;
147 | }
148 |
149 | .footer__label {
150 | color: #8899a6;
151 | font-size: .85rem;
152 | }
153 |
154 | .footer__favorite {
155 | display: inline-block;
156 | margin-left: 1.96875rem;
157 | }
158 |
159 | .footer__actions {
160 | align-items: center;
161 | border-bottom: 1px solid #e1e8ed;
162 | border-top: 1px solid #e1e8ed;
163 | color: #8899a6;
164 | display: flex;
165 | font-size: 1.5rem;
166 | height: 3.28125rem;
167 | width: 100%;
168 | }
169 |
170 | .footer__icon {
171 | display: flex;
172 | flex-grow: 1;
173 | justify-content: center;
174 | text-align: center;
175 | }
176 |
177 | .footer__button {
178 | background: none;
179 | border: none;
180 | color: inherit;
181 | cursor: pointer;
182 | font-size: inherit;
183 | outline: none;
184 | }
185 |
186 | .footer__button svg {
187 | margin: 0 auto;
188 | }
189 |
190 | .footer__liked {
191 | animation: liked .25s;
192 | color: #e81c4f;
193 | }
194 |
195 | @keyframes liked {
196 | 50% {
197 | transform: scale(1.2);
198 | }
199 | 100% {
200 | transform: scale(1);
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/examples/radium/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import radium from 'radium';
3 |
4 | import ReplyIcon from '../../../../shared/assets/reply.svg';
5 | import RetweetIcon from '../../../../shared/assets/retweet.svg';
6 | import LikeIcon from '../../../../shared/assets/like.svg';
7 | import MoreIcon from '../../../../shared/assets/more.svg';
8 |
9 | const likedAnimation = radium.keyframes({
10 | '50%': {
11 | transform: 'scale(1.2)',
12 | },
13 | '100%': {
14 | tranform: 'scale(1)',
15 | },
16 | }, 'liked');
17 |
18 | const styles = {
19 | date: {
20 | paddingBottom: '.98438rem',
21 | color: '#8899a6',
22 | },
23 | counters: {
24 | borderTop: '1px solid #e1e8ed',
25 | padding: '.98438rem 0',
26 | textTransform: 'uppercase',
27 | },
28 | value: {
29 | fontWeight: '700',
30 | },
31 | label: {
32 | color: '#8899a6',
33 | fontSize: '.85rem',
34 | },
35 | favorite: {
36 | display: 'inline-block',
37 | marginLeft: '1.96875rem',
38 | },
39 | actions: {
40 | alignItems: 'center',
41 | borderBottom: '1px solid #e1e8ed',
42 | borderTop: '1px solid #e1e8ed',
43 | color: '#8899a6',
44 | display: 'flex',
45 | fontSize: '1.5rem',
46 | height: '3.28125rem',
47 | width: '100%',
48 | },
49 | icon: {
50 | display: 'flex',
51 | flexGrow: '1',
52 | justifyContent: 'center',
53 | textAlign: 'center',
54 | },
55 | button: {
56 | display: 'flex',
57 | flexGrow: '1',
58 | justifyContent: 'center',
59 | textAlign: 'center',
60 | background: 'none',
61 | border: 'none',
62 | color: 'inherit',
63 | cursor: 'pointer',
64 | fontSize: 'inherit',
65 | outline: 'none',
66 | },
67 | liked: {
68 | animation: 'x .25s',
69 | animationName: likedAnimation,
70 | color: '#e81c4f',
71 | },
72 | };
73 |
74 | class Footer extends Component {
75 |
76 | constructor(props) {
77 | super(props);
78 |
79 | this.state = {
80 | liked: false,
81 | };
82 | }
83 |
84 | handleClick = () => {
85 | this.setState({
86 | liked: !this.state.liked,
87 | });
88 | }
89 |
90 | render() {
91 | const { createdAt, favoriteCount, retweetCount } = this.props;
92 | const { liked } = this.state;
93 |
94 | return (
95 |
96 |
{createdAt}
97 |
98 |
99 | {retweetCount}
100 | Retweets
101 |
102 |
103 |
104 | {liked ? favoriteCount + 1 : favoriteCount}
105 |
106 | Likes
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | );
125 | }
126 |
127 | }
128 |
129 | Footer.propTypes = {
130 | createdAt: PropTypes.string,
131 | favoriteCount: PropTypes.number,
132 | retweetCount: PropTypes.number,
133 | };
134 |
135 | export default radium(Footer);
136 |
--------------------------------------------------------------------------------
/examples/aphrodite/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { StyleSheet, css } from 'aphrodite';
3 |
4 | import ReplyIcon from '../../../../shared/assets/reply.svg';
5 | import RetweetIcon from '../../../../shared/assets/retweet.svg';
6 | import LikeIcon from '../../../../shared/assets/like.svg';
7 | import MoreIcon from '../../../../shared/assets/more.svg';
8 |
9 | const likedAnimation = {
10 | '50%': {
11 | transform: 'scale(1.2)',
12 | },
13 | '100%': {
14 | tranform: 'scale(1)',
15 | },
16 | };
17 |
18 | const styles = StyleSheet.create({
19 | date: {
20 | paddingBottom: '.98438rem',
21 | color: '#8899a6',
22 | },
23 | counters: {
24 | borderTop: '1px solid #e1e8ed',
25 | padding: '.98438rem 0',
26 | textTransform: 'uppercase',
27 | },
28 | value: {
29 | fontWeight: '700',
30 | },
31 | label: {
32 | color: '#8899a6',
33 | fontSize: '.85rem',
34 | },
35 | favorite: {
36 | display: 'inline-block',
37 | marginLeft: '1.96875rem',
38 | },
39 | actions: {
40 | alignItems: 'center',
41 | borderBottom: '1px solid #e1e8ed',
42 | borderTop: '1px solid #e1e8ed',
43 | color: '#8899a6',
44 | display: 'flex',
45 | fontSize: '1.5rem',
46 | height: '3.28125rem',
47 | width: '100%',
48 | },
49 | icon: {
50 | display: 'flex',
51 | flexGrow: '1',
52 | justifyContent: 'center',
53 | textAlign: 'center',
54 | },
55 | button: {
56 | display: 'flex',
57 | flexGrow: '1',
58 | justifyContent: 'center',
59 | textAlign: 'center',
60 | background: 'none',
61 | border: 'none',
62 | color: 'inherit',
63 | cursor: 'pointer',
64 | fontSize: 'inherit',
65 | outline: 'none',
66 | },
67 | liked: {
68 | animationName: likedAnimation,
69 | animationDuration: '.25s',
70 | color: '#e81c4f',
71 | },
72 | });
73 |
74 | class Footer extends Component {
75 |
76 | constructor(props) {
77 | super(props);
78 |
79 | this.state = {
80 | liked: false,
81 | };
82 | }
83 |
84 | handleClick = () => {
85 | this.setState({
86 | liked: !this.state.liked,
87 | });
88 | }
89 |
90 | render() {
91 | const { createdAt, favoriteCount, retweetCount } = this.props;
92 | const { liked } = this.state;
93 |
94 | return (
95 |
96 |
{createdAt}
97 |
98 |
99 | {retweetCount}
100 | Retweets
101 |
102 |
103 |
104 | {liked ? favoriteCount + 1 : favoriteCount}
105 |
106 | Likes
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | );
125 | }
126 |
127 | }
128 |
129 | Footer.propTypes = {
130 | createdAt: PropTypes.string,
131 | favoriteCount: PropTypes.number,
132 | retweetCount: PropTypes.number,
133 | };
134 |
135 | export default Footer;
136 |
--------------------------------------------------------------------------------
/examples/jss/src/components/footer/footer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import injectSheet from 'react-jss';
3 |
4 | import ReplyIcon from '../../../../shared/assets/reply.svg';
5 | import RetweetIcon from '../../../../shared/assets/retweet.svg';
6 | import LikeIcon from '../../../../shared/assets/like.svg';
7 | import MoreIcon from '../../../../shared/assets/more.svg';
8 |
9 | const styles = {
10 | '@keyframes liked': {
11 | '50%': {
12 | transform: 'scale(1.2)',
13 | },
14 | '100%': {
15 | tranform: 'scale(1)',
16 | },
17 | },
18 | date: {
19 | paddingBottom: '.98438rem',
20 | color: '#8899a6',
21 | },
22 | counters: {
23 | borderTop: '1px solid #e1e8ed',
24 | padding: '.98438rem 0',
25 | textTransform: 'uppercase',
26 | },
27 | value: {
28 | fontWeight: 700,
29 | },
30 | label: {
31 | color: '#8899a6',
32 | fontSize: '.85rem',
33 | },
34 | favorite: {
35 | display: 'inline-block',
36 | marginLeft: '1.96875rem',
37 | },
38 | actions: {
39 | alignItems: 'center',
40 | borderBottom: '1px solid #e1e8ed',
41 | borderTop: '1px solid #e1e8ed',
42 | color: '#8899a6',
43 | display: 'flex',
44 | fontSize: '1.5rem',
45 | height: '3.28125rem',
46 | width: '100%',
47 | },
48 | icon: {
49 | display: 'flex',
50 | flexGrow: 1,
51 | justifyContent: 'center',
52 | textAlign: 'center',
53 | },
54 | button: {
55 | display: 'flex',
56 | flexGrow: 1,
57 | justifyContent: 'center',
58 | textAlign: 'center',
59 | background: 'none',
60 | border: 'none',
61 | color: 'inherit',
62 | cursor: 'pointer',
63 | fontSize: 'inherit',
64 | outline: 'none',
65 | },
66 | liked: {
67 | animationName: 'liked',
68 | animationDuration: '.25s',
69 | color: '#e81c4f',
70 | },
71 | };
72 |
73 | class Footer extends Component {
74 |
75 | constructor(props) {
76 | super(props);
77 |
78 | this.state = {
79 | liked: false,
80 | };
81 | }
82 |
83 | handleClick = () => {
84 | this.setState({
85 | liked: !this.state.liked,
86 | });
87 | }
88 |
89 | render() {
90 | const { createdAt, favoriteCount, retweetCount, sheet } = this.props;
91 | const { liked } = this.state;
92 |
93 | return (
94 |
95 |
{createdAt}
96 |
97 |
98 | {retweetCount}
99 | Retweets
100 |
101 |
102 |
103 | {liked ? favoriteCount + 1 : favoriteCount}
104 |
105 | Likes
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | );
124 | }
125 |
126 | }
127 |
128 | Footer.propTypes = {
129 | createdAt: PropTypes.string,
130 | favoriteCount: PropTypes.number,
131 | retweetCount: PropTypes.number,
132 | };
133 |
134 | export default injectSheet(styles)(Footer);
135 |
--------------------------------------------------------------------------------
/site/README.md:
--------------------------------------------------------------------------------
1 | Below you will find some information on how to perform common tasks.
2 | You can find the most recent version of this guide [here](https://github.com/facebookincubator/create-react-app/blob/master/template/README.md).
3 |
4 | ## Sending Feedback
5 |
6 | We are always open to [your feedback](https://github.com/facebookincubator/create-react-app/issues).
7 |
8 | ## Folder Structure
9 |
10 | After creation, you project should look like this:
11 |
12 | ```
13 | my-app/
14 | README.md
15 | index.html
16 | favicon.ico
17 | node_modules/
18 | package.json
19 | src/
20 | App.css
21 | App.js
22 | index.css
23 | index.js
24 | logo.svg
25 | ```
26 |
27 | For the project to build, **these files must exist with exact filenames**:
28 |
29 | * `index.html` is the page template;
30 | * `favicon.ico` is the icon you see in the browser tab;
31 | * `src/index.js` is the JavaScript entry point.
32 |
33 | You can delete or rename the other files.
34 |
35 | You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.
36 | You need to **put any JS and CSS files inside `src`**, or Webpack won’t see them.
37 |
38 | You can, however, create more top-level directories.
39 | They will not be included in the production build so you can use them for things like documentation.
40 |
41 | ## Available Scripts
42 |
43 | In the project directory, you can run:
44 |
45 | ### `npm start`
46 |
47 | Runs the app in the development mode.
48 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
49 |
50 | The page will reload if you make edits.
51 | You will also see any lint errors in the console.
52 |
53 | ### `npm run build`
54 |
55 | Builds the app for production to the `build` folder.
56 | It correctly bundles React in production mode and optimizes the build for the best performance.
57 |
58 | The build is minified and the filenames include the hashes.
59 | Your app is ready to be deployed!
60 |
61 | ### `npm run eject`
62 |
63 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
64 |
65 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
66 |
67 | Instead, it will copy all the configuration files and the transient dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
68 |
69 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
70 |
71 | ## How To...
72 |
73 | ### Import a Component
74 |
75 | This project setup supports ES6 modules thanks to Babel.
76 | While you can still use `require()` and `module.exports`, we encourage you to use [`import` and `export`](http://exploringjs.com/es6/ch_modules.html) instead.
77 |
78 | For example:
79 |
80 | ### `Button.js`
81 |
82 | ```js
83 | import React, { Component } from 'react';
84 |
85 | class Button extends Component {
86 | render() {
87 | // ...
88 | }
89 | }
90 |
91 | export default Button; // Don’t forget to use export default!
92 | ```
93 |
94 | ### `DangerButton.js`
95 |
96 | ```js
97 | import React, { Component } from 'react';
98 | import Button from './Button'; // Import a component from another file
99 |
100 | class DangerButton extends Component {
101 | render() {
102 | return ;
103 | }
104 | }
105 |
106 | export default DangerButton;
107 | ```
108 |
109 | Be aware of the [difference between default and named exports](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281). It is a common source of mistakes.
110 |
111 | We suggest that you stick to using default imports and exports when a module only exports a single thing (for example, a component). That’s what you get when you use `export default Button` and `import Button from './Button'`.
112 |
113 | Named exports are useful for utility modules that export several functions. A module may have at most one default export and as many named exports as you like.
114 |
115 | Learn more about ES6 modules:
116 |
117 | * [When to use the curly braces?](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281)
118 | * [Exploring ES6: Modules](http://exploringjs.com/es6/ch_modules.html)
119 | * [Understanding ES6: Modules](https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules)
120 |
121 | ### Add a Stylesheet
122 |
123 | This project setup uses [Webpack](https://webpack.github.io/) for handling all assets. Webpack offers a custom way of “extending” the concept of `import` beyond JavaScript. To express that a JavaScript file depends on a CSS file, you need to **import the CSS from the JavaScript file**:
124 |
125 | #### `Button.css`
126 |
127 | ```css
128 | .Button {
129 | padding: 20px;
130 | }
131 | ```
132 |
133 | #### `Button.js`
134 |
135 | ```js
136 | import React, { Component } from 'react';
137 | import './Button.css'; // Tell Webpack that Button.js uses these styles
138 |
139 | class Button extends Component {
140 | render() {
141 | // You can use them as regular CSS styles
142 | return
;
143 | }
144 | }
145 | ```
146 |
147 | **This is not required for React** but many people find this feature convenient. You can read about the benefits of this approach [here](https://medium.com/seek-ui-engineering/block-element-modifying-your-javascript-components-d7f99fcab52b). However you should be aware that this makes your code less portable to other build tools and environments than Webpack.
148 |
149 | In development, expressing dependencies this way allows your styles to be reloaded on the fly as you edit them. In production, all CSS files will be concatenated into a single minified `.css` file in the build output.
150 |
151 | If you are concerned about using Webpack-specific semantics, you can put all your CSS right into `src/index.css`. It would still be imported from `src/index.js`, but you could always remove that import if you later migrate to a different build tool.
152 |
153 | ### Post-Process CSS
154 |
155 | This project setup minifies your CSS and adds vendor prefixes to it automatically through [Autoprefixer](https://github.com/postcss/autoprefixer) so you don’t need to worry about it.
156 |
157 | For example, this:
158 |
159 | ```css
160 | .App {
161 | display: flex;
162 | flex-direction: row;
163 | align-items: center;
164 | }
165 | ```
166 |
167 | becomes this:
168 |
169 | ```css
170 | .App {
171 | display: -webkit-box;
172 | display: -ms-flexbox;
173 | display: flex;
174 | -webkit-box-orient: horizontal;
175 | -webkit-box-direction: normal;
176 | -ms-flex-direction: row;
177 | flex-direction: row;
178 | -webkit-box-align: center;
179 | -ms-flex-align: center;
180 | align-items: center;
181 | }
182 | ```
183 |
184 | There is currently no support for preprocessors such as Less, or for sharing variables across CSS files.
185 |
186 | ### Add Images and Fonts
187 |
188 | With Webpack, using static assets like images and fonts works similarly to CSS.
189 |
190 | You can **`import` an image right in a JavaScript module**. This tells Webpack to include that image in the bundle. Unlike CSS imports, importing an image or a font gives you a string value. This value is the final image path you can reference in your code.
191 |
192 | Here is an example:
193 |
194 | ```js
195 | import React from 'react';
196 | import logo from './logo.png'; // Tell Webpack this JS file uses this image
197 |
198 | console.log(logo); // /84287d09b8053c6fa598893b8910786a.png
199 |
200 | function Header() {
201 | // Import result is the URL of your image
202 | return ;
203 | }
204 |
205 | export default function Header;
206 | ```
207 |
208 | This works in CSS too:
209 |
210 | ```css
211 | .Logo {
212 | background-image: url(./logo.png);
213 | }
214 | ```
215 |
216 | Webpack finds all relative module references in CSS (they start with `./`) and replaces them with the final paths from the compiled bundle. If you make a typo or accidentally delete an important file, you will see a compilation error, just like when you import a non-existent JavaScript module. The final filenames in the compiled bundle are generated by Webpack from content hashes. If the file content changes in the future, Webpack will give it a different name in production so you don’t need to worry about long-term caching of assets.
217 |
218 | Please be advised that this is also a custom feature of Webpack.
219 |
220 | **It is not required for React** but many people enjoy it (and React Native uses a similar mechanism for images). However it may not be portable to some other environments, such as Node.js and Browserify. If you prefer to reference static assets in a more traditional way outside the module system, please let us know [in this issue](https://github.com/facebookincubator/create-react-app/issues/28), and we will consider support for this.
221 |
222 | ### Adding Flow
223 |
224 | Flow typing is currently [not supported out of the box](https://github.com/facebookincubator/create-react-app/issues/72) with the default `.flowconfig` generated by Flow. If you run it, you might get errors like this:
225 |
226 | ```
227 | node_modules/fbjs/lib/Deferred.js.flow:60
228 | 60: Promise.prototype.done.apply(this._promise, arguments);
229 | ^^^^ property `done`. Property not found in
230 | 495: declare class Promise<+R> {
231 | ^ Promise. See lib: /private/tmp/flow/flowlib_34952d31/core.js:495
232 |
233 | node_modules/fbjs/lib/shallowEqual.js.flow:29
234 | 29: return x !== 0 || 1 / (x: $FlowIssue) === 1 / (y: $FlowIssue);
235 | ^^^^^^^^^^ identifier `$FlowIssue`. Could not resolve name
236 |
237 | src/App.js:3
238 | 3: import logo from './logo.svg';
239 | ^^^^^^^^^^^^ ./logo.svg. Required module not found
240 |
241 | src/App.js:4
242 | 4: import './App.css';
243 | ^^^^^^^^^^^ ./App.css. Required module not found
244 |
245 | src/index.js:5
246 | 5: import './index.css';
247 | ^^^^^^^^^^^^^ ./index.css. Required module not found
248 | ```
249 |
250 | To fix this, change your `.flowconfig` to look like this:
251 |
252 | ```
253 | [libs]
254 | ./node_modules/fbjs/flow/lib
255 |
256 | [options]
257 | esproposal.class_static_fields=enable
258 | esproposal.class_instance_fields=enable
259 |
260 | module.name_mapper='^\(.*\)\.css$' -> 'react-scripts/config/flow/css'
261 | module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> 'react-scripts/config/flow/file'
262 |
263 | suppress_type=$FlowIssue
264 | suppress_type=$FlowFixMe
265 | ```
266 |
267 | Re-run flow, and you sholdn’t get any extra issues.
268 |
269 | If you later `eject`, you’ll need to replace `react-scripts` references with the `` placeholder, for example:
270 |
271 | ```
272 | module.name_mapper='^\(.*\)\.css$' -> '/config/flow/css'
273 | module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> '/config/flow/file'
274 | ```
275 |
276 | We will consider integrating more tightly with Flow in the future so that you don’t have to do this.
277 |
278 | ### Something Missing?
279 |
280 | If you have ideas for more “How To” recipes that should be on this page, [let us know](https://github.com/facebookincubator/create-react-app/issues) or [contribute some!](https://github.com/facebookincubator/create-react-app/edit/master/template/README.md)
281 |
--------------------------------------------------------------------------------