├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .nvmrc ├── README.md ├── config ├── .eslintrc ├── postcss │ └── postcss.config.js └── webpack │ ├── babel.partial.js │ ├── html.partial.js │ ├── icon.partial.js │ ├── minify.partial.js │ ├── output.partial.js │ ├── stats.partial.js │ └── webpack.config.babel.js ├── package.json ├── src ├── 404.html ├── asset │ └── icon │ │ └── github.icon.svg ├── component │ ├── base │ │ ├── anchor │ │ │ ├── anchor.css.js │ │ │ ├── anchor.js │ │ │ └── index.js │ │ ├── bold │ │ │ ├── bold.css.js │ │ │ ├── bold.js │ │ │ └── index.js │ │ ├── code │ │ │ ├── code.css.js │ │ │ ├── code.js │ │ │ └── index.js │ │ ├── container │ │ │ ├── container.css.js │ │ │ ├── container.js │ │ │ └── index.js │ │ ├── flex │ │ │ ├── flex.config.js │ │ │ ├── flex.css.js │ │ │ ├── flex.js │ │ │ └── index.js │ │ ├── footer │ │ │ ├── footer.css.js │ │ │ ├── footer.js │ │ │ └── index.js │ │ ├── grid │ │ │ ├── grid.css.js │ │ │ ├── grid.js │ │ │ └── index.js │ │ ├── header │ │ │ ├── header.css.js │ │ │ ├── header.js │ │ │ ├── index.js │ │ │ └── title │ │ │ │ ├── index.js │ │ │ │ ├── title.css.js │ │ │ │ └── title.js │ │ ├── heading │ │ │ ├── heading.css.js │ │ │ ├── heading.js │ │ │ └── index.js │ │ ├── icon │ │ │ ├── icon.css.js │ │ │ ├── icon.js │ │ │ └── index.js │ │ ├── list │ │ │ ├── index.js │ │ │ ├── item │ │ │ │ ├── index.js │ │ │ │ ├── item.css.js │ │ │ │ └── item.js │ │ │ ├── list.css.js │ │ │ └── list.js │ │ ├── nav │ │ │ ├── index.js │ │ │ ├── item │ │ │ │ ├── index.js │ │ │ │ ├── item.css.js │ │ │ │ └── item.js │ │ │ └── nav.js │ │ ├── panel │ │ │ ├── index.js │ │ │ ├── panel.css.js │ │ │ └── panel.js │ │ ├── paragraph │ │ │ ├── index.js │ │ │ ├── paragraph.css.js │ │ │ └── paragraph.js │ │ ├── rich-text │ │ │ ├── index.js │ │ │ ├── rich-text.css.js │ │ │ └── rich-text.js │ │ ├── row │ │ │ ├── index.js │ │ │ ├── row.css.js │ │ │ └── row.js │ │ └── wrapper │ │ │ ├── index.js │ │ │ ├── wrapper.css.js │ │ │ └── wrapper.js │ ├── partial │ │ ├── footer │ │ │ ├── footer.js │ │ │ └── index.js │ │ ├── header │ │ │ ├── header.js │ │ │ └── index.js │ │ └── source-link │ │ │ ├── index.js │ │ │ ├── source-link.css.js │ │ │ └── source-link.js │ ├── root │ │ ├── index.js │ │ └── root.js │ └── view │ │ ├── components │ │ ├── components.css.js │ │ ├── components.js │ │ ├── demo │ │ │ ├── anchor.demo.js │ │ │ ├── bold.demo.js │ │ │ ├── code-block.demo.js │ │ │ ├── code-inline.demo.js │ │ │ ├── container.demo.js │ │ │ ├── flex.demo.js │ │ │ ├── grid-column.demo.js │ │ │ ├── grid-flex.demo.js │ │ │ ├── header-footer.demo.js │ │ │ ├── heading.demo.js │ │ │ ├── icon.demo.js │ │ │ ├── list-ordered.demo.js │ │ │ ├── list-unordered.demo.js │ │ │ ├── paragraph.demo.js │ │ │ └── rich-text.demo.js │ │ └── index.js │ │ ├── config │ │ ├── breakpoint-list │ │ │ ├── breakpoint-list.js │ │ │ └── index.js │ │ ├── color-list │ │ │ ├── color-list.css.js │ │ │ ├── color-list.js │ │ │ └── index.js │ │ ├── config.js │ │ ├── index.js │ │ ├── layout-list │ │ │ ├── index.js │ │ │ └── layout-list.js │ │ └── typography-list │ │ │ ├── index.js │ │ │ ├── typography-list.css.js │ │ │ └── typography-list.js │ │ └── landing │ │ ├── index.js │ │ └── landing.js ├── config │ ├── breakpoint.config.js │ ├── color.config.js │ ├── layout.config.js │ ├── typography.config.js │ └── url.config.js ├── global.css.js ├── index.html ├── index.js └── util │ ├── component.util.js │ ├── css.util.js │ └── url.util.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["metalab", { 4 | "targets": { 5 | "browsers": ["last 2 versions", "safari >= 7"] 6 | } 7 | }] 8 | ], 9 | "plugins": [ 10 | "babel-plugin-transform-runtime", 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # 2 | # EditorConfig: http://EditorConfig.org 3 | # 4 | # This files specifies some basic editor conventions for the files in this 5 | # project. Many editors support this standard, you simply need to find a plugin 6 | # for your favorite! 7 | # 8 | # For a full list of possible values consult the reference. 9 | # https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties 10 | # 11 | 12 | # Stop searching for other .editorconfig files above this folder. 13 | root = true 14 | 15 | # Pick some sane defaults for all files. 16 | [*] 17 | 18 | # UNIX line-endings are preferred. 19 | # http://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/ 20 | end_of_line = lf 21 | 22 | # No reason in these modern times to use anything other than UTF-8. 23 | charset = utf-8 24 | 25 | # Ensure that there's no bogus whitespace in the file. 26 | trim_trailing_whitespace = true 27 | 28 | # A little esoteric, but it's kind of a standard now. 29 | # http://stackoverflow.com/questions/729692/why-should-files-end-with-a-newline 30 | insert_final_newline = true 31 | 32 | # Pragmatism today. 33 | # http://programmers.stackexchange.com/questions/57 34 | indent_style = 2 35 | 36 | # Personal preference here. Smaller indent size means you can fit more on a line 37 | # which can be nice when there are lines with several indentations. 38 | indent_size = 2 39 | 40 | # Prefer a more conservative default line length – this allows editors with 41 | # sidebars, minimaps, etc. to show at least two documents side-by-side. 42 | # Hard wrapping by default for code is useful since many editors don't support 43 | # an elegant soft wrap; however, soft wrap is fine for things where text just 44 | # flows normally, like Markdown documents or git commit messages. Hard wrap 45 | # is also easier for line-based diffing tools to consume. 46 | # See: http://tex.stackexchange.com/questions/54140 47 | max_line_length = 80 48 | 49 | # Markdown uses trailing spaces to create line breaks. 50 | [*.md] 51 | trim_trailing_whitespace = false 52 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/**/* 2 | node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 2 | - metalab 3 | - metalab/react 4 | rules: 5 | func-style: 0 6 | globals: 7 | # Environment variables are injected as `process.env.ENV_VAR` by webpack. 8 | process: true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | build 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 6.3.1 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS JS Demo 2 | 3 | [Live Version] 4 | 5 | This is an example [React] project built using CSS JS styles parsed by [`css-js-loader`]. 6 | 7 | Be sure to read the companion [blog post]. 8 | 9 | ### Getting Started 10 | 11 | Clone the repo. 12 | 13 | ```shell 14 | git clone git@github.com:10xjs/css-js-demo && cd css-js-demo 15 | ``` 16 | 17 | Install and run using [`yarn`]: 18 | 19 | ```shell 20 | yarn && PORT=8080 yarn dev 21 | ``` 22 | 23 | _Or_ install and run using `npm`: 24 | 25 | ```shell 26 | npm i && PORT=8080 npm run dev 27 | ``` 28 | 29 | Visit [http://localhost:8080](http://localhost:8080) in your browser. 30 | 31 | [React]: https://facebook.github.io/react/ 32 | [`css-js-loader`]: https://github.com/10xjs/css-js-loader 33 | [blog post]: https://medium.com/@10xjs/better-css-with-javascript-88463deecf3 34 | [`yarn`]: https://yarnpkg.com/ 35 | [Live Version]: https://10xjs.github.io/css-js-demo/ 36 | -------------------------------------------------------------------------------- /config/.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | -------------------------------------------------------------------------------- /config/postcss/postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = { 3 | plugins: [ 4 | require('postcss-nesting'), 5 | require('autoprefixer')({browsers: ['last 2 versions']}), 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /config/webpack/babel.partial.js: -------------------------------------------------------------------------------- 1 | import {loader} from 'webpack-partial'; 2 | 3 | export default () => loader({ 4 | test: /\.js$/, 5 | exclude: /node_modules/, 6 | loader: require.resolve('babel-loader'), 7 | }); 8 | -------------------------------------------------------------------------------- /config/webpack/html.partial.js: -------------------------------------------------------------------------------- 1 | import {join} from 'path'; 2 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 3 | import {plugin} from 'webpack-partial'; 4 | 5 | export default ({template, ...options}) => (config) => 6 | plugin(new HtmlWebpackPlugin({ 7 | template: join(config.context, 'src', template), 8 | filename: 'index.html', 9 | inject: 'body', 10 | ...options, 11 | }))(config); 12 | -------------------------------------------------------------------------------- /config/webpack/icon.partial.js: -------------------------------------------------------------------------------- 1 | import find from 'lodash/fp/find'; 2 | import forEach from 'lodash/fp/forEach'; 3 | import isMatch from 'lodash/fp/isMatch'; 4 | 5 | /* eslint-disable no-console */ 6 | import {join, relative} from 'path'; 7 | import webpack from 'webpack'; 8 | import compose from 'lodash/fp/flowRight'; 9 | import chalk from 'chalk'; 10 | import instrumentContextModule from 'instrument-context-module'; 11 | import escapeRegExp from 'escape-string-regexp'; 12 | 13 | import {loader, plugin} from 'webpack-partial'; 14 | 15 | // SVG Elements with a default black fill. 16 | const IS_FILLABLE = new RegExp(`^${[ 17 | 'path', 18 | 'rect', 19 | 'circle', 20 | 'ellipse', 21 | 'line', 22 | 'polyline', 23 | 'polygon', 24 | 'altGlyph', 25 | 'textPath', 26 | 'text', 27 | 'tref', 28 | 'tspan', 29 | ].join('|')}$`); 30 | 31 | const replaceColorPlugin = (replace) => ({ 32 | type: 'full', 33 | fn(data) { 34 | const handler = (context = {}) => (item) => { 35 | if (item.elem) { 36 | const fill = find(isMatch({name: 'fill'}), item.attrs); 37 | const stroke = find(isMatch({name: 'stroke'}), item.attrs); 38 | 39 | const defaultFill = !context.fill && !fill && 40 | IS_FILLABLE.test(item.elem) && 41 | '#000000'.match(replace); 42 | 43 | if ((fill && fill.value.match(replace)) || defaultFill) { 44 | item.attrs.fill = { 45 | name: 'fill', 46 | local: 'fill', 47 | value: 'currentColor', 48 | prefix: '', 49 | }; 50 | } 51 | 52 | if (stroke && stroke.value.match(replace)) { 53 | item.attrs.stroke = { 54 | name: 'stroke', 55 | local: 'stroke', 56 | value: 'currentColor', 57 | prefix: '', 58 | }; 59 | } 60 | 61 | if (item.content) { 62 | forEach(handler({fill: fill || defaultFill}), item.content); 63 | } 64 | } 65 | }; 66 | 67 | handler()(data); 68 | 69 | return data; 70 | }, 71 | }); 72 | 73 | export default ({ 74 | iconPath = 'asset/icon', 75 | test = /\.icon\.svg$/, 76 | replaceColor, 77 | svgo = { 78 | plugins: [ 79 | {removeDimensions: true}, 80 | {removeStyleElement: true}, 81 | {removeTitle: true}, 82 | ], 83 | }, 84 | } = {}) => (config) => { 85 | const iconFullPath = join(config.context, 'src', iconPath); 86 | 87 | const logInclude = instrumentContextModule( 88 | (context, contextRegex, moduleRegex) => console.log( 89 | `🗃 ${chalk.bold('Context')} ${ 90 | chalk.yellow(moduleRegex) 91 | } in ${ 92 | chalk.bold(relative(config.context, context)) 93 | }` 94 | ), 95 | (module) => console.log(` ${module.replace(/^\.\//, '')}`), 96 | ); 97 | 98 | const plugins = [...svgo.plugins]; 99 | 100 | if (replaceColor) { 101 | plugins.push({__replaceColor: replaceColorPlugin(replaceColor)}); 102 | } 103 | 104 | return compose( 105 | plugin(new webpack.DefinePlugin({ 106 | 'process.env.ICON_PATH': JSON.stringify(iconFullPath), 107 | })), 108 | loader({ 109 | test, 110 | loaders: [{ 111 | loader: require.resolve('babel-loader'), 112 | query: {plugins: [require.resolve('babel-plugin-transform-react-jsx')]}, 113 | }, { 114 | loader: require.resolve('react-svg-loader'), 115 | query: { 116 | jsx: true, 117 | svgo: {...svgo, plugins}, 118 | }, 119 | }], 120 | }), 121 | plugin(new webpack.ContextReplacementPlugin( 122 | logInclude.context(new RegExp(`^${escapeRegExp(iconFullPath)}$`, 'i')), 123 | logInclude.module(test), 124 | )), 125 | )(config); 126 | }; 127 | -------------------------------------------------------------------------------- /config/webpack/minify.partial.js: -------------------------------------------------------------------------------- 1 | import UglifyJSPlugin from 'uglifyjs-webpack-plugin'; 2 | import {plugin} from 'webpack-partial'; 3 | 4 | export default plugin(new UglifyJSPlugin()); 5 | 6 | -------------------------------------------------------------------------------- /config/webpack/output.partial.js: -------------------------------------------------------------------------------- 1 | import {join} from 'path'; 2 | import {output} from 'webpack-partial'; 3 | 4 | export default ({publicPath, path}) => (config) => output({ 5 | publicPath, 6 | path: join(config.context, path), 7 | ...process.env.NODE_ENV === 'production' && config.target === 'web' ? { 8 | filename: '[name].[chunkhash].js', 9 | chunkFilename: '[id].[name].[chunkhash].js', 10 | } : { 11 | filename: '[name].js', 12 | chunkFilename: '[id].[name].js', 13 | }, 14 | })(config); 15 | -------------------------------------------------------------------------------- /config/webpack/stats.partial.js: -------------------------------------------------------------------------------- 1 | import StatsPlugin from 'stats-webpack-plugin'; 2 | import {plugin} from 'webpack-partial'; 3 | 4 | export default plugin(new StatsPlugin('stats.json')); 5 | -------------------------------------------------------------------------------- /config/webpack/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | import {join, dirname} from 'path'; 2 | import flow from 'lodash/fp/flow'; 3 | import identity from 'lodash/fp/identity'; 4 | import nearest from 'find-nearest-file'; 5 | import css from 'webpack-config-css'; 6 | import env from 'webpack-config-env'; 7 | 8 | import babel from './babel.partial'; 9 | import html from './html.partial'; 10 | import output from './output.partial'; 11 | import stats from './stats.partial'; 12 | import minify from './minify.partial'; 13 | import icon from './icon.partial'; 14 | 15 | const context = dirname(nearest('package.json')); 16 | 17 | const production = process.env.NODE_ENV === 'production'; 18 | 19 | const config = flow( 20 | env({ 21 | NODE_ENV: {}, 22 | WEBPACK_PUBLIC_PATH: {}, 23 | URL_BASE: {}, 24 | }), 25 | output({publicPath: process.env.WEBPACK_PUBLIC_PATH || '/', path: 'build'}), 26 | stats(), 27 | html({template: 'index.html'}), 28 | html({template: '404.html', filename: '404.html', inject: false}), 29 | production ? minify() : identity, 30 | css(), 31 | babel(), 32 | icon({iconPath: '/asset/icon', replaceColor: /^#(000){1,2}$/}), 33 | )({ 34 | entry: join(context, 'src', 'index.js'), 35 | target: 'web', 36 | devtool: 'source-map', 37 | context, 38 | }); 39 | 40 | export default config; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-css-demo", 3 | "version": "0.1.0", 4 | "author": "Neal Granger ", 5 | "repository": "10xjs/js-css-demo", 6 | "scripts": { 7 | "clean": "./node_modules/.bin/rimraf build/*", 8 | "dev": "npm run clean && ./node_modules/.bin/webpack-dev-server --config ./config/webpack/webpack.config.babel.js --content-base build --historyApiFallback", 9 | "build": "npm run clean && ./node_modules/.bin/webpack --config ./config/webpack/webpack.config.babel.js", 10 | "test": "npm run lint", 11 | "lint": "./node_modules/.bin/eslint .", 12 | "deploy": "gh-pages -d build" 13 | }, 14 | "dependencies": { 15 | "autoprefixer": "^6.6.0", 16 | "babel-core": "^6.24.0", 17 | "babel-loader": "^6.2.7", 18 | "babel-plugin-module-resolver": "^2.3.0", 19 | "babel-plugin-transform-react-jsx": "^6.23.0", 20 | "babel-plugin-transform-runtime": "^6.23.0", 21 | "babel-preset-metalab": "^1.0.0", 22 | "classnames": "^2.2.5", 23 | "css-js-loader": "^0.3.0", 24 | "css-loader": "^0.26.1", 25 | "extract-text-webpack-plugin": "^2.1.0", 26 | "find-nearest-file": "^1.0.0", 27 | "history": "^4.6.0", 28 | "instrument-context-module": "^0.1.0", 29 | "lodash": "^4.16.6", 30 | "moment": "^2.17.1", 31 | "polished": "0.0.0", 32 | "postcss-loader": "^1.2.1", 33 | "postcss-nesting": "^2.3.1", 34 | "raw-loader": "^0.5.1", 35 | "react": "^15.4.2", 36 | "react-dom": "^15.4.2", 37 | "react-router-dom": "next", 38 | "react-svg-loader": "2.0.0-alpha.1", 39 | "react-syntax-highlighter": "^5.0.0", 40 | "reset-css": "^2.2.0", 41 | "rimraf": "^2.5.4", 42 | "stats-webpack-plugin": "^0.4.2", 43 | "style-loader": "^0.13.1", 44 | "tinycolor2": "^1.4.1", 45 | "webpack": "^2.2.1", 46 | "webpack-config-css": "^0.1.0", 47 | "webpack-config-env": "^0.1.0", 48 | "webpack-partial": "^2.0.2" 49 | }, 50 | "devDependencies": { 51 | "babel-eslint": "^7.1.1", 52 | "eslint": "^3.10.0", 53 | "eslint-config-metalab": "^6.0.1", 54 | "eslint-plugin-babel": "^4.1.1", 55 | "eslint-plugin-filenames": "^1.1.0", 56 | "eslint-plugin-import": "^2.2.0", 57 | "eslint-plugin-lodash-fp": "^2.1.3", 58 | "eslint-plugin-react": "^6.10.0", 59 | "gh-pages": "^0.12.0", 60 | "html-webpack-plugin": "^2.28.0", 61 | "uglify-js": "^2.7.5", 62 | "uglifyjs-webpack-plugin": "^0.1.4", 63 | "webpack-dev-server": "^2.3.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/asset/icon/github.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | github.icon 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/component/base/anchor/anchor.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import config. 3 | // ============================================================================= 4 | import {BASE_LINE_HEIGHT} from '/config/typography.config'; 5 | 6 | export const BASE = { 7 | fontWeight: 600, 8 | flexBasis: '100%', 9 | position: 'relative', 10 | paddingLeft: `${BASE_LINE_HEIGHT}rem`, 11 | marginLeft: `-${BASE_LINE_HEIGHT}rem`, 12 | '&:hover .LINK': { 13 | opacity: 1, 14 | }, 15 | }; 16 | 17 | export const LINK = { 18 | userSelect: 'none', 19 | position: 'absolute', 20 | width: `${BASE_LINE_HEIGHT}rem`, 21 | height: `${BASE_LINE_HEIGHT}rem`, 22 | lineHeight: `${BASE_LINE_HEIGHT}rem`, 23 | fontSize: '1rem', 24 | left: 0, 25 | top: 0, 26 | bottom: 0, 27 | margin: 'auto', 28 | opacity: 0, 29 | transition: 'opacity 0.125s', 30 | }; 31 | -------------------------------------------------------------------------------- /src/component/base/anchor/anchor.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import kebabCase from 'lodash/fp/kebabCase'; 5 | 6 | // ============================================================================= 7 | // Import modules. 8 | // ============================================================================= 9 | import {createElement} from 'react'; 10 | import classNames from 'classnames'; 11 | 12 | // ============================================================================= 13 | // Import utils. 14 | // ============================================================================= 15 | import {getStringValue} from '/util/component.util'; 16 | 17 | // ============================================================================= 18 | // Import local styles. 19 | // ============================================================================= 20 | import {BASE, LINK} from './anchor.css'; 21 | 22 | const Anchor = ({ 23 | className, 24 | component: Component = 'div', 25 | children, 26 | id = kebabCase(getStringValue(children)), 27 | ...props 28 | }) => 29 | 30 | {!!id && 🔗} 31 | {children} 32 | ; 33 | 34 | Anchor.displayName = 'base/Anchor'; 35 | 36 | export default Anchor; 37 | -------------------------------------------------------------------------------- /src/component/base/anchor/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './anchor'; 2 | -------------------------------------------------------------------------------- /src/component/base/bold/bold.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | fontWeight: 'bold', 3 | }; 4 | -------------------------------------------------------------------------------- /src/component/base/bold/bold.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import local styles. 9 | // ============================================================================= 10 | import {BASE} from './bold.css'; 11 | 12 | const Bold = ({className, component: Component = 'b', ...props}) => 13 | ; 14 | 15 | Bold.displayName = 'base/Bold'; 16 | 17 | export default Bold; 18 | -------------------------------------------------------------------------------- /src/component/base/bold/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './bold'; 2 | -------------------------------------------------------------------------------- /src/component/base/code/code.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import config. 3 | // ============================================================================= 4 | import {ICE, CHARCOAL, CLAY, TEAL, BRICK, WINE, LILAC, INDIGO, BONDI, FOREST} 5 | from '/config/color.config'; 6 | import {BASE_LINE_HEIGHT, MONO_FONT_STACK} from '/config/typography.config'; 7 | 8 | export const INLINE = { 9 | display: 'inline', 10 | fontFamily: MONO_FONT_STACK.join(', '), 11 | paddingLeft: '0.3em', 12 | paddingRight: '0.3em', 13 | paddingTop: '0.2em', 14 | paddingBottom: '0.2em', 15 | backgroundColor: ICE, 16 | borderRadius: '0.2rem', 17 | }; 18 | 19 | export const BLOCK = { 20 | lineHeight: `${BASE_LINE_HEIGHT}rem`, 21 | marginBottom: `${BASE_LINE_HEIGHT}rem`, 22 | flexBasis: '100%', 23 | background: ICE, 24 | borderRadius: '0.2rem', 25 | 26 | '& :global(.hljs)': { 27 | display: 'block', 28 | overflowX: 'auto', 29 | padding: '0.5em', 30 | color: CHARCOAL, 31 | fontFamily: MONO_FONT_STACK.join(', '), 32 | }, 33 | '& :global(.hljs-comment)': { 34 | color: CLAY, 35 | fontStyle: 'italic', 36 | }, 37 | '& :global(.hljs-quote)': { 38 | color: CLAY, 39 | fontStyle: 'italic', 40 | }, 41 | '& :global(.hljs-keyword)': { 42 | color: CHARCOAL, 43 | fontWeight: 'bold', 44 | }, 45 | '& :global(.hljs-selector-tag)': { 46 | color: CHARCOAL, 47 | fontWeight: 'bold', 48 | }, 49 | '& :global(.hljs-subst)': { 50 | color: CHARCOAL, 51 | fontWeight: 'normal', 52 | }, 53 | '& :global(.hljs-number)': { 54 | color: TEAL, 55 | }, 56 | '& :global(.hljs-literal)': { 57 | color: TEAL, 58 | }, 59 | '& :global(.hljs-variable)': { 60 | color: TEAL, 61 | }, 62 | '& :global(.hljs-template-variable)': { 63 | color: TEAL, 64 | }, 65 | '& :global(.hljs-tag .hljs-attr)': { 66 | color: TEAL, 67 | }, 68 | '& :global(.hljs-string)': { 69 | color: BRICK, 70 | }, 71 | '& :global(.hljs-doctag)': { 72 | color: BRICK, 73 | }, 74 | '& :global(.hljs-title)': { 75 | color: WINE, 76 | fontWeight: 'bold', 77 | }, 78 | '& :global(.hljs-section)': { 79 | color: WINE, 80 | fontWeight: 'bold', 81 | }, 82 | '& :global(.hljs-selector-id)': { 83 | color: WINE, 84 | fontWeight: 'bold', 85 | }, 86 | '& :global(.hljs-type)': { 87 | color: LILAC, 88 | fontWeight: 'bold', 89 | }, 90 | '& :global(.hljs-class .hljs-title)': { 91 | color: LILAC, 92 | fontWeight: 'bold', 93 | }, 94 | '& :global(.hljs-tag)': { 95 | color: INDIGO, 96 | fontWeight: 'normal', 97 | }, 98 | '& :global(.hljs-name)': { 99 | color: INDIGO, 100 | fontWeight: 'normal', 101 | }, 102 | '& :global(.hljs-attribute)': { 103 | color: INDIGO, 104 | fontWeight: 'normal', 105 | }, 106 | '& :global(.hljs-regexp)': { 107 | color: FOREST, 108 | }, 109 | '& :global(.hljs-link)': { 110 | color: FOREST, 111 | }, 112 | '& :global(.hljs-symbol)': { 113 | color: '#990073', 114 | }, 115 | '& :global(.hljs-bullet)': { 116 | color: '#990073', 117 | }, 118 | '& :global(.hljs-built_in)': { 119 | color: BONDI, 120 | }, 121 | '& :global(.hljs-builtin-name)': { 122 | color: BONDI, 123 | }, 124 | '& :global(.hljs-meta)': { 125 | color: CLAY, 126 | fontWeight: 'bold', 127 | }, 128 | '& :global(.hljs-deletion)': { 129 | background: '#fdd', 130 | }, 131 | '& :global(.hljs-addition)': { 132 | background: '#dfd', 133 | }, 134 | '& :global(.hljs-emphasis)': { 135 | fontStyle: 'italic', 136 | }, 137 | '& :global(.hljs-strong)': { 138 | fontWeight: 'bold', 139 | }, 140 | }; 141 | -------------------------------------------------------------------------------- /src/component/base/code/code.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | import SyntaxHighlighter, {registerLanguage} 7 | from 'react-syntax-highlighter/dist/light'; 8 | import js from 'highlight.js/lib/languages/javascript'; 9 | import css from 'highlight.js/lib/languages/css'; 10 | import xml from 'highlight.js/lib/languages/xml'; 11 | 12 | // ============================================================================= 13 | // Import local styles. 14 | // ============================================================================= 15 | import {INLINE, BLOCK} from './code.css'; 16 | 17 | registerLanguage('javascript', js); 18 | registerLanguage('js', js); 19 | registerLanguage('css', css); 20 | registerLanguage('xml', xml); 21 | 22 | const Code = ({ 23 | className, 24 | block, 25 | lang, 26 | component: Component = 'code', 27 | children, 28 | ...props 29 | }) => block 30 | ? 31 |
32 | 33 | {children} 34 | 35 |
36 | : 37 | 38 | {children} 39 | ; 40 | 41 | Code.displayName = 'base/Code'; 42 | 43 | export default Code; 44 | -------------------------------------------------------------------------------- /src/component/base/code/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './code'; 2 | -------------------------------------------------------------------------------- /src/component/base/container/container.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import config. 3 | // ============================================================================= 4 | import {LAYOUT_WIDTH} from '/config/layout.config'; 5 | 6 | export const BASE = { 7 | maxWidth: LAYOUT_WIDTH, 8 | marginLeft: 'auto', 9 | marginRight: 'auto', 10 | }; 11 | -------------------------------------------------------------------------------- /src/component/base/container/container.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import every from 'lodash/fp/every'; 5 | import map from 'lodash/fp/map'; 6 | import some from 'lodash/fp/some'; 7 | 8 | // ============================================================================= 9 | // Import modules. 10 | // ============================================================================= 11 | import {createElement, Children} from 'react'; 12 | import classNames from 'classnames'; 13 | 14 | // ============================================================================= 15 | // Import base components. 16 | // ============================================================================= 17 | import Flex from '/component/base/flex'; 18 | import Row from '/component/base/row'; 19 | 20 | // ============================================================================= 21 | // Import local styles. 22 | // ============================================================================= 23 | import {BASE} from './container.css'; 24 | 25 | const normalizeChildren = (elements) => { 26 | if (every(({type}) => type === Row, elements)) { 27 | return elements; 28 | } 29 | 30 | if (some(({type}) => type === Row, elements)) { 31 | return map((element) => { 32 | if (element.type !== Row) { 33 | return {element}; 34 | } 35 | 36 | return element; 37 | }, elements); 38 | } 39 | 40 | return {elements}; 41 | }; 42 | 43 | const Container = ({className, children, ...props}) => 44 | 52 | {normalizeChildren(Children.toArray(children))} 53 | ; 54 | 55 | Container.displayName = 'base/Container'; 56 | 57 | export default Container; 58 | -------------------------------------------------------------------------------- /src/component/base/container/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './container'; 2 | -------------------------------------------------------------------------------- /src/component/base/flex/flex.config.js: -------------------------------------------------------------------------------- 1 | export const ALIGN = { 2 | start: 'flex-start', 3 | end: 'flex-end', 4 | center: 'center', 5 | stretch: 'stretch', 6 | baseline: 'baseline', 7 | }; 8 | 9 | export const JUSTIFY = { 10 | start: 'flex-start', 11 | end: 'flex-end', 12 | center: 'center', 13 | between: 'space-between', 14 | around: 'space-around', 15 | }; 16 | 17 | export const DIRECTION = { 18 | row: 'row', 19 | column: 'column', 20 | rowReverse: 'row-reverse', 21 | columnReverse: 'column-reverse', 22 | }; 23 | 24 | export const BASIS = { 25 | full: '100%', 26 | auto: 'auto', 27 | }; 28 | -------------------------------------------------------------------------------- /src/component/base/flex/flex.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import flow from 'lodash/fp/flow'; 5 | import mapKeys from 'lodash/fp/mapKeys'; 6 | import mapValues from 'lodash/fp/mapValues'; 7 | 8 | // ============================================================================= 9 | // Import local conifg. 10 | // ============================================================================ 11 | import {ALIGN, JUSTIFY, DIRECTION, BASIS} from './flex.config'; 12 | 13 | export const BASE = { 14 | display: 'flex', 15 | 16 | '& > *': { 17 | maxWidth: '100%', 18 | minWidth: '0', 19 | }, 20 | }; 21 | 22 | export const WRAP = { 23 | flexWrap: 'wrap', 24 | }; 25 | 26 | const alignClasses = flow( 27 | mapKeys((key) => `.ALIGN_${key}`), 28 | mapValues((value) => ({alignItems: value})), 29 | )(ALIGN); 30 | 31 | const justifyClasses = flow( 32 | mapKeys((key) => `.JUSTIFY_${key}`), 33 | mapValues((value) => ({justifyContent: value})), 34 | )(JUSTIFY); 35 | 36 | const directionClasses = flow( 37 | mapKeys((key) => `.DIRECTION_${key}`), 38 | mapValues((value) => ({flexDirection: value})), 39 | )(DIRECTION); 40 | 41 | const basisClasses = flow( 42 | mapKeys((key) => `.BASIS_${key}`), 43 | mapValues((value) => ({flexBasis: value})), 44 | )(BASIS); 45 | 46 | export default { 47 | ...alignClasses, 48 | ...justifyClasses, 49 | ...directionClasses, 50 | ...basisClasses, 51 | }; 52 | -------------------------------------------------------------------------------- /src/component/base/flex/flex.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import keys from 'lodash/fp/keys'; 5 | 6 | // ============================================================================= 7 | // Import modules. 8 | // ============================================================================= 9 | import {createElement, 10 | PropTypes} from 'react'; 11 | import classNames from 'classnames'; 12 | 13 | // ============================================================================= 14 | // Import local config. 15 | // ============================================================================= 16 | import {ALIGN, JUSTIFY, DIRECTION, BASIS} from './flex.config'; 17 | 18 | // ============================================================================= 19 | // Import local styles. 20 | // ============================================================================= 21 | import styles, {BASE, WRAP} from './flex.css'; 22 | 23 | const Flex = ({ 24 | className, 25 | align, 26 | justify, 27 | direction, 28 | basis, 29 | wrap, 30 | component: Component = 'div', 31 | props, 32 | ...rest 33 | }) => 34 | ; 47 | 48 | Flex.displayName = 'base/Flex'; 49 | 50 | Flex.propTypes = { 51 | align: PropTypes.oneOf(keys(ALIGN)), 52 | justify: PropTypes.oneOf(keys(JUSTIFY)), 53 | direction: PropTypes.oneOf(keys(DIRECTION)), 54 | basis: PropTypes.oneOf(keys(BASIS)), 55 | wrap: PropTypes.bool, 56 | }; 57 | 58 | export default Flex; 59 | -------------------------------------------------------------------------------- /src/component/base/flex/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './flex'; 2 | -------------------------------------------------------------------------------- /src/component/base/footer/footer.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import tinycolor from 'tinycolor2'; 5 | 6 | // ============================================================================= 7 | // Import utils. 8 | // ============================================================================= 9 | import {anchorColor} from '/util/css.util'; 10 | 11 | // ============================================================================= 12 | // Import config. 13 | // ============================================================================= 14 | import {IRON, WHITE} from '/config/color.config'; 15 | 16 | export const BASE = { 17 | backgroundColor: IRON, 18 | height: 80, 19 | color: WHITE, 20 | '& a': anchorColor(WHITE, tinycolor(WHITE).darken().toHexString()), 21 | }; 22 | -------------------------------------------------------------------------------- /src/component/base/footer/footer.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import base components. 9 | // ============================================================================= 10 | import Container from '/component/base/container'; 11 | import Flex from '/component/base/flex'; 12 | import Wrapper from '/component/base/wrapper'; 13 | 14 | // ============================================================================= 15 | // Import local styles. 16 | // ============================================================================= 17 | import {BASE} from './footer.css'; 18 | 19 | const Footer = ({className, ...props}) => 20 | 21 | 22 | 23 | {props.children} 24 | 25 | 26 | ; 27 | 28 | Footer.displayName = 'base/Footer'; 29 | 30 | export default Footer; 31 | -------------------------------------------------------------------------------- /src/component/base/footer/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './footer'; 2 | -------------------------------------------------------------------------------- /src/component/base/grid/grid.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import flow from 'lodash/fp/flow'; 5 | import map from 'lodash/fp/map'; 6 | import range from 'lodash/fp/range'; 7 | import fromPairs from 'lodash/fp/fromPairs'; 8 | 9 | // ============================================================================= 10 | // Import utils. 11 | // ============================================================================= 12 | import {breakpointMin} from '/util/css.util'; 13 | 14 | // ============================================================================= 15 | // Import config. 16 | // ============================================================================= 17 | import {SMOKE} from '/config/color.config'; 18 | import {SMALL, MEDIUM, LARGE} from '/config/breakpoint.config'; 19 | import {GRID_COLUMNS, GRID_GUTTER, GRID_GUTTER_SMALL} 20 | from '/config/layout.config'; 21 | 22 | export const BASE = { 23 | flexBasis: 0, 24 | flexGrow: 1, 25 | 26 | paddingLeft: GRID_GUTTER_SMALL / 2, 27 | paddingRight: GRID_GUTTER_SMALL / 2, 28 | 29 | [breakpointMin(MEDIUM)]: { 30 | paddingLeft: GRID_GUTTER / 2, 31 | paddingRight: GRID_GUTTER / 2, 32 | }, 33 | }; 34 | 35 | export const INNER = { 36 | flexGrow: 1, 37 | border: `1px solid ${SMOKE}`, 38 | borderRadius: '0.2rem', 39 | }; 40 | 41 | const smallClasses = flow( 42 | map((span) => [`.SMALL_${span}`, { 43 | [breakpointMin(SMALL)]: { 44 | flexBasis: `${span / 12 * 100}%`, 45 | }, 46 | }]), 47 | fromPairs, 48 | )(range(1, GRID_COLUMNS + 1)); 49 | 50 | const mediumClasses = flow( 51 | map((span) => [`.MEDIUM_${span}`, { 52 | [breakpointMin(MEDIUM)]: { 53 | flexBasis: `${span / 12 * 100}%`, 54 | }, 55 | }]), 56 | fromPairs, 57 | )(range(1, GRID_COLUMNS + 1)); 58 | 59 | const largeClasses = flow( 60 | map((span) => [`.LARGE_${span}`, { 61 | [breakpointMin(LARGE)]: { 62 | flexBasis: `${span / 12 * 100}%`, 63 | }, 64 | }]), 65 | fromPairs, 66 | )(range(1, GRID_COLUMNS + 1)); 67 | 68 | export default { 69 | ...smallClasses, 70 | '.SMALL_auto': { 71 | [breakpointMin(SMALL)]: {flexBasis: 'auto'}, 72 | }, 73 | ...mediumClasses, 74 | '.MEDIUM_auto': { 75 | [breakpointMin(MEDIUM)]: {flexBasis: 'auto'}, 76 | }, 77 | ...largeClasses, 78 | '.LARGE_auto': { 79 | [breakpointMin(LARGE)]: {flexBasis: 'auto'}, 80 | }, 81 | }; 82 | -------------------------------------------------------------------------------- /src/component/base/grid/grid.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import range from 'lodash/fp/range'; 5 | 6 | // ============================================================================= 7 | // Import modules. 8 | // ============================================================================= 9 | import {createElement, PropTypes} from 'react'; 10 | import classNames from 'classnames'; 11 | 12 | // ============================================================================= 13 | // Import config. 14 | // ============================================================================= 15 | import {GRID_COLUMNS} from '/config/layout.config'; 16 | 17 | // ============================================================================= 18 | // Import base components. 19 | // ============================================================================= 20 | import Flex from '/component/base/flex'; 21 | 22 | // ============================================================================= 23 | // Import local styles. 24 | // ============================================================================= 25 | import styles, {BASE, INNER} from './grid.css'; 26 | 27 | const Grid = ({className, small, medium, large, border, children, ...props}) => 28 | 40 | {border 41 | ? {children} 42 | : children 43 | } 44 | ; 45 | 46 | Grid.displayName = 'base/Grid'; 47 | 48 | const sizeRange = ['auto', ...range(1, GRID_COLUMNS + 1)]; 49 | 50 | Grid.PropTypes = { 51 | small: PropTypes.oneOf(sizeRange), 52 | medium: PropTypes.oneOf(sizeRange), 53 | large: PropTypes.oneOf(sizeRange), 54 | }; 55 | 56 | export default Grid; 57 | -------------------------------------------------------------------------------- /src/component/base/grid/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './grid'; 2 | -------------------------------------------------------------------------------- /src/component/base/header/header.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import tinycolor from 'tinycolor2'; 5 | 6 | // ============================================================================= 7 | // Import utils. 8 | // ============================================================================= 9 | import {anchorColor} from '/util/css.util'; 10 | 11 | // ============================================================================= 12 | // Import config. 13 | // ============================================================================= 14 | import {IRON, ICE} from '/config/color.config'; 15 | 16 | export const BASE = { 17 | backgroundColor: ICE, 18 | height: 80, 19 | color: IRON, 20 | '& a': anchorColor(IRON, tinycolor(IRON).lighten(20).toHexString()), 21 | }; 22 | -------------------------------------------------------------------------------- /src/component/base/header/header.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import base components. 9 | // ============================================================================= 10 | import Container from '/component/base/container'; 11 | import Flex from '/component/base/flex'; 12 | import Wrapper from '/component/base/wrapper'; 13 | 14 | // ============================================================================= 15 | // Import local styles. 16 | // ============================================================================= 17 | import {BASE} from './header.css'; 18 | 19 | const Header = ({className, ...props}) => 20 | 21 | 22 | 23 | {props.children} 24 | 25 | 26 | ; 27 | 28 | Header.displayName = 'base/Header'; 29 | 30 | export default Header; 31 | -------------------------------------------------------------------------------- /src/component/base/header/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './header'; 2 | export {default as Title} from './title'; 3 | -------------------------------------------------------------------------------- /src/component/base/header/title/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './title'; 2 | -------------------------------------------------------------------------------- /src/component/base/header/title/title.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | fontSize: 24, 3 | display: 'flex', 4 | alignItems: 'center', 5 | whiteSpace: 'nowrap', 6 | }; 7 | -------------------------------------------------------------------------------- /src/component/base/header/title/title.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | import {Link} from 'react-router-dom'; 7 | 8 | // ============================================================================= 9 | // Import local styles. 10 | // ============================================================================= 11 | import {BASE} from './title.css'; 12 | 13 | const Title = ({className, ...props}) => 14 | ; 15 | 16 | Title.displayName = 'base/Header/Title'; 17 | 18 | export default Title; 19 | -------------------------------------------------------------------------------- /src/component/base/heading/heading.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import flow from 'lodash/fp/flow'; 5 | import fromPairs from 'lodash/fp/fromPairs'; 6 | import map from 'lodash/fp/map'; 7 | import toPairs from 'lodash/fp/toPairs'; 8 | import zipAll from 'lodash/fp/zipAll'; 9 | 10 | // ============================================================================= 11 | // Import config. 12 | // ============================================================================= 13 | import { 14 | HEADING_FONT_SIZE, 15 | HEADING_LINE_HEIGHT, 16 | HEADING_MARGIN_TOP, 17 | HEADING_MARGIN_BOTTOM, 18 | } from '/config/typography.config'; 19 | 20 | export const BASE = { 21 | fontWeight: 600, 22 | flexBasis: '100%', 23 | '&:hover .LINK': { 24 | opacity: 1, 25 | }, 26 | }; 27 | 28 | const levelClasses = flow( 29 | toPairs, 30 | map(([i, [size, height, top, bottom]]) => [`.LEVEL_${1 + parseInt(i, 10)}`, { 31 | fontSize: `${size}rem`, 32 | lineHeight: `${height}rem`, 33 | marginBottom: `${bottom}rem`, 34 | marginTop: `${top}rem`, 35 | }]), 36 | fromPairs, 37 | )(zipAll([ 38 | HEADING_FONT_SIZE, 39 | HEADING_LINE_HEIGHT, 40 | HEADING_MARGIN_TOP, 41 | HEADING_MARGIN_BOTTOM, 42 | ])); 43 | 44 | export default { 45 | ...levelClasses, 46 | }; 47 | -------------------------------------------------------------------------------- /src/component/base/heading/heading.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import range from 'lodash/fp/range'; 5 | 6 | // ============================================================================= 7 | // Import modules. 8 | // ============================================================================= 9 | import {createElement, PropTypes} from 'react'; 10 | import classNames from 'classnames'; 11 | 12 | // ============================================================================= 13 | // Import config. 14 | // ============================================================================= 15 | import {HEADING_FONT_SIZE} from '/config/typography.config'; 16 | 17 | // ============================================================================= 18 | // Import local styles. 19 | // ============================================================================= 20 | import styles, {BASE} from './heading.css'; 21 | 22 | const Heading = ({ 23 | className, 24 | level = 1, 25 | component: Component = `h${level}`, 26 | ...props 27 | }) => 28 | ; 32 | 33 | Heading.displayName = 'base/Heading'; 34 | 35 | Heading.propTypes = { 36 | level: PropTypes.oneOf(range(1, HEADING_FONT_SIZE.length + 1)), 37 | }; 38 | 39 | export default Heading; 40 | -------------------------------------------------------------------------------- /src/component/base/heading/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './heading'; 2 | -------------------------------------------------------------------------------- /src/component/base/icon/icon.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | display: 'inline-block', 3 | verticalAlign: '-0.25em', 4 | width: '1.2em', 5 | height: '1.2em', 6 | }; 7 | -------------------------------------------------------------------------------- /src/component/base/icon/icon.js: -------------------------------------------------------------------------------- 1 | 2 | /* global require */ 3 | // ============================================================================= 4 | // Import lodash. 5 | // ============================================================================= 6 | import compact from 'lodash/fp/compact'; 7 | import contains from 'lodash/fp/contains'; 8 | import map from 'lodash/fp/map'; 9 | 10 | // ============================================================================= 11 | // Import modules. 12 | // ============================================================================= 13 | import {createElement, PropTypes} from 'react'; 14 | import classNames from 'classnames'; 15 | 16 | // ============================================================================= 17 | // Import local styles. 18 | // ============================================================================= 19 | import {BASE} from './icon.css'; 20 | 21 | const getIconModule = (type) => `./${type}.icon.svg`; 22 | const getIconName = (module) => 23 | (/\.\/([a-zA-Z0-9\-_]+)\.icon\.svg$/.exec(module) || [])[1]; 24 | 25 | const context = require.context(process.env.ICON_PATH, false, /\.icon\.svg$/); 26 | 27 | export const icons = compact(map(getIconName, context.keys())); 28 | 29 | const Icon = ({type, className, ...props}) => { 30 | if (!contains(type, icons)) { 31 | return null; 32 | } 33 | 34 | const Component = context(getIconModule(type)).default; 35 | 36 | return ; 37 | }; 38 | 39 | Icon.displayName = 'Base/Icon'; 40 | 41 | Icon.propTypes = { 42 | type: PropTypes.oneOf(icons), 43 | }; 44 | 45 | export default Icon; 46 | -------------------------------------------------------------------------------- /src/component/base/icon/index.js: -------------------------------------------------------------------------------- 1 | export {default, icons} from './icon'; 2 | -------------------------------------------------------------------------------- /src/component/base/list/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './list'; 2 | export {default as ListItem} from './item'; 3 | -------------------------------------------------------------------------------- /src/component/base/list/item/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './item'; 2 | -------------------------------------------------------------------------------- /src/component/base/list/item/item.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | display: 'list-item', 3 | }; 4 | -------------------------------------------------------------------------------- /src/component/base/list/item/item.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import local styles. 9 | // ============================================================================= 10 | import {BASE} from './item.css'; 11 | 12 | const Item = ({className, component: Component = 'li', ...props}) => 13 | ; 14 | 15 | Item.displayName = 'base/List/Item'; 16 | 17 | export default Item; 18 | -------------------------------------------------------------------------------- /src/component/base/list/list.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import config. 3 | // ============================================================================= 4 | import {BASE_LINE_HEIGHT} from '/config/typography.config'; 5 | 6 | export const BASE = { 7 | lineHeight: `${BASE_LINE_HEIGHT}rem`, 8 | flexBasis: '100%', 9 | paddingLeft: `${BASE_LINE_HEIGHT}rem`, 10 | marginBottom: `${BASE_LINE_HEIGHT}rem`, 11 | 12 | '& &': { 13 | marginBottom: 0, 14 | }, 15 | }; 16 | 17 | export const ORDERED = { 18 | listStyleType: 'decimal', 19 | }; 20 | 21 | export const UNORDERED = { 22 | listStyleType: 'disc', 23 | }; 24 | -------------------------------------------------------------------------------- /src/component/base/list/list.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import local styles. 9 | // ============================================================================= 10 | import {BASE, UNORDERED, ORDERED} from './list.css'; 11 | 12 | const List = ({ 13 | className, 14 | ordered, 15 | component: Component = ordered ? 'ol' : 'ul', 16 | ...props 17 | }) => 18 | ; 22 | 23 | List.displayName = 'base/List'; 24 | 25 | export default List; 26 | -------------------------------------------------------------------------------- /src/component/base/nav/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './nav'; 2 | export {default as Item} from './item'; 3 | -------------------------------------------------------------------------------- /src/component/base/nav/item/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './item'; 2 | -------------------------------------------------------------------------------- /src/component/base/nav/item/item.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import tinycolor from 'tinycolor2'; 5 | 6 | // ============================================================================= 7 | // Import utils. 8 | // ============================================================================= 9 | import {anchorColor} from '/util/css.util'; 10 | 11 | // ============================================================================= 12 | // Import config. 13 | // ============================================================================= 14 | import {IRON} from '/config/color.config'; 15 | 16 | export const BASE = { 17 | '&:not(:first-child)': { 18 | marginLeft: 10, 19 | }, 20 | '&:not(:last-child)': { 21 | marginRight: 10, 22 | }, 23 | }; 24 | 25 | export const ACTIVE = { 26 | borderTop: '2px solid transparent', 27 | borderBottom: `2px solid ${tinycolor(IRON).lighten(20).toHexString()}`, 28 | }; 29 | 30 | export default { 31 | 'a.ACTIVE': anchorColor(tinycolor(IRON).lighten(20).toHexString()), 32 | }; 33 | -------------------------------------------------------------------------------- /src/component/base/nav/item/item.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | import {NavLink} from 'react-router-dom'; 7 | 8 | // ============================================================================= 9 | // Import utils. 10 | // ============================================================================= 11 | import {hasProtocol} from '/util/url.util'; 12 | 13 | // ============================================================================= 14 | // Import base components. 15 | // ============================================================================= 16 | import Flex from '/component/base/flex'; 17 | 18 | // ============================================================================= 19 | // Import local styles. 20 | // ============================================================================= 21 | import {BASE, ACTIVE} from './item.css'; 22 | 23 | const Item = ({className, to, ...props}) => 24 | ; 35 | 36 | Item.displayName = 'base/Nav/Item'; 37 | 38 | export default Item; 39 | -------------------------------------------------------------------------------- /src/component/base/nav/nav.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import base components. 8 | // ============================================================================= 9 | import Flex from '/component/base/flex'; 10 | 11 | const Nav = (props) => ; 12 | 13 | Nav.displayName = 'base/Nav'; 14 | 15 | export default Nav; 16 | -------------------------------------------------------------------------------- /src/component/base/panel/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './panel'; 2 | -------------------------------------------------------------------------------- /src/component/base/panel/panel.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import config. 3 | // ============================================================================= 4 | import {SMOKE} from '/config/color.config'; 5 | import {BASE_LINE_HEIGHT} from '/config/typography.config'; 6 | 7 | export const BASE = { 8 | lineHeight: `${BASE_LINE_HEIGHT}rem`, 9 | marginBottom: `${BASE_LINE_HEIGHT}rem`, 10 | flexBasis: '100%', 11 | border: `1px solid ${SMOKE}`, 12 | borderRadius: '0.2rem', 13 | }; 14 | 15 | export const INNER = { 16 | margin: `${BASE_LINE_HEIGHT}rem`, 17 | }; 18 | -------------------------------------------------------------------------------- /src/component/base/panel/panel.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import local styles. 9 | // ============================================================================= 10 | import {BASE, INNER} from './panel.css'; 11 | 12 | const Panel = ({className, children, ...props}) => 13 |
14 |
15 | {children} 16 |
17 |
; 18 | 19 | Panel.displayName = 'base/Panel'; 20 | 21 | export default Panel; 22 | -------------------------------------------------------------------------------- /src/component/base/paragraph/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './paragraph'; 2 | -------------------------------------------------------------------------------- /src/component/base/paragraph/paragraph.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import config. 3 | // ============================================================================= 4 | import {BASE_LINE_HEIGHT} from '/config/typography.config'; 5 | 6 | export const BASE = { 7 | lineHeight: `${BASE_LINE_HEIGHT}rem`, 8 | marginBottom: `${BASE_LINE_HEIGHT}rem`, 9 | flexBasis: '100%', 10 | }; 11 | -------------------------------------------------------------------------------- /src/component/base/paragraph/paragraph.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import local styles. 9 | // ============================================================================= 10 | import {BASE} from './paragraph.css'; 11 | 12 | const Paragraph = ({className, component: Component = 'p', ...props}) => 13 | ; 14 | 15 | Paragraph.displayName = 'base/Paragraph'; 16 | 17 | export default Paragraph; 18 | -------------------------------------------------------------------------------- /src/component/base/rich-text/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './rich-text'; 2 | -------------------------------------------------------------------------------- /src/component/base/rich-text/rich-text.css.js: -------------------------------------------------------------------------------- 1 | import headingStyles, {BASE as HEADING} from '../heading/heading.css'; 2 | import {BASE as PARAGRAPH} from '../paragraph/paragraph.css'; 3 | import {BASE as LIST, ORDERED as LIST_ORDERED, UNORDERED as LIST_UNORDERED} 4 | from '../list/list.css'; 5 | import {BASE as LIST_ITEM} from '/component/base/list/item/item.css'; 6 | import {INLINE as CODE} from '/component/base/code/code.css'; 7 | import {BASE as BOLD} from '/component/base/bold/bold.css'; 8 | 9 | export const BASE = { 10 | flexBasis: '100%', 11 | '& h1, & h2, & h3, & h4, & h5, & h6': HEADING, 12 | '& h1': headingStyles['.LEVEL_1'], 13 | '& h2': headingStyles['.LEVEL_2'], 14 | '& h3': headingStyles['.LEVEL_3'], 15 | '& h4, & h5, & h6': headingStyles['.LEVEL_4'], 16 | '& p': PARAGRAPH, 17 | '& ul, & ol': LIST, 18 | '& ul': LIST_UNORDERED, 19 | '& ol': LIST_ORDERED, 20 | '& li': LIST_ITEM, 21 | '& code': CODE, 22 | '& b, & strong': BOLD, 23 | }; 24 | -------------------------------------------------------------------------------- /src/component/base/rich-text/rich-text.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import classNames from 'classnames'; 6 | 7 | // ============================================================================= 8 | // Import local styles. 9 | // ============================================================================= 10 | import {BASE} from './rich-text.css'; 11 | 12 | const RichText = ({className, ...props}) => 13 |
; 14 | 15 | RichText.displayName = 'base/RichText'; 16 | 17 | export default RichText; 18 | -------------------------------------------------------------------------------- /src/component/base/row/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './row'; 2 | -------------------------------------------------------------------------------- /src/component/base/row/row.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import utils. 3 | // ============================================================================= 4 | import {breakpointMin} from '/util/css.util'; 5 | 6 | // ============================================================================= 7 | // Import config. 8 | // ============================================================================= 9 | import {MEDIUM} from '/config/breakpoint.config'; 10 | import { 11 | LAYOUT_GUTTER, 12 | LAYOUT_GUTTER_SMALL, 13 | GRID_GUTTER, 14 | GRID_GUTTER_SMALL, 15 | } from '/config/layout.config'; 16 | 17 | export const BASE = { 18 | marginLeft: LAYOUT_GUTTER_SMALL - GRID_GUTTER_SMALL / 2, 19 | marginRight: LAYOUT_GUTTER_SMALL - GRID_GUTTER_SMALL / 2, 20 | 21 | [breakpointMin(MEDIUM)]: { 22 | marginLeft: LAYOUT_GUTTER - GRID_GUTTER / 2, 23 | marginRight: LAYOUT_GUTTER - GRID_GUTTER / 2, 24 | }, 25 | 26 | '& &': { 27 | marginLeft: - GRID_GUTTER_SMALL / 2, 28 | marginRight: - GRID_GUTTER_SMALL / 2, 29 | 30 | [breakpointMin(MEDIUM)]: { 31 | marginLeft: - GRID_GUTTER / 2, 32 | marginRight: - GRID_GUTTER / 2, 33 | }, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /src/component/base/row/row.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import every from 'lodash/fp/every'; 5 | import map from 'lodash/fp/map'; 6 | import some from 'lodash/fp/some'; 7 | 8 | // ============================================================================= 9 | // Import modules. 10 | // ============================================================================= 11 | import {createElement, Children} from 'react'; 12 | import classNames from 'classnames'; 13 | 14 | // ============================================================================= 15 | // Import base components. 16 | // ============================================================================= 17 | import Flex from '/component/base/flex'; 18 | import Grid from '/component/base/grid'; 19 | 20 | // ============================================================================= 21 | // Import local styles. 22 | // ============================================================================= 23 | import {BASE} from './row.css'; 24 | 25 | const normalizeChildren = (elements) => { 26 | if (every(({type}) => type === Grid, elements)) { 27 | return elements; 28 | } 29 | 30 | if (some(({type}) => type === Grid, elements)) { 31 | return map((element) => { 32 | if (element.type !== Grid) { 33 | return {element}; 34 | } 35 | 36 | return element; 37 | }, elements); 38 | } 39 | 40 | return {elements}; 41 | }; 42 | 43 | const Row = ({className, children, ...props}) => 44 | 51 | {normalizeChildren(Children.toArray(children))} 52 | ; 53 | 54 | Row.displayName = 'base/Row'; 55 | 56 | export default Row; 57 | -------------------------------------------------------------------------------- /src/component/base/wrapper/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './wrapper'; 2 | -------------------------------------------------------------------------------- /src/component/base/wrapper/wrapper.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | display: 'flex', 3 | }; 4 | -------------------------------------------------------------------------------- /src/component/base/wrapper/wrapper.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import base components. 8 | // ============================================================================= 9 | import Flex from '/component/base/flex'; 10 | 11 | const Wrapper = (props) => 12 | ; 13 | 14 | Wrapper.displayName = 'base/Wrapper'; 15 | 16 | export default Wrapper; 17 | -------------------------------------------------------------------------------- /src/component/partial/footer/footer.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import config. 8 | // ============================================================================= 9 | import {POST_URL, GITHUB_URL, AUTHOR_URL, METALAB_URL} 10 | from '/config/url.config'; 11 | 12 | // ============================================================================= 13 | // Import base components. 14 | // ============================================================================= 15 | import Flex from '/component/base/flex'; 16 | import BaseFooter from '/component/base/footer'; 17 | import Icon from '/component/base/icon'; 18 | import Nav, {Item} from '/component/base/nav'; 19 | 20 | const Footer = () => 21 | 22 | 23 | 24 | 2017 Neal Granger{' '} 25 | @ MetaLab 26 | 27 | 28 | 37 | ; 38 | 39 | Footer.displayName = 'partial/Footer'; 40 | 41 | export default Footer; 42 | -------------------------------------------------------------------------------- /src/component/partial/footer/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './footer'; 2 | -------------------------------------------------------------------------------- /src/component/partial/header/header.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import config. 8 | // ============================================================================= 9 | import {GITHUB_URL} from '/config/url.config'; 10 | 11 | // ============================================================================= 12 | // Import base components. 13 | // ============================================================================= 14 | import BaseHeader, {Title} from '/component/base/header'; 15 | import Nav, {Item} from '/component/base/nav'; 16 | import Icon from '/component/base/icon'; 17 | 18 | const Header = () => 19 | 20 | CSS JS Demo 21 | 27 | ; 28 | 29 | Header.displayName = 'partial/Header'; 30 | 31 | export default Header; 32 | -------------------------------------------------------------------------------- /src/component/partial/header/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './header'; 2 | -------------------------------------------------------------------------------- /src/component/partial/source-link/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './source-link'; 2 | -------------------------------------------------------------------------------- /src/component/partial/source-link/source-link.css.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import tinycolor from 'tinycolor2'; 5 | 6 | // ============================================================================= 7 | // Import utils. 8 | // ============================================================================= 9 | import {anchorColor} from '/util/css.util'; 10 | 11 | // ============================================================================= 12 | // Import config. 13 | // ============================================================================= 14 | import {IRON} from '/config/color.config'; 15 | 16 | export const BASE = { 17 | '&:not(:first-child)': { 18 | marginLeft: 10, 19 | }, 20 | '&:not(:last-child)': { 21 | marginRight: 10, 22 | }, 23 | 24 | '&:hover .LINK': { 25 | opacity: 1, 26 | }, 27 | }; 28 | 29 | export const LINK = { 30 | opacity: 0, 31 | transition: 'opacity 0.125s', 32 | }; 33 | 34 | export const ACTIVE = { 35 | borderTop: '2px solid transparent', 36 | borderBottom: `2px solid ${tinycolor(IRON).lighten(20).toHexString()}`, 37 | }; 38 | 39 | export default { 40 | 'a.ACTIVE': anchorColor(tinycolor(IRON).lighten(20).toHexString()), 41 | }; 42 | -------------------------------------------------------------------------------- /src/component/partial/source-link/source-link.js: -------------------------------------------------------------------------------- 1 | /* global window */ 2 | // ============================================================================= 3 | // Import modules. 4 | // ============================================================================= 5 | import {createElement} from 'react'; 6 | 7 | // ============================================================================= 8 | // Import base components. 9 | // ============================================================================= 10 | import Code from '/component/base/code'; 11 | import Icon from '/component/base/icon'; 12 | 13 | // ============================================================================= 14 | // Import local styles. 15 | // ============================================================================= 16 | import {BASE, LINK} from './source-link.css'; 17 | 18 | import {GITHUB_URL} from '/config/url.config'; 19 | 20 | const getURL = (path) => `${GITHUB_URL}/blob/master/${path}`; 21 | 22 | const SourceLink = ({children}) => 23 | 24 | {children} 25 | {' '} 26 | { 31 | window.open(getURL(children)); 32 | event.preventDefault(); 33 | }} 34 | > 35 | 36 | 37 | ; 38 | 39 | SourceLink.displayName = 'partial/SourceLink'; 40 | 41 | export default SourceLink; 42 | -------------------------------------------------------------------------------- /src/component/root/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './root'; 2 | -------------------------------------------------------------------------------- /src/component/root/root.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | import {Router, Route} from 'react-router-dom'; 6 | 7 | // ============================================================================= 8 | // Import partial components. 9 | // ============================================================================= 10 | import Footer from '/component/partial/footer'; 11 | import Header from '/component/partial/header'; 12 | 13 | // ============================================================================= 14 | // Import view components. 15 | // ============================================================================= 16 | import Landing from '/component/view/landing'; 17 | import Config from '/component/view/config'; 18 | import Components from '/component/view/components'; 19 | 20 | const Root = ({history}) => 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 |
29 |
; 30 | 31 | Root.displayName = 'Root'; 32 | 33 | export default Root; 34 | -------------------------------------------------------------------------------- /src/component/view/components/components.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | display: 'flex', 3 | }; 4 | -------------------------------------------------------------------------------- /src/component/view/components/components.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | // ============================================================================= 3 | // Import lodash. 4 | // ============================================================================= 5 | import flatten from 'lodash/fp/flatten'; 6 | import flow from 'lodash/fp/flow'; 7 | import groupBy from 'lodash/fp/groupBy'; 8 | import map from 'lodash/fp/map'; 9 | import toPairs from 'lodash/fp/toPairs'; 10 | import uniqBy from 'lodash/fp/uniqBy'; 11 | 12 | // ============================================================================= 13 | // Import modules. 14 | // ============================================================================= 15 | import {createElement} from 'react'; 16 | 17 | // ============================================================================= 18 | // Import base components. 19 | // ============================================================================= 20 | import Anchor from '/component/base/anchor'; 21 | import Code from '/component/base/code'; 22 | import Container from '/component/base/container'; 23 | import Grid from '/component/base/grid'; 24 | import Heading from '/component/base/heading'; 25 | import Panel from '/component/base/panel'; 26 | import Paragraph from '/component/base/paragraph'; 27 | import RichText from '/component/base/rich-text'; 28 | import Row from '/component/base/row'; 29 | 30 | // ============================================================================= 31 | // Import partial components. 32 | // ============================================================================= 33 | import SourceLink from '/component/partial/source-link'; 34 | 35 | const HIDDEN = /\/\* demo-hide-start \*\/[\S\s]*\/\* demo-hide-end \*\/\n?/g; 36 | 37 | const context = require.context('./demo/', false, /\.demo\.js$/); 38 | 39 | const demos = flow( 40 | map((key) => { 41 | const name = key.replace( /(\.[/\\]|\.demo\.js$)/g, ''); 42 | const {render, description, sources, section} = context(key); 43 | const source = require(`!!raw-loader!./demo/${name}.demo.js`); 44 | 45 | const rawSources = map((path) => [ 46 | path, 47 | require(`!!raw-loader!../../base/${path.replace(/\.js$/, '')}.js`), 48 | ], sources); 49 | 50 | return {name, section, render, source, description, sources: rawSources}; 51 | }), 52 | groupBy('section'), 53 | toPairs, 54 | map(([section, demos]) => 55 |
56 | 57 | {section} 58 | 59 | 60 | {map(({name, render: Demo, source, description}) => 61 |
62 | 63 | 64 | 65 | 66 | {source.replace(HIDDEN, '')} 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
, 76 | demos)} 77 | 78 | {flow( 79 | map('sources'), 80 | flatten, 81 | uniqBy(0), 82 | map(([path, source]) => 83 |
84 | 85 | {`src/component/base/${path}`} 86 | 87 | 88 | {source} 89 | 90 |
91 | ) 92 | )(demos)} 93 |
94 | ) 95 | )(context.keys()); 96 | 97 | const Components = () => 98 | 99 |
100 | 101 | Base Components 102 | 103 | 104 | Base presentational components with custom styles are defined in 105 | the src/components/base directory. 106 | 107 | {demos} 108 | 109 |
110 |
; 111 | 112 | Components.displayName = 'view/Components'; 113 | 114 | export default Components; 115 | -------------------------------------------------------------------------------- /src/component/view/components/demo/anchor.demo.js: -------------------------------------------------------------------------------- 1 | import Anchor from '/component/base/anchor'; 2 | import Heading from '/component/base/heading'; 3 | /* demo-hide-start */ 4 | import {createElement} from 'react'; 5 | 6 | export const section = 'Anchor'; 7 | 8 | export const sources = [ 9 | 'anchor/anchor.js', 10 | 'anchor/anchor.css.js', 11 | ]; 12 | 13 | export const description = ` 14 |

15 | Wrap a Heading (or any component) with Anchor 16 | to include an an anchor link that appears in the margin on hover. 17 |

18 | `; 19 | /* demo-hide-end */ 20 | 21 | export const render = () => 22 | 23 | 24 | Heading with Anchor 25 | 26 | ; 27 | -------------------------------------------------------------------------------- /src/component/view/components/demo/bold.demo.js: -------------------------------------------------------------------------------- 1 | import Bold from '/component/base/bold'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Bold'; 6 | 7 | export const sources = [ 8 | 'bold/bold.js', 9 | 'bold/bold.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Apply bold style to text. 15 |

