├── src ├── mixins │ ├── forms.js │ ├── lists.js │ ├── image.js │ ├── pagination.js │ ├── box-shadow.js │ ├── helpers.js │ ├── border-radius.js │ ├── grid.js │ ├── selectors.js │ └── buttons.js ├── utilities │ ├── align.js │ ├── _float.js │ ├── overflow.js │ ├── sizing.js │ ├── display.js │ ├── shadow.js │ ├── background.js │ ├── spacing.js │ ├── flex.js │ ├── borders.js │ └── text.js ├── code.js ├── reboot.js ├── images.js ├── progress.js ├── toasts.js ├── alerts.js ├── grid.js ├── modal.js ├── type.js ├── pagination.js ├── button-group.js ├── carousel.js ├── buttons.js ├── list-group.js ├── tables.js ├── index.js ├── card.js ├── forms.js └── constants.js ├── .eslintignore ├── index.js ├── jest.config.js ├── babel.config.json ├── .gitignore ├── .npmignore ├── assets ├── logo.old.original.svg ├── logo.old.svg └── logo.svg ├── .editorconfig ├── examples └── hello.js ├── package.json ├── .eslintrc └── README.md /src/mixins/forms.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* -------------------------------------------------------------------------------- /src/utilities/align.js: -------------------------------------------------------------------------------- 1 | // not supported 2 | // use flex.scss: justifyContent*, alighSelf*, ... -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import BootstrapStyleSheet from './src/'; 2 | export default BootstrapStyleSheet; 3 | -------------------------------------------------------------------------------- /src/mixins/lists.js: -------------------------------------------------------------------------------- 1 | export const mixinListUnstyled = ( 2 | // pass 3 | ) => ({ 4 | paddingLeft: 0, // useless 5 | }); 6 | -------------------------------------------------------------------------------- /src/mixins/image.js: -------------------------------------------------------------------------------- 1 | export const mixinImageFluid = ( 2 | constants, 3 | ) => ({ 4 | maxWidth: '100%', 5 | height: 'auto', 6 | }); -------------------------------------------------------------------------------- /src/utilities/_float.js: -------------------------------------------------------------------------------- 1 | // not applicable 2 | // see 3 | // - text: textLeft, textRight, ... 4 | // - flex: alignSelfStart, alignSelfEnd, ... 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bail: 1, 3 | verbose: true, 4 | preset: "react-native", 5 | transform: { 6 | "^.+\\.jsx?$": "babel-jest" 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-flow" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/mixins/pagination.js: -------------------------------------------------------------------------------- 1 | export const mixinPaginationSizePageLink = ( 2 | constants, 3 | paddingY, 4 | paddingX, 5 | fontSize, 6 | lineHeight, 7 | ) => Object.assign({ 8 | paddingVertical: paddingY, 9 | paddingHorizontal: paddingX, 10 | fontSize: fontSize, 11 | lineHeight: lineHeight, 12 | }); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *test.* 2 | .npmignore 3 | .expo/ 4 | node_modules/ 5 | npm-debug.* 6 | *.jks 7 | *.p8 8 | *.p12 9 | *.key 10 | *.mobileprovision 11 | *.orig.* 12 | web-build/ 13 | web-report/ 14 | package-lock.json 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | server.js 19 | server.mjs 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | docs 3 | 4 | *test.* 5 | 6 | node_modules/ 7 | .expo/ 8 | npm-debug.* 9 | *.jks 10 | *.p8 11 | *.p12 12 | *.key 13 | *.mobileprovision 14 | *.orig.* 15 | web-build/ 16 | web-report/ 17 | package-lock.json 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | server.js 22 | server.mjs 23 | .DS_Store 24 | 25 | -------------------------------------------------------------------------------- /src/mixins/box-shadow.js: -------------------------------------------------------------------------------- 1 | export const mixinBoxShadow = ( 2 | constants, 3 | shadowColor, 4 | shadowOffset, 5 | shadowOpacity, 6 | shadowRadius, 7 | ) => (constants.ENABLE_SHADOWS ? { 8 | shadowColor: shadowColor, 9 | shadowOffset: shadowOffset, 10 | shadowOpacity: shadowOpacity, 11 | shadowRadius: shadowRadius, 12 | elevation: 1, 13 | } : {}); -------------------------------------------------------------------------------- /src/utilities/overflow.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | OVERFLOWS, 4 | } = constants; 5 | 6 | const _classes = { 7 | // pass 8 | } 9 | 10 | OVERFLOWS.forEach((item) => { 11 | const classItem = item.charAt(0).toUpperCase() + item.slice(1).toLowerCase(); 12 | _classes['overflow' + classItem] = {overflow: item}; 13 | }); 14 | 15 | return _classes; 16 | }; 17 | -------------------------------------------------------------------------------- /src/utilities/sizing.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | SIZES, 4 | SCREENS_INFIXES, 5 | } = constants; 6 | 7 | const _classes = { 8 | mw100: { 9 | maxWidth: '100%', 10 | }, 11 | 12 | mh100: { 13 | maxHeight: '100%', 14 | }, 15 | }; 16 | 17 | // h%screen%size, w%screen%size / ex: h100, w50, hXs100, wLg50, wAuto 18 | SCREENS_INFIXES.forEach((itemScreen) => { 19 | Object.keys(SIZES).forEach(item => _classes['w' + itemScreen + item] = {width: SIZES[item]}); 20 | Object.keys(SIZES).forEach(item => _classes['h' + itemScreen + item] = {height: SIZES[item]}); 21 | }); 22 | 23 | return _classes; 24 | }; 25 | -------------------------------------------------------------------------------- /assets/logo.old.original.svg: -------------------------------------------------------------------------------- 1 | React Native Bootstrap Styles 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # A special property that should be specified at the top of the file outside of 4 | # any sections. Set to true to stop .editor config file search on current file 5 | root = true 6 | 7 | [*] 8 | # Indentation style 9 | # Possible values - tab, space 10 | indent_style = space 11 | 12 | # Indentation size in single-spaced characters 13 | # Possible values - an integer, tab 14 | indent_size = 2 15 | 16 | # Line ending file format 17 | # Possible values - lf, crlf, cr 18 | end_of_line = lf 19 | 20 | # File character encoding 21 | # Possible values - latin1, utf-8, utf-16be, utf-16le 22 | charset = utf-8 23 | 24 | # Denotes whether to trim whitespace at the end of lines 25 | # Possible values - true, false 26 | trim_trailing_whitespace = true 27 | 28 | # Denotes whether file should end with a newline 29 | # Possible values - true, false 30 | insert_final_newline = true 31 | -------------------------------------------------------------------------------- /src/code.js: -------------------------------------------------------------------------------- 1 | import { mixinBorderRadius } from './mixins/border-radius'; 2 | import { mixinBoxShadow } from './mixins/box-shadow'; 3 | 4 | export default function getClasses(constants, classes) { 5 | const { 6 | BORDER_RADIUS_SM, 7 | KBD_PADDING_Y, 8 | KBD_PADDING_X, 9 | KBD_FONT_SIZE, 10 | KBD_COLOR, 11 | KBD_BG, 12 | } = constants; 13 | 14 | const _classes = { 15 | 16 | kbd: Object.assign({ 17 | paddingHorizontal: KBD_PADDING_X, 18 | paddingVertical: KBD_PADDING_Y, 19 | color: KBD_COLOR, 20 | backgroundColor: KBD_BG, 21 | 22 | // kbd { 23 | // padding: 0; 24 | // @include font-size(100%); 25 | // font-weight: $nested-kbd-font-weight; 26 | // @include box-shadow(none); 27 | // } 28 | }, 29 | mixinBorderRadius(constants, BORDER_RADIUS_SM), 30 | // not supported / inset shadow / mixinBoxShadow(constants, ...), 31 | ), 32 | } 33 | 34 | return _classes; 35 | }; 36 | -------------------------------------------------------------------------------- /src/utilities/display.js: -------------------------------------------------------------------------------- 1 | import { selectorMediaUp } from '../mixins/selectors'; 2 | 3 | export default function getClasses(constants, classes) { 4 | const { 5 | SCREENS_INFIXES, 6 | } = constants; 7 | 8 | // experimental 9 | const TYPES = { 10 | 'None': 'none', 11 | 'Flex': 'flex', 12 | // how about extra values? 13 | // inline 14 | // inline-block 15 | // block 16 | // table 17 | // table-cell 18 | // table-row 19 | // inline-flex 20 | }; 21 | 22 | const _classes = { 23 | // pass 24 | }; 25 | 26 | // d%value / ex: dNone. dFlex 27 | // d%screen%value, / ex: dLgNone 28 | SCREENS_INFIXES.forEach((itemScreen) => { 29 | Object.keys(TYPES).forEach(item => { 30 | _classes['d' + itemScreen + item] = selectorMediaUp(itemScreen, SCREENS_INFIXES, { 31 | display: TYPES[item], 32 | }); 33 | }); 34 | }); 35 | 36 | // dPrint%value, / ex: dPrintNone 37 | Object.keys(TYPES).forEach(item => { 38 | _classes['d' + 'Print' + item] = { 39 | display: TYPES[item], 40 | }; 41 | }); 42 | 43 | return _classes; 44 | }; 45 | -------------------------------------------------------------------------------- /src/mixins/helpers.js: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | // aka helpers 4 | 5 | export const getDefined = (v, d) => v === undefined ? d : v; 6 | 7 | export const getScreens = (breakpoints, size) => ( 8 | // ascending list of screens ("active" breakpoints) 9 | Object.keys(breakpoints) 10 | .sort((a, b) => (breakpoints[a] > breakpoints[b] ? 1 : -1)) 11 | .map(item => [item, breakpoints[item]]) 12 | .filter(item => item[1] <= size) 13 | .map(item => item[0]) 14 | ); 15 | 16 | export const colorYiq = (constants, color, dark, light) => { 17 | const c = Color(color); 18 | const { YIG_CONTRASTED_THRESHOLD, YIG_TEXT_DARK, YIG_TEXT_LIGHT } = constants; 19 | const yiq = ((c.red() * 299) + (c.green() * 587) + (c.blue() * 114)) / 1000; 20 | return (yiq >= YIG_CONTRASTED_THRESHOLD) ? (dark || YIG_TEXT_DARK) : (light || YIG_TEXT_LIGHT); 21 | }; 22 | 23 | export const colorLevel = (constants, color, level = 0) => ( 24 | Color(color || constants.PRIMARY).mix( 25 | Color(level > 0 ? constants.BLACK : constants.WHITE), 26 | Math.abs(level) * constants.THEME_COLOR_INTERVAL, 27 | ).hex() 28 | ); 29 | -------------------------------------------------------------------------------- /examples/hello.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Text } from 'react-native'; 3 | import BootstrapStyleSheet from 'react-native-bootstrap-styles'; 4 | 5 | const 6 | REM = BootstrapStyleSheet.DIMENSIONS_WIDTH < 360 ? 14 : 16, 7 | BODY_COLOR = '#000022', 8 | TEXT_MUTED = '#888888'; 9 | 10 | // custom constants 11 | const constants = { 12 | REM, 13 | BODY_COLOR, TEXT_MUTED, 14 | }; 15 | 16 | // custom classes 17 | const classes = { 18 | title: { 19 | color: 'red', 20 | } 21 | }; 22 | 23 | const bootstrapStyleSheet = new BootstrapStyleSheet(constants, classes); 24 | const s = bootstrapStyleSheet.create(); // styles 25 | const c = bootstrapStyleSheet.constants; // constants 26 | 27 | class Hello extends Component { 28 | 29 | render() { 30 | return ( 31 | 32 | 33 | Hello world! 34 | 🤓🚀🚀🚀 35 | 36 | 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /assets/logo.old.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/utilities/shadow.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | SHADOW, SHADOW_SM, SHADOW_LG, 4 | SHADOW_COLOR, SHADOW_OPACITY, SHADOW_OFFSET, SHADOW_RADIUS, 5 | SHADOW_COLOR_SM, SHADOW_OPACITY_SM, SHADOW_OFFSET_SM, SHADOW_RADIUS_SM, 6 | SHADOW_COLOR_LG, SHADOW_OPACITY_LG, SHADOW_OFFSET_LG, SHADOW_RADIUS_LG, 7 | } = constants; 8 | 9 | const _classes = { 10 | shadow: Object.assign({}, SHADOW, { // for android 11 | shadowColor: SHADOW_COLOR, 12 | shadowOffset: SHADOW_OFFSET, 13 | shadowOpacity: SHADOW_OPACITY, 14 | shadowRadius: SHADOW_RADIUS, 15 | }), 16 | 17 | shadowSm: Object.assign({}, SHADOW_SM, { // for android 18 | shadowColor: SHADOW_COLOR_SM, 19 | shadowOffset: SHADOW_OFFSET_SM, 20 | shadowOpacity: SHADOW_OPACITY_SM, 21 | shadowRadius: SHADOW_RADIUS_SM, 22 | }), 23 | 24 | shadowLg: Object.assign({}, SHADOW_LG, { // for android 25 | shadowColor: SHADOW_COLOR_LG, 26 | shadowOffset: SHADOW_OFFSET_LG, 27 | shadowOpacity: SHADOW_OPACITY_LG, 28 | shadowRadius: SHADOW_RADIUS_LG, 29 | }), 30 | 31 | shadowNone: Object.assign({}, {elevation: 0}, { // for android 32 | shadowOffset: {width: 0, height: 0}, 33 | shadowOpacity: 0, 34 | shadowRadius: 0, 35 | }), 36 | }; 37 | 38 | return _classes; 39 | }; 40 | -------------------------------------------------------------------------------- /src/reboot.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | BODY_BG, 4 | BODY_COLOR, 5 | FONT_FAMILY_BASE, 6 | FONT_WEIGHT_BASE, 7 | FONT_SIZE_BASE, 8 | LINE_HEIGHT_BASE, 9 | LINK_COLOR, 10 | LINK_DECORATION, 11 | LINK_DECORATION_LINE, 12 | LINK_DECORATION_COLOR, 13 | LINK_DECORATION_STYLE, 14 | LABEL_MARGIN_BOTTOM, 15 | } = constants; 16 | 17 | const _classes = { 18 | text: { 19 | fontFamily: FONT_FAMILY_BASE, 20 | fontSize: FONT_SIZE_BASE, 21 | fontWeight: FONT_WEIGHT_BASE, 22 | lineHeight: LINE_HEIGHT_BASE, 23 | color: BODY_COLOR, 24 | }, 25 | 26 | linkText: Object.assign({}, classes.text, { 27 | color: LINK_COLOR, 28 | textDecorationLine: LINK_DECORATION_LINE, 29 | textDecorationColor: LINK_DECORATION_COLOR, 30 | textDecorationStyle: LINK_DECORATION_STYLE, 31 | 32 | // @include hover() { 33 | // color: $link-hover-color; 34 | // text-decoration: $link-hover-decoration; 35 | // } 36 | }), 37 | 38 | image: { 39 | resizeMode: 'contain', 40 | }, 41 | 42 | body: { 43 | flex: 1, // experimental 44 | backgroundColor: BODY_BG, 45 | }, 46 | 47 | hidden: { 48 | display: 'none', 49 | }, 50 | }; 51 | 52 | // aliases 53 | _classes.link = _classes.linkText; 54 | _classes.a = _classes.linkText; 55 | 56 | return _classes; 57 | }; 58 | -------------------------------------------------------------------------------- /src/utilities/background.js: -------------------------------------------------------------------------------- 1 | import { colorLevel } from '../mixins/helpers'; 2 | 3 | export default function getClasses(constants, classes) { 4 | const { 5 | WHITE, 6 | THEME_COLORS, 7 | } = constants; 8 | 9 | const _classes = { 10 | // @if $enable-gradients { 11 | // @each $color, $value in $theme-colors { 12 | // @include bg-gradient-variant(".bg-gradient-#{$color}", $value); 13 | // } 14 | // } 15 | 16 | bgWhite: { 17 | backgroundColor: WHITE, 18 | }, 19 | 20 | bgTransparent: { 21 | backgroundColor: 'transparent', 22 | }, 23 | }; 24 | 25 | // bg%color / ex: bgPrimary 26 | // bg%colorLight / ex: bgPrimaryLight 27 | // bg%colorDark / ex: bgPrimaryDark 28 | Object.keys(THEME_COLORS).forEach((item) => { 29 | const classColor = item.charAt(0).toUpperCase() + item.slice(1).toLowerCase(); 30 | _classes['bg' + classColor] = {backgroundColor: THEME_COLORS[item]}; 31 | 32 | // custom 33 | _classes['bg' + classColor + 'Light'] = {backgroundColor: colorLevel(constants, THEME_COLORS[item], -9)}; 34 | _classes['bg' + classColor + 'Dark'] = {backgroundColor: colorLevel(constants, THEME_COLORS[item], 9)}; 35 | 36 | // custom / experimental 37 | _classes['bg' + classColor + 'Lightest'] = {backgroundColor: colorLevel(constants, THEME_COLORS[item], -11)}; 38 | _classes['bg' + classColor + 'Darkest'] = {backgroundColor: colorLevel(constants, THEME_COLORS[item], -11)}; 39 | }); 40 | 41 | return _classes; 42 | }; 43 | -------------------------------------------------------------------------------- /src/images.js: -------------------------------------------------------------------------------- 1 | import { mixinBorderRadius } from './mixins/border-radius'; 2 | import { mixinBoxShadow } from './mixins/box-shadow'; 3 | import { mixinImageFluid } from './mixins/image'; 4 | 5 | export default function getClasses(constants, classes) { 6 | const { 7 | THUMBNAIL_PADDING, 8 | THUMBNAIL_BG, 9 | THUMBNAIL_BORDER_WIDTH, 10 | THUMBNAIL_BORDER_COLOR, 11 | THUMBNAIL_BORDER_RADIUS, 12 | THUMBNAIL_BOX_SHADOW_COLOR, 13 | THUMBNAIL_BOX_SHADOW_OPACITY, 14 | THUMBNAIL_BOX_SHADOW_OFFSET, 15 | THUMBNAIL_BOX_SHADOW_RADIUS, 16 | } = constants; 17 | 18 | const _classes = { 19 | imgFluid: mixinImageFluid(constants), 20 | 21 | imgThumbnail: Object.assign({ 22 | padding: THUMBNAIL_PADDING, 23 | backgroundColor: THUMBNAIL_BG, 24 | borderWidth: THUMBNAIL_BORDER_WIDTH, 25 | borderColor: THUMBNAIL_BORDER_COLOR, 26 | borderStyle: 'solid', 27 | }, 28 | mixinBorderRadius(constants, THUMBNAIL_BORDER_RADIUS), 29 | mixinBoxShadow(constants, THUMBNAIL_BOX_SHADOW_COLOR, THUMBNAIL_BOX_SHADOW_OFFSET, THUMBNAIL_BOX_SHADOW_OPACITY, THUMBNAIL_BOX_SHADOW_RADIUS), 30 | mixinImageFluid(constants), 31 | ), 32 | 33 | // .figure { 34 | // // Ensures the caption's text aligns with the image. 35 | // display: inline-block; 36 | // } 37 | 38 | // .figure-img { 39 | // margin-bottom: $spacer / 2; 40 | // line-height: 1; 41 | // } 42 | 43 | // .figure-caption { 44 | // @include font-size($figure-caption-font-size); 45 | // color: $figure-caption-color; 46 | // } 47 | } 48 | 49 | return _classes; 50 | }; 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-bootstrap-styles", 3 | "keywords": [ 4 | "react-component", 5 | "react-native", 6 | "bootstrap", 7 | "bootstrap4", 8 | "style", 9 | "responsive", 10 | "scale", 11 | "component", 12 | "flex", 13 | "grid", 14 | "modal", 15 | "form", 16 | "button" 17 | ], 18 | "version": "4.5.0-q", 19 | "description": "Bootstrap style library for React Native", 20 | "author": "Andrei Vasin (aka Savva) ", 21 | "license": "MIT", 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/andrenerd/react-native-bootstrap-styles.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/andrenerd/react-native-bootstrap-styles/issues" 28 | }, 29 | "homepage": "https://github.com/andrenerd/react-native-bootstrap-styles#readme", 30 | "main": "index.js", 31 | "scripts": { 32 | "start": "react-native start", 33 | "lint": "eslint . --ext .js,.jsx", 34 | "lint-fix": "eslint . --ext .js,.jsx --fix", 35 | "test": "jest" 36 | }, 37 | "pre-commit": { 38 | "run": [], 39 | "silent": false 40 | }, 41 | "dependencies": { 42 | "color": "^3.1.2", 43 | "events": "latest" 44 | }, 45 | "peerDependencies": { 46 | "react": "*", 47 | "react-native": "*" 48 | }, 49 | "devDependencies": { 50 | "@babel/core": "^7.8.6", 51 | "@babel/preset-env": "^7.10.2", 52 | "@babel/preset-flow": "^7.10.1", 53 | "babel-eslint": "^8.2.6", 54 | "babel-jest": "^26.0.1", 55 | "babel-plugin-transform-class-properties": "^6.24.1", 56 | "eslint": "^4.19.1", 57 | "eslint-config-airbnb": "^16.1.0", 58 | "eslint-plugin-import": "^2.20.2", 59 | "eslint-plugin-jsx-a11y": "^6.2.3", 60 | "eslint-plugin-react": "^7.19.0", 61 | "jest": "^26.0.1", 62 | "pre-commit": "^1.2.2" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/utilities/spacing.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | SPACERS, 4 | SCREENS_INFIXES, 5 | } = constants; 6 | 7 | const _classes = { 8 | // .m#{$infix}-auto { margin: auto !important; } 9 | // .mt#{$infix}-auto, 10 | // .my#{$infix}-auto { 11 | // margin-top: auto !important; 12 | // } 13 | // .mr#{$infix}-auto, 14 | // .mx#{$infix}-auto { 15 | // margin-right: auto !important; 16 | // } 17 | // .mb#{$infix}-auto, 18 | // .my#{$infix}-auto { 19 | // margin-bottom: auto !important; 20 | // } 21 | // .ml#{$infix}-auto, 22 | // .mx#{$infix}-auto { 23 | // margin-left: auto !important; 24 | // } 25 | }; 26 | 27 | // p/m%screen%size 28 | // ex: pySm3, mb2, plXs0 29 | const SPACERS_TYPES = {margin: 'm', padding: 'p'}; 30 | 31 | SCREENS_INFIXES.forEach((itemScreen) => { 32 | Object.keys(SPACERS).forEach((itemSpacer) => { 33 | Object.keys(SPACERS_TYPES).forEach((itemType) => { 34 | _classes[SPACERS_TYPES[itemType] + itemScreen + itemSpacer] = {[itemType]: SPACERS[itemSpacer]}; 35 | _classes[SPACERS_TYPES[itemType] + 'y' + itemScreen + itemSpacer] = {[itemType + 'Vertical']: SPACERS[itemSpacer]}; 36 | _classes[SPACERS_TYPES[itemType] + 'x' + itemScreen + itemSpacer] = {[itemType + 'Horizontal']: SPACERS[itemSpacer]}; 37 | _classes[SPACERS_TYPES[itemType] + 't' + itemScreen + itemSpacer] = {[itemType + 'Top']: SPACERS[itemSpacer]}; 38 | _classes[SPACERS_TYPES[itemType] + 'b' + itemScreen + itemSpacer] = {[itemType + 'Bottom']: SPACERS[itemSpacer]}; 39 | _classes[SPACERS_TYPES[itemType] + 'r' + itemScreen + itemSpacer] = {[itemType + 'Right']: SPACERS[itemSpacer]}; 40 | _classes[SPACERS_TYPES[itemType] + 'l' + itemScreen + itemSpacer] = {[itemType + 'Left']: SPACERS[itemSpacer]}; 41 | }); 42 | }); 43 | }); 44 | 45 | return _classes; 46 | }; 47 | -------------------------------------------------------------------------------- /src/progress.js: -------------------------------------------------------------------------------- 1 | import { mixinBorderRadius } from './mixins/border-radius'; 2 | import { mixinBoxShadow } from './mixins/box-shadow'; 3 | 4 | export default function getClasses(constants, classes) { 5 | const { 6 | PROGRESS_HEIGHT, 7 | PROGRESS_FONT_SIZE, 8 | PROGRESS_BG, 9 | PROGRESS_BORDER_RADIUS, 10 | PROGRESS_BOX_SHADOW_COLOR, 11 | PROGRESS_BOX_SHADOW_OFFSET, 12 | PROGRESS_BOX_SHADOW_OPACITY, 13 | PROGRESS_BOX_SHADOW_RADIUS, 14 | PROGRESS_BAR_COLOR, 15 | PROGRESS_BAR_BG, 16 | PROGRESS_BAR_ANIMATION_TIMING, 17 | PROGRESS_BAR_TRANSITION, 18 | } = constants; 19 | 20 | const _classes = { 21 | // @keyframes progress-bar-stripes { 22 | // from { background-position: $progress-height 0; } 23 | // to { background-position: 0 0; } 24 | // } 25 | 26 | progress: Object.assign({ 27 | height: PROGRESS_HEIGHT, 28 | overflow: 'hidden', 29 | backgroundColor: PROGRESS_BG, 30 | }, 31 | mixinBorderRadius(constants, PROGRESS_BORDER_RADIUS), 32 | // TODO: replace with inner shadow 33 | // https://stackoverflow.com/questions/38084120/box-shadowinset-for-react-native 34 | // mixinBoxShadow(constants, PROGRESS_BOX_SHADOW_COLOR, PROGRESS_BOX_SHADOW_OFFSET, PROGRESS_BOX_SHADOW_OPACITY, PROGRESS_BOX_SHADOW_RADIUS), 35 | ), 36 | 37 | progressBar: { 38 | flex: 1, // display: flex, 39 | flexDirection: 'column', 40 | justifyContent: 'center', 41 | backgroundColor: PROGRESS_BAR_BG, 42 | // @include transition($progress-bar-transition), 43 | }, 44 | 45 | progressText: { 46 | color: PROGRESS_BAR_COLOR, 47 | fontSize: PROGRESS_FONT_SIZE, 48 | textAlign: 'center', 49 | }, 50 | 51 | // .progress-bar-striped { 52 | // @include gradient-striped(); 53 | // background-size: $progress-height $progress-height; 54 | // } 55 | 56 | // .progress-bar-animated { 57 | // animation: progress-bar-stripes $progress-bar-animation-timing; 58 | // } 59 | }; 60 | 61 | return _classes; 62 | }; 63 | -------------------------------------------------------------------------------- /src/toasts.js: -------------------------------------------------------------------------------- 1 | import { mixinBorderRadius } from './mixins/border-radius'; 2 | 3 | export default function getClasses(constants, classes) { 4 | const { 5 | TOAST_MAX_WIDTH, 6 | TOAST_PADDING_X, 7 | TOAST_PADDING_Y, 8 | TOAST_FONT_SIZE, 9 | TOAST_COLOR, 10 | TOAST_BACKGROUND_COLOR, 11 | TOAST_BORDER_WIDTH, 12 | TOAST_BORDER_COLOR, 13 | TOAST_BORDER_RADIUS, 14 | TOAST_BOX_SHADOW_COLOR, 15 | TOAST_BOX_SHADOW_OPACITY, 16 | TOAST_BOX_SHADOW_OFFSET, 17 | TOAST_BOX_SHADOW_RADIUS, 18 | TOAST_HEADER_COLOR, 19 | TOAST_HEADER_BACKGROUND_COLOR, 20 | TOAST_HEADER_BORDER_COLOR, 21 | } = constants; 22 | 23 | const _classes = { 24 | 25 | toast: Object.assign({ 26 | maxWidth: TOAST_MAX_WIDTH, 27 | overflow: 'hidden', // cheap rounded corners on nested items 28 | color: TOAST_COLOR, 29 | backgroundColor: TOAST_BACKGROUND_COLOR, 30 | borderWidth: TOAST_BORDER_WIDTH, 31 | borderStyle: 'solid', 32 | borderColor: TOAST_BORDER_COLOR, 33 | shadowColor: TOAST_BOX_SHADOW_COLOR, 34 | shadowOffset: TOAST_BOX_SHADOW_OFFSET, 35 | shadowOpacity: TOAST_BOX_SHADOW_OPACITY, 36 | shadowRadius: TOAST_BOX_SHADOW_RADIUS, 37 | opacity: 0, 38 | // not supported / backdrop-filter: blur(10px); // check: BlurView, react-native-blur 39 | // not supported / backgroundClip: padding-box, 40 | }, 41 | mixinBorderRadius(constants, TOAST_BORDER_RADIUS), 42 | // @include font-size($toast-font-size); 43 | ), 44 | 45 | toastNotLastChild: (nOrBool, length) => selectorLastChild(nOrBool, length, { 46 | marginBottom: TOAST_PADDING_X, 47 | }), 48 | 49 | toastHeader: { 50 | alignItems: 'center', 51 | paddingVertical: TOAST_PADDING_Y, 52 | paddingHorizontal: TOAST_PADDING_X, 53 | color: TOAST_HEADER_COLOR, 54 | backgroundColor: TOAST_HEADER_BACKGROUND_COLOR, 55 | borderWidth: TOAST_BORDER_WIDTH, 56 | borderStyle: 'solid', 57 | borderColor: TOAST_HEADER_BORDER_COLOR, 58 | // not supported / background-clip: padding-box; 59 | }, 60 | 61 | toastBody: { 62 | padding: TOAST_PADDING_X, // apply to both vertical and horizontal 63 | }, 64 | 65 | toastShowing: { 66 | opacity: 1, 67 | }, 68 | 69 | toastShow: { 70 | opacity: 1, 71 | }, 72 | 73 | toastHide: { 74 | display: 'none', 75 | }, 76 | }; 77 | 78 | return _classes; 79 | }; 80 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "react-app", 5 | "airbnb" 6 | ], 7 | "env": { 8 | "es6": true, 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "react/jsx-filename-extension": 0, 14 | "react/forbid-prop-types": 0, 15 | "react/prefer-stateless-function": 0, 16 | "react/prop-types": 0, 17 | "react/require-default-props": 0, 18 | "react/no-array-index-key": 0, 19 | "react/sort-comp": 0, 20 | "react/no-did-mount-set-state": 0, 21 | "react/no-multi-comp": 0, 22 | "react/jsx-no-undef": 0, 23 | "react/no-unused-prop-types": 0, 24 | "react/no-unused-state": 0, 25 | "react/default-props-match-prop-types": 0, 26 | "react/jsx-no-bind": 0, 27 | "react/jsx-closing-bracket-location": 0, 28 | "react/style-prop-object": 0, 29 | "jsx-a11y/anchor-is-valid": 0, 30 | "jsx-a11y/href-no-hash": 0, 31 | "jsx-a11y/click-events-have-key-events": 0, 32 | "jsx-a11y/no-static-element-interactions": 0, 33 | "jsx-a11y/accessible-emoji": 0, 34 | "jsx-a11y/label-has-for": 0, 35 | "prefer-destructuring": 0, 36 | "prefer-promise-reject-errors": 0, 37 | "prefer-template": 0, 38 | "import/first": 0, 39 | "import/prefer-default-export": 0, 40 | "import/no-extraneous-dependencies": 0, 41 | "import/no-unresolved": 0, 42 | "import/extensions": 0, 43 | "import/newline-after-import": 0, 44 | "camelcase": 0, 45 | "no-empty-pattern": 0, 46 | "no-param-reassign": 0, 47 | "no-shadow": 0, 48 | "no-class-assign": 0, 49 | "no-console": 0, 50 | "no-debugger": 0, 51 | "no-plusplus": 0, 52 | "no-undef": 0, 53 | "no-unused-vars": 0, 54 | "no-unused-expressions": 0, 55 | "no-underscore-dangle": 0, 56 | "no-useless-escape": 0, 57 | "no-useless-constructor": 0, 58 | "no-use-before-define": 0, 59 | "no-throw-literal": 0, 60 | "no-return-assign": 0, 61 | "no-mixed-operators": 0, 62 | "no-restricted-properties": 0, 63 | "no-case-declarations": 0, 64 | "max-len": 0, 65 | "one-var": 0, 66 | "quotes": 0, 67 | "quote-props": 0, 68 | "consistent-return": 0, 69 | "class-methods-use-this": 0, 70 | "function-paren-newline": 0, 71 | "global-require": 0, 72 | "object-curly-spacing": 0, 73 | "object-curly-newline": 0, 74 | "object-property-newline": 0, 75 | "object-shorthand": 0, 76 | "padded-blocks": 0, 77 | "eol-last": 0, 78 | "eqeqeq": 0, 79 | "indent": 0, 80 | "dot-notation": 0, 81 | "default-case": 0 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/utilities/flex.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | GRID_BREAKPOINTS, 4 | GRID_COLUMNS, 5 | SCREENS_INFIXES, 6 | } = constants; 7 | 8 | const _classes = { 9 | // todo: make it "screen" aware 10 | justifyContentStart: {justifyContent: 'flex-start'}, 11 | justifyContentEnd: {justifyContent: 'flex-end'}, 12 | justifyContentCenter: {justifyContent: 'center'}, 13 | justifyContentBetween: {justifyContent: 'space-between'}, 14 | justifyContentAround: {justifyContent: 'space-around'}, 15 | 16 | alignItemsStart: {alignItems: 'flex-start'}, 17 | alignItemsEnd: {alignItems: 'flex-end'}, 18 | alignItemsCenter: {alignItems: 'center'}, 19 | alignItemsBaseline: {alignItems: 'baseline'}, 20 | alignItemsStretch: {alignItems: 'stretch'}, 21 | 22 | alignContentStart: {alignContent: 'flex-start'}, 23 | alignContentEnd: {alignContent: 'flex-end'}, 24 | alignContentCenter: {alignContent: 'center'}, 25 | alignContentBetween: {alignContent: 'space-between'}, 26 | alignContentAround: {alignContent: 'space-around'}, 27 | alignContentStretch: {alignContent: 'stretch'}, 28 | 29 | alignSelfAuto: {alignSelf: 'auto'}, 30 | alignSelfStart: {alignSelf: 'flex-start'}, 31 | alignSelfEnd: {alignSelf: 'flex-end'}, 32 | alignSelfCenter: {alignSelf: 'center'}, 33 | alignSelfBaseline: {alignSelf: 'baseline'}, 34 | alignSelfStretch: {alignSelf: 'stretch'}, 35 | }; 36 | 37 | // flex%screen%columns 38 | // flex%screen (synonym to flex%screen1) 39 | SCREENS_INFIXES.forEach((itemScreen) => { 40 | Array.from(Array(GRID_COLUMNS + 1).keys()).forEach(item => _classes['flex' + itemScreen + (item + 1)] = {flex: item + 1}); 41 | _classes['flex' + itemScreen] = _classes['flex' + itemScreen + 1]; // experimental 42 | 43 | _classes['flex' + itemScreen + 'Row'] = {flexDirection: 'row'}; 44 | _classes['flex' + itemScreen + 'Column'] = {flexDirection: 'column'}; 45 | _classes['flex' + itemScreen + 'RowReverse'] = {flexDirection: 'row-reverse'}; 46 | _classes['flex' + itemScreen + 'ColumnReverse'] = {flexDirection: 'column-reverse'}; 47 | 48 | _classes['flex' + itemScreen + 'Wrap'] = {flexWrap: 'wrap'}; 49 | _classes['flex' + itemScreen + 'Nowrap'] = {flexWrap: 'nowrap'}; 50 | // reserved / .flex#{$infix}-fill { flex: 1 1 auto !important; } 51 | _classes['flex' + itemScreen + 'Grow0'] = {flexGrow: 0}; 52 | _classes['flex' + itemScreen + 'Shrink0'] = {flexShrink: 0}; 53 | _classes['flex' + itemScreen + 'Shrink1'] = {flexShrink: 1}; 54 | }); 55 | 56 | return _classes; 57 | }; 58 | -------------------------------------------------------------------------------- /src/mixins/border-radius.js: -------------------------------------------------------------------------------- 1 | import { getDefined } from './helpers'; 2 | 3 | // TODO: think to drop "constants" as param for all the mixins 4 | 5 | export const mixinBorderRadius = ( 6 | constants, 7 | allOrTopLeftRadius, 8 | topRightRadius, 9 | bottomRightRadius, 10 | bottomLeftRadius, 11 | ) => (constants.ENABLE_ROUNDED ? ( 12 | arguments.length <= 2 ? { 13 | borderRadius: 1, // getDefined(allOrTopLeftRadius, constants.BORDER_RADIUS), 14 | } : { 15 | borderTopLeftRadius: getDefined(allOrTopLeftRadius, constants.BORDER_RADIUS), 16 | borderTopRightRadius: getDefined(topRightRadius, constants.BORDER_RADIUS), 17 | borderBottomRightRadius: getDefined(bottomRightRadius, constants.BORDER_RADIUS), 18 | borderBottomLeftRadius: getDefined(bottomLeftRadius, constants.BORDER_RADIUS), 19 | } 20 | ) : {}); 21 | 22 | export const mixinBorderTopRadius = ( 23 | constants, 24 | radius, 25 | ) => (constants.ENABLE_ROUNDED ? { 26 | borderTopLeftRadius: getDefined(radius, constants.BORDER_RADIUS), 27 | borderTopRightRadius: getDefined(radius, constants.BORDER_RADIUS), 28 | } : {}); 29 | 30 | export const mixinBorderBottomRadius = ( 31 | constants, 32 | radius, 33 | ) => (constants.ENABLE_ROUNDED ? { 34 | borderBottomLeftRadius: getDefined(radius, constants.BORDER_RADIUS), 35 | borderBottomRightRadius: getDefined(radius, constants.BORDER_RADIUS), 36 | } : {}); 37 | 38 | export const mixinBorderLeftRadius = ( 39 | constants, 40 | radius, 41 | ) => (constants.ENABLE_ROUNDED ? { 42 | borderTopLeftRadius: getDefined(radius, constants.BORDER_RADIUS), 43 | borderBottomLeftRadius: getDefined(radius, constants.BORDER_RADIUS), 44 | } : {}); 45 | 46 | export const mixinBorderRightRadius = ( 47 | constants, 48 | radius, 49 | ) => (constants.ENABLE_ROUNDED ? { 50 | borderTopRightRadius: getDefined(radius, constants.BORDER_RADIUS), 51 | borderBottomRightRadius: getDefined(radius, constants.BORDER_RADIUS), 52 | } : {}); 53 | 54 | export const mixinBorderTopLeftRadius = ( 55 | constants, 56 | radius, 57 | ) => (constants.ENABLE_ROUNDED ? { 58 | borderTopLeftRadius: getDefined(radius, constants.BORDER_RADIUS), 59 | } : {}); 60 | 61 | export const mixinBorderTopRightRadius = ( 62 | constants, 63 | radius, 64 | ) => (constants.ENABLE_ROUNDED ? { 65 | borderTopRightRadius: getDefined(radius, constants.BORDER_RADIUS), 66 | } : {}); 67 | 68 | export const mixinBorderBottomLeftRadius = ( 69 | constants, 70 | radius, 71 | ) => (constants.ENABLE_ROUNDED ? { 72 | borderBottomLeftRadius: getDefined(radius, constants.BORDER_RADIUS), 73 | } : {}); 74 | 75 | export const mixinBorderBottomRightRadius = ( 76 | constants, 77 | radius, 78 | ) => (constants.ENABLE_ROUNDED ? { 79 | borderBottomRightRadius: getDefined(radius, constants.BORDER_RADIUS), 80 | } : {}); 81 | -------------------------------------------------------------------------------- /src/alerts.js: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | import { mixinBorderRadius } from './mixins/border-radius'; 4 | import { colorLevel } from './mixins/helpers'; 5 | 6 | export default function getClasses(constants, classes) { 7 | const { 8 | THEME_COLORS, 9 | ALERT_PADDING_Y, 10 | ALERT_PADDING_X, 11 | ALERT_MARGIN_BOTTOM, 12 | ALERT_BORDER_RADIUS, 13 | ALERT_LINK_FONT_WEIGHT, 14 | ALERT_BORDER_WIDTH, 15 | ALERT_BG_LEVEL, 16 | ALERT_BORDER_LEVEL, 17 | ALERT_COLOR_LEVEL, 18 | 19 | CLOSE_FONT_SIZE, 20 | CLOSE_FONT_WEIGHT, 21 | CLOSE_COLOR, 22 | } = constants; 23 | 24 | const _classes = { 25 | alert: Object.assign({ 26 | position: 'relative', 27 | width: '100%', 28 | paddingVertical: ALERT_PADDING_Y, 29 | paddingHorizontal: ALERT_PADDING_X, 30 | marginBottom: ALERT_MARGIN_BOTTOM, 31 | borderWidth: ALERT_BORDER_WIDTH, 32 | borderStyle: 'solid', 33 | borderColor: 'transparent', // is it ok? 34 | }, 35 | mixinBorderRadius(constants, ALERT_BORDER_RADIUS), 36 | ), 37 | 38 | alertText: Object.assign({}, classes.text, { 39 | // pass 40 | }), 41 | 42 | alertLink: { 43 | fontWeight: ALERT_LINK_FONT_WEIGHT, 44 | }, 45 | 46 | // obsoleted 47 | // .alert-heading { 48 | // // Specified to prevent conflicts of changing $headings-color 49 | // color: inherit; 50 | // } 51 | 52 | alertDismissible: { 53 | paddingRight: CLOSE_FONT_SIZE + ALERT_PADDING_X * 2, 54 | }, 55 | 56 | alertDismissibleClose: { 57 | position: 'absolute', 58 | top: 0, 59 | right: 0, 60 | paddingVertical: ALERT_PADDING_Y, 61 | paddingHorizontal: ALERT_PADDING_X, 62 | // TODO: color: inherit; 63 | }, 64 | }; 65 | 66 | // alert%color / ex: alertPrimary 67 | Object.keys(THEME_COLORS).forEach((item) => { 68 | const classColor = item.charAt(0).toUpperCase() + item.slice(1).toLowerCase(); 69 | 70 | const background = colorLevel(constants, THEME_COLORS[item], ALERT_BG_LEVEL); // theme-color-level($color, $alert-bg-level) 71 | const border = colorLevel(constants, THEME_COLORS[item], ALERT_BORDER_LEVEL); // theme-color-level($color, $alert-border-level) 72 | const color = colorLevel(constants, THEME_COLORS[item], ALERT_COLOR_LEVEL); // theme-color-level($color, $alert-color-level) 73 | 74 | classes['alert' + classColor] = { 75 | borderColor: border, 76 | backgroundColor: background, // @include gradient-bg($background); 77 | } 78 | 79 | classes['alert' + classColor + 'Text'] = { 80 | color: color, 81 | } 82 | 83 | classes['alert' + classColor + 'Hr'] = { 84 | borderTopColor: Color(border).darken(0.05).rgb().string(), 85 | } 86 | 87 | classes['alert' + classColor + 'Link'] = { 88 | color: Color(color).darken(0.10).rgb().string(), 89 | } 90 | }); 91 | 92 | return _classes; 93 | }; 94 | -------------------------------------------------------------------------------- /src/utilities/borders.js: -------------------------------------------------------------------------------- 1 | export default function getClasses(constants, classes) { 2 | const { 3 | WHITE, 4 | THEME_COLORS, 5 | BORDER_COLOR, 6 | BORDER_WIDTH, 7 | BORDER_RADIUS, 8 | BORDER_RADIUS_SM, 9 | BORDER_RADIUS_LG, 10 | ROUNDED_PILL, 11 | } = constants; 12 | 13 | const _classes = { 14 | // experimental 15 | border: { 16 | borderColor: BORDER_COLOR, 17 | borderWidth: BORDER_WIDTH, 18 | borderStyle: 'solid', 19 | }, 20 | 21 | borderTop: { 22 | borderColor: BORDER_COLOR, 23 | borderTopWidth: BORDER_WIDTH, 24 | borderStyle: 'solid', 25 | }, 26 | 27 | borderRight: { 28 | borderColor: BORDER_COLOR, 29 | borderRightWidth: BORDER_WIDTH, 30 | borderStyle: 'solid', 31 | }, 32 | 33 | borderBottom: { 34 | borderColor: BORDER_COLOR, 35 | borderBottomWidth: BORDER_WIDTH, 36 | borderStyle: 'solid', 37 | }, 38 | 39 | borderLeft: { 40 | borderColor: BORDER_COLOR, 41 | borderLeftWidth: BORDER_WIDTH, 42 | borderStyle: 'solid', 43 | }, 44 | 45 | border0: {borderWidth: 0}, 46 | borderTop0: {borderTopWidth: 0}, 47 | borderRight0: {borderRightWidth: 0}, 48 | borderBottom0: {borderBottomWidth: 0}, 49 | borderLeft0: {borderLeftWidth: 0}, 50 | 51 | borderWhite: {borderColor: WHITE}, 52 | 53 | // DEPRECATED 54 | borderRounded: { 55 | borderRadius: BORDER_RADIUS, 56 | }, 57 | 58 | roundedSm: { 59 | borderRadius: BORDER_RADIUS_SM, 60 | }, 61 | 62 | rounded: { 63 | borderRadius: BORDER_RADIUS, 64 | }, 65 | 66 | roundedTop: { 67 | borderTopLeftRadius: BORDER_RADIUS, 68 | borderTopRightRadius: BORDER_RADIUS, 69 | }, 70 | 71 | roundedRight: { 72 | borderTopRightRadius: BORDER_RADIUS, 73 | borderBottomRightRadius: BORDER_RADIUS, 74 | }, 75 | 76 | roundedBottom: { 77 | borderBottomRightRadius: BORDER_RADIUS, 78 | borderBottomLeftRadius: BORDER_RADIUS, 79 | }, 80 | 81 | roundedLeft: { 82 | borderTopLeftRadius: BORDER_RADIUS, 83 | borderBottomLeftRadius: BORDER_RADIUS, 84 | }, 85 | 86 | roundedLg: { 87 | borderRadius: BORDER_RADIUS_LG, 88 | }, 89 | 90 | // workaround / number, not percentage, accepted only 91 | roundedCircle: widthHeight => widthHeight / 2, 92 | 93 | roundedPill: { 94 | borderRadius: ROUNDED_PILL, 95 | }, 96 | 97 | rounded0: { 98 | borderRadius: 0, 99 | }, 100 | }; 101 | 102 | // border%color / ex: borderPrimary 103 | Object.keys(THEME_COLORS).forEach((item) => { 104 | const classColor = item.charAt(0).toUpperCase() + item.slice(1).toLowerCase(); 105 | _classes['border' + classColor] = {borderColor: THEME_COLORS[item]}; 106 | }); 107 | 108 | return _classes; 109 | }; 110 | -------------------------------------------------------------------------------- /src/mixins/grid.js: -------------------------------------------------------------------------------- 1 | // import { mixinBorderRadius } from './border-radius'; 2 | 3 | export const mixinMakeContainer = ( 4 | constants, 5 | gutter = null, 6 | ) => (constants.ENABLE_GRID_CLASSES ? { 7 | width: '100%', // experimental 8 | flexDirection: 'column', 9 | paddingRight: (gutter || constants.GRID_GUTTER_WIDTH) / 2, 10 | paddingLeft: (gutter || constants.GRID_GUTTER_WIDTH) / 2, 11 | marginRight: 'auto', // experimental 12 | marginLeft: 'auto', // experimental 13 | } : {}); 14 | 15 | export const mixinMakeContainerMaxWidths = ( 16 | constants, 17 | screen = null, 18 | ) => (constants.ENABLE_GRID_CLASSES ? { 19 | maxWidth: constants.CONTAINER_MAX_WIDTHS[screen || constants.SCREEN], 20 | } : {}); 21 | 22 | export const mixinMakeRow = ( 23 | constants, 24 | gutter = null, 25 | ) => (constants.ENABLE_GRID_CLASSES ? { 26 | flexWrap: 'wrap', 27 | marginRight: -(gutter || constants.GRID_GUTTER_WIDTH) / 2, 28 | marginLeft: -(gutter || constants.GRID_GUTTER_WIDTH) / 2, 29 | } : {}); 30 | 31 | // RESERVED 32 | // export const mixinMakeColReady = ( 33 | // constants, 34 | // gutter = null, 35 | // ) => (constants.ENABLE_GRID_CLASSES ? { 36 | // paddingRight: (gutter || constants.GRID_GUTTER_WIDTH) / 2, 37 | // paddingLeft: (gutter || constants.GRID_GUTTER_WIDTH) / 2, 38 | // } : {}); 39 | 40 | // experimental 41 | export const mixinMakeCol = ( 42 | constants, 43 | gutter = null, 44 | ) => (constants.ENABLE_GRID_CLASSES ? { 45 | flexDirection: 'column', // critical / why? / TODO: document it 46 | // OBSOLETED / flexBasis: 0, // harmful 47 | // OBSOLETED / flexGrow: 1, // harmful 48 | paddingRight: (gutter || constants.GRID_GUTTER_WIDTH) / 2, 49 | paddingLeft: (gutter || constants.GRID_GUTTER_WIDTH) / 2, 50 | minWidth: 0, // excessive? 51 | maxWidth: '100%', // excessive? 52 | } : {}); 53 | 54 | // RESERVED 55 | // @mixin make-col($size, $columns: $grid-columns) { 56 | // flex: 0 0 percentage($size / $columns); 57 | // // Add a `max-width` to ensure content within each column does not blow out 58 | // // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari 59 | // // do not appear to require this. 60 | // max-width: percentage($size / $columns); 61 | // } 62 | 63 | // RESERVED 64 | // @mixin make-col-auto() { 65 | // flex: 0 0 auto; 66 | // width: auto; 67 | // max-width: 100%; // Reset earlier grid tiers 68 | // } 69 | 70 | // SKIPPED 71 | // @mixin make-col-offset($size, $columns: $grid-columns) { 72 | // $num: $size / $columns; 73 | // margin-left: if($num == 0, 0, percentage($num)); 74 | // } 75 | 76 | // SKIPPED 77 | // // Row columns 78 | // // 79 | // // Specify on a parent element(e.g., .row) to force immediate children into NN 80 | // // numberof columns. Supports wrapping to new lines, but does not do a Masonry 81 | // // style grid. 82 | // @mixin row-cols($count) { 83 | // & > * { 84 | // flex: 0 0 100% / $count; 85 | // max-width: 100% / $count; 86 | // } 87 | // } 88 | -------------------------------------------------------------------------------- /src/grid.js: -------------------------------------------------------------------------------- 1 | import { mixinMakeContainer, mixinMakeContainerMaxWidths } from './mixins/grid'; 2 | import { mixinMakeRow, mixinMakeCol } from './mixins/grid'; 3 | import { getScreens } from './mixins/helpers'; 4 | 5 | export default function getClasses(constants, classes) { 6 | const { 7 | ENABLE_GRID_CLASSES, 8 | GRID_BREAKPOINTS, 9 | GRID_COLUMNS, 10 | GRID_GUTTER_WIDTH, 11 | SCREENS_INFIXES, 12 | // OBSOLETED / SCREEN, 13 | } = constants; 14 | 15 | const _classes = ENABLE_GRID_CLASSES ? { 16 | 17 | // TODO: add later 18 | // container: Object.assign({}, 19 | // mixinMakeContainer(constants), 20 | // mixinMakeContainerMaxWidths(constants), 21 | // ), 22 | 23 | containerFluid: Object.assign({}, 24 | mixinMakeContainer(constants), 25 | ), 26 | 27 | noGutters: { 28 | marginRight: 0, 29 | marginLeft: 0, 30 | }, 31 | 32 | noGuttersCol: { 33 | paddingRight: 0, 34 | paddingLeft: 0, 35 | }, 36 | 37 | // experimental / in use 38 | Gutters: { 39 | marginRight: -GRID_GUTTER_WIDTH / 2, 40 | marginLeft: -GRID_GUTTER_WIDTH / 2, 41 | }, 42 | 43 | // experimental / in use 44 | GuttersCol: { 45 | paddingRight: GRID_GUTTER_WIDTH / 2, 46 | paddingLeft: GRID_GUTTER_WIDTH / 2, 47 | }, 48 | } : {}; 49 | 50 | // container%screen / ex: containerMd 51 | if (ENABLE_GRID_CLASSES) { 52 | SCREENS_INFIXES.forEach((item) => { 53 | _classes['container' + item] = Object.assign({}, 54 | mixinMakeContainer(constants), 55 | mixinMakeContainerMaxWidths(constants, item), 56 | ); 57 | }); 58 | } 59 | 60 | if (ENABLE_GRID_CLASSES) { 61 | const SCREENS_INFIXES_ALL = [''].concat(Object.keys(GRID_BREAKPOINTS)); 62 | const gridColumnsArray = Array.from(Array(GRID_COLUMNS).keys()); 63 | 64 | SCREENS_INFIXES_ALL.forEach((itemScreen) => { 65 | _classes['row' + itemScreen] = Object.assign(mixinMakeRow(constants), 66 | SCREENS_INFIXES.indexOf(itemScreen) > -1 ? { // dirty 67 | flexDirection: 'row', 68 | // more? 69 | } : {} 70 | ); 71 | }); 72 | 73 | SCREENS_INFIXES_ALL.forEach((itemScreen) => { 74 | GRID_COLUMNS && gridColumnsArray.forEach(item => { 75 | _classes['col' + itemScreen + (item || '')] = Object.assign(mixinMakeCol(constants), 76 | SCREENS_INFIXES.indexOf(itemScreen) > -1 ? { // dirty 77 | flex: item || 1, 78 | // more? 79 | } : {} 80 | ); 81 | }); 82 | 83 | // SKIPPED 84 | // _classes['col' + itemScreen + 'Auto'] = mixinMakeCol(constants); 85 | }); 86 | 87 | // Row columns 88 | // .row-cols-* 89 | // SKIPPED 90 | 91 | // Offsetting columns 92 | // .offset-*-* 93 | // SKIPPED 94 | 95 | // Reordering 96 | // .order-*-* 97 | // SKIPPED 98 | } 99 | 100 | return _classes; 101 | }; 102 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/utilities/text.js: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | import { colorLevel } from '../mixins/helpers'; 3 | import { selectorMediaUp } from '../mixins/selectors'; 4 | 5 | export default function getClasses(constants, classes) { 6 | const { 7 | WHITE, 8 | BLACK, 9 | BODY_COLOR, 10 | THEME_COLORS, 11 | SCREENS_INFIXES, 12 | TEXT_MUTED, 13 | FONT_FAMILY_BASE, 14 | FONT_FAMILY_BASE_LIGHT, // experimental 15 | FONT_FAMILY_BASE_BOLD, // experimental 16 | FONT_FAMILY_MONOSPACE, 17 | FONT_WEIGHT_NORMAL, 18 | FONT_WEIGHT_LIGHT, 19 | FONT_WEIGHT_BOLD, 20 | FONT_SIZE_BASE, 21 | FONT_SIZE_SM, 22 | FONT_SIZE_LG, 23 | } = constants; 24 | 25 | const _classes = { 26 | fontWeightLight: {fontWeight: FONT_WEIGHT_LIGHT, fontFamily: FONT_FAMILY_BASE_LIGHT}, 27 | fontWeightNormal: {fontWeight: FONT_WEIGHT_NORMAL}, 28 | fontWeightBold: {fontWeight: FONT_WEIGHT_BOLD, fontFamily: FONT_FAMILY_BASE_BOLD}, 29 | fontItalic: {fontStyle: 'italic'}, 30 | 31 | textBody: {color: BODY_COLOR}, 32 | textWhite: {color: WHITE}, 33 | textMuted: {color: TEXT_MUTED}, 34 | 35 | textBlack50: {color: Color(BLACK).fade(0.5).rgb().string()}, 36 | textWhite50: {color: Color(WHITE).fade(0.5).rgb().string()}, 37 | 38 | // textAuto: {textAlign: 'auto'}, 39 | // textLeft: {textAlign: 'left'}, 40 | // textRight: {textAlign: 'right'}, 41 | textCenter: {textAlign: 'center'}, 42 | textJustify: {textAlign: 'justify'}, 43 | 44 | // experimental 45 | textSmall: {fontSize: FONT_SIZE_SM}, 46 | textLarge: {fontSize: FONT_SIZE_LG}, 47 | textSpaced: {letterSpacing: FONT_SIZE_BASE * 0.075}, 48 | 49 | textMonospace: {fontFamily: FONT_FAMILY_MONOSPACE}, 50 | }; 51 | 52 | const ALIGN_TYPES = { 53 | 'Auto': 'auto', 54 | 'Left': 'left', 55 | 'Right': 'right', 56 | 'Center': 'center', 57 | 'Justify': 'justify', 58 | }; 59 | 60 | // text%value / ex: floatRight 61 | // text%screen%value, / ex: floatLgRight 62 | SCREENS_INFIXES.forEach((itemScreen) => { 63 | Object.keys(ALIGN_TYPES).forEach(item => { 64 | _classes['float' + itemScreen + item] = selectorMediaUp(itemScreen, SCREENS_INFIXES, { 65 | textAlign: ALIGN_TYPES[item], 66 | }); 67 | }); 68 | }); 69 | 70 | // text%color / ex: textPrimary 71 | Object.keys(THEME_COLORS).forEach((item) => { 72 | const classColor = item.charAt(0).toUpperCase() + item.slice(1).toLowerCase(); 73 | _classes['text' + classColor] = {color: THEME_COLORS[item]}; 74 | 75 | // custom 76 | _classes['text' + classColor + 'Light'] = {color: colorLevel(constants, THEME_COLORS[item], -9)}; 77 | _classes['text' + classColor + 'Dark'] = {color: colorLevel(constants, THEME_COLORS[item], 9)}; 78 | 79 | // custom / experimental 80 | _classes['text' + classColor + 'Lightest'] = {color: colorLevel(constants, THEME_COLORS[item], -11)}; 81 | _classes['text' + classColor + 'Darkest'] = {color: colorLevel(constants, THEME_COLORS[item], -11)}; 82 | }); 83 | // RESERVED / a#color%item: @include hover-focus { color: darken($color, 10%) !important; } // MIXIN_TEXT_EMPHASIS_VARIANT 84 | 85 | // @mixin text-emphasis-variant($parent, $color) { 86 | // #{$parent} { 87 | // color: $color !important; 88 | // } 89 | // a#{$parent} { 90 | // @include hover-focus { 91 | // color: darken($color, 10%) !important; 92 | // } 93 | // } 94 | // } 95 | 96 | return _classes; 97 | }; 98 | -------------------------------------------------------------------------------- /src/mixins/selectors.js: -------------------------------------------------------------------------------- 1 | // selectors accept raw loop param(s) or booleans 2 | // TODO: how about returning FALSE, instead of empty dict? 3 | 4 | export const selectorFirstChild = (indexOrBool, style = {}) => ( 5 | typeof indexOrBool === 'boolean' ? ( 6 | indexOrBool ? style : {} 7 | ) : ( 8 | indexOrBool == 0 ? style : {} 9 | ) 10 | ); 11 | 12 | export const selectorLastChild = (indexOrBool, lengthOrStyle = {}, style = {}) => ( 13 | arguments.length < 3 ? ( 14 | indexOrBool ? style : {} 15 | ) : ( 16 | typeof indexOrBool === 'boolean' ? ( 17 | indexOrBool ? style : {} 18 | ) : ( 19 | indexOrBool == lengthOrStyle - 1 ? style : {} 20 | ) 21 | ) 22 | ); 23 | 24 | export const selectorNotFirstChild = (indexOrBool, style = {}) => ( 25 | typeof indexOrBool === 'boolean' ? ( 26 | indexOrBool ? style : {} 27 | ) : ( 28 | indexOrBool > 0 ? style : {} 29 | ) 30 | ); 31 | 32 | export const selectorNotLastChild = (indexOrBool, lengthOrStyle, style = {}) => ( 33 | arguments.length < 3 ? ( 34 | indexOrBool ? style : {} 35 | ) : ( 36 | typeof indexOrBool === 'boolean' ? ( 37 | indexOrBool ? style : {} 38 | ) : ( 39 | indexOrBool < lengthOrStyle - 1 ? style : {} 40 | ) 41 | ) 42 | ); 43 | 44 | export const selectorPreviousChild = (indexOrBool, lengthOrStyle = {}, style = {}) => ( 45 | arguments.length < 3 ? ( 46 | indexOrBool ? lengthOrStyle : {} 47 | ) : ( 48 | typeof indexOrBool === 'boolean' ? ( 49 | indexOrBool ? style : {} 50 | ) : ( 51 | indexOrBool < lengthOrStyle - 1 ? style : {} 52 | ) 53 | ) 54 | ); 55 | 56 | export const selectorMediaUp = (screen, screens, style = {}) => ( 57 | !screen || screens.indexOf(screen) > -1 ? style : false 58 | ); 59 | 60 | export const selectorMediaDown = (screen, screens, style = {}) => ( 61 | !screen || screens.indexOf(screen) == -1 ? style : false 62 | ); 63 | 64 | // RESERVED 65 | // export const selectorCondition = (bool, styleTrue = {}, styleFalse = {}) => ( 66 | // bool ? styleTrue : styleFalse 67 | // ); 68 | 69 | // OBSOLETED 70 | // bool makes no sense here, in fact 71 | // export const selectorNthChild = (index, condition, style = {}) => ( 72 | // condition(index) ? style : {} 73 | // ); 74 | 75 | // experimental 76 | export function getSelectors(constants, classes) { 77 | 78 | const { 79 | SCREENS, 80 | GRID_BREAKPOINTS, 81 | ORIENTATION_PORTRAIT, 82 | ORIENTATION_LANDSCAPE, 83 | } = constants; 84 | 85 | const _selectors = { 86 | // pass 87 | }; 88 | 89 | // selectorMediaUp%screen, / ex: selectorMediaUpMd 90 | // selectorMediaDown%screen, / ex: selectorMediaDownMd 91 | const SCREENS_INFIXES_ALL = Object.keys(GRID_BREAKPOINTS); 92 | 93 | SCREENS_INFIXES_ALL.forEach((item) => { 94 | _selectors['selectorMediaUp' + item] = ( 95 | SCREENS.indexOf(item) > -1 ? style => style : style => {} 96 | ); 97 | }); 98 | 99 | SCREENS_INFIXES_ALL.forEach((item) => { 100 | _selectors['selectorMediaDown' + item] = ( 101 | SCREENS.indexOf(item) == - 1 || SCREENS.indexOf(item) == SCREENS.length - 1 ? style => style : style => {} 102 | ); 103 | }); 104 | 105 | _selectors['selectorMediaPortrait'] = ORIENTATION_PORTRAIT ? style => style : style => {}; 106 | _selectors['selectorMediaLandscape'] = ORIENTATION_LANDSCAPE ? style => style : style => {}; 107 | 108 | return { 109 | selectorFirstChild, 110 | selectorLastChild, 111 | selectorNotFirstChild, 112 | selectorPreviousChild, 113 | ..._selectors 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/modal.js: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | import { mixinBorderRadius, mixinBorderTopRadius } from './mixins/border-radius'; 4 | import { mixinBoxShadow } from './mixins/box-shadow'; 5 | 6 | export default function getClasses(constants, classes) { 7 | const { 8 | MODAL_INNER_PADDING, 9 | MODAL_BACKDROP_BG, 10 | MODAL_BACKDROP_OPACITY, 11 | MODAL_DIALOG_MARGIN, 12 | MODAL_TITLE_LINE_HEIGHT, 13 | MODAL_CONTENT_BG, 14 | MODAL_CONTENT_BORDER_WIDTH, 15 | MODAL_CONTENT_BORDER_COLOR, 16 | MODAL_CONTENT_BORDER_RADIUS, 17 | MODAL_CONTENT_SHADOW_COLOR, 18 | MODAL_CONTENT_SHADOW_OFFSET, 19 | MODAL_CONTENT_SHADOW_OPACITY, 20 | MODAL_CONTENT_SHADOW_RADIUS, 21 | MODAL_HEADER_BORDER_COLOR, 22 | MODAL_FOOTER_BORDER_COLOR, 23 | MODAL_HEADER_BORDER_WIDTH, 24 | MODAL_FOOTER_BORDER_WIDTH, 25 | MODAL_HEADER_PADDING_X, 26 | MODAL_HEADER_PADDING_Y, 27 | } = constants; 28 | 29 | const _classes = { 30 | modal: { 31 | backgroundColor: Color(MODAL_BACKDROP_BG).fade(MODAL_BACKDROP_OPACITY).rgb().string(), 32 | flex: 1, // experimental 33 | }, 34 | 35 | modalDialog: { 36 | margin: MODAL_DIALOG_MARGIN, 37 | marginTop: MODAL_DIALOG_MARGIN * 3, // custom 38 | flex: 1, // experimental 39 | }, 40 | 41 | modalContent: Object.assign({ 42 | backgroundColor: MODAL_CONTENT_BG, 43 | borderWidth: MODAL_CONTENT_BORDER_WIDTH, 44 | borderColor: MODAL_CONTENT_BORDER_COLOR, 45 | flex: 1, // experimental 46 | }, 47 | mixinBorderRadius(constants, MODAL_CONTENT_BORDER_RADIUS), 48 | mixinBoxShadow(constants, MODAL_CONTENT_SHADOW_COLOR, MODAL_CONTENT_SHADOW_OFFSET, MODAL_CONTENT_SHADOW_OPACITY, MODAL_CONTENT_SHADOW_RADIUS), 49 | ), 50 | 51 | modalHeader: Object.assign({ 52 | // display: flex, 53 | alignItems: 'flex-start', // so the close btn always stays on the upper right corner 54 | justifyContent: 'space-between', // Put modal header elements (title and dismiss) on opposite ends 55 | paddingVertical: MODAL_HEADER_PADDING_Y, 56 | paddingHorizontal: MODAL_HEADER_PADDING_X, 57 | borderBottomWidth: MODAL_HEADER_BORDER_WIDTH, 58 | borderBottomColor: MODAL_HEADER_BORDER_COLOR, 59 | borderStyle: 'solid', 60 | }, 61 | mixinBorderTopRadius(constants, MODAL_CONTENT_BORDER_RADIUS), 62 | ), 63 | 64 | // experimental 65 | modalHeaderClose: { 66 | position: 'absolute', 67 | right: MODAL_HEADER_PADDING_X, 68 | top: MODAL_HEADER_PADDING_Y, 69 | zIndex: 1, 70 | // RESERVED / padding: $modal-header-padding; 71 | // RESERVED / margin: (-$modal-header-padding) (-$modal-header-padding) (-$modal-header-padding) auto; 72 | }, 73 | 74 | modalTitle: { 75 | marginBottom: 0, 76 | lineHeight: MODAL_TITLE_LINE_HEIGHT, 77 | }, 78 | 79 | modalBody: { 80 | padding: MODAL_INNER_PADDING, 81 | }, 82 | 83 | modalFooter: { 84 | // display: flex, 85 | alignItems: 'center', 86 | justifyContent: 'flex-end', // Right align buttons with flex property because text-align doesn't work on flex items 87 | padding: MODAL_INNER_PADDING, 88 | borderTopWidth: MODAL_FOOTER_BORDER_WIDTH, 89 | borderTopColor: MODAL_FOOTER_BORDER_COLOR, 90 | borderStyle: 'solid', 91 | 92 | // Easily place margin between footer elements 93 | // > :not(:first-child) { margin-left: .25rem; } 94 | // > :not(:last-child) { margin-right: .25rem; } 95 | }, 96 | }; 97 | 98 | return _classes; 99 | }; 100 | -------------------------------------------------------------------------------- /src/type.js: -------------------------------------------------------------------------------- 1 | import { mixinListUnstyled } from './mixins/lists'; 2 | 3 | export default function getClasses(constants, classes) { 4 | const { 5 | SPACER, 6 | H1_FONT_SIZE, 7 | H2_FONT_SIZE, 8 | H3_FONT_SIZE, 9 | H4_FONT_SIZE, 10 | H5_FONT_SIZE, 11 | H6_FONT_SIZE, 12 | 13 | HEADINGS_MARGIN_BOTTOM, 14 | HEADINGS_FONT_FAMILY, 15 | HEADINGS_FONT_WEIGHT, 16 | HEADINGS_COLOR, 17 | 18 | HR_MARGIN_Y, 19 | HR_BORDER_WIDTH, 20 | HR_BORDER_COLOR, 21 | MARK_PADDING, MARK_BG, 22 | BLOCKQUOTE_FONT_SIZE, 23 | 24 | LEAD_FONT_SIZE, 25 | LEAD_FONT_WEIGHT, 26 | } = constants; 27 | 28 | const _classes = { 29 | 30 | hr: { 31 | marginTop: HR_MARGIN_Y, 32 | marginBottom: HR_MARGIN_Y, 33 | borderWidth: 0, 34 | borderTopWidth: HR_BORDER_WIDTH, 35 | borderTopColor: HR_BORDER_COLOR, 36 | borderStyle: 'solid', 37 | }, 38 | 39 | // TODO: Could be a selector or not to be at all 40 | // small, 41 | // .small { 42 | // @include font-size($small-font-size); 43 | // font-weight: $font-weight-normal; 44 | // } 45 | 46 | mark: { 47 | padding: MARK_PADDING, 48 | backgroundColor: MARK_BG, 49 | }, 50 | 51 | listUnstyled: Object.assign({}, 52 | mixinListUnstyled, 53 | ), 54 | 55 | // Inline turns list items into inline-block 56 | // .list-inline { 57 | // @include list-unstyled(); 58 | // } 59 | // .list-inline-item { 60 | // display: inline-block; 61 | 62 | // &:not(:last-child) { 63 | // margin-right: $list-inline-padding; 64 | // } 65 | // } 66 | 67 | initialism: { 68 | // reserved / @include font-size(90%), 69 | textTransform: 'uppercase', 70 | }, 71 | 72 | blockquote: { 73 | marginBottom: SPACER, 74 | fontSize: BLOCKQUOTE_FONT_SIZE, 75 | }, 76 | 77 | // blockquoteFooter { 78 | // display: block; 79 | // @include font-size($blockquote-small-font-size); 80 | // color: $blockquote-small-color; 81 | 82 | // &::before { 83 | // content: "\2014\00A0"; // em dash, nbsp 84 | // } 85 | // }, 86 | 87 | lead: { 88 | fontSize: LEAD_FONT_SIZE, 89 | fontWeight: LEAD_FONT_WEIGHT, 90 | }, 91 | }; 92 | 93 | // h1, h2, h3, ... 94 | const HEADINGS_SIZES = [H1_FONT_SIZE, H2_FONT_SIZE, H3_FONT_SIZE, H4_FONT_SIZE, H5_FONT_SIZE, H6_FONT_SIZE]; 95 | ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].forEach((item, index) => { 96 | classes[item] = { 97 | fontSize: HEADINGS_SIZES[index], 98 | marginBottom: HEADINGS_MARGIN_BOTTOM, 99 | fontFamily: HEADINGS_FONT_FAMILY, 100 | fontWeight: HEADINGS_FONT_WEIGHT, 101 | color: HEADINGS_COLOR, 102 | lineHeight: HEADINGS_SIZES[index] * 1.2, // temporal 103 | // RESERVED / lineHeight: HEADINGS_SIZES[index] / FONT_SIZE_BASE * LINE_HEIGHT_BASE, 104 | }; 105 | }); 106 | 107 | // // Type display classes 108 | // .display-1 { 109 | // font-size: $display1-size; 110 | // font-weight: $display1-weight; 111 | // line-height: $display-line-height; 112 | // } 113 | // .display-2 { 114 | // font-size: $display2-size; 115 | // font-weight: $display2-weight; 116 | // line-height: $display-line-height; 117 | // } 118 | // .display-3 { 119 | // font-size: $display3-size; 120 | // font-weight: $display3-weight; 121 | // line-height: $display-line-height; 122 | // } 123 | // .display-4 { 124 | // font-size: $display4-size; 125 | // font-weight: $display4-weight; 126 | // line-height: $display-line-height; 127 | // } 128 | 129 | return _classes; 130 | }; 131 | -------------------------------------------------------------------------------- /src/pagination.js: -------------------------------------------------------------------------------- 1 | import { mixinPaginationSizePageLink } from './mixins/pagination'; 2 | import { mixinBorderRadius, mixinBorderLeftRadius, mixinBorderRightRadius } from './mixins/border-radius'; 3 | import { selectorFirstChild, selectorLastChild, selectorCondition } from './mixins/selectors'; 4 | 5 | export default function getClasses(constants, classes) { 6 | const { 7 | FONT_SIZE_SM, 8 | FONT_SIZE_LG, 9 | LINE_HEIGHT_SM, 10 | LINE_HEIGHT_LG, 11 | BORDER_RADIUS_SM, 12 | BORDER_RADIUS_LG, 13 | PAGINATION_PADDING_Y, PAGINATION_PADDING_X, 14 | PAGINATION_PADDING_Y_SM, PAGINATION_PADDING_X_SM, 15 | PAGINATION_PADDING_Y_LG, PAGINATION_PADDING_X_LG, 16 | PAGINATION_LINE_HEIGHT, 17 | PAGINATION_COLOR, PAGINATION_BG, 18 | PAGINATION_BORDER_WIDTH, PAGINATION_BORDER_COLOR, 19 | PAGINATION_FOCUS_BOX_SHADOW, PAGINATION_FOCUS_OUTLINE, 20 | PAGINATION_ACTIVE_COLOR, 21 | PAGINATION_ACTIVE_BG, 22 | PAGINATION_ACTIVE_BORDER_COLOR, 23 | PAGINATION_DISABLED_COLOR, 24 | PAGINATION_DISABLED_BG, 25 | PAGINATION_DISABLED_BORDER_COLOR, 26 | } = constants; 27 | 28 | const _classes = { 29 | pagination: Object.assign({ 30 | flexDirection: 'row', // TODO: to constant? 31 | }, 32 | mixinBorderRadius(constants), 33 | // SKIPPED / @include list-unstyled(); 34 | ), 35 | 36 | pageLink: { 37 | position: 'relative', 38 | paddingVertical: PAGINATION_PADDING_Y, 39 | paddingHorizontal: PAGINATION_PADDING_X, 40 | marginLeft: -PAGINATION_BORDER_WIDTH, 41 | lineHeight: PAGINATION_LINE_HEIGHT, 42 | color: PAGINATION_COLOR, 43 | textDecorationLine: 'none', // RFP: any ideas? / if($link-decoration == none, null, none), 44 | backgroundColor: PAGINATION_BG, 45 | borderWidth: PAGINATION_BORDER_WIDTH, 46 | borderColor: PAGINATION_BORDER_COLOR, 47 | borderStyle: 'solid', 48 | 49 | // SKIPPED 50 | // &:hover { 51 | // z-index: 2; 52 | // color: $pagination-hover-color; 53 | // text-decoration: none; 54 | // background-color: $pagination-hover-bg; 55 | // border-color: $pagination-hover-border-color; 56 | // } 57 | 58 | // RESERVED 59 | // &:focus { 60 | // z-index: 3; 61 | // outline: $pagination-focus-outline; 62 | // box-shadow: $pagination-focus-box-shadow; 63 | // } 64 | }, 65 | 66 | pageItemFirstChildPageLink: nOrBool => selectorFirstChild(nOrBool, Object.assign({ 67 | marginLeft: 0, 68 | }, 69 | mixinBorderLeftRadius(constants, BORDER_RADIUS), 70 | )), 71 | 72 | pageItemLastChildPageLink: (nOrBool, lengthOrNone) => selectorLastChild(nOrBool, lengthOrNone, Object.assign({}, 73 | mixinBorderRightRadius(constants, BORDER_RADIUS), 74 | )), 75 | 76 | pageItemActivePageLink: e => selectorCondition(e, { 77 | zIndex: 3, 78 | color: PAGINATION_ACTIVE_COLOR, 79 | backgroundColor: PAGINATION_ACTIVE_BG, 80 | borderColor: PAGINATION_ACTIVE_BORDER_COLOR, 81 | }), 82 | 83 | pageItemDisabledPageLink: e => selectorCondition(e, { 84 | color: PAGINATION_DISABLED_COLOR, 85 | backgroundColor: PAGINATION_DISABLED_BG, 86 | borderColor: PAGINATION_DISABLED_BORDER_COLOR, 87 | }), 88 | 89 | // // 90 | // // Sizing 91 | // // 92 | 93 | paginationLg: { 94 | // see paginationLgPageLink, etc 95 | }, 96 | 97 | paginationLgPageLink: mixinPaginationSizePageLink(PAGINATION_PADDING_Y_LG, PAGINATION_PADDING_X_LG, FONT_SIZE_LG, LINE_HEIGHT_LG), 98 | paginationLgPageItemFirstChildPageLink: nOrBool => selectorFirstChild(nOrBool, Object.assign({}, 99 | mixinBorderLeftRadius(constants, BORDER_RADIUS_LG), 100 | )), 101 | paginationLgPageItemLastChildPageLink: (nOrBool, lengthOrNone) => selectorLastChild(nOrBool, lengthOrNone, Object.assign({}, 102 | mixinBorderRightRadius(constants, BORDER_RADIUS_LG), 103 | )), 104 | 105 | paginationSm: { 106 | // see paginationSmPageLink, etc 107 | }, 108 | 109 | paginationSmPageLink: mixinPaginationSizePageLink(PAGINATION_PADDING_Y_SM, PAGINATION_PADDING_X_SM, FONT_SIZE_SM, LINE_HEIGHT_SM), 110 | paginationSmPageItemFirstChildPageLink: nOrBool => selectorFirstChild(nOrBool, Object.assign({}, 111 | mixinBorderLeftRadius(constants, BORDER_RADIUS_SM), 112 | )), 113 | paginationSmPageItemLastChildPageLink: (nOrBool, lengthOrNone) => selectorLastChild(nOrBool, lengthOrNone, Object.assign({}, 114 | mixinBorderRightRadius(constants, BORDER_RADIUS_SM), 115 | )), 116 | }; 117 | 118 | return _classes; 119 | }; 120 | -------------------------------------------------------------------------------- /src/button-group.js: -------------------------------------------------------------------------------- 1 | import { mixinBorderLeftRadius, mixinBorderRightRadius } from './mixins/border-radius'; 2 | import { mixinBorderTopRadius, mixinBorderBottomRadius } from './mixins/border-radius'; 3 | import { selectorNotFirstChild, selectorNotLastChild } from './mixins/selectors'; 4 | 5 | export default function getClasses(constants, classes) { 6 | const { 7 | BTN_BORDER_WIDTH, 8 | } = constants; 9 | 10 | const _classes = { 11 | 12 | btnGroup: { 13 | position: 'relative', 14 | // ignored / display: inline-flex, 15 | // ignored /vertical-align: middle; // match .btn alignment given font-size hack above 16 | 17 | // > .btn { 18 | // position: relative; 19 | // flex: 1 1 auto; 20 | 21 | // // Bring the hover, focused, and "active" buttons to the front to overlay 22 | // // the borders properly 23 | // @include hover() { 24 | // z-index: 1; 25 | // } 26 | // &:focus, 27 | // &:active, 28 | // &.active { 29 | // z-index: 1; 30 | // } 31 | // } 32 | }, 33 | 34 | btnGroupVertical: { 35 | position: 'relative', 36 | // ignored / display: inline-flex; 37 | // ignored / vertical-align: middle; // match .btn alignment given font-size hack above 38 | 39 | flexDirection: 'column', 40 | // ignored / alignItems: 'flex-start', 41 | // ignored / justifyContent: 'center', 42 | 43 | // > .btn, 44 | // > .btn-group { 45 | // width: 100%; 46 | // } 47 | 48 | // > .btn { 49 | // position: relative; 50 | // flex: 1 1 auto; 51 | 52 | // @include hover() { 53 | // z-index: 1; 54 | // } 55 | // &:focus, 56 | // &:active, 57 | // &.active { 58 | // z-index: 1; 59 | // } 60 | // } 61 | }, 62 | 63 | btnToolbar: { 64 | display: 'flex', 65 | flexWrap: 'wrap', 66 | justifyContent: 'flex-start', 67 | }, 68 | 69 | // .btn-toolbar { 70 | // .input-group { 71 | // width: auto; 72 | // } 73 | // } 74 | 75 | btnGroupBtn: (n, length) => Object.assign({}, 76 | selectorNotFirstChild(n, length, { 77 | marginLeft: -BTN_BORDER_WIDTH, 78 | }), 79 | selectorNotFirstChild(n, mixinBorderLeftRadius(constants, 0)), 80 | selectorNotLastChild(n, length, mixinBorderRightRadius(constants, 0)), 81 | ), 82 | 83 | // ignored / .btn-group-sm > .btn { @extend .btn-sm; } 84 | // ignored / .btn-group-lg > .btn { @extend .btn-lg; } 85 | 86 | // .dropdown-toggle-split { 87 | // padding-right: $btn-padding-x * .75; 88 | // padding-left: $btn-padding-x * .75; 89 | 90 | // &::after, 91 | // .dropup &::after, 92 | // .dropright &::after { 93 | // margin-left: 0; 94 | // } 95 | 96 | // .dropleft &::before { 97 | // margin-right: 0; 98 | // } 99 | // } 100 | 101 | // .btn-sm + .dropdown-toggle-split { 102 | // padding-right: $btn-padding-x-sm * .75; 103 | // padding-left: $btn-padding-x-sm * .75; 104 | // } 105 | 106 | // .btn-lg + .dropdown-toggle-split { 107 | // padding-right: $btn-padding-x-lg * .75; 108 | // padding-left: $btn-padding-x-lg * .75; 109 | // } 110 | 111 | // // The clickable button for toggling the menu 112 | // // Set the same inset shadow as the :active state 113 | // .btn-group.show .dropdown-toggle { 114 | // @include box-shadow($btn-active-box-shadow); 115 | 116 | // // Show no shadow for `.btn-link` since it has no other button styles. 117 | // &.btn-link { 118 | // @include box-shadow(none); 119 | // } 120 | // } 121 | 122 | btnGroupVerticalBtn: (n, length) => Object.assign({}, 123 | selectorNotFirstChild(n, length, { 124 | marginTop: -BTN_BORDER_WIDTH, 125 | }), 126 | selectorNotFirstChild(n, mixinBorderTopRadius(constants, 0)), 127 | selectorNotLastChild(n, length, mixinBorderBottomRadius(constants, 0)), 128 | ), 129 | 130 | // // Checkbox and radio options 131 | // // 132 | // // In order to support the browser's form validation feedback, powered by the 133 | // // `required` attribute, we have to "hide" the inputs via `clip`. We cannot use 134 | // // `display: none;` or `visibility: hidden;` as that also hides the popover. 135 | // // Simply visually hiding the inputs via `opacity` would leave them clickable in 136 | // // certain cases which is prevented by using `clip` and `pointer-events`. 137 | // // This way, we ensure a DOM element is visible to position the popover from. 138 | // // 139 | // // See https://github.com/twbs/bootstrap/pull/12794 and 140 | // // https://github.com/twbs/bootstrap/pull/14559 for more information. 141 | 142 | // .btn-group-toggle { 143 | // > .btn, 144 | // > .btn-group > .btn { 145 | // margin-bottom: 0; // Override default `