├── .babelrc ├── .eslintignore ├── .gitignore ├── lerna.json ├── packages ├── benchmarks │ ├── src │ │ ├── reactxp.js │ │ ├── glamor.js │ │ ├── emotion.js │ │ ├── aphrodite.js │ │ ├── glamorous.js │ │ ├── styletron.js │ │ ├── emotion-css.js │ │ ├── new-css-in-js.js │ │ ├── jss.js │ │ ├── emotion-obj-style.js │ │ ├── radium.js │ │ ├── styled-components.js │ │ ├── css-modules.js │ │ ├── styled-components-primitives.js │ │ ├── react-native.js │ │ ├── react-native-stylesheet.js │ │ └── components │ │ │ ├── View │ │ │ ├── styles.css │ │ │ ├── css-modules.js │ │ │ ├── emotion.js │ │ │ ├── new-css-in-js.js │ │ │ ├── styled-components.js │ │ │ ├── glamorous.js │ │ │ ├── glamor.js │ │ │ ├── emotion-css.js │ │ │ ├── emotion-obj-style.js │ │ │ ├── radium.js │ │ │ ├── aphrodite.js │ │ │ ├── jss.js │ │ │ ├── styletron.js │ │ │ └── react-native-stylesheet.js │ │ │ ├── Icons │ │ │ ├── styles.js │ │ │ ├── Reply.js │ │ │ ├── Heart.js │ │ │ ├── DirectMessage.js │ │ │ └── Retweet.js │ │ │ ├── Box │ │ │ ├── styles.css │ │ │ ├── css-modules.js │ │ │ ├── glamorous.js │ │ │ ├── styled-components-primitives.js │ │ │ ├── new-css-in-js.js │ │ │ ├── styled-components.js │ │ │ ├── glamor.js │ │ │ ├── emotion-css.js │ │ │ ├── emotion-obj-style.js │ │ │ ├── react-native.js │ │ │ ├── radium.js │ │ │ ├── aphrodite.js │ │ │ ├── react-native-stylesheet.js │ │ │ ├── jss.js │ │ │ ├── emotion.js │ │ │ ├── reactxp.js │ │ │ └── styletron.js │ │ │ ├── TweetText │ │ │ └── index.js │ │ │ ├── theme.js │ │ │ ├── AspectRatio │ │ │ └── index.js │ │ │ ├── NestedTree │ │ │ └── index.js │ │ │ ├── TweetActionsBar │ │ │ └── index.js │ │ │ ├── UserNames │ │ │ └── index.js │ │ │ ├── GridView │ │ │ └── index.js │ │ │ ├── UserAvatar │ │ │ └── index.js │ │ │ ├── TweetAction │ │ │ └── index.js │ │ │ ├── AppText │ │ │ └── index.js │ │ │ ├── TweetTextPart │ │ │ └── index.js │ │ │ └── Tweet │ │ │ └── index.js │ ├── index.html │ ├── tests │ │ ├── renderDeepTree.js │ │ ├── renderWideTree.js │ │ └── renderTweet.js │ ├── createRenderBenchmark.js │ ├── run-headless.js │ ├── package.json │ ├── webpack.config.js │ ├── benchmark.js │ ├── README.md │ └── index.js ├── preact │ └── package.json ├── server │ ├── package.json │ └── src │ │ └── index.js ├── core │ ├── package.json │ ├── src │ │ ├── hash.js │ │ ├── index.js │ │ ├── sheet.js │ │ └── stylis.js │ └── __tests__ │ │ ├── __snapshots__ │ │ └── css.js.snap │ │ └── css.js └── react │ ├── package.json │ ├── src │ └── index.js │ └── __tests__ │ ├── __snapshots__ │ └── index.js.snap │ └── index.js ├── README.md ├── .travis.yml ├── .eslintrc.js ├── LICENSE ├── rollup.config.js ├── package.json └── CODE_OF_CONDUCT.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "flow", "react"] 3 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | node_modules 4 | stylis.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | yarn.lock 4 | dist 5 | coverage 6 | *.log -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "0.0.1-5" 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/reactxp.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/reactxp' 2 | import { View } from 'reactxp' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/glamor.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/glamor' 2 | import View from './components/View/glamor' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/emotion.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/emotion' 2 | import View from './components/View/emotion' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/aphrodite.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/aphrodite' 2 | import View from './components/View/aphrodite' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/glamorous.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/glamorous' 2 | import View from './components/View/glamorous' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/styletron.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/styletron' 2 | import View from './components/View/styletron' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/emotion-css.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/emotion-css' 2 | import View from './components/View/emotion-css' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/new-css-in-js.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/new-css-in-js' 2 | import View from './components/View/new-css-in-js' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/jss.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/jss' 2 | import View from './components/View/jss' 3 | 4 | const api = { 5 | Box, 6 | View 7 | } 8 | 9 | export default api 10 | -------------------------------------------------------------------------------- /packages/benchmarks/src/emotion-obj-style.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/emotion-obj-style' 2 | import View from './components/View/emotion-obj-style' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/radium.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/radium' 2 | import View from './components/View/radium' 3 | 4 | const api = { 5 | Box, 6 | View 7 | } 8 | 9 | export default api 10 | -------------------------------------------------------------------------------- /packages/benchmarks/src/styled-components.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/styled-components' 2 | import View from './components/View/styled-components' 3 | 4 | export default { 5 | Box, 6 | View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/css-modules.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/css-modules' 2 | import View from './components/View/css-modules' 3 | 4 | const api = { 5 | Box, 6 | View 7 | } 8 | 9 | export default api 10 | -------------------------------------------------------------------------------- /packages/benchmarks/src/styled-components-primitives.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/styled-components' 2 | import styled from 'styled-components/primitives' 3 | 4 | export default { 5 | Box, 6 | View: styled.View 7 | } 8 | -------------------------------------------------------------------------------- /packages/benchmarks/src/react-native.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/react-native' 2 | import Tweet from './components/Tweet' 3 | import { View } from 'react-native' 4 | 5 | export default { 6 | Box, 7 | Tweet, 8 | View 9 | } 10 | -------------------------------------------------------------------------------- /packages/benchmarks/src/react-native-stylesheet.js: -------------------------------------------------------------------------------- 1 | import Box from './components/Box/react-native-stylesheet' 2 | import View from './components/View/react-native-stylesheet' 3 | 4 | const api = { 5 | Box, 6 | View 7 | } 8 | 9 | export default api 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # new-css-in-js 2 | 3 | 4 | 5 | ### Todo 6 | 7 | - clean falsy values 8 | - test that specificity is correct for composition and stuff 9 | - auto px 10 | - regular function calls with objects 11 | - automatically run stylis through closure compiler 12 | - composes 13 | -------------------------------------------------------------------------------- /packages/benchmarks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Performance tests 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8" 5 | 6 | before_script: 7 | - yarn run bootstrap 8 | 9 | after_success: 10 | - cat ./coverage/lcov.info | ./node_modules/codecov/bin/codecov 11 | 12 | script: npm run test 13 | 14 | cache: 15 | directories: 16 | - node_modules -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/styles.css: -------------------------------------------------------------------------------- 1 | .initial { 2 | align-items: stretch; 3 | border-width: 0; 4 | border-style: solid; 5 | box-sizing: border-box; 6 | display: flex; 7 | flex-basis: auto; 8 | flex-direction: column; 9 | flex-shrink: 0; 10 | margin: 0; 11 | padding: 0; 12 | position: relative; 13 | min-height: 0; 14 | min-width: 0; 15 | } 16 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Icons/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native' 2 | 3 | const styles = StyleSheet.create({ 4 | icon: { 5 | display: 'inline-block', 6 | fill: 'currentcolor', 7 | height: '1.25em', 8 | maxWidth: '100%', 9 | position: 'relative', 10 | userSelect: 'none', 11 | verticalAlign: 'text-bottom' 12 | } 13 | }) 14 | 15 | export default styles 16 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/css-modules.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import classnames from 'classnames' 3 | import React from 'react' 4 | import styles from './styles.css' 5 | 6 | class View extends React.Component { 7 | render() { 8 | const props = this.props 9 | return ( 10 |
11 | ) 12 | } 13 | } 14 | 15 | export default View 16 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/emotion.js: -------------------------------------------------------------------------------- 1 | import styled from 'emotion/react/macro' 2 | 3 | const View = styled('div')` 4 | align-items: stretch; 5 | border-width: 0; 6 | border-style: solid; 7 | box-sizing: border-box; 8 | display: flex; 9 | flex-basis: auto; 10 | flex-direction: column; 11 | flex-shrink: 0; 12 | margin: 0; 13 | padding: 0; 14 | position: relative; 15 | min-height: 0; 16 | min-width: 0; 17 | ` 18 | 19 | export default View 20 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/new-css-in-js.js: -------------------------------------------------------------------------------- 1 | import styled from 'react-new-css-in-js' 2 | 3 | const View = styled('div')` 4 | align-items: stretch; 5 | border-width: 0; 6 | border-style: solid; 7 | box-sizing: border-box; 8 | display: flex; 9 | flex-basis: auto; 10 | flex-direction: column; 11 | flex-shrink: 0; 12 | margin: 0; 13 | padding: 0; 14 | position: relative; 15 | min-height: 0; 16 | min-width: 0; 17 | ` 18 | 19 | export default View 20 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/styled-components.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const View = styled.div` 4 | align-items: stretch; 5 | border-width: 0; 6 | border-style: solid; 7 | box-sizing: border-box; 8 | display: flex; 9 | flex-basis: auto; 10 | flex-direction: column; 11 | flex-shrink: 0; 12 | margin: 0; 13 | padding: 0; 14 | position: relative; 15 | min-height: 0; 16 | min-width: 0; 17 | ` 18 | 19 | export default View 20 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/glamorous.js: -------------------------------------------------------------------------------- 1 | import glamorous from 'glamorous' 2 | 3 | const View = glamorous.div({ 4 | alignItems: 'stretch', 5 | borderWidth: 0, 6 | borderStyle: 'solid', 7 | boxSizing: 'border-box', 8 | display: 'flex', 9 | flexBasis: 'auto', 10 | flexDirection: 'column', 11 | flexShrink: 0, 12 | margin: 0, 13 | padding: 0, 14 | position: 'relative', 15 | // fix flexbox bugs 16 | minHeight: 0, 17 | minWidth: 0 18 | }) 19 | 20 | export default View 21 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/styles.css: -------------------------------------------------------------------------------- 1 | .outer { 2 | padding: 4px; 3 | } 4 | 5 | .row { 6 | flex-direction: row; 7 | } 8 | 9 | .color0 { 10 | background-color: #222; 11 | } 12 | 13 | .color1 { 14 | background-color: #666; 15 | } 16 | 17 | .color2 { 18 | background-color: #999; 19 | } 20 | 21 | .color3 { 22 | background-color: blue; 23 | } 24 | 25 | .color4 { 26 | background-color: orange; 27 | } 28 | 29 | .color5 { 30 | background-color: red; 31 | } 32 | 33 | .fixed { 34 | width: 20px; 35 | height: 20px; 36 | } 37 | -------------------------------------------------------------------------------- /packages/benchmarks/tests/renderDeepTree.js: -------------------------------------------------------------------------------- 1 | import createRenderBenchmark from '../createRenderBenchmark' 2 | import NestedTree from '../src/components/NestedTree' 3 | import React from 'react' 4 | 5 | const renderDeepTree = (label, components) => 6 | createRenderBenchmark({ 7 | name: `Deep tree [${label}]`, 8 | runs: 20, 9 | getElement() { 10 | return ( 11 | 18 | ) 19 | } 20 | }) 21 | 22 | export default renderDeepTree 23 | -------------------------------------------------------------------------------- /packages/benchmarks/tests/renderWideTree.js: -------------------------------------------------------------------------------- 1 | import createRenderBenchmark from '../createRenderBenchmark' 2 | import NestedTree from '../src/components/NestedTree' 3 | import React from 'react' 4 | 5 | const renderWideTree = (label, components) => 6 | createRenderBenchmark({ 7 | name: `Wide tree [${label}]`, 8 | runs: 20, 9 | getElement() { 10 | return ( 11 | 18 | ) 19 | } 20 | }) 21 | 22 | export default renderWideTree 23 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/css-modules.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import classnames from 'classnames' 3 | import React from 'react' 4 | import View from '../View/css-modules' 5 | import styles from './styles.css' 6 | 7 | const Box = ({ 8 | color, 9 | fixed = false, 10 | layout = 'column', 11 | outer = false, 12 | ...other 13 | }) => ( 14 | 22 | ) 23 | 24 | export default Box 25 | -------------------------------------------------------------------------------- /packages/benchmarks/createRenderBenchmark.js: -------------------------------------------------------------------------------- 1 | import benchmark from './benchmark' 2 | import ReactDOM from 'react-dom' 3 | 4 | const node = document.querySelector('.root') 5 | 6 | const createRenderBenchmark = ({ 7 | description, 8 | getElement, 9 | name, 10 | runs 11 | }) => () => { 12 | const setup = () => {} 13 | const teardown = () => { 14 | ReactDOM.unmountComponentAtNode(node) 15 | } 16 | 17 | return benchmark({ 18 | name, 19 | description, 20 | runs, 21 | setup, 22 | teardown, 23 | task: () => { 24 | ReactDOM.render(getElement(), node) 25 | } 26 | }) 27 | } 28 | 29 | export default createRenderBenchmark 30 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Icons/Reply.js: -------------------------------------------------------------------------------- 1 | import { createDOMElement } from 'react-native' 2 | import React from 'react' 3 | import styles from './styles' 4 | 5 | const IconReply = props => 6 | createDOMElement('svg', { 7 | children: ( 8 | 9 | 10 | 11 | ), 12 | style: [styles.icon, props.style], 13 | viewBox: '0 0 62 72' 14 | }) 15 | 16 | IconReply.metadata = { height: 72, width: 62 } 17 | 18 | export default IconReply 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = {"extends": [ 2 | "standard", 3 | "standard-react", 4 | "prettier", 5 | "prettier/react" 6 | ], 7 | "plugins": [ 8 | "prettier" 9 | ], 10 | "parser": "babel-eslint", 11 | "rules": { 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "singleQuote": true, 16 | "semi": false 17 | } 18 | ], 19 | "react/prop-types": 0, 20 | "react/no-unused-prop-types": 0, 21 | "standard/computed-property-even-spacing": 0, 22 | "no-template-curly-in-string": 0 23 | }, 24 | "overrides": [ 25 | { 26 | "files": [ 27 | "*.test.js", 28 | "**/__tests__/**/*" 29 | ], 30 | "env": { 31 | "jest": true 32 | } 33 | } 34 | ]} -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Icons/Heart.js: -------------------------------------------------------------------------------- 1 | import { createDOMElement } from 'react-native' 2 | import React from 'react' 3 | import styles from './styles' 4 | 5 | const IconHeart = props => 6 | createDOMElement('svg', { 7 | children: ( 8 | 9 | 10 | 11 | ), 12 | style: [styles.icon, props.style], 13 | viewBox: '0 0 54 72' 14 | }) 15 | 16 | IconHeart.metadata = { height: 72, width: 54 } 17 | 18 | export default IconHeart 19 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/glamor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { css } from 'glamor' 3 | import React from 'react' 4 | 5 | class View extends React.Component { 6 | render() { 7 | const { style, ...other } = this.props 8 | return
9 | } 10 | } 11 | 12 | const viewStyle = { 13 | alignItems: 'stretch', 14 | borderWidth: 0, 15 | borderStyle: 'solid', 16 | boxSizing: 'border-box', 17 | display: 'flex', 18 | flexBasis: 'auto', 19 | flexDirection: 'column', 20 | flexShrink: 0, 21 | margin: 0, 22 | padding: 0, 23 | position: 'relative', 24 | // fix flexbox bugs 25 | minHeight: 0, 26 | minWidth: 0 27 | } 28 | 29 | export default View 30 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/emotion-css.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { css } from 'emotion' 3 | import React from 'react' 4 | 5 | class View extends React.Component { 6 | render() { 7 | const { style, ...other } = this.props 8 | return
9 | } 10 | } 11 | 12 | const viewStyle = { 13 | alignItems: 'stretch', 14 | borderWidth: 0, 15 | borderStyle: 'solid', 16 | boxSizing: 'border-box', 17 | display: 'flex', 18 | flexBasis: 'auto', 19 | flexDirection: 'column', 20 | flexShrink: 0, 21 | margin: 0, 22 | padding: 0, 23 | position: 'relative', 24 | // fix flexbox bugs 25 | minHeight: 0, 26 | minWidth: 0 27 | } 28 | 29 | export default View 30 | -------------------------------------------------------------------------------- /packages/preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preact-new-css-in-js", 3 | "version": "0.0.1-5", 4 | "main": "dist/index.cjs.js", 5 | "module": "dist/index.es.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "author": "mitchellhamilton ", 10 | "license": "MIT", 11 | "scripts": { 12 | "build": "npm-run-all clean rollup rollup:umd", 13 | "clean": "rimraf dist", 14 | "rollup": "rollup -c ../../rollup.config.js" 15 | }, 16 | "dependencies": { 17 | "new-css-in-js": "^0.0.1-4" 18 | }, 19 | "devDependencies": { 20 | "npm-run-all": "^4.0.2", 21 | "rimraf": "^2.6.1", 22 | "rollup": "^0.48.2" 23 | }, 24 | "repository": "https://github.com/mitchellhamilton/new-css-in-js/tree/master/packages/preact" 25 | } 26 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/emotion-obj-style.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { objStyle } from 'emotion' 3 | import React from 'react' 4 | 5 | class View extends React.Component { 6 | render() { 7 | const { style, ...other } = this.props 8 | return
9 | } 10 | } 11 | 12 | const viewStyle = { 13 | alignItems: 'stretch', 14 | borderWidth: 0, 15 | borderStyle: 'solid', 16 | boxSizing: 'border-box', 17 | display: 'flex', 18 | flexBasis: 'auto', 19 | flexDirection: 'column', 20 | flexShrink: 0, 21 | margin: 0, 22 | padding: 0, 23 | position: 'relative', 24 | // fix flexbox bugs 25 | minHeight: 0, 26 | minWidth: 0 27 | } 28 | 29 | export default View 30 | -------------------------------------------------------------------------------- /packages/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "new-css-in-js-server", 3 | "version": "0.0.1-5", 4 | "main": "dist/index.cjs.js", 5 | "module": "dist/index.es.js", 6 | "files": [ 7 | "src", 8 | "dist" 9 | ], 10 | "author": "mitchellhamilton ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "npm-run-all clean rollup", 14 | "clean": "rimraf dist", 15 | "rollup": "rollup -c ../../rollup.config.js" 16 | }, 17 | "dependencies": { 18 | "new-css-in-js": "^0.0.1-4" 19 | }, 20 | "devDependencies": { 21 | "npm-run-all": "^4.0.2", 22 | "rimraf": "^2.6.1", 23 | "rollup": "^0.48.2" 24 | }, 25 | "repository": "https://github.com/mitchellhamilton/new-css-in-js/tree/master/packages/server" 26 | } 27 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/radium.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import Radium from 'radium' 3 | import React from 'react' 4 | 5 | class View extends React.Component { 6 | render() { 7 | const { style, ...other } = this.props 8 | return
9 | } 10 | } 11 | 12 | const styles = { 13 | root: { 14 | alignItems: 'stretch', 15 | borderWidth: 0, 16 | borderStyle: 'solid', 17 | boxSizing: 'border-box', 18 | display: 'flex', 19 | flexBasis: 'auto', 20 | flexDirection: 'column', 21 | flexShrink: 0, 22 | margin: 0, 23 | padding: 0, 24 | position: 'relative', 25 | // fix flexbox bugs 26 | minHeight: 0, 27 | minWidth: 0 28 | } 29 | } 30 | 31 | export default Radium(View) 32 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/glamorous.js: -------------------------------------------------------------------------------- 1 | import glamorous from 'glamorous' 2 | import View from '../View/glamorous' 3 | 4 | const getColor = color => { 5 | switch (color) { 6 | case 0: 7 | return '#222' 8 | case 1: 9 | return '#666' 10 | case 2: 11 | return '#999' 12 | case 3: 13 | return 'blue' 14 | case 4: 15 | return 'orange' 16 | case 5: 17 | return 'red' 18 | default: 19 | return 'transparent' 20 | } 21 | } 22 | 23 | const Box = glamorous(View)(props => ({ 24 | flexDirection: props.layout === 'column' ? 'column' : 'row', 25 | padding: props.outer ? 4 : 0, 26 | height: props.fixed ? 20 : 'auto', 27 | width: props.fixed ? 20 : 'auto', 28 | backgroundColor: getColor(props.color) 29 | })) 30 | 31 | export default Box 32 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Icons/DirectMessage.js: -------------------------------------------------------------------------------- 1 | import { createDOMElement } from 'react-native' 2 | import React from 'react' 3 | import styles from './styles' 4 | 5 | const IconDirectMessage = props => 6 | createDOMElement('svg', { 7 | children: ( 8 | 9 | 10 | 11 | 12 | ), 13 | style: [styles.icon, props.style], 14 | viewBox: '0 0 56 72' 15 | }) 16 | 17 | IconDirectMessage.metadata = { height: 72, width: 56 } 18 | 19 | export default IconDirectMessage 20 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "new-css-in-js", 3 | "version": "0.0.1-4", 4 | "main": "dist/index.cjs.js", 5 | "module": "dist/index.es.js", 6 | "files": [ 7 | "src", 8 | "dist" 9 | ], 10 | "author": "mitchellhamilton ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "npm-run-all clean rollup rollup:umd", 14 | "clean": "rimraf dist", 15 | "rollup": "rollup -c ../../rollup.config.js", 16 | "rollup:umd": "UMD=true rollup -c ../../rollup.config.js" 17 | }, 18 | "dependencies": { 19 | "stylis": "^3.2.11" 20 | }, 21 | "devDependencies": { 22 | "npm-run-all": "^4.0.2", 23 | "rimraf": "^2.6.1", 24 | "rollup": "^0.48.2" 25 | }, 26 | "repository": "https://github.com/mitchellhamilton/new-css-in-js/tree/master/packages/core" 27 | } 28 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/aphrodite.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import { css, StyleSheet } from 'aphrodite' 4 | 5 | class View extends React.Component { 6 | render() { 7 | const { style, ...other } = this.props 8 | return
9 | } 10 | } 11 | 12 | const styles = StyleSheet.create({ 13 | root: { 14 | alignItems: 'stretch', 15 | borderWidth: 0, 16 | borderStyle: 'solid', 17 | boxSizing: 'border-box', 18 | display: 'flex', 19 | flexBasis: 'auto', 20 | flexDirection: 'column', 21 | flexShrink: 0, 22 | margin: 0, 23 | padding: 0, 24 | position: 'relative', 25 | // fix flexbox bugs 26 | minHeight: 0, 27 | minWidth: 0 28 | } 29 | }) 30 | 31 | export default View 32 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-new-css-in-js", 3 | "version": "0.0.1-5", 4 | "main": "dist/index.cjs.js", 5 | "module": "dist/index.es.js", 6 | "files": [ 7 | "src", 8 | "dist" 9 | ], 10 | "author": "mitchellhamilton ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "npm-run-all clean rollup rollup:umd", 14 | "clean": "rimraf dist", 15 | "rollup": "rollup -c ../../rollup.config.js", 16 | "rollup:umd": "UMD=true rollup -c ../../rollup.config.js" 17 | }, 18 | "dependencies": { 19 | "new-css-in-js": "^0.0.1-4" 20 | }, 21 | "devDependencies": { 22 | "npm-run-all": "^4.0.2", 23 | "rimraf": "^2.6.1", 24 | "rollup": "^0.48.2" 25 | }, 26 | "repository": "https://github.com/mitchellhamilton/new-css-in-js/tree/master/packages/react" 27 | } 28 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/styled-components-primitives.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components/primitives' 2 | 3 | const getColor = color => { 4 | switch (color) { 5 | case 0: 6 | return '#222' 7 | case 1: 8 | return '#666' 9 | case 2: 10 | return '#999' 11 | case 3: 12 | return 'blue' 13 | case 4: 14 | return 'orange' 15 | case 5: 16 | return 'red' 17 | default: 18 | return 'transparent' 19 | } 20 | } 21 | 22 | const Box = styled.View` 23 | flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')}; 24 | padding: ${props => (props.outer ? '4px' : '0')}; 25 | height: ${props => (props.fixed ? '20px' : 'auto')}; 26 | width: ${props => (props.fixed ? '20px' : 'auto')}; 27 | background-color: ${props => getColor(props.color)}; 28 | ` 29 | 30 | export default Box 31 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Icons/Retweet.js: -------------------------------------------------------------------------------- 1 | import { createDOMElement } from 'react-native' 2 | import React from 'react' 3 | import styles from './styles' 4 | 5 | const IconRetweet = props => 6 | createDOMElement('svg', { 7 | children: ( 8 | 9 | 10 | 11 | ), 12 | style: [styles.icon, props.style], 13 | viewBox: '0 0 74 72' 14 | }) 15 | 16 | IconRetweet.metadata = { height: 72, width: 74 } 17 | 18 | export default IconRetweet 19 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/new-css-in-js.js: -------------------------------------------------------------------------------- 1 | import styled from 'react-new-css-in-js' 2 | import View from '../View/new-css-in-js' 3 | 4 | const getColor = color => { 5 | switch (color) { 6 | case 0: 7 | return '#222' 8 | case 1: 9 | return '#666' 10 | case 2: 11 | return '#999' 12 | case 3: 13 | return 'blue' 14 | case 4: 15 | return 'orange' 16 | case 5: 17 | return 'red' 18 | default: 19 | return 'transparent' 20 | } 21 | } 22 | 23 | const Box = styled(View)` 24 | flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')}; 25 | padding: ${props => (props.outer ? '4px' : '0')}; 26 | height: ${props => (props.fixed ? '20px' : 'auto')}; 27 | width: ${props => (props.fixed ? '20px' : 'auto')}; 28 | background-color: ${props => getColor(props.color)}; 29 | ` 30 | 31 | export default Box 32 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/jss.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import classnames from 'classnames' 3 | import injectSheet from 'react-jss' 4 | import React from 'react' 5 | 6 | class View extends React.Component { 7 | render() { 8 | const { classes, className, ...other } = this.props 9 | return
10 | } 11 | } 12 | 13 | const styles = { 14 | root: { 15 | alignItems: 'stretch', 16 | borderWidth: 0, 17 | borderStyle: 'solid', 18 | boxSizing: 'border-box', 19 | display: 'flex', 20 | flexBasis: 'auto', 21 | flexDirection: 'column', 22 | flexShrink: 0, 23 | margin: 0, 24 | padding: 0, 25 | position: 'relative', 26 | // fix flexbox bugs 27 | minHeight: 0, 28 | minWidth: 0 29 | } 30 | } 31 | 32 | export default injectSheet(styles)(View) 33 | -------------------------------------------------------------------------------- /packages/server/src/index.js: -------------------------------------------------------------------------------- 1 | import { sheet, inserted, registered } from 'new-css-in-js' 2 | 3 | export function extractCritical(html) { 4 | // parse out ids from html 5 | // reconstruct css/rules/cache to pass 6 | const RGX = /css-([a-zA-Z0-9]+)/gm 7 | 8 | let o = { html, ids: [], css: '', rules: [] } 9 | let match 10 | let ids = {} 11 | while ((match = RGX.exec(html)) !== null) { 12 | if (!ids[match[1]]) { 13 | ids[match[1]] = true 14 | } 15 | } 16 | 17 | o.rules = sheet.sheet.cssRules.slice().filter(x => { 18 | RGX.lastIndex = 0 19 | let match = RGX.exec(x.cssText) 20 | const ret = match == null || ids[match[1]] || false 21 | return ret 22 | }) 23 | 24 | o.ids = Object.keys(inserted).filter(id => ids[id] || !registered[id]) 25 | 26 | let css = '' 27 | o.rules.forEach(x => (css += x.cssText)) 28 | o.css = css 29 | 30 | return o 31 | } 32 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/styled-components.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import View from '../View/styled-components' 3 | 4 | const getColor = color => { 5 | switch (color) { 6 | case 0: 7 | return '#222' 8 | case 1: 9 | return '#666' 10 | case 2: 11 | return '#999' 12 | case 3: 13 | return 'blue' 14 | case 4: 15 | return 'orange' 16 | case 5: 17 | return 'red' 18 | default: 19 | return 'transparent' 20 | } 21 | } 22 | 23 | const Box = styled(View)` 24 | flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')}; 25 | padding: ${props => (props.outer ? '4px' : '0')}; 26 | height: ${props => (props.fixed ? '20px' : 'auto')}; 27 | width: ${props => (props.fixed ? '20px' : 'auto')}; 28 | background-color: ${props => getColor(props.color)}; 29 | ` 30 | 31 | export default Box 32 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/TweetText/index.js: -------------------------------------------------------------------------------- 1 | import AppText from '../AppText' 2 | import React from 'react' 3 | import TweetTextPart from '../TweetTextPart' 4 | import { array, number, string } from 'prop-types' 5 | 6 | class TweetText extends React.Component { 7 | static displayName = 'TweetText' 8 | 9 | static propTypes = { 10 | displayMode: TweetTextPart.propTypes.displayMode, 11 | lang: string, 12 | numberOfLines: number, 13 | textParts: array.isRequired 14 | } 15 | 16 | render() { 17 | const { displayMode, lang, numberOfLines, textParts, ...other } = this.props 18 | 19 | return ( 20 | 21 | {textParts.map((part, i) => ( 22 | 23 | ))} 24 | 25 | ) 26 | } 27 | } 28 | 29 | export default TweetText 30 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/styletron.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import classnames from 'classnames' 3 | import Styletron from 'styletron-client' 4 | import { injectStylePrefixed } from 'styletron-utils' 5 | import React from 'react' 6 | 7 | export const styletron = new Styletron() 8 | 9 | class View extends React.Component { 10 | render() { 11 | const { style, ...other } = this.props 12 | return
13 | } 14 | } 15 | 16 | const viewStyle = injectStylePrefixed(styletron, { 17 | alignItems: 'stretch', 18 | borderWidth: '0px', 19 | borderStyle: 'solid', 20 | boxSizing: 'border-box', 21 | display: 'flex', 22 | flexBasis: 'auto', 23 | flexDirection: 'column', 24 | flexShrink: '0', 25 | margin: '0px', 26 | padding: '0px', 27 | position: 'relative', 28 | // fix flexbox bugs 29 | minHeight: '0px', 30 | minWidth: '0px' 31 | }) 32 | 33 | export default View 34 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/theme.js: -------------------------------------------------------------------------------- 1 | const colors = { 2 | blue: '#1B95E0', 3 | lightBlue: '#71C9F8', 4 | green: '#17BF63', 5 | orange: '#F45D22', 6 | purple: '#794BC4', 7 | red: '#E0245E', 8 | white: '#FFFFFF', 9 | yellow: '#FFAD1F', 10 | deepGray: '#657786', 11 | fadedGray: '#E6ECF0', 12 | faintGray: '#F5F8FA', 13 | gray: '#AAB8C2', 14 | lightGray: '#CCD6DD', 15 | textBlack: '#14171A' 16 | } 17 | 18 | const fontSize = { 19 | root: '14px', 20 | // font scale 21 | small: '0.85rem', 22 | normal: '1rem', 23 | large: '1.25rem' 24 | } 25 | 26 | const theme = { 27 | colors, 28 | fontFamily: 29 | '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' + 30 | '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts 31 | fontSize, 32 | lineHeight: 1.3125, 33 | spaceX: 0.6, 34 | spaceY: 1.3125, 35 | createLength(num, unit) { 36 | return `${num}${unit}` 37 | } 38 | } 39 | 40 | export default theme 41 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/View/react-native-stylesheet.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import StyleSheet from 'react-native/apis/StyleSheet' 4 | import registry from 'react-native/apis/StyleSheet/registry' 5 | 6 | const emptyObject = {} 7 | 8 | class View extends React.Component { 9 | render() { 10 | const { style, ...other } = this.props 11 | const styleProps = registry.resolve([styles.root, style]) || emptyObject 12 | return
13 | } 14 | } 15 | 16 | const styles = StyleSheet.create({ 17 | root: { 18 | alignItems: 'stretch', 19 | borderWidth: 0, 20 | borderStyle: 'solid', 21 | boxSizing: 'border-box', 22 | display: 'flex', 23 | flexBasis: 'auto', 24 | flexDirection: 'column', 25 | flexShrink: 0, 26 | margin: 0, 27 | padding: 0, 28 | position: 'relative', 29 | // fix flexbox bugs 30 | minHeight: 0, 31 | minWidth: 0 32 | } 33 | }) 34 | 35 | export default View 36 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/AspectRatio/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React, { PureComponent } from 'react' 3 | import { StyleSheet, View } from 'react-native' 4 | 5 | class AspectRatio extends PureComponent { 6 | static displayName = 'AspectRatio' 7 | 8 | static propTypes = { 9 | children: PropTypes.any, 10 | ratio: PropTypes.number, 11 | style: PropTypes.object 12 | } 13 | 14 | static defaultProps = { 15 | ratio: 1 16 | } 17 | 18 | render() { 19 | const { children, ratio, style } = this.props 20 | const percentage = 100 / ratio 21 | 22 | return ( 23 | 24 | 25 | {children} 26 | 27 | ) 28 | } 29 | } 30 | 31 | const styles = StyleSheet.create({ 32 | root: { 33 | overflow: 'hidden' 34 | }, 35 | shim: { 36 | display: 'block', 37 | width: '100%' 38 | } 39 | }) 40 | 41 | export default AspectRatio 42 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/glamor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import View from '../View/glamor' 4 | 5 | const Box = ({ 6 | color, 7 | fixed = false, 8 | layout = 'column', 9 | outer = false, 10 | ...other 11 | }) => ( 12 | 21 | ) 22 | 23 | const styles = { 24 | outer: { 25 | padding: 4 26 | }, 27 | row: { 28 | flexDirection: 'row' 29 | }, 30 | color0: { 31 | backgroundColor: '#222' 32 | }, 33 | color1: { 34 | backgroundColor: '#666' 35 | }, 36 | color2: { 37 | backgroundColor: '#999' 38 | }, 39 | color3: { 40 | backgroundColor: 'blue' 41 | }, 42 | color4: { 43 | backgroundColor: 'orange' 44 | }, 45 | color5: { 46 | backgroundColor: 'red' 47 | }, 48 | fixed: { 49 | width: 20, 50 | height: 20 51 | } 52 | } 53 | 54 | export default Box 55 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/emotion-css.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import View from '../View/emotion-css' 4 | 5 | const Box = ({ 6 | color, 7 | fixed = false, 8 | layout = 'column', 9 | outer = false, 10 | ...other 11 | }) => ( 12 | 21 | ) 22 | 23 | const styles = { 24 | outer: { 25 | padding: 4 26 | }, 27 | row: { 28 | flexDirection: 'row' 29 | }, 30 | color0: { 31 | backgroundColor: '#222' 32 | }, 33 | color1: { 34 | backgroundColor: '#666' 35 | }, 36 | color2: { 37 | backgroundColor: '#999' 38 | }, 39 | color3: { 40 | backgroundColor: 'blue' 41 | }, 42 | color4: { 43 | backgroundColor: 'orange' 44 | }, 45 | color5: { 46 | backgroundColor: 'red' 47 | }, 48 | fixed: { 49 | width: 20, 50 | height: 20 51 | } 52 | } 53 | 54 | export default Box 55 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/emotion-obj-style.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import View from '../View/emotion-obj-style' 4 | 5 | const Box = ({ 6 | color, 7 | fixed = false, 8 | layout = 'column', 9 | outer = false, 10 | ...other 11 | }) => ( 12 | 21 | ) 22 | 23 | const styles = { 24 | outer: { 25 | padding: 4 26 | }, 27 | row: { 28 | flexDirection: 'row' 29 | }, 30 | color0: { 31 | backgroundColor: '#222' 32 | }, 33 | color1: { 34 | backgroundColor: '#666' 35 | }, 36 | color2: { 37 | backgroundColor: '#999' 38 | }, 39 | color3: { 40 | backgroundColor: 'blue' 41 | }, 42 | color4: { 43 | backgroundColor: 'orange' 44 | }, 45 | color5: { 46 | backgroundColor: 'red' 47 | }, 48 | fixed: { 49 | width: 20, 50 | height: 20 51 | } 52 | } 53 | 54 | export default Box 55 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/react-native.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import { StyleSheet, View } from 'react-native' 4 | 5 | const Box = ({ 6 | color, 7 | fixed = false, 8 | layout = 'column', 9 | outer = false, 10 | ...other 11 | }) => ( 12 | 21 | ) 22 | 23 | const styles = StyleSheet.create({ 24 | outer: { 25 | padding: 4 26 | }, 27 | row: { 28 | flexDirection: 'row' 29 | }, 30 | color0: { 31 | backgroundColor: '#222' 32 | }, 33 | color1: { 34 | backgroundColor: '#666' 35 | }, 36 | color2: { 37 | backgroundColor: '#999' 38 | }, 39 | color3: { 40 | backgroundColor: 'blue' 41 | }, 42 | color4: { 43 | backgroundColor: 'orange' 44 | }, 45 | color5: { 46 | backgroundColor: 'red' 47 | }, 48 | fixed: { 49 | width: 20, 50 | height: 20 51 | } 52 | }) 53 | 54 | export default Box 55 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/radium.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import Radium from 'radium' 3 | import React from 'react' 4 | import View from '../View/radium' 5 | 6 | const Box = ({ 7 | color, 8 | fixed = false, 9 | layout = 'column', 10 | outer = false, 11 | ...other 12 | }) => ( 13 | 22 | ) 23 | 24 | const styles = { 25 | outer: { 26 | padding: 4 27 | }, 28 | row: { 29 | flexDirection: 'row' 30 | }, 31 | color0: { 32 | backgroundColor: '#222' 33 | }, 34 | color1: { 35 | backgroundColor: '#666' 36 | }, 37 | color2: { 38 | backgroundColor: '#999' 39 | }, 40 | color3: { 41 | backgroundColor: 'blue' 42 | }, 43 | color4: { 44 | backgroundColor: 'orange' 45 | }, 46 | color5: { 47 | backgroundColor: 'red' 48 | }, 49 | fixed: { 50 | width: 20, 51 | height: 20 52 | } 53 | } 54 | 55 | export default Radium(Box) 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mitchell Hamilton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/aphrodite.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import View from '../View/aphrodite' 4 | import { StyleSheet } from 'aphrodite' 5 | 6 | const Box = ({ 7 | color, 8 | fixed = false, 9 | layout = 'column', 10 | outer = false, 11 | ...other 12 | }) => ( 13 | 22 | ) 23 | 24 | const styles = StyleSheet.create({ 25 | outer: { 26 | padding: 4 27 | }, 28 | row: { 29 | flexDirection: 'row' 30 | }, 31 | color0: { 32 | backgroundColor: '#222' 33 | }, 34 | color1: { 35 | backgroundColor: '#666' 36 | }, 37 | color2: { 38 | backgroundColor: '#999' 39 | }, 40 | color3: { 41 | backgroundColor: 'blue' 42 | }, 43 | color4: { 44 | backgroundColor: 'orange' 45 | }, 46 | color5: { 47 | backgroundColor: 'red' 48 | }, 49 | fixed: { 50 | width: 20, 51 | height: 20 52 | } 53 | }) 54 | 55 | export default Box 56 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/react-native-stylesheet.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import StyleSheet from 'react-native/apis/StyleSheet' 4 | import View from '../View/react-native-stylesheet' 5 | 6 | const Box = ({ 7 | color, 8 | fixed = false, 9 | layout = 'column', 10 | outer = false, 11 | ...other 12 | }) => ( 13 | 22 | ) 23 | 24 | const styles = StyleSheet.create({ 25 | outer: { 26 | padding: 4 27 | }, 28 | row: { 29 | flexDirection: 'row' 30 | }, 31 | color0: { 32 | backgroundColor: '#222' 33 | }, 34 | color1: { 35 | backgroundColor: '#666' 36 | }, 37 | color2: { 38 | backgroundColor: '#999' 39 | }, 40 | color3: { 41 | backgroundColor: 'blue' 42 | }, 43 | color4: { 44 | backgroundColor: 'orange' 45 | }, 46 | color5: { 47 | backgroundColor: 'red' 48 | }, 49 | fixed: { 50 | width: 20, 51 | height: 20 52 | } 53 | }) 54 | 55 | export default Box 56 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/jss.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import classnames from 'classnames' 3 | import injectSheet from 'react-jss' 4 | import React from 'react' 5 | import View from '../View/jss' 6 | 7 | const Box = ({ 8 | classes, 9 | color, 10 | fixed = false, 11 | layout = 'column', 12 | outer = false, 13 | ...other 14 | }) => ( 15 | 24 | ) 25 | 26 | const styles = { 27 | outer: { 28 | padding: 4 29 | }, 30 | row: { 31 | flexDirection: 'row' 32 | }, 33 | color0: { 34 | backgroundColor: '#222' 35 | }, 36 | color1: { 37 | backgroundColor: '#666' 38 | }, 39 | color2: { 40 | backgroundColor: '#999' 41 | }, 42 | color3: { 43 | backgroundColor: 'blue' 44 | }, 45 | color4: { 46 | backgroundColor: 'orange' 47 | }, 48 | color5: { 49 | backgroundColor: 'red' 50 | }, 51 | fixed: { 52 | width: 20, 53 | height: 20 54 | } 55 | } 56 | 57 | export default injectSheet(styles)(Box) 58 | -------------------------------------------------------------------------------- /packages/benchmarks/run-headless.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer') 2 | const Chart = require('pwmetrics/lib/chart/chart') 3 | const path = require('path') 4 | 5 | // need to add a timeout if it never completes 6 | // needs error handling 7 | 8 | // the last benchmark that's run isn't included in the graph for some reason 9 | 10 | async function run() { 11 | const browser = await puppeteer.launch() 12 | const page = await browser.newPage() 13 | await page.goto(`file://${path.resolve(__dirname, './index.html')}`) 14 | 15 | const results = [] 16 | page.on('console', result => { 17 | console.log(results) 18 | if (result && result.name) { 19 | results.push(result) 20 | } 21 | if (result === 'done') { 22 | browser.close() 23 | const chart = new Chart({ 24 | xlabel: 'Time (ms)', 25 | ylabel: 'Benchmark', 26 | direction: 'x' 27 | }) 28 | results.forEach(result => { 29 | chart.addBar({ 30 | size: result.mean, 31 | label: /\[(.*)\]/.exec(result.name)[1], 32 | barLabel: `${result.mean}ms ${/(.*)\[/.exec(result.name)[1]}` 33 | }) 34 | }) 35 | chart.draw() 36 | } 37 | }) 38 | } 39 | 40 | run() 41 | -------------------------------------------------------------------------------- /packages/benchmarks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarks", 3 | "private": true, 4 | "dependencies": { 5 | "aphrodite": "^1.2.3", 6 | "classnames": "^2.2.5", 7 | "emotion": "latest", 8 | "glamor": "^2.20.37", 9 | "glamorous": "^4.1.0", 10 | "marky": "^1.2.0", 11 | "new-css-in-js": "^0.0.1-4", 12 | "postcss": "^6.0.8", 13 | "radium": "^0.19.1", 14 | "react": "15.6.1", 15 | "react-dom": "15.6.1", 16 | "react-jss": "^7.0.1", 17 | "react-new-css-in-js": "^0.0.1-5", 18 | "react-primitives": "^0.4.3", 19 | "reactxp": "0.34.3", 20 | "styled-components": "latest", 21 | "styletron-client": "^2.5.7", 22 | "styletron-utils": "^2.5.4" 23 | }, 24 | "devDependencies": { 25 | "babel-loader": "^7.1.1", 26 | "babel-macros": "^0.5.2", 27 | "cli-chart": "^0.3.1", 28 | "css-loader": "^0.28.4", 29 | "puppeteer": "^0.10.1", 30 | "pwmetrics": "^3.1.2", 31 | "react-addons-perf": "^15.6.0-rc.1", 32 | "serve": "^6.0.6", 33 | "style-loader": "^0.18.2", 34 | "webpack": "^3.4.1", 35 | "webpack-bundle-analyzer": "^2.9.0" 36 | }, 37 | "scripts": { 38 | "build": "webpack", 39 | "benchmark": "npm run build && node run-headless.js" 40 | }, 41 | "version": "0.0.1-5" 42 | } 43 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/emotion.js: -------------------------------------------------------------------------------- 1 | import styled from 'emotion/react/macro' 2 | // import View from '../View/emotion' 3 | 4 | const getColor = color => { 5 | switch (color) { 6 | case 0: 7 | return '#222' 8 | case 1: 9 | return '#666' 10 | case 2: 11 | return '#999' 12 | case 3: 13 | return 'blue' 14 | case 4: 15 | return 'orange' 16 | case 5: 17 | return 'red' 18 | default: 19 | return 'transparent' 20 | } 21 | } 22 | 23 | const viewStyles = { 24 | alignItems: 'stretch', 25 | borderWidth: 0, 26 | borderStyle: 'solid', 27 | boxSizing: 'border-box', 28 | display: 'flex', 29 | flexBasis: 'auto', 30 | flexDirection: 'column', 31 | flexShrink: 0, 32 | margin: 0, 33 | padding: 0, 34 | position: 'relative', 35 | // fix flexbox bugs 36 | minHeight: 0, 37 | minWidth: 0 38 | } 39 | 40 | const Box = styled('div')` 41 | ${viewStyles}; 42 | flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')}; 43 | padding: ${props => (props.outer ? '4px' : '0')}; 44 | height: ${props => (props.fixed ? '20px' : 'auto')}; 45 | width: ${props => (props.fixed ? '20px' : 'auto')}; 46 | background-color: ${props => getColor(props.color)}; 47 | ` 48 | 49 | export default Box 50 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/reactxp.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import { Styles, View } from 'reactxp' 4 | 5 | const Box = ({ 6 | color, 7 | fixed = false, 8 | layout = 'column', 9 | outer = false, 10 | ...other 11 | }) => ( 12 | 21 | ) 22 | 23 | const styles = { 24 | outer: Styles.createViewStyle({ 25 | padding: 4 26 | }), 27 | row: Styles.createViewStyle({ 28 | flexDirection: 'row' 29 | }), 30 | color0: Styles.createViewStyle({ 31 | backgroundColor: '#222' 32 | }), 33 | color1: Styles.createViewStyle({ 34 | backgroundColor: '#666' 35 | }), 36 | color2: Styles.createViewStyle({ 37 | backgroundColor: '#999' 38 | }), 39 | color3: Styles.createViewStyle({ 40 | backgroundColor: 'blue' 41 | }), 42 | color4: Styles.createViewStyle({ 43 | backgroundColor: 'orange' 44 | }), 45 | color5: Styles.createViewStyle({ 46 | backgroundColor: 'red' 47 | }), 48 | fixed: Styles.createViewStyle({ 49 | width: 20, 50 | height: 20 51 | }) 52 | } 53 | 54 | export default Box 55 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/NestedTree/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React, { Component } from 'react' 3 | 4 | class DeepTree extends Component { 5 | static propTypes = { 6 | breadth: PropTypes.number.isRequired, 7 | components: PropTypes.object, 8 | depth: PropTypes.number.isRequired, 9 | id: PropTypes.number.isRequired, 10 | wrap: PropTypes.number.isRequired 11 | } 12 | 13 | render() { 14 | const { breadth, components, depth, id, wrap } = this.props 15 | const { Box } = components 16 | 17 | let result = ( 18 | 24 | {depth === 0 && ( 25 | 26 | )} 27 | {depth !== 0 && 28 | Array.from({ length: breadth }).map((el, i) => ( 29 | 37 | ))} 38 | 39 | ) 40 | for (let i = 0; i < wrap; i++) { 41 | result = {result} 42 | } 43 | return result 44 | } 45 | } 46 | 47 | export default DeepTree 48 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Box/styletron.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { injectStylePrefixed } from 'styletron-utils' 3 | import React from 'react' 4 | import View, { styletron } from '../View/styletron' 5 | 6 | const Box = ({ 7 | color, 8 | fixed = false, 9 | layout = 'column', 10 | outer = false, 11 | ...other 12 | }) => ( 13 | 22 | ) 23 | 24 | const styles = { 25 | outer: injectStylePrefixed(styletron, { 26 | padding: '4px' 27 | }), 28 | row: injectStylePrefixed(styletron, { 29 | flexDirection: 'row' 30 | }), 31 | color0: injectStylePrefixed(styletron, { 32 | backgroundColor: '#222' 33 | }), 34 | color1: injectStylePrefixed(styletron, { 35 | backgroundColor: '#666' 36 | }), 37 | color2: injectStylePrefixed(styletron, { 38 | backgroundColor: '#999' 39 | }), 40 | color3: injectStylePrefixed(styletron, { 41 | backgroundColor: 'blue' 42 | }), 43 | color4: injectStylePrefixed(styletron, { 44 | backgroundColor: 'orange' 45 | }), 46 | color5: injectStylePrefixed(styletron, { 47 | backgroundColor: 'red' 48 | }), 49 | fixed: injectStylePrefixed(styletron, { 50 | width: '20px', 51 | height: '20px' 52 | }) 53 | } 54 | 55 | export default Box 56 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/TweetActionsBar/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import TweetAction from '../TweetAction' 3 | import { View, StyleSheet } from 'react-native' 4 | import React, { PureComponent } from 'react' 5 | 6 | const actionNames = ['reply', 'retweet', 'like', 'directMessage'] 7 | 8 | export default class TweetActionsBar extends PureComponent { 9 | static propTypes = { 10 | actions: PropTypes.arrayOf( 11 | PropTypes.shape({ 12 | count: PropTypes.number, 13 | label: PropTypes.string, 14 | highlighted: PropTypes.bool, 15 | name: PropTypes.oneOf(actionNames).isRequired, 16 | onPress: PropTypes.func 17 | }) 18 | ), 19 | style: PropTypes.object 20 | } 21 | 22 | render() { 23 | const { actions, style } = this.props 24 | 25 | /* eslint-disable react/jsx-handler-names */ 26 | return ( 27 | 28 | {actions.map((action, i) => ( 29 | 38 | ))} 39 | 40 | ) 41 | } 42 | } 43 | 44 | const styles = StyleSheet.create({ 45 | root: { 46 | flexDirection: 'row' 47 | }, 48 | action: { 49 | display: 'block', 50 | marginRight: '10%' 51 | } 52 | }) 53 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/UserNames/index.js: -------------------------------------------------------------------------------- 1 | import AppText from '../AppText' 2 | import PropTypes from 'prop-types' 3 | import { StyleSheet } from 'react-native' 4 | import React, { PureComponent } from 'react' 5 | 6 | class UserNames extends PureComponent { 7 | static displayName = 'UserNames' 8 | 9 | static propTypes = { 10 | fullName: PropTypes.string, 11 | layout: PropTypes.oneOf(['nowrap', 'stack']), 12 | onPress: PropTypes.func, 13 | screenName: PropTypes.string, 14 | style: PropTypes.object 15 | } 16 | 17 | static defaultProps = { 18 | layout: 'nowrap' 19 | } 20 | 21 | render() { 22 | const { 23 | fullName, 24 | layout, 25 | onPress, 26 | screenName, 27 | style, 28 | ...other 29 | } = this.props 30 | 31 | return ( 32 | 39 | 40 | {fullName} 41 | 42 | {layout === 'stack' ? ' \u000A' : ' '} 43 | {`@${screenName}`} 47 | 48 | ) 49 | } 50 | } 51 | 52 | const styles = StyleSheet.create({ 53 | root: { 54 | display: 'inline-block' 55 | }, 56 | screenName: { 57 | unicodeBidi: 'embed', 58 | writingDirection: 'ltr' 59 | } 60 | }) 61 | 62 | export default UserNames 63 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/GridView/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import { StyleSheet, View } from 'react-native' 3 | import React, { Component } from 'react' 4 | import theme from '../theme' 5 | 6 | class GridView extends Component { 7 | static displayName = 'GridView' 8 | 9 | static propTypes = { 10 | children: PropTypes.node, 11 | hasGap: PropTypes.bool, 12 | style: PropTypes.object 13 | } 14 | 15 | render() { 16 | const { children, hasGap, style, ...other } = this.props 17 | 18 | return ( 19 | 20 | {React.Children.map(children, child => { 21 | return ( 22 | child && 23 | React.cloneElement(child, { 24 | style: [ 25 | child.props.style, 26 | styles.column, 27 | hasGap && styles.hasGapColumn 28 | ] 29 | }) 30 | ) 31 | })} 32 | 33 | ) 34 | } 35 | } 36 | 37 | const styles = StyleSheet.create({ 38 | root: { 39 | flexDirection: 'row' 40 | }, 41 | /** 42 | * 1. Distribute all space (rather than extra space) 43 | * 2. Prevent wide content from forcing wider flex columns 44 | */ 45 | column: { 46 | flexBasis: 0, // 1 47 | minWidth: 0 // 2 48 | }, 49 | hasGap: { 50 | marginHorizontal: theme.createLength(theme.spaceX * -0.5, 'rem') 51 | }, 52 | hasGapColumn: { 53 | marginHorizontal: theme.createLength(theme.spaceX * 0.5, 'rem') 54 | } 55 | }) 56 | 57 | export default GridView 58 | -------------------------------------------------------------------------------- /packages/core/src/hash.js: -------------------------------------------------------------------------------- 1 | export default function hashString(str: string) { 2 | return hash(str, str.length).toString(36) 3 | } 4 | 5 | function hash(str: string, seed: number) { 6 | let m = 0x5bd1e995 7 | let r = 24 8 | let h = seed ^ str.length 9 | let length = str.length 10 | let currentIndex = 0 11 | 12 | while (length >= 4) { 13 | let k = UInt32(str, currentIndex) 14 | 15 | k = Umul32(k, m) 16 | k ^= k >>> r 17 | k = Umul32(k, m) 18 | 19 | h = Umul32(h, m) 20 | h ^= k 21 | 22 | currentIndex += 4 23 | length -= 4 24 | } 25 | 26 | switch (length) { 27 | case 3: 28 | h ^= UInt16(str, currentIndex) 29 | h ^= str.charCodeAt(currentIndex + 2) << 16 30 | h = Umul32(h, m) 31 | break 32 | 33 | case 2: 34 | h ^= UInt16(str, currentIndex) 35 | h = Umul32(h, m) 36 | break 37 | 38 | case 1: 39 | h ^= str.charCodeAt(currentIndex) 40 | h = Umul32(h, m) 41 | break 42 | } 43 | 44 | h ^= h >>> 13 45 | h = Umul32(h, m) 46 | h ^= h >>> 15 47 | 48 | return h >>> 0 49 | } 50 | 51 | function UInt32(str, pos) { 52 | return ( 53 | str.charCodeAt(pos++) + 54 | (str.charCodeAt(pos++) << 8) + 55 | (str.charCodeAt(pos++) << 16) + 56 | (str.charCodeAt(pos) << 24) 57 | ) 58 | } 59 | 60 | function UInt16(str, pos) { 61 | return str.charCodeAt(pos++) + (str.charCodeAt(pos++) << 8) 62 | } 63 | 64 | function Umul32(n, m) { 65 | n = n | 0 66 | m = m | 0 67 | let nlo = n & 0xffff 68 | let nhi = n >>> 16 69 | let res = (nlo * m + (((nhi * m) & 0xffff) << 16)) | 0 70 | return res 71 | } 72 | -------------------------------------------------------------------------------- /packages/benchmarks/webpack.config.js: -------------------------------------------------------------------------------- 1 | // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') 2 | // .BundleAnalyzerPlugin 3 | const path = require('path') 4 | const webpack = require('webpack') 5 | 6 | module.exports = { 7 | // context: __dirname, 8 | // devtool: 'eval', 9 | entry: './index', 10 | output: { 11 | path: path.resolve(__dirname, 'dist'), 12 | filename: 'performance.bundle.js' 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.css$/, 18 | use: [ 19 | 'style-loader', 20 | { 21 | loader: 'css-loader', 22 | options: { module: true, localIdentName: '[hash:base64:8]' } 23 | } 24 | ] 25 | }, 26 | { 27 | test: /\.js$/, 28 | exclude: /node_modules/, 29 | use: { 30 | loader: 'babel-loader', 31 | options: { 32 | babelrc: false, 33 | presets: [['env', { modules: false }], 'react', 'stage-0'], 34 | plugins: ['babel-macros'], 35 | cacheDirectory: true 36 | } 37 | } 38 | } 39 | ] 40 | }, 41 | plugins: [ 42 | // new BundleAnalyzerPlugin({ 43 | // analyzerMode: 'static', 44 | // openAnalyzer: false 45 | // }), 46 | new webpack.DefinePlugin({ 47 | 'process.env.NODE_ENV': JSON.stringify('production') 48 | }), 49 | new webpack.optimize.UglifyJsPlugin() 50 | // new webpack.optimize.UglifyJsPlugin({ 51 | // compress: { 52 | // dead_code: true, 53 | // screw_ie8: true, 54 | // warnings: false 55 | // } 56 | // }) 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/UserAvatar/index.js: -------------------------------------------------------------------------------- 1 | import AspectRatio from '../AspectRatio' 2 | import PropTypes from 'prop-types' 3 | import { Image, StyleSheet } from 'react-native' 4 | import React, { PureComponent } from 'react' 5 | import theme from '../theme' 6 | 7 | class UserAvatar extends PureComponent { 8 | static displayName = 'UserAvatar' 9 | 10 | static propTypes = { 11 | accessibilityLabel: PropTypes.string, 12 | circle: PropTypes.bool, 13 | style: PropTypes.object, 14 | uri: PropTypes.string 15 | } 16 | 17 | static defaultProps = { 18 | circle: false 19 | } 20 | 21 | render() { 22 | const { accessibilityLabel, circle, style, uri } = this.props 23 | 24 | return ( 25 | 26 | {uri ? ( 27 | 34 | ) : null} 35 | 36 | ) 37 | } 38 | 39 | _handleLoad = () => { 40 | this._imageRef && this._imageRef.setNativeProps(nativeProps) 41 | } 42 | 43 | _setImageRef = component => { 44 | this._imageRef = component 45 | } 46 | } 47 | 48 | const nativeProps = { style: { backgroundColor: '#fff' } } 49 | 50 | const styles = StyleSheet.create({ 51 | root: { 52 | borderRadius: '0.35rem' 53 | }, 54 | circle: { 55 | borderRadius: '9999px' 56 | }, 57 | image: { 58 | backgroundColor: theme.colors.fadedGray, 59 | display: 'block', 60 | height: '100%', 61 | width: '100%' 62 | } 63 | }) 64 | 65 | export default UserAvatar 66 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import resolve from 'rollup-plugin-node-resolve' 3 | import commonjs from 'rollup-plugin-commonjs' 4 | import replace from 'rollup-plugin-replace' 5 | import uglify from 'rollup-plugin-uglify' 6 | import alias from 'rollup-plugin-alias' 7 | import path from 'path' 8 | 9 | const pkg = require(path.resolve(process.cwd(), './package.json')) 10 | 11 | const config = { 12 | input: './src/index.js', 13 | external: ['react', 'new-css-in-js'], 14 | exports: 'named', 15 | sourcemap: true, 16 | plugins: [ 17 | commonjs(), 18 | resolve(), 19 | babel({ 20 | presets: [ 21 | [ 22 | 'env', 23 | { 24 | loose: true, 25 | modules: false, 26 | exclude: ['transform-es2015-typeof-symbol'] 27 | } 28 | ], 29 | 'flow' 30 | ], 31 | babelrc: false 32 | }) 33 | ], 34 | output: [ 35 | { file: pkg.main, format: 'cjs' }, 36 | { file: pkg.module, format: 'es' } 37 | ] 38 | } 39 | 40 | if (process.env.UMD) { 41 | config.external = ['react'] 42 | config.globals = { react: 'React' } 43 | config.plugins.push( 44 | alias({ 45 | 'new-css-in-js': path.resolve(__dirname, './packages/core/src/index.js') 46 | }), 47 | replace({ 48 | 'process.env.NODE_ENV': JSON.stringify('production') 49 | }), 50 | uglify() 51 | ) 52 | config.output = [ 53 | { 54 | file: './dist/DO-NOT-USE.min.js', 55 | format: 'umd', 56 | name: pkg.name 57 | } 58 | ] 59 | } 60 | 61 | if (pkg.name === 'preact-new-css-in-js') { 62 | config.input = '../react/src/index.js' 63 | config.external = ['preact', 'new-css-in-js'] 64 | config.plugins.unshift(alias({ react: 'preact' })) 65 | } 66 | 67 | export default config 68 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/TweetAction/index.js: -------------------------------------------------------------------------------- 1 | import IconReply from '../Icons/Reply' 2 | import IconHeart from '../Icons/Heart' 3 | import IconRetweet from '../Icons/Retweet' 4 | import IconDirectMessage from '../Icons/DirectMessage' 5 | import PropTypes from 'prop-types' 6 | import React from 'react' 7 | import theme from '../theme' 8 | import { Text, View, StyleSheet } from 'react-native' 9 | 10 | const getIcon = (icon, highlighted) => { 11 | switch (icon) { 12 | case 'like': 13 | return 14 | case 'reply': 15 | return 16 | case 'retweet': 17 | return 18 | case 'directMessage': 19 | return 20 | default: 21 | return null 22 | } 23 | } 24 | 25 | export default class TweetAction extends React.Component { 26 | static displayName = 'TweetAction' 27 | 28 | static propTypes = { 29 | count: PropTypes.number, 30 | displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']), 31 | highlighted: PropTypes.bool, 32 | onPress: PropTypes.func, 33 | style: PropTypes.object 34 | } 35 | 36 | render() { 37 | const { count, displayMode, highlighted, onPress, style } = this.props 38 | 39 | return ( 40 | 45 | 52 | {getIcon(displayMode, highlighted)} 53 | {count > 0 ? {count} : null} 54 | 55 | 56 | ) 57 | } 58 | } 59 | 60 | const styles = StyleSheet.create({ 61 | root: { 62 | minHeight: theme.createLength(theme.lineHeight, 'rem'), 63 | overflow: 'visible', 64 | userSelect: 'none', 65 | whiteSpace: 'nowrap' 66 | }, 67 | inner: { 68 | alignItems: 'center', 69 | color: theme.colors.deepGray, 70 | display: 'flex', 71 | flexDirection: 'row' 72 | }, 73 | count: { 74 | marginLeft: '0.25em' 75 | }, 76 | retweetedColor: { 77 | color: theme.colors.green 78 | }, 79 | likedColor: { 80 | color: theme.colors.red 81 | } 82 | }) 83 | -------------------------------------------------------------------------------- /packages/benchmarks/benchmark.js: -------------------------------------------------------------------------------- 1 | import * as marky from 'marky' 2 | 3 | const fmt = time => Math.round(time * 100) / 100 4 | 5 | const measure = (name, fn) => { 6 | marky.mark(name) 7 | fn() 8 | const performanceMeasure = marky.stop(name) 9 | return performanceMeasure.duration 10 | } 11 | 12 | const mean = values => { 13 | const sum = values.reduce((sum, value) => sum + value, 0) 14 | return sum / values.length 15 | } 16 | 17 | const median = values => { 18 | if (!Array.isArray(values)) { 19 | return 0 20 | } 21 | if (values.length === 1) { 22 | return values[0] 23 | } 24 | 25 | const numbers = [...values].sort((a, b) => a - b) 26 | return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2 27 | } 28 | 29 | const standardDeviation = values => { 30 | const avg = mean(values) 31 | 32 | const squareDiffs = values.map(value => { 33 | const diff = value - avg 34 | return diff * diff 35 | }) 36 | 37 | const meanSquareDiff = mean(squareDiffs) 38 | return Math.sqrt(meanSquareDiff) 39 | } 40 | 41 | const benchmark = ({ name, description, setup, teardown, task, runs }) => { 42 | return new Promise(resolve => { 43 | const durations = [] 44 | let i = 0 45 | 46 | setup() 47 | const first = measure('first', task) 48 | teardown() 49 | 50 | const done = () => { 51 | const stdDev = standardDeviation(durations) 52 | const formattedFirst = fmt(first) 53 | const formattedMean = fmt(mean(durations)) 54 | const formattedMedian = fmt(median(durations)) 55 | const formattedStdDev = fmt(stdDev) 56 | 57 | console.log({ 58 | name, 59 | description, 60 | first: formattedFirst, 61 | mean: formattedMean, 62 | median: formattedMedian, 63 | stdDev: formattedStdDev, 64 | durations 65 | }) 66 | resolve() 67 | } 68 | 69 | const a = () => { 70 | setup() 71 | window.requestAnimationFrame(b) 72 | } 73 | 74 | const b = () => { 75 | const duration = measure('mean', task) 76 | durations.push(duration) 77 | window.requestAnimationFrame(c) 78 | } 79 | 80 | const c = () => { 81 | teardown() 82 | window.requestAnimationFrame(d) 83 | } 84 | 85 | const d = () => { 86 | i += 1 87 | if (i < runs) { 88 | window.requestAnimationFrame(a) 89 | } else { 90 | window.requestAnimationFrame(done) 91 | } 92 | } 93 | 94 | window.requestAnimationFrame(a) 95 | }) 96 | } 97 | 98 | export default benchmark 99 | -------------------------------------------------------------------------------- /packages/benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | To run these benchmarks: 4 | 5 | ``` 6 | npm run build:performance 7 | open ./performance/index.html 8 | ``` 9 | 10 | By default the emotion, glamorous and styled-components tests are run. To choose what tests to run add a comma delimited list of benchmarks in the hash of the url. 11 | ``` 12 | file://some/file/path/index.html#emotion,emotionCSS,emotionObjStyle,styled-components,glamor,glamorous 13 | ``` 14 | 15 | 16 | ## Notes 17 | 18 | The components used in the render benchmarks are simple enough to be 19 | implemented by multiple UI or style libraries. The implementations are not 20 | equivalent in functionality. For example, React Native for Web's stylesheet is 21 | unique in that it also converts React Native styles to DOM styles, has 22 | deterministic resolution, and supports RTL layout. 23 | 24 | `react-native-web/stylesheet` is a comparative baseline that implements a 25 | simple `View` without much of React Native's functionality. 26 | 27 | ## Benchmark results 28 | 29 | Typical render timings*: mean ± two standard deviations. 30 | 31 | | Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) | 32 | | :--- | ---: | ---: | ---: | 33 | | `css-modules` | `84.19` `±14.69` | `183.37` `±22.98` | | 34 | | `react-native-web/stylesheet@0.0.113` | `88.83` `±14.31` | `185.54` `±24.62` | | 35 | | `react-native-web@0.0.113` | `110.45` `±19.63` | `251.53` `±32.52` | `15.52` `±7.93ms` | 36 | 37 | Other libraries 38 | 39 | | Implementation | Deep tree (ms) | Wide tree (ms) | 40 | | :--- | ---: | ---: | 41 | | `aphrodite@1.2.3` | `84.68` `±18.80` | `180.62` `±41.98` | 42 | | `styletron@2.5.1` | `83.93` `±13.10` | `185.96` `±45.65` | 43 | | `react-jss@7.0.1` | `174.75` `±30.87` | `411.77` `±83.83` | 44 | | `glamor@3.0.0-3` | `255.21` `±45.68` | `545.74` `±107.79` | 45 | | `reactxp@0.34.3` | `237.46` `±36.72` | `514.48` `±84.87` | 46 | | `styled-components@2.1.1` | `266.91` `±50.04` | `598.29` `±95.13` | 47 | | `styled-components/primitives@2.1.1` | `266.62` `±50.39` | `567.13` `±68.12` | 48 | | `radium@0.19.1` | `518.48` `±69.74` | `1058.85` `±120.85` | 49 | 50 | These results indicate that styled render performance is not a significant 51 | differentiating factor between `aphrodite`, `css-modules`, `react-native-web`, 52 | and `styletron`. 53 | 54 | *MacBook Pro (13-inch, Early 2015); 3.1 GHz Intel Core i7; 16 GB 1867 MHz DDR3. Google Chrome 58 (2x CPU slowdown). 55 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/AppText/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import theme from '../theme' 3 | import React, { PureComponent } from 'react' 4 | import { StyleSheet, Text } from 'react-native' 5 | 6 | class AppText extends PureComponent { 7 | static displayName = 'AppText' 8 | 9 | static propTypes = { 10 | align: PropTypes.oneOf(['center', 'left', 'right']), 11 | color: PropTypes.oneOf(['blue', 'deepGray', 'normal', 'red', 'white']), 12 | fontStyle: PropTypes.oneOf(['normal', 'italic']), 13 | size: PropTypes.oneOf(['small', 'normal', 'large']), 14 | uppercase: PropTypes.bool, 15 | weight: PropTypes.oneOf(['normal', 'bold']) 16 | } 17 | 18 | render() { 19 | const { 20 | align, 21 | color, 22 | fontStyle, 23 | size, 24 | uppercase, 25 | weight, 26 | ...other 27 | } = this.props 28 | 29 | const style = [ 30 | styles.root, 31 | align && alignStyles[align], 32 | color && colorStyles[color], 33 | fontStyle && fontStyles[fontStyle], 34 | size && sizeStyles[size], 35 | weight && weightStyles[weight], 36 | uppercase === true && styles.uppercase 37 | ] 38 | 39 | return 40 | } 41 | } 42 | 43 | const styles = StyleSheet.create({ 44 | root: { 45 | fontFamily: theme.fontFamily, 46 | fontSize: theme.fontSize.normal, 47 | fontWeight: 'normal', 48 | lineHeight: theme.createLength(theme.lineHeight), 49 | wordWrap: 'break-word' 50 | }, 51 | uppercase: { 52 | textTransform: 'uppercase' 53 | } 54 | }) 55 | 56 | const alignStyles = StyleSheet.create({ 57 | center: { 58 | textAlign: 'center' 59 | }, 60 | left: { 61 | textAlign: 'left' 62 | }, 63 | right: { 64 | textAlign: 'right' 65 | } 66 | }) 67 | 68 | const colorStyles = StyleSheet.create({ 69 | blue: { 70 | color: theme.colors.blue 71 | }, 72 | deepGray: { 73 | color: theme.colors.deepGray 74 | }, 75 | normal: { 76 | color: theme.colors.textBlack 77 | }, 78 | red: { 79 | color: theme.colors.red 80 | }, 81 | white: { 82 | color: theme.colors.white 83 | } 84 | }) 85 | 86 | const fontStyles = StyleSheet.create({ 87 | normal: { 88 | fontStyle: 'normal' 89 | }, 90 | italic: { 91 | fontStyle: 'italic' 92 | } 93 | }) 94 | 95 | const sizeStyles = StyleSheet.create({ 96 | small: { 97 | fontSize: theme.fontSize.small 98 | }, 99 | normal: { 100 | fontSize: theme.fontSize.normal 101 | }, 102 | large: { 103 | fontSize: theme.fontSize.large 104 | } 105 | }) 106 | 107 | const weightStyles = StyleSheet.create({ 108 | normal: { 109 | fontWeight: '400' 110 | }, 111 | bold: { 112 | fontWeight: 'bold' 113 | } 114 | }) 115 | 116 | export default AppText 117 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/index.cjs.js", 3 | "module": "dist/index.es.js", 4 | "files": [ 5 | "src", 6 | "dist" 7 | ], 8 | "author": "mitchellhamilton ", 9 | "license": "MIT", 10 | "scripts": { 11 | "test:watch": "jest --watch --coverage --verbose", 12 | "test": "npm-run-all -p coverage test:size lint && npm run benchmark", 13 | "rollup": "lerna run rollup", 14 | "rollup:umd": "lerna run rollup:umd", 15 | "bundlesize": "bundlesize", 16 | "test:size": "npm-run-all rollup:umd bundlesize", 17 | "build": "npm run rollup && UMD=true npm run rollup", 18 | "lint": "eslint .", 19 | "coverage": "jest --coverage --ci --runInBand", 20 | "bootstrap": "lerna bootstrap", 21 | "benchmark": "npm run rollup && lerna exec --scope benchmarks npm run benchmark" 22 | }, 23 | "dependencies": { 24 | "stylis": "^3.2.11" 25 | }, 26 | "jest": { 27 | "snapshotSerializers": [ 28 | "enzyme-to-json/serializer" 29 | ], 30 | "moduleNameMapper": { 31 | "^new-css-in-js$": "/packages/core/src", 32 | "^react-new-css-in-js$": "/packages/react/src", 33 | "^new-css-in-js-server$": "/packages/server/src" 34 | } 35 | }, 36 | "devDependencies": { 37 | "babel-core": "^6.26.0", 38 | "babel-eslint": "^7.2.3", 39 | "babel-macros": "^0.5.2", 40 | "babel-preset-env": "^1.6.0", 41 | "babel-preset-flow": "^6.23.0", 42 | "babel-preset-react": "^6.24.1", 43 | "babel-preset-stage-0": "^6.24.1", 44 | "bundlesize": "^0.13.2", 45 | "codecov": "^2.3.0", 46 | "enzyme": "^2.9.1", 47 | "enzyme-to-json": "^1.5.1", 48 | "eslint": "^4.3.0", 49 | "eslint-config-prettier": "^2.3.0", 50 | "eslint-config-react": "^1.1.7", 51 | "eslint-config-standard": "^10.2.1", 52 | "eslint-config-standard-react": "^5.0.0", 53 | "eslint-plugin-import": "^2.7.0", 54 | "eslint-plugin-node": "^5.1.1", 55 | "eslint-plugin-prettier": "^2.1.2", 56 | "eslint-plugin-promise": "^3.5.0", 57 | "eslint-plugin-react": "^7.1.0", 58 | "eslint-plugin-standard": "^3.0.1", 59 | "jest": "^20.0.4", 60 | "jest-glamor-react": "^3.1.0", 61 | "lerna": "^2.1.0", 62 | "npm-run-all": "^4.0.2", 63 | "prettier": "^1.6.1", 64 | "react": "^15.6.1", 65 | "react-dom": "^15.6.1", 66 | "react-test-renderer": "^15.6.1", 67 | "rollup": "^0.48.2", 68 | "rollup-plugin-alias": "^1.3.1", 69 | "rollup-plugin-babel": "^3.0.2", 70 | "rollup-plugin-commonjs": "^8.2.0", 71 | "rollup-plugin-node-resolve": "^3.0.0", 72 | "rollup-plugin-replace": "^1.1.1", 73 | "rollup-plugin-uglify": "^2.0.1" 74 | }, 75 | "bundlesize": [ 76 | { 77 | "path": "./packages/core/dist/DO-NOT-USE.min.js", 78 | "threshold": "6 Kb" 79 | }, 80 | { 81 | "path": "./packages/react/dist/DO-NOT-USE.min.js", 82 | "threshold": "7 Kb" 83 | } 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /packages/benchmarks/tests/renderTweet.js: -------------------------------------------------------------------------------- 1 | import createRenderBenchmark from '../createRenderBenchmark' 2 | import Tweet from '../src/components/Tweet' 3 | import React from 'react' 4 | 5 | const tweet1 = { 6 | favorite_count: 30, 7 | favorited: true, 8 | id: '834889712556875776', 9 | lang: 'en', 10 | retweet_count: 6, 11 | retweeted: false, 12 | textParts: [ 13 | { 14 | prefix: '', 15 | text: 'Living burrito to burrito ' 16 | }, 17 | { 18 | emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg', 19 | isEmoji: true, 20 | prefix: '', 21 | text: '🌯' 22 | }, 23 | { 24 | emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg', 25 | isEmoji: true, 26 | prefix: '', 27 | text: '🌯' 28 | }, 29 | { 30 | emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg', 31 | isEmoji: true, 32 | prefix: '', 33 | text: '🌯' 34 | } 35 | ], 36 | timestamp: 'Feb 23', 37 | user: { 38 | fullName: 'Nicolas', 39 | screenName: 'necolas', 40 | profileImageUrl: 41 | 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg' 42 | } 43 | } 44 | 45 | const tweet2 = { 46 | favorite_count: 84, 47 | favorited: false, 48 | id: '730896800060579840', 49 | lang: 'en', 50 | media: { 51 | source: { 52 | uri: 'https://pbs.twimg.com/media/CiSqvsJVEAAtLZ1.jpg', 53 | width: 600, 54 | height: 338 55 | } 56 | }, 57 | retweet_count: 4, 58 | retweeted: true, 59 | textParts: [ 60 | { 61 | prefix: '', 62 | text: 'Presenting ' 63 | }, 64 | { 65 | displayUrl: 'mobile.twitter.com', 66 | expandedUrl: 'https://mobile.twitter.com', 67 | isEntity: true, 68 | isUrl: true, 69 | linkRelation: 'nofollow', 70 | prefix: '', 71 | text: '', 72 | textDirection: 'ltr', 73 | url: 'https://t.co/4hRCAxiUUG' 74 | }, 75 | { 76 | prefix: '', 77 | text: ' with ' 78 | }, 79 | { 80 | isEntity: true, 81 | isMention: true, 82 | prefix: '@', 83 | text: 'davidbellona', 84 | textDirection: 'ltr', 85 | url: '/davidbellona' 86 | }, 87 | { 88 | prefix: '', 89 | text: " at Twitter's all hands meeting " 90 | } 91 | ], 92 | timestamp: 'May 12', 93 | user: { 94 | fullName: 'Nicolas', 95 | screenName: 'necolas', 96 | profileImageUrl: 97 | 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg' 98 | } 99 | } 100 | 101 | const renderTweet = label => 102 | createRenderBenchmark({ 103 | name: `Tweet [${label}]`, 104 | runs: 10, 105 | getElement() { 106 | return ( 107 |
108 | 109 | 110 |
111 | ) 112 | } 113 | }) 114 | 115 | export default renderTweet 116 | -------------------------------------------------------------------------------- /packages/core/src/index.js: -------------------------------------------------------------------------------- 1 | import hashString from './hash' 2 | import StyleSheet from './sheet' 3 | import Stylis from './stylis' 4 | 5 | const stylis = new Stylis() 6 | 7 | export const sheet = new StyleSheet() 8 | 9 | sheet.inject() 10 | 11 | export const registered = {} 12 | 13 | export const inserted = {} 14 | 15 | function plugin(context, content, selector, parent) { 16 | switch (context) { 17 | // on property declaration 18 | case 1: 19 | return registered[content] 20 | // after a selector block 21 | case 2: { 22 | if (parent.length !== 0 && parent[0] === selector[0]) { 23 | break 24 | } 25 | } 26 | // after an at rule block 27 | case 3: // eslint-disable-line no-fallthrough 28 | sheet.insert(`${selector.join(',')}{${content}}`) 29 | } 30 | } 31 | 32 | stylis.use(plugin) 33 | 34 | const hyphenateRegex = /[A-Z]|^ms/g 35 | 36 | function flatten(inArr) { 37 | let arr = [] 38 | inArr.forEach(val => { 39 | if (Array.isArray(val)) arr = arr.concat(flatten(val)) 40 | else arr = arr.concat(val) 41 | }) 42 | 43 | return arr 44 | } 45 | 46 | function handleInterpolation(interpolation) { 47 | if (typeof interpolation === 'object') { 48 | return createStringFromObject(interpolation) 49 | } 50 | return interpolation 51 | } 52 | 53 | function createStringFromObject(obj) { 54 | let string = '' 55 | 56 | if (Array.isArray(obj)) { 57 | flatten(obj).forEach(interpolation => { 58 | string += handleInterpolation(interpolation) 59 | }) 60 | } else { 61 | Object.keys(obj).forEach(key => { 62 | if (typeof obj[key] === 'string') { 63 | string += `${key.replace(hyphenateRegex, '-$&').toLowerCase()}:${obj[ 64 | key 65 | ]};` 66 | } else { 67 | string += `${key}{${createStringFromObject(obj[key])}}` 68 | } 69 | }) 70 | } 71 | return string 72 | } 73 | 74 | function createCss(strings, ...interpolations) { 75 | let thing = strings[0] || '' 76 | interpolations.forEach((interpolation, i) => { 77 | if (typeof interpolation === 'string') { 78 | thing += interpolation 79 | } else if (typeof interpolation === 'object') { 80 | thing += createStringFromObject(interpolation) 81 | } 82 | thing += strings[i + 1] 83 | }) 84 | return thing 85 | } 86 | 87 | export function css(...args) { 88 | const thing = createCss(...args) 89 | const hash = hashString(thing) 90 | const cls = `css-${hash}` 91 | if (registered[cls] === undefined) { 92 | registered[cls] = thing 93 | } 94 | if (inserted[cls] === undefined) { 95 | stylis(`.${cls}`, thing) 96 | inserted[cls] = true 97 | } 98 | return cls 99 | } 100 | 101 | export function injectGlobal(...args) { 102 | const thing = createCss(...args) 103 | const hash = hashString(thing) 104 | if (inserted[hash] === undefined) { 105 | stylis('', thing) 106 | inserted[hash] = true 107 | } 108 | } 109 | 110 | export function hydrate(ids) { 111 | ids.forEach(id => { 112 | inserted[id] = true 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /packages/react/src/index.js: -------------------------------------------------------------------------------- 1 | import { createElement } from 'react' 2 | import { css } from 'new-css-in-js' 3 | 4 | export * from 'new-css-in-js' 5 | 6 | // use babel-plugin-codegen to make this later 7 | const reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|accept|acceptCharset|accessKey|action|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|default|defer|dir|disabled|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|itemProp|itemScope|itemType|itemID|itemRef|results|security|unselectable)|(on[A-Z].*)|((data|aria)-.*))$/ 8 | 9 | const testOmitPropsOnStringTag = key => reactPropsRegex.test(key) 10 | const testOmitPropsOnComponent = key => key !== 'theme' && key !== 'innerRef' 11 | 12 | const omitAssign = function(testFn, target) { 13 | let i = 2 14 | let length = arguments.length 15 | for (; i < length; i++) { 16 | let source = arguments[i] 17 | let key 18 | for (key in source) { 19 | if (testFn(key)) { 20 | target[key] = source[key] 21 | } 22 | } 23 | } 24 | return target 25 | } 26 | 27 | export default function(tag) { 28 | return (initialStrings, ...initialInterpolations) => { 29 | const baseTag = tag.__emotion_base || tag 30 | 31 | const omitFn = 32 | typeof baseTag === 'string' 33 | ? testOmitPropsOnStringTag 34 | : testOmitPropsOnComponent 35 | const strings = 36 | tag.__emotion_strings !== undefined 37 | ? tag.__emotion_strings.concat(initialStrings) 38 | : initialStrings 39 | const interpolations = 40 | tag.__emotion_interp !== undefined 41 | ? tag.__emotion_interp.concat([';'], initialInterpolations) 42 | : initialInterpolations 43 | const Styled = (props, context) => { 44 | let className = css( 45 | strings, 46 | ...interpolations.map(v => { 47 | if (typeof v === 'function') { 48 | return v(props, context) 49 | } 50 | return v 51 | }) 52 | ) 53 | if (props.className) { 54 | className += ` ${props.className}` 55 | } 56 | 57 | return createElement( 58 | baseTag, 59 | omitAssign(omitFn, {}, props, { className, ref: props.innerRef }) 60 | ) 61 | } 62 | Styled.__emotion_strings = strings 63 | Styled.__emotion_interp = interpolations 64 | Styled.__emotion_base = baseTag 65 | return Styled 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mitchell@mitchellhamilton.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/TweetTextPart/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Image, StyleSheet, Text } from 'react-native' 3 | import PropTypes from 'prop-types' 4 | import React from 'react' 5 | import theme from '../theme' 6 | 7 | const createTextEntity = ({ part }) => ( 8 | {`${part.prefix}${part.text}`} 9 | ) 10 | 11 | const createTwemojiEntity = ({ part }) => ( 12 | 18 | ) 19 | 20 | // @mention, #hashtag, $cashtag 21 | const createSymbolEntity = ({ displayMode, part }) => { 22 | const links = displayMode === 'links' 23 | return ( 24 | 29 | {`${part.prefix}${part.text}`} 30 | 31 | ) 32 | } 33 | 34 | // internal links 35 | const createLinkEntity = ({ displayMode, part }) => { 36 | const { displayUrl, linkRelation, url } = part 37 | const links = displayMode === 'links' 38 | 39 | return ( 40 | 46 | {displayUrl} 47 | 48 | ) 49 | } 50 | 51 | // external links 52 | const createExternalLinkEntity = ({ displayMode, part }) => { 53 | const { displayUrl, linkRelation, url } = part 54 | const links = displayMode === 'links' 55 | 56 | return ( 57 | 64 | {displayUrl} 65 | 66 | ) 67 | } 68 | 69 | class TweetTextPart extends React.Component { 70 | static displayName = 'TweetTextPart' 71 | 72 | static propTypes = { 73 | displayMode: PropTypes.oneOf(['links', 'no-links']), 74 | part: PropTypes.object 75 | } 76 | 77 | static defaultProps = { 78 | displayMode: 'links' 79 | } 80 | 81 | render() { 82 | let renderer 83 | const { 84 | isEmoji, 85 | isEntity, 86 | isHashtag, 87 | isMention, 88 | isMedia, 89 | isUrl 90 | } = this.props.part 91 | 92 | if (isEmoji || isEntity || isUrl || isMedia) { 93 | if (isUrl) { 94 | renderer = createExternalLinkEntity 95 | } else if (isHashtag || isMention) { 96 | renderer = createSymbolEntity 97 | } else if (isEmoji) { 98 | renderer = createTwemojiEntity 99 | } else { 100 | renderer = createLinkEntity 101 | } 102 | } else { 103 | renderer = createTextEntity 104 | } 105 | 106 | return renderer(this.props) 107 | } 108 | } 109 | 110 | const styles = StyleSheet.create({ 111 | link: { 112 | color: theme.colors.blue, 113 | textDecorationLine: 'none', 114 | unicodeBidi: 'embed' 115 | }, 116 | twemoji: { 117 | display: 'inline-block', 118 | height: '1.25em', 119 | width: '1.25em', 120 | paddingRight: '0.05em', 121 | paddingLeft: '0.1em', 122 | verticalAlign: '-0.2em' 123 | } 124 | }) 125 | 126 | export default TweetTextPart 127 | -------------------------------------------------------------------------------- /packages/core/__tests__/__snapshots__/css.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`css @supports 1`] = ` 4 | @supports (display:grid) { 5 | .glamor-0 { 6 | display: grid; 7 | } 8 | } 9 | 10 |
13 | `; 14 | 15 | exports[`css css variables 1`] = ` 16 | .glamor-0 { 17 | --some-var: 1px; 18 | width: var(--some-var); 19 | } 20 | 21 |
24 | `; 25 | 26 | exports[`css empty rule 1`] = ` 27 |
30 | `; 31 | 32 | exports[`css float property 1`] = ` 33 | .glamor-0 { 34 | float: left; 35 | } 36 | 37 |
40 | `; 41 | 42 | exports[`css handles more than 10 dynamic properties 1`] = ` 43 | .glamor-0 { 44 | text-decoration: underline; 45 | border-right: solid blue 54px; 46 | background: white; 47 | color: black; 48 | display: block; 49 | border-radius: 3px; 50 | padding: 25px; 51 | width: 500px; 52 | z-index: ; 53 | font-size: 18px; 54 | text-align: center; 55 | } 56 | 57 |
60 | `; 61 | 62 | exports[`css nested 1`] = ` 63 | .glamor-0 .some-class .some-other-class { 64 | background-color: hotpink; 65 | } 66 | 67 | .glamor-0 .some-class { 68 | display: -webkit-box; 69 | display: -webkit-flex; 70 | display: -ms-flexbox; 71 | display: flex; 72 | } 73 | 74 | .glamor-0 { 75 | color: yellow; 76 | } 77 | 78 | @media (max-width:600px) { 79 | .glamor-0 .some-class { 80 | background-color: pink; 81 | } 82 | } 83 | 84 |
87 |
90 |
93 |
94 |
95 | `; 96 | 97 | exports[`css nested at rules 1`] = ` 98 | @supports (display:flex) { 99 | .glamor-0 { 100 | display: -webkit-box; 101 | display: -webkit-flex; 102 | display: -ms-flexbox; 103 | display: flex; 104 | } 105 | } 106 | 107 | @supports (display:grid) { 108 | .glamor-0 { 109 | display: grid; 110 | } 111 | } 112 | 113 | @media (max-width:500px) { 114 | .glamor-0 { 115 | color: hotpink; 116 | } 117 | } 118 | 119 | @media (min-width:420px) { 120 | .glamor-0 { 121 | color: pink; 122 | } 123 | } 124 | 125 |
128 | `; 129 | 130 | exports[`css random expression 1`] = ` 131 | .glamor-0 { 132 | font-size: 20px; 133 | background: green; 134 | } 135 | 136 | @media (min-width:420px) { 137 | .glamor-0 { 138 | color: blue; 139 | width: 96px; 140 | height: 96px; 141 | line-height: 40px; 142 | } 143 | } 144 | 145 |
148 | `; 149 | 150 | exports[`css with array interpolation 1`] = ` 151 | .glamor-0 { 152 | display: -webkit-box; 153 | display: -webkit-flex; 154 | display: -ms-flexbox; 155 | display: flex; 156 | -webkit-box-pack: center; 157 | -webkit-justify-content: center; 158 | -ms-flex-pack: center; 159 | justify-content: center; 160 | align-items: center; 161 | } 162 | 163 |
166 | `; 167 | 168 | exports[`css with object interpolation 1`] = ` 169 | .glamor-0 { 170 | display: -webkit-box; 171 | display: -webkit-flex; 172 | display: -ms-flexbox; 173 | display: flex; 174 | } 175 | 176 |
179 | `; 180 | -------------------------------------------------------------------------------- /packages/core/src/sheet.js: -------------------------------------------------------------------------------- 1 | function last(arr) { 2 | return arr[arr.length - 1] 3 | } 4 | 5 | function sheetForTag(tag) { 6 | if (tag.sheet) { 7 | return tag.sheet 8 | } 9 | 10 | // this weirdness brought to you by firefox 11 | for (let i = 0; i < document.styleSheets.length; i++) { 12 | if (document.styleSheets[i].ownerNode === tag) { 13 | return document.styleSheets[i] 14 | } 15 | } 16 | } 17 | 18 | const isBrowser: boolean = typeof window !== 'undefined' 19 | const isDev: boolean = process.env.NODE_ENV !== 'production' 20 | 21 | function makeStyleTag() { 22 | let tag = document.createElement('style') 23 | tag.type = 'text/css' 24 | tag.setAttribute('data-new-css-in-js', '') 25 | tag.appendChild(document.createTextNode('')) 26 | document.head.appendChild(tag) 27 | return tag 28 | } 29 | 30 | export default class StyleSheet { 31 | constructor(speedy = !isDev) { 32 | this.isSpeedy = speedy // the big drawback here is that the css won't be editable in devtools 33 | this.sheet = undefined 34 | this.tags = [] 35 | this.maxLength = 65000 36 | this.ctr = 0 37 | } 38 | getSheet() { 39 | return sheetForTag(last(this.tags)) 40 | } 41 | inject() { 42 | if (this.injected) { 43 | throw new Error('already injected!') 44 | } 45 | if (isBrowser) { 46 | this.tags[0] = makeStyleTag() 47 | } else { 48 | // server side 'polyfill'. just enough behavior to be useful. 49 | this.sheet = { 50 | cssRules: [], 51 | insertRule: rule => { 52 | // enough 'spec compliance' to be able to extract the rules later 53 | // in other words, just the cssText field 54 | this.sheet.cssRules.push({ cssText: rule }) 55 | } 56 | } 57 | } 58 | this.injected = true 59 | } 60 | speedy(bool) { 61 | if (this.ctr !== 0) { 62 | // cannot change speedy mode after inserting any rule to sheet. Either call speedy(${bool}) earlier in your app, or call flush() before speedy(${bool}) 63 | throw new Error(`cannot change speedy now`) 64 | } 65 | this.isSpeedy = !!bool 66 | } 67 | _insert(rule) { 68 | // this weirdness for perf, and chrome's weird bug 69 | // https://stackoverflow.com/questions/20007992/chrome-suddenly-stopped-accepting-insertrule 70 | let sheet = this.getSheet() 71 | sheet.insertRule(rule, sheet.cssRules.length) 72 | } 73 | insert(rule) { 74 | if (isBrowser) { 75 | // this is the ultrafast version, works across browsers 76 | if (this.isSpeedy && this.getSheet().insertRule) { 77 | this._insert(rule) 78 | } else { 79 | // more browser weirdness. I don't even know 80 | // else if(this.tags.length > 0 && this.tags::last().styleSheet) { 81 | // this.tags::last().styleSheet.cssText+= rule 82 | // } 83 | last(this.tags).appendChild(document.createTextNode(rule)) 84 | } 85 | } else { 86 | // server side is pretty simple 87 | this.sheet.insertRule(rule) 88 | } 89 | 90 | this.ctr++ 91 | if (isBrowser && this.ctr % this.maxLength === 0) { 92 | this.tags.push(makeStyleTag()) 93 | } 94 | return this.ctr - 1 95 | } 96 | flush() { 97 | if (isBrowser) { 98 | this.tags.forEach(tag => tag.parentNode.removeChild(tag)) 99 | this.tags = [] 100 | this.sheet = null 101 | this.ctr = 0 102 | // todo - look for remnants in document.styleSheets 103 | } else { 104 | // simpler on server 105 | this.sheet.cssRules = [] 106 | } 107 | this.injected = false 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /packages/core/__tests__/css.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'enzyme' 3 | import serializer from 'jest-glamor-react' 4 | import { css, sheet } from '../src' 5 | 6 | expect.addSnapshotSerializer(serializer(sheet)) 7 | 8 | describe('css', () => { 9 | test('float property', () => { 10 | const cls1 = css`float: left;` 11 | 12 | const tree = render(
) 13 | expect(tree).toMatchSnapshot() 14 | }) 15 | 16 | test('handles more than 10 dynamic properties', () => { 17 | const cls1 = css` 18 | text-decoration: ${'underline'}; 19 | border-right: solid blue 54px; 20 | background: ${'white'}; 21 | color: ${'black'}; 22 | display: ${'block'}; 23 | border-radius: ${'3px'}; 24 | padding: ${'25px'}; 25 | width: ${'500px'}; 26 | z-index: ${100}; 27 | font-size: ${'18px'}; 28 | text-align: ${'center'}; 29 | ` 30 | 31 | const tree = render(
) 32 | expect(tree).toMatchSnapshot() 33 | }) 34 | 35 | test('random expression', () => { 36 | const cls1 = css` 37 | font-size: 20px; 38 | @media (min-width: 420px) { 39 | color: blue; 40 | ${css` 41 | width: 96px; 42 | height: 96px; 43 | `}; 44 | line-height: 40px; 45 | } 46 | background: green; 47 | ` 48 | const tree = render(
) 49 | expect(tree).toMatchSnapshot() 50 | }) 51 | test('@supports', () => { 52 | const cls1 = css`@supports (display: grid) {display: grid;}` 53 | const tree = render(
) 54 | expect(tree).toMatchSnapshot() 55 | }) 56 | test('nested at rules', () => { 57 | const cls1 = css` 58 | @supports (display: grid) { 59 | display: grid; 60 | @supports (display: flex) { 61 | display: flex; 62 | } 63 | } 64 | @media (min-width: 420px) { 65 | color: pink; 66 | @media (max-width: 500px) { 67 | color: hotpink; 68 | } 69 | } 70 | ` 71 | const tree = render(
) 72 | expect(tree).toMatchSnapshot() 73 | }) 74 | test('nested', () => { 75 | const cls1 = css` 76 | color: yellow; 77 | & .some-class { 78 | display: flex; 79 | & .some-other-class { 80 | background-color: hotpink; 81 | } 82 | @media (max-width: 600px) { 83 | background-color: pink; 84 | } 85 | } 86 | &.another-class { 87 | display: flex; 88 | } 89 | ` 90 | const tree = render( 91 |
92 |
93 |
94 |
95 |
96 | ) 97 | expect(tree).toMatchSnapshot() 98 | }) 99 | test('empty rule', () => { 100 | const cls1 = css`` 101 | 102 | const tree = render(
) 103 | expect(tree).toMatchSnapshot() 104 | }) 105 | test('css variables', () => { 106 | const cls1 = css` 107 | --some-var: 1px; 108 | width: var(--some-var); 109 | ` 110 | const tree = render(
) 111 | expect(tree).toMatchSnapshot() 112 | }) 113 | test('with object interpolation', () => { 114 | const cls1 = css`${{ display: 'flex' }};` 115 | const tree = render(
) 116 | expect(tree).toMatchSnapshot() 117 | }) 118 | test('with array interpolation', () => { 119 | const cls = css`align-items: center;` 120 | const cls1 = css`${[{ display: 'flex' }, `justify-content: center;`, cls]};` 121 | const tree = render(
) 122 | expect(tree).toMatchSnapshot() 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /packages/benchmarks/index.js: -------------------------------------------------------------------------------- 1 | // import aphrodite from './src/aphrodite' 2 | import cssModules from './src/css-modules' 3 | import emotion from './src/emotion' 4 | import emotionCSS from './src/emotion-css' 5 | import emotionObjStyle from './src/emotion-obj-style' 6 | import glamor from './src/glamor' 7 | import glamorous from './src/glamorous' 8 | import newCssInJs from './src/new-css-in-js' 9 | // import jss from './src/jss' 10 | // import radium from './src/radium' 11 | // import reactNative from './src/react-native' 12 | // import reactNativeStyleSheet from './src/react-native-stylesheet' 13 | import styledComponents from './src/styled-components' 14 | // import styledComponentsPrimitives from './src/styled-components-primitives' 15 | // import styletron from './src/styletron' 16 | // import xp from './src/reactxp' 17 | 18 | import renderDeepTree from './tests/renderDeepTree' 19 | // import renderTweet from './tests/renderTweet' 20 | import renderWideTree from './tests/renderWideTree' 21 | 22 | const allTests = { 23 | emotion: [ 24 | () => renderDeepTree('emotion', emotion), 25 | () => renderWideTree('emotion', emotion) 26 | ], 27 | emotionCSS: [ 28 | () => renderDeepTree('emotionCSS', emotionCSS), 29 | () => renderWideTree('emotionCSS', emotionCSS) 30 | ], 31 | emotionObjStyle: [ 32 | () => renderDeepTree('emotionObjStyle', emotionObjStyle), 33 | () => renderWideTree('emotionObjStyle', emotionObjStyle) 34 | ], 35 | glamor: [ 36 | () => renderDeepTree('glamor', glamor), 37 | () => renderWideTree('glamor', glamor) 38 | ], 39 | glamorous: [ 40 | () => renderDeepTree('glamorous', glamorous), 41 | () => renderWideTree('glamorous', glamorous) 42 | ], 43 | 'styled-components': [ 44 | () => renderDeepTree('styled-components', styledComponents), 45 | () => renderWideTree('styled-components', styledComponents) 46 | ], 47 | 'new-css-in-js': [ 48 | () => renderDeepTree('new-css-in-js', newCssInJs), 49 | () => renderWideTree('new-css-in-js', newCssInJs) 50 | ], 51 | 'css-modules': [ 52 | () => renderDeepTree('css-modules', cssModules), 53 | () => renderWideTree('css-modules', cssModules) 54 | ] 55 | } 56 | 57 | // const coreTests = [ 58 | // () => renderTweet('emotion', emotion), 59 | // () => renderDeepTree('emotionCSS', emotionCSS), 60 | // () => renderWideTree('emotionCSS', emotionCSS), 61 | // () => renderDeepTree('emotionObjStyle', emotionObjStyle), 62 | // () => renderWideTree('emotionObjStyle', emotionObjStyle), 63 | // () => renderTweet('react-native-web', reactNative), 64 | // () => renderDeepTree('css-modules', cssModules), 65 | // () => renderWideTree('css-modules', cssModules), 66 | // () => renderTweet('react-native-web/stylesheet', reactNativeStyleSheet), 67 | // () => renderDeepTree('react-native-web/stylesheet', reactNativeStyleSheet), 68 | // () => renderWideTree('react-native-web/stylesheet', reactNativeStyleSheet), 69 | // () => renderDeepTree('react-native-web', reactNative), 70 | // () => renderWideTree('react-native-web', reactNative) 71 | // () => renderTweet('glamor', glamor), 72 | // () => renderTweet('glamorous', glamorous), 73 | // () => renderTweet('styled-components', styledComponents), 74 | // ] 75 | 76 | const tests = [] 77 | 78 | if (window.location.hash) { 79 | window.location.hash 80 | .slice(1) 81 | .split(',') 82 | .forEach(test => { 83 | if (Array.isArray(allTests[test])) { 84 | tests.push(...allTests[test]) 85 | } else { 86 | throw new Error(`Benchmark for ${test} not found`) 87 | } 88 | }) 89 | } else { 90 | tests.push(...allTests['new-css-in-js']) 91 | tests.push(...allTests.emotion) 92 | tests.push(...allTests['css-modules']) 93 | tests.push(...allTests.glamorous) 94 | tests.push(...allTests['styled-components']) 95 | } 96 | 97 | tests.push(() => () => Promise.resolve(console.log('done'))) 98 | 99 | tests.reduce((promise, test) => promise.then(test()), Promise.resolve()) 100 | -------------------------------------------------------------------------------- /packages/react/__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`styled basic render 1`] = ` 4 | .glamor-0 { 5 | color: blue; 6 | font-size: ; 7 | } 8 | 9 | @media (min-width:520px) { 10 | .glamor-0 { 11 | color: green; 12 | } 13 | } 14 | 15 | @media (min-width:420px) { 16 | .glamor-0 { 17 | color: blue; 18 | } 19 | } 20 | 21 |

24 | hello world 25 |

26 | `; 27 | 28 | exports[`styled composing components 1`] = ` 29 | .glamor-0 { 30 | color: green; 31 | display: none; 32 | display: -webkit-box; 33 | display: -webkit-flex; 34 | display: -ms-flexbox; 35 | display: flex; 36 | -webkit-box-pack: center; 37 | -webkit-justify-content: center; 38 | -ms-flex-pack: center; 39 | justify-content: center; 40 | } 41 | 42 | 47 | `; 48 | 49 | exports[`styled composition 1`] = ` 50 | .glamor-0 { 51 | font-size: 20px; 52 | font-size: ; 53 | } 54 | 55 |

58 | hello world 59 |

60 | `; 61 | 62 | exports[`styled function in expression 1`] = ` 63 | .glamor-0 { 64 | font-size: 20px; 65 | font-size: 40px; 66 | } 67 | 68 |

71 | hello world 72 |

73 | `; 74 | 75 | exports[`styled handles more than 10 dynamic properties 1`] = ` 76 | .glamor-0 { 77 | text-decoration: underline; 78 | border-right: solid blue 54px; 79 | background: white; 80 | color: black; 81 | display: block; 82 | border-radius: 3px; 83 | padding: 25px; 84 | width: 500px; 85 | z-index: ; 86 | font-size: 18px; 87 | text-align: center; 88 | border-left: blue; 89 | } 90 | 91 |

94 | hello world 95 |

96 | `; 97 | 98 | exports[`styled innerRef 1`] = ` 99 | .glamor-0 { 100 | font-size: 12px; 101 | } 102 | 103 | 106 |

109 | hello world 110 |

111 |
112 | `; 113 | 114 | exports[`styled input placeholder 1`] = ` 115 | .glamor-0::placeholder { 116 | background-color: green; 117 | } 118 | 119 | 122 | `; 123 | 124 | exports[`styled nested 1`] = ` 125 | .glamor-1 div span { 126 | color: red; 127 | } 128 | 129 | .glamor-1 div { 130 | color: green; 131 | } 132 | 133 | .glamor-1 { 134 | display: -webkit-box; 135 | display: -webkit-flex; 136 | display: -ms-flexbox; 137 | display: flex; 138 | } 139 | 140 | .glamor-0 { 141 | font-size: 20px; 142 | } 143 | 144 |
147 | hello 148 |

151 | This will be green 152 |

153 | world 154 |
155 | `; 156 | 157 | exports[`styled no dynamic 1`] = ` 158 | .glamor-0 { 159 | float: left; 160 | } 161 | 162 |

165 | hello world 166 |

167 | `; 168 | 169 | exports[`styled no prop filtering on non string tags 1`] = ` 170 | 183 | hello world 184 | 185 | `; 186 | 187 | exports[`styled prop filtering 1`] = ` 188 | 195 | hello world 196 | 197 | `; 198 | 199 | exports[`styled prop filtering on composed styled components that are string tags 1`] = ` 200 | 207 | hello world 208 | 209 | `; 210 | 211 | exports[`styled random expressions undefined return 1`] = ` 212 | .glamor-0 { 213 | color: green; 214 | } 215 | 216 |

219 | hello world 220 |

221 | `; 222 | 223 | exports[`styled random object expression 1`] = ` 224 | .glamor-0 { 225 | background-color: hotpink; 226 | font-size: 1rem; 227 | margin-right: auto; 228 | margin-left: auto; 229 | color: green; 230 | } 231 | 232 |

235 | hello world 236 |

237 | `; 238 | -------------------------------------------------------------------------------- /packages/benchmarks/src/components/Tweet/index.js: -------------------------------------------------------------------------------- 1 | import AspectRatio from '../AspectRatio' 2 | import GridView from '../GridView' 3 | import PropTypes from 'prop-types' 4 | import TweetActionsBar from '../TweetActionsBar' 5 | import TweetText from '../TweetText' 6 | import UserAvatar from '../UserAvatar' 7 | import UserNames from '../UserNames' 8 | import { Image, StyleSheet, Text, View } from 'react-native' 9 | import React, { Component } from 'react' 10 | import theme from '../theme' 11 | 12 | export class Tweet extends Component { 13 | static displayName = 'Tweet' 14 | 15 | static propTypes = { 16 | tweet: PropTypes.object.isRequired 17 | } 18 | 19 | render() { 20 | const { tweet } = this.props 21 | const { id, lang, media, textParts, timestamp, user } = tweet 22 | const { fullName, profileImageUrl, screenName } = user 23 | 24 | return ( 25 | 26 | 27 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | {media ? ( 59 | 60 | 61 | 66 | 67 | 68 | ) : null} 69 | 70 | 71 | 90 | 91 | 92 | 93 | ) 94 | } 95 | } 96 | 97 | const styles = StyleSheet.create({ 98 | root: { 99 | paddingVertical: theme.createLength(theme.spaceY * 0.75, 'rem'), 100 | paddingHorizontal: theme.createLength(theme.spaceX, 'rem') 101 | }, 102 | avatarColumn: { 103 | flexGrow: 1, 104 | minWidth: 32 105 | }, 106 | bodyColumn: { 107 | flexGrow: 7 108 | }, 109 | row: { 110 | flexDirection: 'row', 111 | justifyContent: 'space-between' 112 | }, 113 | avatarLink: { 114 | display: 'block', 115 | flexShrink: 1, 116 | flexGrow: 0, 117 | width: '100%' 118 | }, 119 | avatar: { 120 | width: '100%' 121 | }, 122 | body: { 123 | marginTop: '-0.15rem' 124 | }, 125 | timestamp: { 126 | color: theme.colors.deepGray, 127 | marginLeft: theme.createLength(theme.spaceX, 'rem'), 128 | order: 1, 129 | textDecorationLine: 'none', 130 | whiteSpace: 'nowrap' 131 | }, 132 | actionBar: { 133 | marginTop: theme.createLength(theme.spaceY * 0.5, 'rem') 134 | }, 135 | richContent: { 136 | borderRadius: '0.35rem', 137 | marginTop: theme.createLength(theme.spaceY * 0.5, 'rem'), 138 | overflow: 'hidden' 139 | }, 140 | media: { 141 | ...StyleSheet.absoluteFillObject, 142 | margin: 'auto', 143 | width: 'auto', 144 | height: 'auto' 145 | } 146 | }) 147 | 148 | export default Tweet 149 | -------------------------------------------------------------------------------- /packages/react/__tests__/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import serializer from 'jest-glamor-react' 3 | import { css, sheet } from 'new-css-in-js' 4 | import styled from 'react-new-css-in-js' 5 | import { render, mount } from 'enzyme' 6 | 7 | expect.addSnapshotSerializer(serializer(sheet)) 8 | 9 | describe('styled', () => { 10 | test('no dynamic', () => { 11 | const H1 = styled('h1')`float: left;` 12 | 13 | const tree = render(

hello world

) 14 | 15 | expect(tree).toMatchSnapshot() 16 | }) 17 | 18 | test('basic render', () => { 19 | const fontSize = 20 20 | const H1 = styled('h1')` 21 | color: blue; 22 | font-size: ${fontSize}; 23 | @media (min-width: 420px) { 24 | color: blue; 25 | @media (min-width: 520px) { 26 | color: green; 27 | } 28 | } 29 | ` 30 | 31 | const tree = render(

hello world

) 32 | 33 | expect(tree).toMatchSnapshot() 34 | }) 35 | 36 | test('nested', () => { 37 | const fontSize = '20px' 38 | const H1 = styled('h1')`font-size: ${fontSize};` 39 | 40 | const Thing = styled('div')` 41 | display: flex; 42 | & div { 43 | color: green; 44 | 45 | & span { 46 | color: red; 47 | } 48 | } 49 | ` 50 | 51 | const tree = render( 52 | 53 | hello

This will be green

world 54 |
55 | ) 56 | 57 | expect(tree).toMatchSnapshot() 58 | }) 59 | 60 | test.skip('random expressions', () => { 61 | const margin = (t, r, b, l) => { 62 | return props => css` 63 | margin-top: ${t}; 64 | margin-right: ${r}; 65 | margin-bottom: ${b}; 66 | margin-left: ${l}; 67 | ` 68 | } 69 | 70 | const mq = css` 71 | @media (min-width: 420px) { 72 | color: blue; 73 | @media (min-width: 520px) { 74 | color: green; 75 | } 76 | } 77 | ` 78 | 79 | const H1 = styled('h1')` 80 | ${mq}; 81 | ${props => props.prop && css`font-size: 1rem;`}; 82 | ${margin(0, 'auto', 0, 'auto')}; 83 | color: green; 84 | ` 85 | 86 | const tree = render( 87 |

88 | hello world 89 |

90 | ) 91 | 92 | expect(tree).toMatchSnapshot() 93 | }) 94 | 95 | test('random expressions undefined return', () => { 96 | const H1 = styled('h1')` 97 | ${props => props.prop && css`font-size: 1rem;`}; 98 | color: green; 99 | ` 100 | 101 | const tree = render(

hello world

) 102 | 103 | expect(tree).toMatchSnapshot() 104 | }) 105 | 106 | test('random object expression', () => { 107 | const margin = (t, r, b, l) => { 108 | return props => ({ 109 | marginTop: t, 110 | marginRight: r, 111 | marginBottom: b, 112 | marginLeft: l 113 | }) 114 | } 115 | const H1 = styled('h1')` 116 | background-color: hotpink; 117 | ${props => props.prop && { fontSize: '1rem' }}; 118 | ${margin(0, 'auto', 0, 'auto')}; 119 | color: green; 120 | ` 121 | 122 | const tree = render( 123 |

124 | hello world 125 |

126 | ) 127 | 128 | expect(tree).toMatchSnapshot() 129 | }) 130 | 131 | test('composition', () => { 132 | const fontSize = 20 133 | const H1 = styled('h1')`font-size: ${fontSize + 'px'};` 134 | 135 | const H2 = styled(H1)`font-size: ${fontSize * 2 / 3};` 136 | 137 | const tree = render(

hello world

) 138 | 139 | expect(tree).toMatchSnapshot() 140 | }) 141 | 142 | test('input placeholder', () => { 143 | const Input = styled('input')` 144 | ::placeholder { 145 | background-color: green; 146 | } 147 | ` 148 | const tree = render() 149 | 150 | expect(tree).toMatchSnapshot() 151 | }) 152 | 153 | test('handles more than 10 dynamic properties', () => { 154 | const H1 = styled('h1')` 155 | text-decoration: ${'underline'}; 156 | border-right: solid blue 54px; 157 | background: ${'white'}; 158 | color: ${'black'}; 159 | display: ${'block'}; 160 | border-radius: ${'3px'}; 161 | padding: ${'25px'}; 162 | width: ${'500px'}; 163 | z-index: ${100}; 164 | font-size: ${'18px'}; 165 | text-align: ${'center'}; 166 | border-left: ${p => p.theme.blue}; 167 | ` 168 | 169 | const tree = render( 170 |

171 | hello world 172 |

173 | ) 174 | 175 | expect(tree).toMatchSnapshot() 176 | }) 177 | 178 | test('function in expression', () => { 179 | const fontSize = 20 180 | const H1 = styled('h1')`font-size: ${fontSize + 'px'};` 181 | 182 | const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale + 'px'};` 183 | 184 | const tree = render( 185 |

186 | hello world 187 |

188 | ) 189 | 190 | expect(tree).toMatchSnapshot() 191 | }) 192 | 193 | test('innerRef', () => { 194 | const H1 = styled('h1')`font-size: 12px;` 195 | 196 | const refFunction = jest.fn() 197 | 198 | const tree = mount(

hello world

) 199 | 200 | expect(tree).toMatchSnapshot() 201 | expect(refFunction).toBeCalled() 202 | }) 203 | 204 | test('composing components', () => { 205 | const Button = styled('button')`color: green;` 206 | const OtherButton = styled(Button)`display: none;` 207 | 208 | const AnotherButton = styled(OtherButton)` 209 | display: flex; 210 | justify-content: center; 211 | ` 212 | const tree = render(hello world) 213 | 214 | expect(tree).toMatchSnapshot() 215 | }) 216 | 217 | test('prop filtering', () => { 218 | const Link = styled('a')`color: green;` 219 | const rest = { m: [3], pt: [4] } 220 | 221 | const tree = render( 222 | 235 | hello world 236 | 237 | ) 238 | 239 | expect(tree).toMatchSnapshot() 240 | }) 241 | test('no prop filtering on non string tags', () => { 242 | const Link = styled(props => )`color: green;` 243 | 244 | const tree = render( 245 | 257 | hello world 258 | 259 | ) 260 | 261 | expect(tree).toMatchSnapshot() 262 | }) 263 | test('prop filtering on composed styled components that are string tags', () => { 264 | const BaseLink = styled('a')`background-color: hotpink;` 265 | const Link = styled(BaseLink)`color: green;` 266 | 267 | const tree = render( 268 | 280 | hello world 281 | 282 | ) 283 | 284 | expect(tree).toMatchSnapshot() 285 | }) 286 | }) 287 | -------------------------------------------------------------------------------- /packages/core/src/stylis.js: -------------------------------------------------------------------------------- 1 | var la=function ba(ca){function S(b,c,d,m){for(var a=0,h=0,k=0,f=0,l,n,e,v=0,z=0,A=0,x=0,t=l=0,q=0,p=0,w=n=0,H=0,B=d.length,I=B-1,u,g="",r="",R="",K="",E;pl)&&g.indexOf(" ")&&(g=g.replace(" ",":"))),0A||1>v[A-1].length)f=k+G+f}break;case 44:k="";default:f=1a&&(a=(c=c.trim()).charCodeAt(0));switch(a){case 38:switch(D+m){case 0:case 1:if(0===b.trim().length)break;default:return c.replace(K,"$1"+b.trim())}break;case 58:switch(c.charCodeAt(1)){case 103:if(0l||96l||95===l||45===l&&45!==k.charCodeAt(1)))switch(isNaN(parseFloat(k))+(-1!==k.indexOf("("))){case 1:switch(k){case "infinite":case "alternate":case "backwards":case "running":case "normal":case "forwards":case "both":case "none":case "linear":case "ease":case "ease-in":case "ease-out":case "ease-in-out":case "paused":case "reverse":case "alternate-reverse":case "inherit":case "initial":case "unset":case "step-start":case "step-end":break; 12 | default:k+=O}}f[m++]=k}b+=(0===h?"":",")+f.join(" ")}}else b+=110===c.charCodeAt(10)?d+(1===N?O:""):d;b=a+b+";";a=0b.charCodeAt(8))break;case 115:a=a.replace(b,"-webkit-"+b)+";"+a;break;case 207:case 102:a=a.replace(b,"-webkit-"+ 14 | (102m&&(m=(d=d.trim()).charCodeAt(0));0])/g,Da=/([[}=:>])\s+/g,Ea=/(\{[^{]+?);(?=\})/g,Fa=/\s{2,}/g,ra=/([^\(])(:+) */g,X=/[svh]\w+-[tblr]{2}/,xa=/([\w-]+t\()/g,na=/\(\s*([^]*?)\s*\)/g,Aa=/([^]*?);/g,y=1,F=1,J=0,D=1,P=1,V=1,ka=0,fa=0,U=0,T=[],Y=[],C=0,ha=0,N=1,O="",G="",Q="";w.use=Z;w.set=aa;void 0!==ca&&aa(ca);return w}; 20 | export default la --------------------------------------------------------------------------------