16 | `; 17 | /* demo-hide-end */ 18 | 19 | export const render = () => 20 | 21 | Some bold text in a span. 22 | ; 23 | -------------------------------------------------------------------------------- /src/component/view/components/demo/code-block.demo.js: -------------------------------------------------------------------------------- 1 | import Code from '/component/base/code'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Code'; 6 | 7 | export const sources = [ 8 | 'code/code.js', 9 | 'code/code.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Create formatted code blocks. 15 |

16 | `; 17 | /* demo-hide-end */ 18 | 19 | export const render = () => 20 | 21 | {`const hello = 'hello'; 22 | const world = 'world'; 23 | 24 | console.log([hello, world].join(' ')); 25 | `} 26 | ; 27 | -------------------------------------------------------------------------------- /src/component/view/components/demo/code-inline.demo.js: -------------------------------------------------------------------------------- 1 | import Code from '/component/base/code'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Code'; 6 | 7 | export const sources = [ 8 | 'code/code.js', 9 | 'code/code.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Create inline code snippets. 15 |

16 | `; 17 | /* demo-hide-end */ 18 | 19 | export const render = () => 20 | 21 | Some inline code in a span. 22 | ; 23 | -------------------------------------------------------------------------------- /src/component/view/components/demo/container.demo.js: -------------------------------------------------------------------------------- 1 | import Container from '/component/base/container'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Container'; 6 | 7 | export const sources = [ 8 | 'container/container.js', 9 | 'container/container.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Create a center-aligned layout container. 15 |

16 | `; 17 | /* demo-hide-end */ 18 | 19 | export const render = () => 20 | 21 | Content within center column. 22 | ; 23 | -------------------------------------------------------------------------------- /src/component/view/components/demo/flex.demo.js: -------------------------------------------------------------------------------- 1 | import Flex from '/component/base/flex'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Flex'; 6 | 7 | export const sources = [ 8 | 'flex/flex.js', 9 | 'flex/flex.css.js', 10 | 'flex/flex.config.js', 11 | ]; 12 | 13 | export const description = ` 14 |

