├── .gitignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json └── src ├── components ├── custom-properties.js └── generator.js ├── default └── config.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # postgres data files 64 | pgdata 65 | 66 | # Vim swap files 67 | .swp 68 | 69 | # Mac slime 70 | .DS_Store 71 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | quoteProps: "consistent", 3 | printWidth: 110, 4 | tabWidth: 2, 5 | singleQuote: true, 6 | bracketSpacing: false 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Goron 2 | Goron is a simple design token utility class generator. 3 | 4 | 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 5 | 6 | **This is extremely under development and the docs are rubbish.** 7 | 8 | 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 9 | 10 | ## Getting started 11 | 12 | First up, install: 13 | 14 | ```bash 15 | npm install goron 16 | ``` 17 | 18 | Then run goron and determine your CSS output: 19 | 20 | ```bash 21 | goron -out src/css/tokens.css 22 | ``` 23 | 24 | ⚠️ You have to set a CSS file output for it to work properly. 25 | 26 | ## Config 27 | 28 | To customise the classes with your own config, create a file at the root of your project called `goron.config.js`. 29 | 30 | ### Default config 31 | 32 | If you don’t define a config in your project, Goron will use the default config which is this: 33 | 34 | ```javascript 35 | const colors = { 36 | primary: '#ff00ff', 37 | light: '#ffffff', 38 | dark: '#252525' 39 | }; 40 | 41 | const fonts = { 42 | base: 'Helvetica, sans-serif', 43 | serif: 'Georgia, serif' 44 | }; 45 | 46 | const sizeScale = { 47 | '300': '0.8rem', 48 | '400': '1rem', 49 | '500': '1.25rem', 50 | '600': '1.56rem', 51 | '700': '1.95rem', 52 | '800': '2.44rem', 53 | '900': '3.05rem' 54 | }; 55 | 56 | module.exports = { 57 | colors, 58 | sizeScale, 59 | fonts, 60 | generateCustomProperties: true, 61 | utilities: { 62 | 'bg': { 63 | items: colors, 64 | output: 'standard', 65 | property: 'background' 66 | }, 67 | 'color': { 68 | items: colors, 69 | output: 'standard', 70 | property: 'color' 71 | }, 72 | 'font': { 73 | items: fonts, 74 | output: 'standard', 75 | property: 'font-family' 76 | }, 77 | 'gap-top': { 78 | items: sizeScale, 79 | output: 'standard', 80 | property: 'margin-top' 81 | }, 82 | 'gap-bottom': { 83 | items: sizeScale, 84 | output: 'standard', 85 | property: 'margin-bottom' 86 | }, 87 | 'leading': { 88 | items: { 89 | tight: '1.2', 90 | mid: '1.5', 91 | loose: '1.7' 92 | }, 93 | output: 'standard', 94 | property: 'line-height' 95 | }, 96 | 'measure': { 97 | items: { 98 | long: '75ch', 99 | short: '60ch', 100 | compact: '40ch' 101 | }, 102 | output: 'standard', 103 | property: 'max-width' 104 | }, 105 | 'pad-top': { 106 | items: sizeScale, 107 | output: 'standard', 108 | property: 'padding-top' 109 | }, 110 | 'pad-bottom': { 111 | items: sizeScale, 112 | output: 'standard', 113 | property: 'padding-bottom' 114 | }, 115 | 'pad-left': { 116 | items: sizeScale, 117 | output: 'standard', 118 | property: 'padding-left' 119 | }, 120 | 'text': { 121 | items: sizeScale, 122 | output: 'responsive', 123 | property: 'font-size' 124 | }, 125 | 'weight': { 126 | items: { 127 | light: '300', 128 | regular: '400', 129 | mid: '600', 130 | bold: '700' 131 | }, 132 | output: 'standard', 133 | property: 'font-weight' 134 | } 135 | }, 136 | breakpoints: { 137 | md: '48em', 138 | lg: '68em' 139 | } 140 | }; 141 | ``` 142 | 143 | ## Generated classes example 144 | 145 | Let’s use the `leading` option from the default config for this. The config looks like this: 146 | 147 | ```js 148 | 'leading': { 149 | items: { 150 | tight: '1.2', 151 | mid: '1.5', 152 | loose: '1.7' 153 | }, 154 | output: 'standard', 155 | property: 'line-height' 156 | } 157 | ``` 158 | 159 | Goron takes that and generates the following CSS: 160 | 161 | ```css 162 | .leading-tight { 163 | line-height: 1.2; 164 | } 165 | 166 | .leading-mid { 167 | line-height: 1.5; 168 | } 169 | 170 | .leading-loose { 171 | line-height: 1.7; 172 | } 173 | ``` 174 | 175 | If I set the output to be `responsive` instead of `standard`, we get this: 176 | 177 | ```css 178 | .leading-tight { 179 | line-height: 1.2; 180 | } 181 | 182 | .leading-mid { 183 | line-height: 1.5; 184 | } 185 | 186 | .leading-loose { 187 | line-height: 1.7; 188 | } 189 | 190 | @media (min-width: 48em) { 191 | .md\:leading-tight { 192 | line-height: 1.2; 193 | } 194 | 195 | .md\:leading-mid { 196 | line-height: 1.5; 197 | } 198 | 199 | .md\:leading-loose { 200 | line-height: 1.7; 201 | } 202 | } 203 | 204 | @media (min-width: 68em) { 205 | .lg\:leading-tight { 206 | line-height: 1.2; 207 | } 208 | 209 | .lg\:leading-mid { 210 | line-height: 1.5; 211 | } 212 | 213 | .lg\:leading-loose { 214 | line-height: 1.7; 215 | } 216 | } 217 | ``` 218 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goron", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.5.5", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", 10 | "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", 11 | "requires": { 12 | "@babel/highlight": "^7.0.0" 13 | } 14 | }, 15 | "@babel/highlight": { 16 | "version": "7.5.0", 17 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", 18 | "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", 19 | "requires": { 20 | "chalk": "^2.0.0", 21 | "esutils": "^2.0.2", 22 | "js-tokens": "^4.0.0" 23 | }, 24 | "dependencies": { 25 | "ansi-styles": { 26 | "version": "3.2.1", 27 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 28 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 29 | "requires": { 30 | "color-convert": "^1.9.0" 31 | } 32 | }, 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "requires": { 38 | "ansi-styles": "^3.2.1", 39 | "escape-string-regexp": "^1.0.5", 40 | "supports-color": "^5.3.0" 41 | } 42 | }, 43 | "color-convert": { 44 | "version": "1.9.3", 45 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 46 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 47 | "requires": { 48 | "color-name": "1.1.3" 49 | } 50 | }, 51 | "color-name": { 52 | "version": "1.1.3", 53 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 54 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 55 | }, 56 | "has-flag": { 57 | "version": "3.0.0", 58 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 59 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 60 | }, 61 | "supports-color": { 62 | "version": "5.5.0", 63 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 64 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 65 | "requires": { 66 | "has-flag": "^3.0.0" 67 | } 68 | } 69 | } 70 | }, 71 | "@babel/runtime": { 72 | "version": "7.7.6", 73 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.6.tgz", 74 | "integrity": "sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==", 75 | "requires": { 76 | "regenerator-runtime": "^0.13.2" 77 | } 78 | }, 79 | "@types/color-name": { 80 | "version": "1.1.1", 81 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 82 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" 83 | }, 84 | "@types/parse-json": { 85 | "version": "4.0.0", 86 | "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", 87 | "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" 88 | }, 89 | "ansi-styles": { 90 | "version": "4.2.0", 91 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", 92 | "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", 93 | "requires": { 94 | "@types/color-name": "^1.1.1", 95 | "color-convert": "^2.0.1" 96 | } 97 | }, 98 | "balanced-match": { 99 | "version": "1.0.0", 100 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 101 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 102 | }, 103 | "brace-expansion": { 104 | "version": "1.1.11", 105 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 106 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 107 | "requires": { 108 | "balanced-match": "^1.0.0", 109 | "concat-map": "0.0.1" 110 | } 111 | }, 112 | "callsites": { 113 | "version": "3.1.0", 114 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 115 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" 116 | }, 117 | "chalk": { 118 | "version": "3.0.0", 119 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", 120 | "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", 121 | "requires": { 122 | "ansi-styles": "^4.1.0", 123 | "supports-color": "^7.1.0" 124 | } 125 | }, 126 | "clean-css": { 127 | "version": "4.2.1", 128 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", 129 | "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", 130 | "requires": { 131 | "source-map": "~0.6.0" 132 | } 133 | }, 134 | "color-convert": { 135 | "version": "2.0.1", 136 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 137 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 138 | "requires": { 139 | "color-name": "~1.1.4" 140 | } 141 | }, 142 | "color-name": { 143 | "version": "1.1.4", 144 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 145 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 146 | }, 147 | "concat-map": { 148 | "version": "0.0.1", 149 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 150 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 151 | }, 152 | "cosmiconfig": { 153 | "version": "6.0.0", 154 | "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", 155 | "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", 156 | "requires": { 157 | "@types/parse-json": "^4.0.0", 158 | "import-fresh": "^3.1.0", 159 | "parse-json": "^5.0.0", 160 | "path-type": "^4.0.0", 161 | "yaml": "^1.7.2" 162 | } 163 | }, 164 | "error-ex": { 165 | "version": "1.3.2", 166 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 167 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 168 | "requires": { 169 | "is-arrayish": "^0.2.1" 170 | } 171 | }, 172 | "escape-string-regexp": { 173 | "version": "1.0.5", 174 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 175 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 176 | }, 177 | "esutils": { 178 | "version": "2.0.3", 179 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 180 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 181 | }, 182 | "fs.realpath": { 183 | "version": "1.0.0", 184 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 185 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 186 | }, 187 | "glob": { 188 | "version": "7.1.6", 189 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 190 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 191 | "requires": { 192 | "fs.realpath": "^1.0.0", 193 | "inflight": "^1.0.4", 194 | "inherits": "2", 195 | "minimatch": "^3.0.4", 196 | "once": "^1.3.0", 197 | "path-is-absolute": "^1.0.0" 198 | } 199 | }, 200 | "has-flag": { 201 | "version": "4.0.0", 202 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 203 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 204 | }, 205 | "import-fresh": { 206 | "version": "3.2.1", 207 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 208 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 209 | "requires": { 210 | "parent-module": "^1.0.0", 211 | "resolve-from": "^4.0.0" 212 | } 213 | }, 214 | "inflight": { 215 | "version": "1.0.6", 216 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 217 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 218 | "requires": { 219 | "once": "^1.3.0", 220 | "wrappy": "1" 221 | } 222 | }, 223 | "inherits": { 224 | "version": "2.0.4", 225 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 226 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 227 | }, 228 | "interpret": { 229 | "version": "1.2.0", 230 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", 231 | "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" 232 | }, 233 | "is-arrayish": { 234 | "version": "0.2.1", 235 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 236 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 237 | }, 238 | "js-tokens": { 239 | "version": "4.0.0", 240 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 241 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 242 | }, 243 | "json-parse-better-errors": { 244 | "version": "1.0.2", 245 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 246 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" 247 | }, 248 | "lines-and-columns": { 249 | "version": "1.1.6", 250 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", 251 | "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" 252 | }, 253 | "minimatch": { 254 | "version": "3.0.4", 255 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 256 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 257 | "requires": { 258 | "brace-expansion": "^1.1.7" 259 | } 260 | }, 261 | "once": { 262 | "version": "1.4.0", 263 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 264 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 265 | "requires": { 266 | "wrappy": "1" 267 | } 268 | }, 269 | "parent-module": { 270 | "version": "1.0.1", 271 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 272 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 273 | "requires": { 274 | "callsites": "^3.0.0" 275 | } 276 | }, 277 | "parse-json": { 278 | "version": "5.0.0", 279 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", 280 | "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", 281 | "requires": { 282 | "@babel/code-frame": "^7.0.0", 283 | "error-ex": "^1.3.1", 284 | "json-parse-better-errors": "^1.0.1", 285 | "lines-and-columns": "^1.1.6" 286 | } 287 | }, 288 | "path-is-absolute": { 289 | "version": "1.0.1", 290 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 291 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 292 | }, 293 | "path-parse": { 294 | "version": "1.0.6", 295 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 296 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 297 | }, 298 | "path-type": { 299 | "version": "4.0.0", 300 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 301 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" 302 | }, 303 | "rechoir": { 304 | "version": "0.6.2", 305 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 306 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 307 | "requires": { 308 | "resolve": "^1.1.6" 309 | } 310 | }, 311 | "regenerator-runtime": { 312 | "version": "0.13.3", 313 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", 314 | "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" 315 | }, 316 | "resolve": { 317 | "version": "1.13.1", 318 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", 319 | "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", 320 | "requires": { 321 | "path-parse": "^1.0.6" 322 | } 323 | }, 324 | "resolve-from": { 325 | "version": "4.0.0", 326 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 327 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" 328 | }, 329 | "shelljs": { 330 | "version": "0.8.3", 331 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", 332 | "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", 333 | "requires": { 334 | "glob": "^7.0.0", 335 | "interpret": "^1.0.0", 336 | "rechoir": "^0.6.2" 337 | } 338 | }, 339 | "source-map": { 340 | "version": "0.6.1", 341 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 342 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 343 | }, 344 | "supports-color": { 345 | "version": "7.1.0", 346 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 347 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 348 | "requires": { 349 | "has-flag": "^4.0.0" 350 | } 351 | }, 352 | "wrappy": { 353 | "version": "1.0.2", 354 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 355 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 356 | }, 357 | "yaml": { 358 | "version": "1.7.2", 359 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", 360 | "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==", 361 | "requires": { 362 | "@babel/runtime": "^7.6.3" 363 | } 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goron", 3 | "version": "0.1.1", 4 | "description": "A simple design token utility class generator", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "bin": { 10 | "goron": "./src/index.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/hankchizljaw/goron.git" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/hankchizljaw/goron/issues" 21 | }, 22 | "homepage": "https://github.com/hankchizljaw/goron#readme", 23 | "dependencies": { 24 | "chalk": "^3.0.0", 25 | "clean-css": "^4.2.1", 26 | "cosmiconfig": "^6.0.0", 27 | "shelljs": "^0.8.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/custom-properties.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates CSS Custom Properties for colors, fonts and size scale 3 | * 4 | * @param {Object} config 5 | * @returns {String} 6 | */ 7 | module.exports = config => { 8 | let response = ''; 9 | const tokenKeys = [ 10 | {key: 'colors', prefix: 'color'}, 11 | {key: 'fonts', prefix: 'font'}, 12 | {key: 'sizeScale', prefix: 'size'} 13 | ]; 14 | 15 | if (!config.generateCustomProperties) { 16 | return response; 17 | } 18 | 19 | // Loops each option and if that config exists, it generates custom properties 20 | tokenKeys.forEach(tokenKey => { 21 | if (config.hasOwnProperty(tokenKey.key)) { 22 | Object.keys(config[tokenKey.key]).forEach(key => { 23 | response += `--${tokenKey.prefix}-${key}: ${config[tokenKey.key][key]};`; 24 | }); 25 | } 26 | }); 27 | 28 | // If we generated some props, wrap them in a :root selector 29 | if (response.length) { 30 | response = `:root { 31 | ${response} 32 | }`; 33 | } 34 | 35 | return response; 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/generator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Takes the config an optional array of targets and an 3 | * optional prefix which generates a CSS string of 4 | * utility classes 5 | * 6 | * @param {Object} config The goron config object 7 | * @param {Array} targets 8 | * @param {String} prefix='' 9 | * @returns {String} 10 | */ 11 | module.exports = (config, targets, prefix = '') => { 12 | let response = ''; 13 | 14 | Object.keys(config.utilities).forEach(key => { 15 | const data = config.utilities[key]; 16 | 17 | if (targets.includes(data.output)) { 18 | Object.keys(data.items).forEach(itemKey => { 19 | response += ` 20 | .${prefix}${key}-${itemKey} { 21 | ${data.property}: ${data.items[itemKey]}; 22 | }`.trim(); 23 | }); 24 | } 25 | }); 26 | 27 | return response; 28 | }; 29 | -------------------------------------------------------------------------------- /src/default/config.js: -------------------------------------------------------------------------------- 1 | const colors = { 2 | primary: '#ff00ff', 3 | light: '#ffffff', 4 | dark: '#252525' 5 | }; 6 | 7 | const fonts = { 8 | base: 'Helvetica, sans-serif', 9 | serif: 'Georgia, serif' 10 | }; 11 | 12 | const sizeScale = { 13 | '300': '0.8rem', 14 | '400': '1rem', 15 | '500': '1.25rem', 16 | '600': '1.56rem', 17 | '700': '1.95rem', 18 | '800': '2.44rem', 19 | '900': '3.05rem' 20 | }; 21 | 22 | module.exports = { 23 | colors, 24 | sizeScale, 25 | fonts, 26 | generateCustomProperties: true, 27 | utilities: { 28 | 'bg': { 29 | items: colors, 30 | output: 'standard', 31 | property: 'background' 32 | }, 33 | 'color': { 34 | items: colors, 35 | output: 'standard', 36 | property: 'color' 37 | }, 38 | 'font': { 39 | items: fonts, 40 | output: 'standard', 41 | property: 'font-family' 42 | }, 43 | 'gap-top': { 44 | items: sizeScale, 45 | output: 'standard', 46 | property: 'margin-top' 47 | }, 48 | 'gap-bottom': { 49 | items: sizeScale, 50 | output: 'standard', 51 | property: 'margin-bottom' 52 | }, 53 | 'leading': { 54 | items: { 55 | tight: '1.2', 56 | mid: '1.5', 57 | loose: '1.7' 58 | }, 59 | output: 'standard', 60 | property: 'line-height' 61 | }, 62 | 'measure': { 63 | items: { 64 | long: '75ch', 65 | short: '60ch', 66 | compact: '40ch' 67 | }, 68 | output: 'standard', 69 | property: 'max-width' 70 | }, 71 | 'pad-top': { 72 | items: sizeScale, 73 | output: 'standard', 74 | property: 'padding-top' 75 | }, 76 | 'pad-bottom': { 77 | items: sizeScale, 78 | output: 'standard', 79 | property: 'padding-bottom' 80 | }, 81 | 'pad-left': { 82 | items: sizeScale, 83 | output: 'standard', 84 | property: 'padding-left' 85 | }, 86 | 'text': { 87 | items: sizeScale, 88 | output: 'responsive', 89 | property: 'font-size' 90 | }, 91 | 'weight': { 92 | items: { 93 | light: '300', 94 | regular: '400', 95 | mid: '600', 96 | bold: '700' 97 | }, 98 | output: 'standard', 99 | property: 'font-weight' 100 | } 101 | }, 102 | breakpoints: { 103 | md: '48em', 104 | lg: '68em' 105 | } 106 | }; 107 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {cosmiconfig, cosmiconfigSync} = require('cosmiconfig'); 4 | const chalk = require('chalk'); 5 | const CleanCSS = require('clean-css'); 6 | const fs = require('fs'); 7 | const shell = require('shelljs'); 8 | 9 | const customProperties = require('./components/custom-properties.js'); 10 | const generator = require('./components/generator.js'); 11 | 12 | let config = require('./default/config.js'); 13 | 14 | // The main organ grinder 15 | const init = () => { 16 | let css = ''; 17 | const pathIndex = process.argv.indexOf('-out'); 18 | const cleanCSS = new CleanCSS(); 19 | 20 | // Try to load the user’s config 21 | const userConfig = cosmiconfigSync('goron', {searchPlaces: ['goron.config.js']}).search(); 22 | 23 | if (userConfig) { 24 | config = userConfig.config; 25 | } 26 | 27 | // Bail out if the path isn't defined 28 | if (pathIndex <= 0 && !config.hasOwnProperty('outputPath')) { 29 | console.log( 30 | chalk.red( 31 | `Please determine a path. You can do this by passing the path with a '-out' option or setting 'outputPath' in your config.` 32 | ) 33 | ); 34 | console.log(chalk.blue('Exiting.')); 35 | return; 36 | } 37 | 38 | const outputPath = config.outputPath || process.argv.slice(pathIndex + 1)[0]; 39 | 40 | // The path has to contain a filename so we need to bail if that's not the case 41 | if (outputPath.indexOf('.css') < 0) { 42 | console.log(chalk.red(`Please add a css file to your path.`)); 43 | console.log(chalk.red(`Example: path/to/my/folder/tokens.css`)); 44 | console.log(chalk.blue('Exiting.')); 45 | return; 46 | } 47 | 48 | // Add the custom props and the media query-less clases 49 | css += customProperties(config); 50 | css += generator(config, ['responsive', 'standard']); 51 | 52 | // If there's some breakpoints, generate the classes that are responsive 53 | Object.keys(config.breakpoints).forEach(key => { 54 | css += ` 55 | @media (min-width: ${config.breakpoints[key]}) { 56 | ${generator(config, ['responsive'], `${key}\\:`)} 57 | } 58 | `.trim(); 59 | }); 60 | 61 | css = cleanCSS.minify(css).styles; 62 | 63 | // Create the directory if it doesn't already exist 64 | if (!fs.existsSync(outputPath)) { 65 | shell.exec(`mkdir -p ${outputPath.replace(/[^\/]*$/, '')}`); 66 | } 67 | 68 | shell.exec(`echo "${css}" > ${outputPath}`); 69 | 70 | console.log(chalk.green('Token utility classes generated!')); 71 | }; 72 | 73 | init(); 74 | --------------------------------------------------------------------------------