├── .gitignore
├── .travis.yml
├── test
├── setup.js
├── index.test.js
└── __snapshots__
│ └── index.test.js.snap
├── .babelrc
├── src
└── index.js
├── rollup.config.js
├── README.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /demo/dist
3 | dist/
4 | lib/
5 | node_modules/
6 | *.log
7 | .idea
8 | packages/site/build
9 | package-lock.json
10 | .DS_Store
11 | .cache
12 | public/
13 | yarn.lock
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "8"
5 |
6 | script: yarn test
7 |
8 | install: yarn
9 |
10 | after_success:
11 | - cat ./coverage/lcov.info | ./node_modules/codecov/bin/codecov
12 |
13 | cache:
14 | yarn: true
15 | directories:
16 | - node_modules
17 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jest */
2 | import serializer from 'jest-glamor-react'
3 | import 'jest-styled-components'
4 | import { sheet } from 'emotion'
5 | import { parse, stringify } from 'css'
6 |
7 | expect.addSnapshotSerializer(serializer(sheet))
8 |
9 | expect.addSnapshotSerializer({
10 | test: val => val === sheet,
11 | print(val, printer) {
12 | return printer(
13 | stringify(parse(sheet.tags.map(tag => tag.textContent || '').join('')))
14 | )
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "loose": true,
7 | "exclude": ["transform-es2015-typeof-symbol"]
8 | }
9 | ]
10 | ],
11 | "env": {
12 | "test": {
13 | "presets": [
14 | [
15 | "env",
16 | {
17 | "loose": true,
18 | "exclude": ["transform-es2015-typeof-symbol"]
19 | }
20 | ],
21 | "react"
22 | ],
23 | "plugins": ["emotion"]
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { css as magic } from 'emotion'
2 |
3 | export default function createResponsiveCss(breakpoints) {
4 | const mq = ['&'].concat(breakpoints)
5 |
6 | return function css() {
7 | const applied = [magic.apply(this, arguments)]
8 |
9 | function dynamicCss() {
10 | let cls = magic.apply(this, arguments)
11 | applied.push(cls)
12 | return dynamicCss
13 | }
14 |
15 | Object.defineProperty(dynamicCss, 'toString', {
16 | value() {
17 | // should return the sum of the styles to this point
18 | return magic.apply(
19 | this,
20 | applied.reduce((accum, cls, i) => {
21 | accum.push({ [mq[i]]: cls })
22 | return accum
23 | }, [])
24 | )
25 | }
26 | })
27 |
28 | return dynamicCss
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import uglify from 'rollup-plugin-uglify'
2 | import babel from 'rollup-plugin-babel'
3 | import path from 'path'
4 |
5 | const pkg = require(path.resolve(process.cwd(), './package.json'))
6 |
7 | const config = {
8 | entry: './src/index.js',
9 | sourceMap: true,
10 | plugins: [
11 | babel({
12 | presets: [
13 | [
14 | 'env',
15 | {
16 | loose: true,
17 | modules: false,
18 | exclude: ['transform-es2015-typeof-symbol']
19 | }
20 | ]
21 | ],
22 | babelrc: false
23 | })
24 | ],
25 | targets: [
26 | { dest: pkg.main, format: 'cjs' },
27 | { dest: pkg.module, format: 'es' }
28 | ]
29 | }
30 |
31 | if (process.env.UMD) {
32 | config.plugins.push(uglify())
33 | config.targets = [
34 | {
35 | dest: './dist/facepaint.umd.min.js',
36 | format: 'umd',
37 | moduleName: pkg.name
38 | }
39 | ]
40 | }
41 |
42 | export default config
43 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-sparse-arrays */
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import { sheet, flush } from 'emotion'
5 |
6 | import createResponsiveCss from '../src/index'
7 |
8 | const css = createResponsiveCss([
9 | '@media(min-width: 420px)',
10 | '@media(min-width: 920px)',
11 | '@media(min-width: 1120px)',
12 | '@media(min-width: 11200px)'
13 | ])
14 |
15 | describe('heptapod', () => {
16 | afterEach(() => flush())
17 | test('basic', () => {
18 | const cls3 = css`
19 | font-size: 16px;
20 | background: rgba(45, 213, 47, 0.11);
21 | color: aquamarine;
22 | ``
23 | background-color: hotpink;
24 | ``
25 | font-size: 16px;
26 | background: rgba(0, 0, 0, 0.11);
27 | `
28 |
29 | const tree = renderer
30 | .create(
Basic
)
31 | .toJSON()
32 | expect(tree).toMatchSnapshot()
33 | expect(sheet).toMatchSnapshot()
34 | })
35 |
36 | test('object styles', () => {
37 | const cls3 = css({
38 | fontSize: 16,
39 | background: 'rgba(45, 213, 47, 0.11)',
40 | color: 'aquamarine'
41 | })({ backgroundColor: 'hotpink' })({
42 | fontSize: 16,
43 | background: 'rgba(0, 0, 0, 0.11)'
44 | })
45 |
46 | const tree = renderer
47 | .create(Basic
)
48 | .toJSON()
49 | expect(tree).toMatchSnapshot()
50 | expect(sheet).toMatchSnapshot()
51 | })
52 | })
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Heptapod
2 |
3 | #### Experiments with tagged template literals and custom `css` functions for [emotion](https://emotion.sh)
4 |
5 | ## Install
6 |
7 | ```bash
8 | npm i heptapod -S
9 | ```
10 |
11 | **or**
12 |
13 | ```bash
14 | yarn add heptapod
15 | ```
16 |
17 | ---
18 |
19 | ```javascript
20 | import createResponsiveCss from 'heptapod'
21 |
22 | const css = createResponsiveCss([
23 | '@media(min-width: 420px)',
24 | '@media(min-width: 920px)',
25 | ])
26 |
27 | const cls3 = css`
28 | font-size: 16px;
29 | background: rgba(45, 213, 47, 0.11);
30 | color: aquamarine;
31 | ``
32 | background-color: hotpink;
33 | ``
34 | font-size: 16px;
35 | background: rgba(0, 0, 0, 0.11);
36 | `
37 |
38 | Basic
39 |
40 | ```
41 |
42 | This will insert the following styles into the current Stylesheet emotion is using.
43 |
44 | ```css
45 | .emotion-0 {
46 | font-size: 16px;
47 | background: rgba(45,213,47,0.11);
48 | color: aquamarine;
49 | }
50 |
51 | @media (min-width:420px) {
52 | .emotion-0 {
53 | background-color: hotpink;
54 | }
55 | }
56 |
57 | @media (min-width:920px) {
58 | .emotion-0 {
59 | font-size: 16px;
60 | background: rgba(0,0,0,0.11);
61 | }
62 | }
63 | ```
64 |
65 | It works for both string and object based styles. The following object styles will output the same styles as the string variant above.
66 |
67 | ```javascript
68 | const cls3 = css({
69 | fontSize: 16,
70 | background: 'rgba(45, 213, 47, 0.11)',
71 | color: 'aquamarine'
72 | })({ backgroundColor: 'hotpink' })({
73 | fontSize: 16,
74 | background: 'rgba(0, 0, 0, 0.11)'
75 | })
76 |
77 | Basic
78 | ```
79 |
80 |
81 |
82 |
83 | ## API
84 |
85 | #### createResponsiveCss `function`
86 |
87 | ```javascript
88 | import createResponsiveCss from 'heptapod'
89 |
90 | createResponsiveCss(selectors: Array) : DynamicStyleFunction
91 | ```
92 |
93 | **Arguments**
94 | * *breakpoints*
95 | ```javascript
96 | const customCssFunction = createResponsiveCss([
97 | '@media(min-width: 420px)',
98 | '@media(min-width: 920px)',
99 | '@media(min-width: 1120px)'
100 | ])
101 | ```
102 |
103 | **Returns**
104 |
105 | `heptapod` returns a function that can be used in place of emotion`s `css` function. This function can be partially applied to add further media query styles.
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/test/__snapshots__/index.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`heptapod basic 1`] = `
4 | .glamor-0 {
5 | font-size: 16px;
6 | background: rgba(45,213,47,0.11);
7 | color: aquamarine;
8 | }
9 |
10 | @media (min-width:420px) {
11 | .glamor-0 {
12 | background-color: hotpink;
13 | }
14 | }
15 |
16 | @media (min-width:920px) {
17 | .glamor-0 {
18 | font-size: 16px;
19 | background: rgba(0,0,0,0.11);
20 | }
21 | }
22 |
23 |
26 | Basic
27 |
28 | `;
29 |
30 | exports[`heptapod basic 2`] = `
31 | ".css-zkome7 {
32 | font-size: 16px;
33 | background: rgba(45,213,47,0.11);
34 | color: aquamarine;
35 | }
36 |
37 | .css-txi5oi {
38 | background-color: hotpink;
39 | }
40 |
41 | .css-126my4u {
42 | font-size: 16px;
43 | background: rgba(0,0,0,0.11);
44 | }
45 |
46 | .css-1u159q8 {
47 | font-size: 16px;
48 | background: rgba(45,213,47,0.11);
49 | color: aquamarine;
50 | }
51 |
52 | @media (min-width:420px) {
53 | .css-1u159q8 {
54 | background-color: hotpink;
55 | }
56 | }
57 |
58 | @media (min-width:920px) {
59 | .css-1u159q8 {
60 | font-size: 16px;
61 | background: rgba(0,0,0,0.11);
62 | }
63 | }"
64 | `;
65 |
66 | exports[`heptapod object styles 1`] = `
67 | .glamor-0 {
68 | font-size: 16px;
69 | background: rgba(45,213,47,0.11);
70 | color: aquamarine;
71 | }
72 |
73 | @media (min-width:420px) {
74 | .glamor-0 {
75 | background-color: hotpink;
76 | }
77 | }
78 |
79 | @media (min-width:920px) {
80 | .glamor-0 {
81 | font-size: 16px;
82 | background: rgba(0,0,0,0.11);
83 | }
84 | }
85 |
86 |
89 | Basic
90 |
91 | `;
92 |
93 | exports[`heptapod object styles 2`] = `
94 | ".css-4r6ctt {
95 | font-size: 16px;
96 | background: rgba(45,213,47,0.11);
97 | color: aquamarine;
98 | }
99 |
100 | .css-a2d05i {
101 | background-color: hotpink;
102 | }
103 |
104 | .css-aop3sx {
105 | font-size: 16px;
106 | background: rgba(0,0,0,0.11);
107 | }
108 |
109 | .css-1v9evw0 {
110 | font-size: 16px;
111 | background: rgba(45,213,47,0.11);
112 | color: aquamarine;
113 | }
114 |
115 | @media (min-width:420px) {
116 | .css-1v9evw0 {
117 | background-color: hotpink;
118 | }
119 | }
120 |
121 | @media (min-width:920px) {
122 | .css-1v9evw0 {
123 | font-size: 16px;
124 | background: rgba(0,0,0,0.11);
125 | }
126 | }"
127 | `;
128 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "heptapod",
3 | "version": "1.2.0",
4 | "description": "Responsive style values for css-in-js.",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "files": [
8 | "src",
9 | "dist"
10 | ],
11 | "scripts": {
12 | "build": "npm-run-all clean rollup rollup:umd",
13 | "clean": "rimraf dist",
14 | "test": "jest --coverage --no-cache --ci --runInBand",
15 | "rollup": "rollup -c rollup.config.js",
16 | "watch": "rollup -c rollup.config.js --watch",
17 | "rollup:umd": "cross-env UMD=true rollup -c rollup.config.js"
18 | },
19 | "devDependencies": {
20 | "babel-cli": "^6.24.1",
21 | "babel-eslint": "^7.2.3",
22 | "babel-jest": "^20.0.3",
23 | "babel-plugin-emotion": "^8.0.6",
24 | "babel-plugin-transform-define": "^1.3.0",
25 | "babel-preset-env": "^1.5.1",
26 | "babel-preset-react": "^6.24.1",
27 | "codecov": "^2.3.1",
28 | "cross-env": "^5.0.5",
29 | "css": "^2.2.1",
30 | "emotion": "^8.0.8",
31 | "eslint": "^4.5.0",
32 | "eslint-config-prettier": "^2.3.0",
33 | "eslint-config-react": "^1.1.7",
34 | "eslint-config-standard": "^10.2.1",
35 | "eslint-config-standard-react": "^5.0.0",
36 | "eslint-plugin-import": "^2.7.0",
37 | "eslint-plugin-node": "^5.1.1",
38 | "eslint-plugin-prettier": "^2.2.0",
39 | "eslint-plugin-promise": "^3.5.0",
40 | "eslint-plugin-react": "^7.3.0",
41 | "eslint-plugin-standard": "^3.0.1",
42 | "jest": "^20.0.4",
43 | "jest-cli": "^20.0.4",
44 | "jest-glamor-react": "^3.1.0",
45 | "jest-styled-components": "^4.9.0",
46 | "npm-run-all": "^4.0.2",
47 | "prettier": "^1.7.4",
48 | "prettier-eslint-cli": "^4.0.3",
49 | "react": "^16.0.0",
50 | "react-dom": "^16.0.0",
51 | "react-test-renderer": "^16.0.0",
52 | "rimraf": "^2.6.1",
53 | "rollup": "^0.43.0",
54 | "rollup-plugin-babel": "^2.7.1",
55 | "rollup-plugin-uglify": "^2.0.1",
56 | "rollup-watch": "^4.3.1",
57 | "styled-components": "^2.2.1"
58 | },
59 | "author": "Kye Hohenberger",
60 | "homepage": "https://github.com/emotion-js/facepaint",
61 | "license": "MIT",
62 | "repository": "https://github.com/emotion-js/facepaint",
63 | "keywords": [
64 | "styles",
65 | "emotion",
66 | "react",
67 | "css",
68 | "css-in-js"
69 | ],
70 | "bugs": {
71 | "url": "https://github.com/emotion-js/facepaint/issues"
72 | },
73 | "eslintConfig": {
74 | "extends": [
75 | "standard",
76 | "standard-react",
77 | "prettier",
78 | "prettier/react"
79 | ],
80 | "plugins": [
81 | "prettier"
82 | ],
83 | "parser": "babel-eslint",
84 | "rules": {
85 | "prettier/prettier": [
86 | "error",
87 | {
88 | "singleQuote": true,
89 | "semi": false
90 | }
91 | ],
92 | "react/prop-types": 0,
93 | "react/no-unused-prop-types": 0,
94 | "standard/computed-property-even-spacing": 0,
95 | "no-template-curly-in-string": 0
96 | },
97 | "overrides": [
98 | {
99 | "files": [
100 | "*.test.js"
101 | ],
102 | "env": {
103 | "jest": true
104 | }
105 | }
106 | ]
107 | },
108 | "jest": {
109 | "transform": {
110 | "^.+\\.js?$": "babel-jest"
111 | },
112 | "moduleNameMapper": {
113 | "^emotion-theming$": "/packages/facepaint/src"
114 | },
115 | "setupTestFrameworkScriptFile": "/test/setup.js"
116 | }
117 | }
118 |
--------------------------------------------------------------------------------