15 | Render a flexbox layout. 16 |

17 | `; 18 | /* demo-hide-end */ 19 | 20 | export const render = () => 21 | 22 |
Left
23 |
Center
24 |
Right
25 |
; 26 | -------------------------------------------------------------------------------- /src/component/view/components/demo/grid-column.demo.js: -------------------------------------------------------------------------------- 1 | import Grid from '/component/base/grid'; 2 | import Row from '/component/base/row'; 3 | /* demo-hide-start */ 4 | import {createElement} from 'react'; 5 | 6 | import {GRID_COLUMNS} from '/config/layout.config'; 7 | 8 | export const section = 'Grid and Row'; 9 | 10 | export const sources = [ 11 | 'grid/grid.js', 12 | 'grid/grid.css.js', 13 | ]; 14 | 15 | export const description = ` 16 |

17 | Create a responsive ${GRID_COLUMNS} column grid layout. 18 |

19 | `; 20 | /* demo-hide-end */ 21 | 22 | export const render = () => 23 | 24 | A 25 | B 26 | C 27 | ; 28 | -------------------------------------------------------------------------------- /src/component/view/components/demo/grid-flex.demo.js: -------------------------------------------------------------------------------- 1 | import Grid from '/component/base/grid'; 2 | import Row from '/component/base/row'; 3 | /* demo-hide-start */ 4 | import {createElement} from 'react'; 5 | 6 | export const section = 'Grid and Row'; 7 | 8 | export const sources = [ 9 | 'grid/grid.js', 10 | 'grid/grid.css.js', 11 | ]; 12 | 13 | export const description = ` 14 |

