├── .gitignore ├── demo └── src │ ├── index.js │ ├── App.less │ ├── index.html │ ├── reset.less │ └── App.js ├── .editorconfig ├── .babelrc ├── server.js ├── LICENSE ├── webpack.dev.config.js ├── .eslintrc ├── src └── Baseline.js ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | demo/dist 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from 'App'; 4 | 5 | render( 6 | , 7 | document.getElementById('demo') 8 | ); 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /demo/src/App.less: -------------------------------------------------------------------------------- 1 | @import "./reset.less"; 2 | 3 | html { 4 | font-family: Helvetica, Arial, sans-serif; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | } 10 | 11 | .root { 12 | margin: 50px auto; 13 | width: 320px; 14 | } 15 | 16 | .controls { 17 | margin-top: 27px; 18 | } 19 | -------------------------------------------------------------------------------- /demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React Baseline 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"], 3 | "env": { 4 | "development": { 5 | "plugins": [ 6 | [ 7 | "react-transform", 8 | { 9 | "transforms": [ 10 | { 11 | "transform": "react-transform-hmr", 12 | "imports": ["react"], 13 | "locals": ["module"] 14 | }, 15 | { 16 | "transform": "react-transform-catch-errors", 17 | "imports": ["react", "redbox-react"] 18 | } 19 | ] 20 | } 21 | ] 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var express = require('express'); 3 | var webpack = require('webpack'); 4 | var config = require('./webpack.dev.config'); 5 | var opn = require('opn'); 6 | var app = express(); 7 | var compiler = webpack(config); 8 | var port = 1704; 9 | 10 | app.use(require('webpack-dev-middleware')(compiler, { 11 | noInfo: true, 12 | publicPath: config.output.publicPath 13 | })); 14 | 15 | app.use(require('webpack-hot-middleware')(compiler)); 16 | 17 | app.use(express.static(path.join(__dirname, 'demo/dist'))); 18 | app.get('/', function(req, res) { 19 | res.sendFile(path.join(__dirname, 'demo/dist/index.html')); 20 | }); 21 | 22 | app.listen(port, 'localhost', function(err) { 23 | if (err) { 24 | console.log(err); // eslint-disable-line no-console 25 | } else { 26 | opn('http://localhost:' + port); 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2016 Misha Moroshko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the “Software”), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var autoprefixer = require('autoprefixer'); 4 | 5 | module.exports = { 6 | entry: [ 7 | 'webpack-hot-middleware/client', 8 | './demo/src/index' 9 | ], 10 | 11 | output: { 12 | path: path.resolve(__dirname, 'dist'), // Must be an absolute path 13 | filename: 'index.js', 14 | publicPath: '/' 15 | }, 16 | 17 | module: { 18 | loaders: [ 19 | { 20 | test: /\.js$/, 21 | loaders: ['babel'], 22 | include: [ 23 | path.resolve(__dirname, 'src'), // Must be an absolute path 24 | path.resolve(__dirname, 'demo', 'src') // Must be an absolute path 25 | ] 26 | }, 27 | { 28 | test: /\.less$/, 29 | loader: 'style!css?modules&localIdentName=[name]__[local]___[hash:base64:5]!postcss!less', 30 | exclude: /node_modules/ 31 | } 32 | ] 33 | }, 34 | 35 | postcss: function() { 36 | return [autoprefixer]; 37 | }, 38 | 39 | resolve: { 40 | modulesDirectories: ['node_modules', 'src'] 41 | }, 42 | 43 | plugins: [ 44 | new webpack.HotModuleReplacementPlugin() 45 | ] 46 | }; 47 | -------------------------------------------------------------------------------- /demo/src/reset.less: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "browser": true 6 | }, 7 | "parser": "babel-eslint", 8 | "plugins": [ 9 | "react" 10 | ], 11 | "extends": "plugin:react/recommended", 12 | "rules": { 13 | "array-callback-return": 2, 14 | "brace-style": [2, "1tbs"], 15 | "camelcase": [2, { "properties": "always" }], 16 | "comma-dangle": [2, "never"], 17 | "comma-style": [2, "last"], 18 | "eol-last": 2, 19 | "indent": [2, 2, { "SwitchCase": 1 }], 20 | "jsx-quotes": [2, "prefer-double"], 21 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 22 | "keyword-spacing": 2, 23 | "linebreak-style": [2, "unix"], 24 | "no-cond-assign": [2, "always"], 25 | "no-console": 2, 26 | "no-multiple-empty-lines": [2, { "max": 1 }], 27 | "no-spaced-func": 2, 28 | "no-trailing-spaces": 2, 29 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], 30 | "no-whitespace-before-property": 2, 31 | "newline-after-var": [2, "always"], 32 | "object-curly-spacing": [2, "always"], 33 | "prefer-rest-params": 2, 34 | "quote-props": [2, "as-needed"], 35 | "quotes": [2, "single"], 36 | "semi": [2, "always"], 37 | "space-before-blocks": [2, "always"], 38 | "space-before-function-paren": [2, "never"], 39 | "space-in-parens": [2, "never"], 40 | "template-curly-spacing": [2, "never"], 41 | 42 | "react/jsx-boolean-value": [2, "always"], 43 | "react/jsx-space-before-closing": [2, "always"] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Baseline.js: -------------------------------------------------------------------------------- 1 | import React, { Children, PropTypes } from 'react'; 2 | 3 | function calculateBackground(type, lineHeight, color) { 4 | if (type === 'line') { 5 | const percentage = (lineHeight - 1) / lineHeight * 100; 6 | 7 | return { 8 | backgroundSize: `100% ${lineHeight}px`, 9 | backgroundImage: `linear-gradient(to bottom, transparent 0%, transparent ${percentage}%, ${color} ${percentage}%, ${color} 100%)` 10 | }; 11 | } 12 | 13 | return { 14 | backgroundSize: `100% ${lineHeight * 2}px`, 15 | backgroundImage: `linear-gradient(to bottom, ${color} 50%, transparent 50%, transparent 100%)` 16 | }; 17 | } 18 | 19 | function Baseline(props) { 20 | const { isVisible, type, lineHeight, color, children, style, ...restProps } = props; 21 | const baselineStyle = { 22 | position: 'absolute', 23 | top: 0, 24 | left: 0, 25 | bottom: 0, 26 | right: 0, 27 | pointerEvents: 'none', 28 | ...calculateBackground(type, lineHeight, color) 29 | }; 30 | const rootProps = { 31 | ...restProps, 32 | style: { ...style, position: 'relative' } 33 | }; 34 | 35 | return ( 36 |
37 | { isVisible &&
} 38 | {Children.only(children)} 39 |
40 | ); 41 | } 42 | 43 | Baseline.propTypes = { 44 | isVisible: PropTypes.bool, 45 | type: PropTypes.string, 46 | lineHeight: PropTypes.number, 47 | color: PropTypes.string, 48 | children: PropTypes.node.isRequired, 49 | style: PropTypes.object 50 | }; 51 | 52 | Baseline.defaultProps = { 53 | isVisible: true, 54 | type: 'line', 55 | lineHeight: 9, 56 | color: 'rgba(0, 0, 0, 0.15)', 57 | style: {} 58 | }; 59 | 60 | export default Baseline; 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Build Status 4 | 5 | 6 | NPM Version 8 | 9 | 10 | # React Baseline 11 | 12 | This project provides a `` component that allows you to easily render a baseline overlay. To actually place the text on the baseline, check out basekick. 13 | 14 | ## Installation 15 | 16 | ```shell 17 | npm install react-baseline 18 | ``` 19 | 20 | ## Basic Usage 21 | 22 | ```js 23 | import Baseline from 'react-baseline'; 24 | 25 | // Then, just wrap some element with 26 | render() { 27 | return ( 28 | 29 |
30 | Some element 31 |
32 |
33 | ); 34 | } 35 | ``` 36 | 37 | ## Props 38 | 39 | * [`isVisible`](#isVisibleProp) 40 | * [`type`](#typeProp) 41 | * [`lineHeight`](#lineHeightProp) 42 | * [`color`](#colorProp) 43 | * You can also pass any other props like `className`, `style`, etc. 44 | 45 | 46 | #### isVisible (optional) 47 | 48 | Controls whether the baseline overlay will be rendered or not. Default: `true` 49 | 50 | 51 | #### type (optional) 52 | 53 | Baseline overlay comes with two types: `'line'` and `'bar'` 54 | 55 | Default: `'line'` 56 | 57 | 58 | #### lineHeight (optional) 59 | 60 | The distance in `px` between two consecutive baselines. 61 | 62 | Default: `9` 63 | 64 | 65 | #### color (optional) 66 | 67 | The color of the baseline overlay. 68 | 69 | All the usual CSS color formats are suppoted: `'#123456'`, `'#555'`, `'blue'`, `'rgb(100, 200, 300)'` 70 | 71 | Default: `'rgba(0, 0, 0, 0.15)'` 72 | 73 | ## Development 74 | 75 | ```shell 76 | $ npm install 77 | $ npm start 78 | ``` 79 | 80 | ## License 81 | 82 | MIT 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-baseline", 3 | "version": "1.0.1", 4 | "description": "Add baseline overlay to your React components", 5 | "main": "dist/Baseline.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/moroshko/react-baseline.git" 9 | }, 10 | "author": "Misha Moroshko ", 11 | "scripts": { 12 | "start": "rm -rf demo/dist && mkdir demo/dist && npm run copy-static-files && node server", 13 | "lint": "eslint demo/src src server.js webpack.dev.config.js", 14 | "copy-static-files": "cp demo/src/index.html demo/dist/", 15 | "dist": "rm -rf dist && mkdir dist && babel src/Baseline.js --out-file dist/Baseline.js", 16 | "build": "npm run lint", 17 | "preversion": "npm run build", 18 | "postversion": "git push && git push --tags", 19 | "prepublish": "npm run dist" 20 | }, 21 | "peerDependencies": { 22 | "react": "^15.0.1" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^6.3.6", 26 | "babel-cli": "^6.7.7", 27 | "babel-core": "^6.7.7", 28 | "babel-eslint": "^6.0.4", 29 | "babel-loader": "^6.2.4", 30 | "babel-plugin-react-transform": "^2.0.2", 31 | "babel-preset-es2015": "^6.6.0", 32 | "babel-preset-react": "^6.5.0", 33 | "babel-preset-stage-0": "^6.5.0", 34 | "basekick": "^2.0.0", 35 | "css-loader": "^0.23.1", 36 | "eslint": "^2.8.0", 37 | "eslint-plugin-react": "^5.0.1", 38 | "express": "^4.13.4", 39 | "less": "^2.6.1", 40 | "less-loader": "^2.2.3", 41 | "opn": "^4.0.1", 42 | "postcss-loader": "^0.8.2", 43 | "react": "^15.0.1", 44 | "react-dom": "^15.0.1", 45 | "react-transform-catch-errors": "^1.0.2", 46 | "react-transform-hmr": "^1.0.4", 47 | "redbox-react": "^1.2.3", 48 | "style-loader": "^0.13.1", 49 | "webpack": "^1.13.0", 50 | "webpack-dev-middleware": "^1.6.1", 51 | "webpack-hot-middleware": "^2.10.0" 52 | }, 53 | "files": [ 54 | "dist" 55 | ], 56 | "keywords": [ 57 | "baseline", 58 | "vertical rhythm", 59 | "vertical-rhythm", 60 | "typography", 61 | "grid", 62 | "react baseline", 63 | "react vertical rhythm", 64 | "react vertical-rhythm", 65 | "react typography", 66 | "react grid", 67 | "react-component" 68 | ], 69 | "license": "MIT" 70 | } 71 | -------------------------------------------------------------------------------- /demo/src/App.js: -------------------------------------------------------------------------------- 1 | import styles from './App.less'; 2 | 3 | import React, { Component } from 'react'; 4 | import basekick from 'basekick'; 5 | import Baseline from 'Baseline'; 6 | 7 | export default class App extends Component { 8 | constructor() { 9 | super(); 10 | 11 | this.state = { 12 | isVisible: true, 13 | type: 'line', 14 | lineHeight: 12 15 | }; 16 | 17 | this.toggleIsVisible = this.toggleIsVisible.bind(this); 18 | this.updateType = this.updateType.bind(this); 19 | this.updateLineHeight = this.updateLineHeight.bind(this); 20 | } 21 | 22 | toggleIsVisible() { 23 | const { isVisible } = this.state; 24 | 25 | this.setState({ 26 | isVisible: !isVisible 27 | }); 28 | } 29 | 30 | updateType(event) { 31 | this.setState({ 32 | type: event.target.value 33 | }); 34 | } 35 | 36 | updateLineHeight(event) { 37 | this.setState({ 38 | lineHeight: parseInt(event.target.value, 10) 39 | }); 40 | } 41 | 42 | render() { 43 | const { isVisible, type, lineHeight } = this.state; 44 | 45 | const headingStyle = { 46 | ...basekick({ 47 | baseFontSize: 10, 48 | descenderHeightScale: 0.12, 49 | gridRowHeight: lineHeight, 50 | typeSizeModifier: 2.1, 51 | typeRowSpan: 3 52 | }), 53 | fontWeight: 600 54 | }; 55 | 56 | const textStyle = basekick({ 57 | baseFontSize: 10, 58 | descenderHeightScale: 0.12, 59 | gridRowHeight: lineHeight, 60 | typeSizeModifier: 1.6, 61 | typeRowSpan: 2 62 | }); 63 | 64 | return ( 65 |
66 | 70 |
71 |

72 | React Baseline 73 |

74 |

75 | Lorem ipsum dolor sit amet, ex duo ponderum mandamus scriptorem. 76 | Sed nonumy principes iracundia et. 77 |

78 |
79 |
80 |
81 | 84 | { 85 | isVisible && 86 |
87 |

88 | Type: 89 | 97 | 105 |

106 |

107 | Line height: 108 | 113 |

114 |
115 | } 116 |
117 |
118 | ); 119 | } 120 | } 121 | --------------------------------------------------------------------------------