├── .babelrc
├── .gitignore
├── .npmignore
├── README.md
├── __snapshots__
└── test.js.snap
├── docs
├── Animations.js
├── App.js
├── Banner.js
├── Box.js
├── Btn.js
├── Cat.js
├── Columns.js
├── Demo.js
├── Example.js
├── Features.js
├── Flex.js
├── Footer.js
├── H1.js
├── Header.js
├── Heading.js
├── Icon.js
├── Input.js
├── Intro.js
├── Label.js
├── Lead.js
├── Link.js
├── ListItem.js
├── Pre.js
├── Slider.js
├── Spacer.js
├── Spin.js
├── Square.js
├── Text.js
├── Textarea.js
├── Theme.js
├── Theming.js
├── bundle.js
├── entry.js
├── icon.png
├── index.html
└── styles.js
├── package.json
├── src
└── index.js
├── test.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "env",
4 | "stage-0",
5 | "react"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | docs
2 | __snapshots__
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # diet-cola
3 |
4 | A lightweight
5 | [styled-components](https://github.com/styled-components/styled-components)
6 | clone for creating React UI component primitives.
7 | Built with
8 | [stylis](https://github.com/thysultan/stylis.js) and
9 | [glamor](https://github.com/threepointone/glamor).
10 |
11 | ```sh
12 | npm i diet-cola
13 | ```
14 |
15 | ```js
16 | import dc from 'diet-cola'
17 |
18 | const Button = dc('button')(`
19 | font-family: inherit;
20 | font-size: inherit;
21 | padding: 8px;
22 | margin: 0;
23 | color: white;
24 | background-color: tomato;
25 | border: 0;
26 | border-radius: 4px;
27 | appearance: none;
28 | &:hover {
29 | background-color: black;
30 | }
31 | `)
32 | ```
33 |
34 | ## Features
35 |
36 | - Under 10KB
37 | - Create UI component primitives with a simple API
38 | - Use plain CSS strings
39 | - Pseudoclass support
40 | - Media query support
41 | - CSS animation support
42 | - Injects style before rendering
43 | - Server-side rendering support
44 |
45 |
46 | ## Motivation
47 |
48 | Styled Components is an excellent API for creating UI component primitives in React,
49 | but in its current state, it has several features that might not be needed for most use cases.
50 | This library is also meant as a proof of concept of using existing libraries to create a small, custom css-in-js solution.
51 |
52 |
53 | ## Differences
54 |
55 | Compared to styled-components, diet-cola:
56 |
57 | - Has no theme support - (though thematic constants can be imported)
58 | - No dynamic styling
59 | - Styles injected on module instantiation, independent of render props
60 | - No React Native support
61 | - No management of HTML elements or attributes
62 | - Relies on the glamor and stylis libraries
63 | - Only 48 custom LOC
64 | - Potentially smaller and more performant
65 |
66 |
67 | ### Related:
68 |
69 | - [cxs-components](https://github.com/jxnblk/cxs/tree/master/packages/cxs-components)
70 | - [cxs](https://github.com/jxnblk/cxs)
71 | - [glamor](https://github.com/threepointone/glamor)
72 | - [stylis](https://github.com/thysultan/stylis.js)
73 | - [styled-components](https://github.com/styled-components/styled-components)
74 |
75 |
76 | MIT License
77 |
--------------------------------------------------------------------------------
/__snapshots__/test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`handles keyframes 1`] = `
4 |
9 | `;
10 |
11 | exports[`handles pseudoclasses 1`] = `
12 |
17 | `;
18 |
19 | exports[`renders 1`] = `
20 |
23 | Hello
24 |
25 | `;
26 |
27 | exports[`supports composition 1`] = `
28 |
31 | `;
32 |
--------------------------------------------------------------------------------
/docs/Animations.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Box = require('./Box')
3 | const Heading = require('./Heading')
4 | const Spin = require('./Spin')
5 | const Cat = require('./Cat')
6 |
7 | module.exports = () => (
8 |
9 | CSS Animations
10 |
11 |
12 |
13 |
14 | )
15 |
--------------------------------------------------------------------------------
/docs/App.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Header = require('./Header')
3 | const Intro = require('./Intro')
4 | const Features = require('./Features')
5 | const Example = require('./Example')
6 | const Demo = require('./Demo')
7 | const Animations = require('./Animations')
8 | // const Theming = require('./Theming')
9 | const Footer = require('./Footer')
10 |
11 | module.exports = class extends React.Component {
12 | state = {}
13 | update = fn => this.setState(fn)
14 |
15 | render () {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 | {/*
24 |
25 |
26 | */}
27 |
28 |
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/docs/Banner.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('header')(`
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | padding-left: 48px;
8 | padding-right: 48px;
9 | padding-top: 128px;
10 | padding-bottom: 128px;
11 | background-color: #0cf;
12 | background-image: linear-gradient(-60deg, rgba(255, 0, 0, 1), rgba(0, 255, 255, 1));
13 | background-blend-mode: overlay;
14 |
15 | animation-name: rotate;
16 | animation-duration: 16s;
17 | animation-timing-function: linear;
18 | animation-iteration-count: infinite;
19 |
20 | @keyframes rotate {
21 | 0% { background-color: #0cf }
22 | 33% { background-color: #f0c }
23 | 66% { background-color: #c0f }
24 | 100% { background-color: #0cf }
25 | }
26 | `)
27 |
--------------------------------------------------------------------------------
/docs/Box.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('div')(`padding:32px;`)
4 |
--------------------------------------------------------------------------------
/docs/Btn.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const { space, colors } = require('./styles')
3 |
4 | module.exports = dc('a')(`
5 | display: inline-flex;
6 | padding: ${space[1]}px;
7 | text-decoration: none;
8 | color: ${colors.white};
9 | background-color: ${colors.blue};
10 | border-radius: 4px;
11 | &:hover {
12 | box-shadow: inset 0 0 0 128px rgba(0, 0, 0, .125);
13 | }
14 | &:focus {
15 | outline: none;
16 | box-shadow: 0 0 0 2px ${colors.blue};
17 | }
18 | &:active {
19 | box-shadow: inset 0 0 0 128px rgba(0, 0, 0, .25);
20 | }
21 | `)
22 |
--------------------------------------------------------------------------------
/docs/Cat.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Square = require('./Square')
3 |
4 | module.exports = () => (
5 |
6 | )
7 |
--------------------------------------------------------------------------------
/docs/Columns.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('ul')(`
4 | list-style: none;
5 | padding: 0;
6 | column-width: 16em;
7 | `)
8 |
--------------------------------------------------------------------------------
/docs/Demo.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { throttle, debounce } = require('lodash')
3 | const dc = require('diet-cola')
4 | const Flex = require('./Flex')
5 | const Box = require('./Box')
6 | const Heading = require('./Heading')
7 | const Textarea = require('./Textarea')
8 |
9 | const initCss = `font-family: inherit;
10 | font-size: inherit;
11 | font-weight: bold;
12 | margin: 0;
13 | padding: 12px;
14 | color: #032;
15 | background-color: #0fc;
16 | border: 0;
17 | border-radius: 4px;
18 | appearance: none;
19 | &:hover {
20 | background-color: black;
21 | }
22 | `
23 |
24 | module.exports = class extends React.Component {
25 | state = {
26 | css: initCss,
27 | Btn: null
28 | }
29 |
30 | update = fn => this.setState(fn, () => {
31 | this.getBtn()
32 | })
33 |
34 | getBtn = throttle(() => {
35 | const Btn = dc('button')(this.state.css)
36 | this.setState({ Btn })
37 | }, 500)
38 |
39 | componentWillMount () {
40 | this.getBtn()
41 | }
42 |
43 | render () {
44 | const { Btn, css } = this.state
45 |
46 | return (
47 |
48 | Demo
49 |
50 |
63 |
64 | )
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/docs/Example.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Box = require('./Box')
3 | const Heading = require('./Heading')
4 | const Pre = require('./Pre')
5 |
6 | module.exports = () => (
7 |
8 | Example
9 |
10 |
11 | )
12 |
13 | const example = `import dc from 'diet-cola'
14 |
15 | const Heading = dc('h2')(\`
16 | font-size: 48px;
17 | color: tomato;
18 | \`)
19 | `
20 |
--------------------------------------------------------------------------------
/docs/Features.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Box = require('./Box')
3 | const Heading = require('./Heading')
4 | const Columns = require('./Columns')
5 | const ListItem = require('./ListItem')
6 |
7 | const features = [
8 | 'Under 10KB',
9 | 'Simple API',
10 | 'Use plain CSS strings',
11 | 'Pseudoclass support',
12 | 'Media query support',
13 | 'CSS animation support',
14 | 'No theming',
15 | 'No dynamic styling',
16 | 'Performant',
17 | 'Server-side rendering',
18 | 'Only 48 custom LOC'
19 | ]
20 |
21 | module.exports = () => (
22 |
23 | Features
24 |
25 | {features.map((feature, i) => (
26 |
30 | ))}
31 |
32 |
33 | )
34 |
--------------------------------------------------------------------------------
/docs/Flex.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('div')(`
4 | display: flex;
5 | align-items: center;
6 | `)
7 |
--------------------------------------------------------------------------------
/docs/Footer.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Flex = require('./Flex')
3 | const Box = require('./Box')
4 | const Link = require('./Link')
5 | const Spacer = require('./Spacer')
6 |
7 | module.exports = () => (
8 |
9 |
10 | GitHub
11 |
12 |
13 |
14 | Made by Jxnblk
15 |
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/docs/H1.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const { media } = require('./styles')
3 |
4 | module.exports = dc('h1')(`
5 | font-size: 32px;
6 | margin-top: .25em;
7 | margin-bottom: .25em;
8 |
9 | ${media[0]} {
10 | font-size: 48px;
11 | }
12 |
13 | ${media[1]} {
14 | font-size: 6vw;
15 | }
16 | `)
17 |
18 |
--------------------------------------------------------------------------------
/docs/Header.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Banner = require('./Banner')
3 | const H1 = require('./H1')
4 | const Lead = require('./Lead')
5 | const Link = require('./Link')
6 |
7 | module.exports = () => (
8 |
9 |
10 |
diet-cola
11 |
12 | A lightweight
13 | {' '}
14 |
15 | styled-components
16 |
17 | {' '}
18 | clone for creating React UI component primitives. Built with
19 | {' '}
20 |
21 | stylis
22 |
23 | {' '}
24 | and
25 | {' '}
26 |
27 | glamor
28 | . 10KB
29 |
30 |
31 | GitHub
32 |
33 |
34 |
35 | )
36 |
--------------------------------------------------------------------------------
/docs/Heading.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const { media, fs } = require('./styles')
3 |
4 | module.exports = dc('h2')(`
5 | font-size: ${fs[2]}px;
6 | text-transform: uppercase;
7 | letter-spacing: .2em;
8 | ${media[2]} {
9 | font-size: ${fs[3]}px;
10 | }
11 | `)
12 |
--------------------------------------------------------------------------------
/docs/Icon.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 |
3 | module.exports = () => (
4 |
40 | )
41 |
42 | const sx = {
43 | text: {
44 | fontFamily: '-apple-system, BlinkMacSystemFont, sans-serif',
45 | fontSize: 12,
46 | fontWeight: 600,
47 | // textTransform: 'uppercase'
48 | },
49 | cat: {}
50 | }
51 |
--------------------------------------------------------------------------------
/docs/Input.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('input')(`
4 | font-family: Menlo, monospace;
5 | font-size: 12px;
6 | margin: 0;
7 | padding: 4px;
8 | appearance: none;
9 | color: inherit;
10 | background-color: rgba(0, 0, 0, .0625);
11 | border: 0;
12 | border-radius: 4px;
13 | &:focus {
14 | outline: none;
15 | box-shadow: 0 0 0 2px rgba(0, 0, 0, .125);
16 | }
17 | `)
18 |
--------------------------------------------------------------------------------
/docs/Intro.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const Box = require('./Box')
3 | const Lead = require('./Lead')
4 |
5 | module.exports = () => (
6 |
7 |
8 | Styled Components is an excellent API for creating UI component primitives in React,
9 | but in its current state, it has several features that might not be needed for most use cases.
10 | This library is also meant as a proof of concept of using existing libraries to create a small, custom css-in-js solution.
11 |
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/docs/Label.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const { fs } = require('./styles')
3 |
4 | module.exports = dc('label')(`
5 | font-size: ${fs[0]}px;
6 | font-weight: bold;
7 | white-space: nowrap;
8 | `)
9 |
--------------------------------------------------------------------------------
/docs/Lead.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const Text = require('./Text')
3 | const { fs } = require('./styles')
4 |
5 | module.exports = dc(Text)(`
6 | font-size: ${fs[2]}px;
7 | max-width: 38em;
8 | `)
9 |
--------------------------------------------------------------------------------
/docs/Link.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const { colors } = require('./styles')
3 |
4 | module.exports = dc('a')(`
5 | text-decoration: none;
6 | font-weight: bold;
7 | color: inherit;
8 | &:hover {
9 | text-decoration: underline;
10 | }
11 | `)
12 |
--------------------------------------------------------------------------------
/docs/ListItem.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('li')(`
4 | font-weight: bold;
5 | margin-top: 1em;
6 | margin-bottom: 1em;
7 | break-inside: avoid;
8 |
9 | &:first-child {
10 | margin-top: 0;
11 | }
12 | `)
13 |
--------------------------------------------------------------------------------
/docs/Pre.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('pre')(`
4 | font-family: Menlo, monospace;
5 | font-size: 14px;
6 | `)
7 |
--------------------------------------------------------------------------------
/docs/Slider.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 | const { space, colors } = require('./styles')
3 |
4 | module.exports = dc('input', {
5 | type: 'range'
6 | })(`
7 | display: block;
8 | width: 100%;
9 | height: 4px;
10 | margin-top: 4px;
11 | margin-top: 4px;
12 | cursor: pointer;
13 | border-radius: 9999px;
14 | color: inherit;
15 | background-color: rgba(0, 0, 0, .125);
16 | appearance: none;
17 |
18 | &::-webkit-slider-thumb {
19 | width: 16px;
20 | height: 16px;
21 | background-color: currentcolor;
22 | border: 0;
23 | border-radius: 9999px;
24 | appearance: none;
25 | }
26 |
27 | &:focus {
28 | outline: none;
29 | &::-webkit-slider-thumb {
30 | background-color: ${colors.blue};
31 | }
32 | }
33 | `)
34 |
--------------------------------------------------------------------------------
/docs/Spacer.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('div')(`
4 | display: inline-block;
5 | padding: 8px
6 | `)
7 |
--------------------------------------------------------------------------------
/docs/Spin.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('div')(`
4 | display: inline-block;
5 | transform-origin: 50% 50%;
6 | animation-name: spin;
7 | animation-duration: 2s;
8 | animation-timing-function: linear;
9 | animation-iteration-count: infinite;
10 | @keyframes spin {
11 | 0% { transform: rotate(0deg) }
12 | 100% { transform: rotate(360deg) }
13 | }
14 | `)
15 |
--------------------------------------------------------------------------------
/docs/Square.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('div')(`
4 | font-size: 48px;
5 | line-height: 1;
6 | width: 1em;
7 | height: 1em;
8 | `)
9 |
--------------------------------------------------------------------------------
/docs/Text.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('p')(``)
4 |
--------------------------------------------------------------------------------
/docs/Textarea.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('textarea')(`
4 | font-family: Menlo, monospace;
5 | font-size: 12px;
6 | width: 100%;
7 | margin: 0;
8 | padding: 8px;
9 | color: inherit;
10 | background-color: rgba(0, 0, 0, .0625);
11 | border: 0;
12 | border-radius: 4px;
13 | &:focus {
14 | outline: none;
15 | box-shadow: 0 0 0 2px rgba(0, 0, 0, .125);
16 | }
17 | `)
18 |
--------------------------------------------------------------------------------
/docs/Theme.js:
--------------------------------------------------------------------------------
1 | const dc = require('diet-cola')
2 |
3 | module.exports = dc('div')(`
4 | color: var(--text);
5 | background-color: var(--bg);
6 | `)
7 |
--------------------------------------------------------------------------------
/docs/Theming.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const CSSVar = require('react-css-var')
3 | const hello = require('hello-color').default
4 | const chroma = require('chroma-js')
5 | const Flex = require('./Flex')
6 | const Box = require('./Box')
7 | const Heading = require('./Heading')
8 | const Text = require('./Text')
9 | const Label = require('./Label')
10 | const Input = require('./Input')
11 | const Slider = require('./Slider')
12 | const Theme = require('./Theme')
13 |
14 | module.exports = class extends React.Component {
15 | state = {
16 | h: 160,
17 | s: 1,
18 | l: .5
19 | }
20 |
21 | update = fn => this.setState(fn)
22 |
23 | render () {
24 | const { h, s, l } = this.state
25 | const bg = chroma.hsl([ h, s, l ]).css()
26 |
27 | const colors = {
28 | bg
29 | }
30 |
31 | return (
32 |
33 |
34 |
35 | Theming
36 | With CSS variables
37 |
38 |
39 |
43 | {
49 | const h = parseFloat(e.target.value)
50 | this.update(state => ({ h }))
51 | }}
52 | />
53 | {
59 | const h = parseFloat(e.target.value)
60 | this.update(state => ({ h }))
61 | }}
62 | />
63 |
64 |
65 |
69 | {
76 | const s = parseFloat(e.target.value)
77 | this.update(state => ({ s }))
78 | }}
79 | />
80 | {
87 | const s = parseFloat(e.target.value)
88 | this.update(state => ({ s }))
89 | }}
90 | />
91 |
92 |
93 |
97 | {
104 | const l = parseFloat(e.target.value)
105 | this.update(state => ({ l }))
106 | }}
107 | />
108 | {
115 | const l = parseFloat(e.target.value)
116 | this.update(state => ({ l }))
117 | }}
118 | />
119 |
120 |
121 |
122 |
123 | )
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/docs/entry.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { render } = require('react-dom')
3 | const App = require('./App')
4 |
5 | render(, app)
6 |
--------------------------------------------------------------------------------
/docs/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jxnblk/diet-cola/a9a041be101baf23a601f28d03650aa74e1ef7bc/docs/icon.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 | diet-cola
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/styles.js:
--------------------------------------------------------------------------------
1 |
2 | const fs = [
3 | 12,
4 | 16,
5 | 20,
6 | 24,
7 | 32,
8 | 48,
9 | 64
10 | ]
11 |
12 | const space = [
13 | 0,
14 | 8,
15 | 16,
16 | 32,
17 | 64
18 | ]
19 |
20 | const black = '#000'
21 | const white = '#fff'
22 | const blue = '#07c'
23 |
24 | const colors = {
25 | black,
26 | white,
27 | blue
28 | }
29 |
30 | const media = [
31 | 'min-width:40em',
32 | 'min-width:52em',
33 | 'min-width:64em',
34 | ].map(v => `@media screen and (${v})`)
35 |
36 | module.exports = {
37 | fs,
38 | space,
39 | colors,
40 | media
41 | }
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "diet-cola",
3 | "version": "1.0.0",
4 | "description": "A lightweight styled-components clone for creating React UI component primitives",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "prepublish": "babel src --out-dir dist",
8 | "dev": "webpack-dev-server",
9 | "docs": "webpack",
10 | "icon": "repng docs/Icon.js -w 512 -h 512 --out-dir docs --filename icon",
11 | "icon-dev": "repng docs/Icon.js --dev",
12 | "size": "npm run prepublish && bundle-size ./dist/index.js",
13 | "test": "ava"
14 | },
15 | "keywords": [
16 | "react",
17 | "react-component",
18 | "css-in-js",
19 | "stylis",
20 | "glamor"
21 | ],
22 | "author": "Brent Jackson",
23 | "license": "MIT",
24 | "dependencies": {
25 | "classnames": "^2.2.5",
26 | "glamor": "^2.20.25",
27 | "stylis": "^2.0.4"
28 | },
29 | "devDependencies": {
30 | "ava": "^0.19.1",
31 | "babel-cli": "^6.24.1",
32 | "babel-core": "^6.24.1",
33 | "babel-loader": "^7.0.0",
34 | "babel-preset-env": "^1.4.0",
35 | "babel-preset-react": "^6.24.1",
36 | "babel-preset-stage-0": "^6.24.1",
37 | "babel-register": "^6.24.1",
38 | "bundle-size": "^1.1.5",
39 | "chroma-js": "^1.3.3",
40 | "hello-color": "^1.0.1",
41 | "lodash": "^4.17.4",
42 | "react": "^15.5.4",
43 | "react-dom": "^15.5.4",
44 | "react-test-renderer": "^15.5.4",
45 | "repng": "^2.0.0-alpha.1",
46 | "webpack": "^2.5.1",
47 | "webpack-dev-server": "^2.4.5"
48 | },
49 | "ava": {
50 | "require": [
51 | "babel-register"
52 | ],
53 | "babel": "inherit"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { StyleSheet } = require('glamor/lib/sheet')
3 | const stylis = require('stylis')
4 | const classnames = require('classnames')
5 |
6 | let _id = 0
7 | const uuid = () => _id++
8 | const cache = {}
9 | const sheet = new StyleSheet()
10 |
11 | sheet.inject()
12 |
13 | const createComponent = (Tag = 'div', defaultProps) => (styles) => {
14 | const cn = cache[styles] || '_' + uuid().toString(36)
15 |
16 | if (!cache[styles]) {
17 | const css = stylis('.' + cn, styles)
18 | sheet.insert(css)
19 | cache[styles] = cn
20 | }
21 |
22 | const Component = props => (
23 |
28 | )
29 |
30 | return Component
31 | }
32 |
33 | module.exports = createComponent
34 |
35 | module.exports.sheet = sheet
36 | module.exports.cache = cache
37 |
38 | module.exports.getCss = () => {
39 | return sheet.rules()
40 | .map(r => r.cssText)
41 | .join('')
42 | }
43 |
44 | module.exports.clearCache = () => {
45 | sheet.flush()
46 | _id = 0
47 | for (let key in cache) {
48 | delete cache[key]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | const test = require('ava')
2 | const React = require('react')
3 | const render = require('react-test-renderer').create
4 | const dc = require('./src')
5 |
6 | test.afterEach(() => {
7 | dc.clearCache()
8 | })
9 |
10 | test('renders', t => {
11 | const Comp = dc('div')(`
12 | color: tomato;
13 | padding: 16px;
14 | `)
15 | const tree = render().toJSON()
16 | t.snapshot(tree)
17 | })
18 |
19 | test('handles pseudoclasses', t => {
20 | const Comp = dc('button')(`
21 | color: tomato;
22 | &:hover {
23 | color: black;
24 | }
25 | `)
26 | const tree = render().toJSON()
27 | const css = dc.getCss()
28 | t.snapshot(tree)
29 | t.regex(css, /:hover/)
30 | })
31 |
32 | test('handles keyframes', t => {
33 | const Comp = dc('button')(`
34 | animation: pulsate;
35 | @keyframes pulsate {
36 | 0% { opacity: 1 }
37 | 50% { opacity: 0 }
38 | 100% { opacity: 1 }
39 | }
40 | `)
41 | const tree = render().toJSON()
42 | const css = dc.getCss()
43 | t.snapshot(tree)
44 | t.regex(css, /@keyframes/)
45 | })
46 |
47 | test('dedupes repeated styles', t => {
48 | const One = dc('div')('color:tomato')
49 | const Two = dc('div')('color:tomato')
50 | const one = render().toJSON()
51 | const two = render().toJSON()
52 | t.is(one.props.className, two.props.className)
53 | })
54 |
55 | test('exports cache', t => {
56 | t.is(typeof dc.cache, 'object')
57 | })
58 |
59 | test('exports glamor stylesheet', t => {
60 | t.is(typeof dc.sheet, 'object')
61 | t.is(typeof dc.sheet.rules, 'function')
62 | t.is(typeof dc.sheet.flush, 'function')
63 | })
64 |
65 | test('exports getCss()', t => {
66 | t.is(typeof dc.getCss, 'function')
67 | })
68 |
69 | test('exports clearCache()', t => {
70 | t.is(typeof dc.clearCache, 'function')
71 | })
72 |
73 | test('supports server side rendering', t => {
74 | const Comp = dc('h1')(`color:tomato`)
75 | const css = dc.getCss()
76 | dc.clearCache()
77 | t.is(typeof css, 'string')
78 | t.is(css, '._0 {color:tomato}')
79 | })
80 |
81 | test('clears cache', t => {
82 | const Comp = dc('h1')('color:tomato')
83 | dc.clearCache()
84 | t.deepEqual(dc.cache, {})
85 | })
86 |
87 | test('supports composition', t => {
88 | const Tomato = dc('h1')('color:tomato')
89 | const BigTomato = dc(Tomato)('font-size:64px')
90 | const tree = render().toJSON()
91 | const css = dc.getCss()
92 | t.is(tree.props.className, '_0 _1')
93 | t.is(css, '._0 {color:tomato}._1 {font-size:64px}')
94 | t.snapshot(tree)
95 | })
96 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path')
3 |
4 | module.exports = {
5 | entry: './docs/entry.js',
6 |
7 | output: {
8 | path: path.join(__dirname, 'docs'),
9 | filename: 'bundle.js'
10 | },
11 |
12 | resolve: {
13 | alias: {
14 | 'diet-cola': path.join(__dirname, 'src/index')
15 | }
16 | },
17 |
18 | module: {
19 | rules: [
20 | {
21 | test: /\.js$/,
22 | exclude: /node_modules/,
23 | use: 'babel-loader'
24 | }
25 | ]
26 | },
27 |
28 | devServer: {
29 | contentBase: 'docs/',
30 | historyApiFallback: true
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------