15 | Create a flexible equal-width column grid layout. 16 |

17 | `; 18 | /* demo-hide-end */ 19 | 20 | export const render = () => 21 | 22 | A 23 | B 24 | C 25 | D 26 | E 27 | ; 28 | -------------------------------------------------------------------------------- /src/component/view/components/demo/header-footer.demo.js: -------------------------------------------------------------------------------- 1 | import Flex from '/component/base/flex'; 2 | import Footer from '/component/base/footer'; 3 | import Header, {Title} from '/component/base/header'; 4 | import Nav, {Item} from '/component/base/nav'; 5 | /* demo-hide-start */ 6 | import {createElement} from 'react'; 7 | 8 | export const section = 'Header and Footer'; 9 | 10 | export const sources = [ 11 | 'header/header.js', 12 | 'header/header.css.js', 13 | 'header/title/title.js', 14 | 'header/title/title.css.js', 15 | 'footer/footer.js', 16 | 'footer/footer.css.js', 17 | 'nav/nav.js', 18 | 'nav/item/item.js', 19 | 'nav/item/item.css.js', 20 | ]; 21 | 22 | export const description = ` 23 |

24 | Create a full-width page header and footer. 25 |

26 | `; 27 | /* demo-hide-end */ 28 | 29 | export const render = () => 30 |
31 |
32 | Title 33 | 37 |
38 | 39 | App content. 40 | 41 |
42 | 43 | Footer 44 | 45 | 49 |
50 |
; 51 | -------------------------------------------------------------------------------- /src/component/view/components/demo/heading.demo.js: -------------------------------------------------------------------------------- 1 | import range from 'lodash/fp/range'; 2 | import map from 'lodash/fp/map'; 3 | import {HEADING_FONT_SIZE} from '/config/typography.config'; 4 | import Heading from '/component/base/heading'; 5 | /* demo-hide-start */ 6 | import {createElement} from 'react'; 7 | 8 | export const section = 'Heading'; 9 | 10 | export const sources = [ 11 | 'heading/heading.js', 12 | 'heading/heading.css.js', 13 | ]; 14 | 15 | export const description = ` 16 |

17 | Render styled h1 through h${ 18 | HEADING_FONT_SIZE.length 19 | } elements. 20 |

21 | `; 22 | /* demo-hide-end */ 23 | 24 | export const render = () => 25 |
26 | {map((level) => 27 | 28 | Level {level} 29 | , 30 | range(1, HEADING_FONT_SIZE.length + 1))} 31 |
; 32 | -------------------------------------------------------------------------------- /src/component/view/components/demo/icon.demo.js: -------------------------------------------------------------------------------- 1 | import Icon from '/component/base/icon'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Icon'; 6 | 7 | export const sources = [ 8 | 'icon/icon.js', 9 | 'icon/icon.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Render an icon. 15 |

16 | 17 |

18 | All .icon.svg files from within 19 | the src/asset/icon directory are automatically 20 | bundled and accessible through the type prop. 21 |

22 | `; 23 | /* demo-hide-end */ 24 | 25 | export const render = () => ; 26 | -------------------------------------------------------------------------------- /src/component/view/components/demo/list-ordered.demo.js: -------------------------------------------------------------------------------- 1 | import List, {ListItem as Item} from '/component/base/list'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'List'; 6 | 7 | export const sources = [ 8 | 'list/list.js', 9 | 'list/list.css.js', 10 | 'list/item/item.js', 11 | 'list/item/item.css.js', 12 | ]; 13 | 14 | export const description = ` 15 |

16 | Render an ordered (ol style) list. 17 |

18 | `; 19 | /* demo-hide-end */ 20 | 21 | export const render = () => 22 | 23 | List item A 24 | List item B 25 | List item C 26 | ; 27 | -------------------------------------------------------------------------------- /src/component/view/components/demo/list-unordered.demo.js: -------------------------------------------------------------------------------- 1 | import List, {ListItem as Item} from '/component/base/list'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'List'; 6 | 7 | export const sources = [ 8 | 'list/list.js', 9 | 'list/list.css.js', 10 | 'list/item/item.js', 11 | 'list/item/item.css.js', 12 | ]; 13 | 14 | export const description = ` 15 |

16 | Render an unordered (ul style) list. 17 |

18 | `; 19 | /* demo-hide-end */ 20 | 21 | export const render = () => 22 | 23 | List item A 24 | List item B 25 | List item C 26 | ; 27 | -------------------------------------------------------------------------------- /src/component/view/components/demo/paragraph.demo.js: -------------------------------------------------------------------------------- 1 | import Paragraph from '/component/base/paragraph'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'Paragraph'; 6 | 7 | export const sources = [ 8 | 'paragraph/paragraph.js', 9 | 'paragraph/paragraph.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Render a styled paragraph. 15 |

16 | `; 17 | /* demo-hide-end */ 18 | 19 | export const render = () => 20 |
21 | 22 | A paragraph is a block element with a bottom margin. 23 | 24 | 25 | Consecutive paragraphs are visually separated. 26 | 27 |
; 28 | -------------------------------------------------------------------------------- /src/component/view/components/demo/rich-text.demo.js: -------------------------------------------------------------------------------- 1 | import RichText from '/component/base/rich-text'; 2 | /* demo-hide-start */ 3 | import {createElement} from 'react'; 4 | 5 | export const section = 'RichText'; 6 | 7 | export const sources = [ 8 | 'rich-text/rich-text.js', 9 | 'rich-text/rich-text.css.js', 10 | ]; 11 | 12 | export const description = ` 13 |

14 | Since any default browser styles are reset using reset.css, 15 | basic presentational elements appear as unstyled block or inline elements. 16 |

17 |

18 | The Richtext component wraps basic elements and applies 19 | appropriate styles. This can be more convenient than importing base 20 | components and is a great way to format managed or loaded HTML content. 21 |

22 | `; 23 | /* demo-hide-end */ 24 | 25 | export const render = () => 26 | 27 |

Argumentum

28 |

Sit ad Ullum

29 |

30 | Quidam appellantur et qui, ei vel stet percipit, accusata 31 | dignissim efficiendi ea quo. Scripta facilis signiferumque cu eam, 32 | his at persius verterem rationibus. Ius sale brute liberavisse an. 33 | Mea eu partem tincidunt. Te qualisque comprehensam sit, euripidis 34 | honestatis ad nam. 35 |

36 |

Sit ad Ullum

37 |
    38 |
  • Ex his lobortis necessitatibus, vel labitur
  • 39 |
  • Errem recusabo cum ne, nobis minimum
  • 40 |
  • Per an aperiam feugiat convenire
  • 41 |
42 |
    43 |
  1. Ex his lobortis necessitatibus, vel labitur
  2. 44 |
  3. Errem recusabo cum ne, nobis minimum
  4. 45 |
  5. Per an aperiam feugiat convenire
  6. 46 |
47 |
; 48 | -------------------------------------------------------------------------------- /src/component/view/components/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './components'; 2 | // export {default as AnchorDemo} from './anchor-demo'; 3 | // export {default as BoldDemo} from './bold-demo'; 4 | // export {default as CodeDemo} from './code-demo'; 5 | // export {default as ContainerDemo} from './container-demo'; 6 | // export {default as FlexDemo} from './flex-demo'; 7 | // export {default as GridDemo} from './grid-demo'; 8 | // export {default as ListDemo} from './list-demo'; 9 | // export {default as ParagraphDemo} from './paragraph-demo'; 10 | // export {default as RichTextDemo} from './rich-text-demo'; 11 | // export {default as HeadingDemo} from './heading-demo'; 12 | -------------------------------------------------------------------------------- /src/component/view/config/breakpoint-list/breakpoint-list.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import map from 'lodash/fp/map'; 5 | import toPairs from 'lodash/fp/toPairs'; 6 | 7 | // ============================================================================= 8 | // Import modules. 9 | // ============================================================================= 10 | import {createElement} from 'react'; 11 | 12 | // ============================================================================= 13 | // Import config. 14 | // ============================================================================= 15 | import * as breakpoints from '/config/breakpoint.config'; 16 | 17 | // ============================================================================= 18 | // Import base components. 19 | // ============================================================================= 20 | import Bold from '/component/base/bold'; 21 | import Code from '/component/base/code'; 22 | import List, {ListItem as Item} from '/component/base/list'; 23 | 24 | const BreakpointList = () => 25 | 26 | {map(([name, {min, max}]) => 27 | 28 | {name} 29 | {' - '} 30 | {!!min && !!max && 31 | 32 | between {min}px and {max}px 33 | 34 | } 35 | {!!min && !max && 36 | 37 | above {min}px 38 | 39 | } 40 | {!!max && !min && 41 | 42 | below {max}px 43 | 44 | } 45 | , 46 | toPairs(breakpoints))} 47 | ; 48 | 49 | BreakpointList.displayName = 'view/Config/BreakpointList'; 50 | 51 | export default BreakpointList; 52 | -------------------------------------------------------------------------------- /src/component/view/config/breakpoint-list/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './breakpoint-list'; 2 | -------------------------------------------------------------------------------- /src/component/view/config/color-list/color-list.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | display: 'flex', 3 | }; 4 | -------------------------------------------------------------------------------- /src/component/view/config/color-list/color-list.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import flow from 'lodash/fp/flow'; 5 | import map from 'lodash/fp/map'; 6 | import sortBy from 'lodash/fp/sortBy'; 7 | import toPairs from 'lodash/fp/toPairs'; 8 | 9 | // ============================================================================= 10 | // Import modules. 11 | // ============================================================================= 12 | import {createElement} from 'react'; 13 | import tinycolor from 'tinycolor2'; 14 | 15 | // ============================================================================= 16 | // Import config. 17 | // ============================================================================= 18 | import * as colors from '/config/color.config'; 19 | 20 | // ============================================================================= 21 | // Import base components. 22 | // ============================================================================= 23 | import Bold from '/component/base/bold'; 24 | import Code from '/component/base/code'; 25 | import List, {ListItem as Item} from '/component/base/list'; 26 | 27 | const ColorList = () => 28 | 29 | {flow( 30 | toPairs, 31 | sortBy(([, color]) => { 32 | const {s: saturation, l: lightness} = tinycolor(color).toHsl(); 33 | const darkness = (1 - lightness); 34 | const intensity = darkness * darkness * saturation * saturation; 35 | return lightness + intensity * 20; 36 | }), 37 | map(([name, color]) => 38 | 39 | 48 |
49 | 50 | {' '} 51 | {name} 52 | 53 | ), 54 | )(colors)} 55 | ; 56 | 57 | ColorList.displayName = 'view/Config/ColorList'; 58 | 59 | export default ColorList; 60 | -------------------------------------------------------------------------------- /src/component/view/config/color-list/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './color-list'; 2 | -------------------------------------------------------------------------------- /src/component/view/config/config.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import base components. 8 | // ============================================================================= 9 | import Anchor from '/component/base/anchor'; 10 | import Code from '/component/base/code'; 11 | import Container from '/component/base/container'; 12 | import Heading from '/component/base/heading'; 13 | import Paragraph from '/component/base/paragraph'; 14 | 15 | // ============================================================================= 16 | // Import partial components. 17 | // ============================================================================= 18 | import SoureceLink from '/component/partial/source-link'; 19 | 20 | // ============================================================================= 21 | // Import local components. 22 | // ============================================================================= 23 | import BreakpointList from './breakpoint-list'; 24 | import ColorList from './color-list'; 25 | import LayoutList from './layout-list'; 26 | import TypographyList from './typography-list'; 27 | 28 | const Config = () => 29 | 30 |
31 | 32 | Style Config 33 | 34 | 35 | Any variables controlling aspects of the generated styles are stored 36 | in the src/config directory. These values are univerasally 37 | accessible to all .css and .js files in the 38 | project. 39 | 40 | 41 | 42 | Breakpoint Config 43 | 44 | 45 | src/config/breakpoint.config.js 46 | 47 | 48 | 49 | 50 | Colors 51 | 52 | 53 | src/config/color.config.js 54 | 55 | 56 | 57 | 58 | Layout Config 59 | 60 | 61 | src/config/layout.config.js 62 | 63 | 64 | 65 | 66 | Typographic Config 67 | 68 | 69 | src/config/typography.config.js 70 | 71 | 72 | 73 |
74 |
; 75 | 76 | Config.displayName = 'view/Config'; 77 | 78 | export default Config; 79 | -------------------------------------------------------------------------------- /src/component/view/config/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './config'; 2 | export {default as ColorList} from './color-list'; 3 | export {default as BreakpointList} from './breakpoint-list'; 4 | export {default as LayoutList} from './layout-list'; 5 | export {default as TypographyList} from './typography-list'; 6 | -------------------------------------------------------------------------------- /src/component/view/config/layout-list/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './layout-list'; 2 | -------------------------------------------------------------------------------- /src/component/view/config/layout-list/layout-list.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import base components. 8 | // ============================================================================= 9 | import Bold from '/component/base/bold'; 10 | import Code from '/component/base/code'; 11 | import List, {ListItem as Item} from '/component/base/list'; 12 | 13 | // ============================================================================= 14 | // Import config. 15 | // ============================================================================= 16 | import { 17 | LAYOUT_WIDTH, 18 | LAYOUT_GUTTER, 19 | LAYOUT_GUTTER_SMALL, 20 | GRID_COLUMNS, 21 | GRID_GUTTER, 22 | GRID_GUTTER_SMALL, 23 | } from '/config/layout.config'; 24 | 25 | const LayoutList = () => 26 | 27 | 28 | LAYOUT_WIDTH 29 | {' - '} 30 | Maximum width of center layout column: 31 | {' '} 32 | {`${LAYOUT_WIDTH}px`} 33 | 34 | 35 | 36 | LAYOUT_GUTTER 37 | {' - '} 38 | Margin at the left and right of the center layout column: 39 | {' '} 40 | {`${LAYOUT_GUTTER}px`} 41 | 42 | 43 | 44 | LAYOUT_GUTTER_SMALL 45 | {' - '} 46 | Margin at the left and right of the center layout column 47 | at SMALL breakpoint: 48 | {' '} 49 | {`${LAYOUT_GUTTER_SMALL}px`} 50 | 51 | 52 | 53 | GRID_COLUMNS 54 | {' - '} 55 | Number of columns in the layout grid: 56 | {' '} 57 | {`${GRID_COLUMNS}`} 58 | 59 | 60 | 61 | GRID_GUTTER 62 | {' - '} 63 | Margin between layout grid columns: 64 | {' '} 65 | {`${GRID_GUTTER}px`} 66 | 67 | 68 | 69 | GRID_GUTTER_SMALL 70 | {' - '} 71 | Maximum width of center layout column. 72 | at SMALL breakpoint: 73 | {' '} 74 | {`${GRID_GUTTER_SMALL}px`} 75 | 76 | ; 77 | 78 | LayoutList.displayName = 'view/Config/LayoutList'; 79 | 80 | export default LayoutList; 81 | -------------------------------------------------------------------------------- /src/component/view/config/typography-list/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './typography-list'; 2 | -------------------------------------------------------------------------------- /src/component/view/config/typography-list/typography-list.css.js: -------------------------------------------------------------------------------- 1 | export const BASE = { 2 | display: 'flex', 3 | }; 4 | -------------------------------------------------------------------------------- /src/component/view/config/typography-list/typography-list.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import map from 'lodash/fp/map'; 5 | 6 | // ============================================================================= 7 | // Import modules. 8 | // ============================================================================= 9 | import {createElement} from 'react'; 10 | 11 | // ============================================================================= 12 | // Import base components. 13 | // ============================================================================= 14 | import Bold from '/component/base/bold'; 15 | import Code from '/component/base/code'; 16 | import List, {ListItem as Item} from '/component/base/list'; 17 | 18 | // ============================================================================= 19 | // Import config. 20 | // ============================================================================= 21 | import { 22 | BASE_FONT_SIZE, 23 | BASE_FONT_STACK, 24 | MONO_FONT_STACK, 25 | BASE_LINE_HEIGHT, 26 | HEADING_SCALE_FACTOR, 27 | } from '/config/typography.config'; 28 | 29 | const TypographyList = () => 30 | 31 | 32 | BASE_FONT_SIZE 33 | {' - '} 34 | Document font size: 35 | {' '} 36 | {`${BASE_FONT_SIZE.toFixed(2).replace(/\.0+$/, '')}px`} 37 | 38 | 39 | 40 | BASE_FONT_STACK 41 | {' - '} 42 | Document font family stack: 43 | 44 | {map((font) => 45 | {font.replace(/^"|"$/g, '')}, 46 | BASE_FONT_STACK)} 47 | 48 | 49 | 50 | 51 | MONO_FONT_STACK 52 | {' - '} 53 | Fixed-width font family stack: 54 | 55 | {map((font) => 56 | {font.replace(/^"|"$/g, '')}, 57 | MONO_FONT_STACK)} 58 | 59 | 60 | 61 | 62 | BASE_LINE_HEIGHT 63 | {' - '} 64 | Document line height: 65 | {' '} 66 | {`${BASE_LINE_HEIGHT.toFixed(2).replace(/\.0+$/, '')}rem`} 67 | 68 | 69 | 70 | HEADING_SCALE_FACTOR 71 | {' - '} 72 | Ratio between heading element sizes: 73 | {' '} 74 | 75 | {`${HEADING_SCALE_FACTOR.toFixed(2).replace(/\.0+$/, '')}`} 76 | 77 | 78 | 79 | {/* 80 | HEADING_FONT_SIZE 81 | {' - '} 82 | Heading element font sizes. 83 | {' '} 84 | 85 | {map(([i, size]) => 86 | 87 | {`h${parseInt(i, 10) + 1}`} 88 | {' - '} 89 | {`${size.toFixed(2).replace(/\.0+$/, '')}px`} 90 | , 91 | toPairs(HEADING_FONT_SIZE))} 92 | 93 | 94 | 95 | 96 | HEADING_LINE_HEIGHT 97 | {' - '} 98 | Heading element line heights: 99 | {' '} 100 | 101 | {map(([i, size]) => 102 | 103 | {`h${parseInt(i, 10) + 1}`} 104 | {' - '} 105 | {`${size.toFixed(2).replace(/\.0+$/, '')}rem`} 106 | , 107 | toPairs(HEADING_LINE_HEIGHT))} 108 | 109 | 110 | 111 | 112 | HEADING_MARGIN_TOP 113 | {' - '} 114 | Heading element top margin values: 115 | {' '} 116 | 117 | {map(([i, size]) => 118 | 119 | {`h${parseInt(i, 10) + 1}`} 120 | {' - '} 121 | {`${size.toFixed(2).replace(/\.0+$/, '')}rem`} 122 | , 123 | toPairs(HEADING_MARGIN_TOP))} 124 | 125 | 126 | 127 | 128 | HEADING_MARGIN_BOTTOM 129 | {' - '} 130 | Heading element bottom margin values: 131 | {' '} 132 | 133 | {map(([i, size]) => 134 | 135 | {`h${parseInt(i, 10) + 1}`} 136 | {' - '} 137 | {`${size.toFixed(2).replace(/\.0+$/, '')}rem`} 138 | , 139 | toPairs(HEADING_MARGIN_BOTTOM))} 140 | 141 | */} 142 | ; 143 | 144 | TypographyList.displayName = 'view/Config/TypographyList'; 145 | 146 | export default TypographyList; 147 | -------------------------------------------------------------------------------- /src/component/view/landing/index.js: -------------------------------------------------------------------------------- 1 | export {default} from './landing'; 2 | -------------------------------------------------------------------------------- /src/component/view/landing/landing.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import modules. 3 | // ============================================================================= 4 | import {createElement} from 'react'; 5 | 6 | // ============================================================================= 7 | // Import base components. 8 | // ============================================================================= 9 | import Code from '/component/base/code'; 10 | import Container from '/component/base/container'; 11 | import Grid from '/component/base/grid'; 12 | import RichText from '/component/base/rich-text'; 13 | import Row from '/component/base/row'; 14 | 15 | // ============================================================================= 16 | // Import config. 17 | // ============================================================================= 18 | import { 19 | POST_URL, 20 | GITHUB_URL, 21 | REACT_URL, 22 | CSS_JS_LOADER_URL, 23 | CSS_LOADER_URL, 24 | WEBPACK_URL, 25 | } from '/config/url.config'; 26 | 27 | // ============================================================================= 28 | // Import raw samples. 29 | // ============================================================================= 30 | /* eslint-disable import/no-unresolved */ 31 | /* eslint-disable max-len */ 32 | import sampleRaw from '!!raw-loader!../../base/anchor/anchor.css'; 33 | import sampleCss from '!!raw-loader!postcss-loader?config=config/postcss!css-js-loader?pretty!babel-loader!../../base/anchor/anchor.css'; 34 | /* eslint-enable import/no-unresolved */ 35 | /* eslint-enable max-len */ 36 | 37 | const Landing = () => 38 | 39 | 40 |

41 | CSS JS Demo 42 |

43 |

44 | This is an example React{' '} 45 | project built using CSS JS styles parsed by{' '} 46 | 47 | css-js-loader 48 | . 49 |

50 |

Basic Concept

51 |
    52 |
  1. 53 | CSS styles are defined as plain objects exported 54 | from .css.js files. 55 |
  2. 56 |
  3. 57 | At build time, .css.js file exports are converted to 58 | CSS markup. 59 |
  4. 60 |
  5. 61 | Converted CSS markup is processed 62 | by Webpack{' '} 63 | css-loader 64 | . 65 |
  6. 66 |
67 |

68 | Be sure to read the companion{' '} 69 | blog post and 70 | check out the GitHub source. 71 |

72 |
73 | 74 | 75 | 76 |

Example .css.js File Result

77 |
78 |
79 | 80 | 81 | {sampleRaw} 82 | 83 | 84 | 85 | 86 | {sampleCss} 87 | 88 | 89 |
90 |
; 91 | 92 | Landing.displayName = 'view/Landing'; 93 | 94 | export default Landing; 95 | -------------------------------------------------------------------------------- /src/config/breakpoint.config.js: -------------------------------------------------------------------------------- 1 | export const SMALL = {max: 500}; 2 | export const MEDIUM = {min: 501, max: 900}; 3 | export const LARGE = {min: 901}; 4 | -------------------------------------------------------------------------------- /src/config/color.config.js: -------------------------------------------------------------------------------- 1 | export const CHARCOAL = '#2b2e34'; 2 | export const IRON = '#4d5256'; 3 | export const ORANGE = '#f86759'; 4 | export const LILAC = '#9068be'; 5 | export const INDIGO = '#5a1ba2'; 6 | export const BRICK = '#e62739'; 7 | export const WINE = '#b9091a'; 8 | export const CLAY = '#9da1a8'; 9 | export const SMOKE = '#e1e8f0'; 10 | export const ICE = '#f4f8fd'; 11 | export const TEAL = '#6ed3cf'; 12 | export const BONDI = '#0086b3'; 13 | export const FOREST = '#23A944'; 14 | export const WHITE = '#ffffff'; 15 | -------------------------------------------------------------------------------- /src/config/layout.config.js: -------------------------------------------------------------------------------- 1 | export const LAYOUT_WIDTH = 1200; 2 | export const LAYOUT_GUTTER = 24; 3 | export const LAYOUT_GUTTER_SMALL = 12; 4 | export const GRID_COLUMNS = 12; 5 | export const GRID_GUTTER = 24; 6 | export const GRID_GUTTER_SMALL = 18; 7 | -------------------------------------------------------------------------------- /src/config/typography.config.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // Import lodash. 3 | // ============================================================================= 4 | import map from 'lodash/fp/map'; 5 | import times from 'lodash/fp/times'; 6 | 7 | // Base font size in px. 8 | export const BASE_FONT_SIZE = 16; 9 | 10 | export const BASE_FONT_STACK = [ 11 | '-apple-system', 12 | 'BlinkMacSystemFont', 13 | '"Segoe UI"', 14 | 'Roboto', 15 | 'Helvetica', 16 | 'Arial', 17 | 'sans-serif', 18 | '"Apple Color Emoji"', 19 | '"Segoe UI Emoji"', 20 | '"Segoe UI Symbol"', 21 | ]; 22 | 23 | export const MONO_FONT_STACK = [ 24 | 'SFMono-Regular', 25 | 'Consolas', 26 | '"Liberation Mono"', 27 | 'Menlo', 28 | 'Courier', 29 | 'monospace', 30 | ]; 31 | 32 | // Base line height in rem. 33 | export const BASE_LINE_HEIGHT = 2; 34 | 35 | // Scale factor between each heading size, i.e. h1 size = FACTOR * h2 size. 36 | export const HEADING_SCALE_FACTOR = 1.618; 37 | 38 | // Array of heading font sizes in rem. From h1 to h4. 39 | export const HEADING_FONT_SIZE = 40 | times((i) => Math.pow(HEADING_SCALE_FACTOR, i), 4).reverse(); 41 | 42 | // Array of heading line counts. From h1 to h4. 43 | export const HEADING_LINES = 44 | map((size) => Math.ceil(size / BASE_LINE_HEIGHT + 0.1), HEADING_FONT_SIZE); 45 | 46 | // Array of heading line heights in rem. From h1 to h4. 47 | export const HEADING_LINE_HEIGHT = 48 | map((lines) => lines * BASE_LINE_HEIGHT, HEADING_FONT_SIZE); 49 | 50 | // Array of heading top margins in rem. From h1 to h4. 51 | export const HEADING_MARGIN_TOP = map(() => BASE_LINE_HEIGHT, HEADING_LINES); 52 | 53 | // Array of heading bottom margins in rem. From h1 to h4. 54 | export const HEADING_MARGIN_BOTTOM = 55 | map( 56 | (lines) => Math.min(1, Math.max(0, lines - 1)) * BASE_LINE_HEIGHT, 57 | HEADING_LINES, 58 | ); 59 | -------------------------------------------------------------------------------- /src/config/url.config.js: -------------------------------------------------------------------------------- 1 | export const POST_URL = 2 | 'https://medium.com/@10xjs/better-css-with-javascript-88463deecf3'; 3 | export const GITHUB_URL = 'https://github.com/10xjs/css-js-demo'; 4 | export const REACT_URL = 'https://facebook.github.io/react/'; 5 | export const CSS_JS_LOADER_URL = 'https://github.com/10xjs/css-js-loader'; 6 | export const CSS_LOADER_URL = 'https://github.com/webpack-contrib/css-loader'; 7 | export const WEBPACK_URL = 'https://webpack.js.org/'; 8 | export const AUTHOR_URL = 'http://🌭.ws/'; 9 | export const METALAB_URL = 'http://metalab.co/'; 10 | -------------------------------------------------------------------------------- /src/global.css.js: -------------------------------------------------------------------------------- 1 | import {CHARCOAL, LILAC} from '/config/color.config'; 2 | import {BASE_FONT_SIZE, BASE_FONT_STACK, BASE_LINE_HEIGHT} 3 | from '/config/typography.config'; 4 | import {anchorColor} from '/util/css.util'; 5 | 6 | export default [{ 7 | /** 8 | * Ensure content spans at least the size of the browser window. 9 | * See: http://codepen.io/absolutholz/post/html-and-body-element-height-100 10 | * ======================================================================== 11 | */ 12 | html: { 13 | height: '100%', 14 | }, 15 | body: { 16 | minHeight: '100%', 17 | }, 18 | }, { 19 | /** 20 | * Consistent `box-sizing`. 21 | * See: http://www.paulirish.com/2012/box-sizing-border-box-ftw/ 22 | * ======================================================================== 23 | */ 24 | html: { 25 | boxSizing: 'border-box', 26 | }, 27 | '*, *:before, *:after': { 28 | boxSizing: 'inherit', 29 | }, 30 | }, { 31 | 'html, body': { 32 | color: CHARCOAL, 33 | fontFamily: BASE_FONT_STACK.join(', '), 34 | fontWeight: 400, 35 | fontSize: BASE_FONT_SIZE, 36 | lineHeight: `${BASE_LINE_HEIGHT}rem`, 37 | }, 38 | a: { 39 | ...anchorColor(LILAC), 40 | textDecoration: 'none', 41 | }, 42 | }]; 43 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CSS JS Demo 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* global window */ 2 | import {parse} from 'url'; 3 | import {createElement} from 'react'; 4 | import {render} from 'react-dom'; 5 | import createHistory from 'history/createBrowserHistory'; 6 | 7 | import 'reset-css'; 8 | import './global.css'; 9 | 10 | import Root from '/component/root'; 11 | 12 | const history = createHistory({basename: process.env.URL_BASE}); 13 | 14 | const redirect = window.sessionStorage.redirect; 15 | delete window.sessionStorage.redirect; 16 | 17 | if (redirect && redirect !== window.location.href) { 18 | const {pathname} = parse(redirect); 19 | history.replace(pathname.slice((process.env.URL_BASE || '').length)); 20 | } 21 | 22 | render(, window.document.getElementById('app')); 23 | -------------------------------------------------------------------------------- /src/util/component.util.js: -------------------------------------------------------------------------------- 1 | import map from 'lodash/fp/map'; 2 | import isString from 'lodash/fp/isString'; 3 | 4 | import {Children} from 'react'; 5 | 6 | export const getStringValue = (children) => { 7 | return map( 8 | (child) => isString(child) ? child : getStringValue(child.props.children), 9 | Children.toArray(children), 10 | ).join(''); 11 | }; 12 | -------------------------------------------------------------------------------- /src/util/css.util.js: -------------------------------------------------------------------------------- 1 | import compact from 'lodash/fp/compact'; 2 | 3 | export const anchorColor = (color, hover = color, visited = color) => ({ 4 | '&': {color}, 5 | '&:hover, &:visited:hover': {color: hover}, 6 | '&:visited': {color: visited}, 7 | }); 8 | 9 | export const mediaQuery = (rules) => { 10 | let _rules = compact(rules); 11 | 12 | if (_rules.length === 0) { 13 | _rules = ['all']; 14 | } 15 | 16 | return `@media ${rules.join(' and ')}`; 17 | }; 18 | 19 | export const breakpointMin = ({min}) => 20 | mediaQuery([min ? `(min-width: ${min}px)` : null]); 21 | 22 | export const breakpointMax = ({max}) => 23 | mediaQuery([max ? `(max-width: ${max}px)` : null]); 24 | 25 | export const breakpointMinMax = ({min, max}) => mediaQuery([ 26 | min ? `(min-width: ${min}px)` : null, 27 | max ? `(max-width: ${max}px)` : null, 28 | ]); 29 | -------------------------------------------------------------------------------- /src/util/url.util.js: -------------------------------------------------------------------------------- 1 | export const hasProtocol = (url) => /^(?:[a-z]+:)?\/\//i.test(url); 2 | --------------------------------------------------------------------------------