├── .gitignore ├── .storybook ├── addons.js ├── config.js └── webpack.config.js ├── README.md ├── components ├── button │ ├── button.js │ ├── index.js │ └── mixin.js ├── style │ └── mixins │ │ ├── color.js │ │ ├── font.js │ │ └── size.js └── theme │ ├── color │ ├── bezierEasing.js │ ├── colorPalete.js │ ├── colors.js │ └── tinyColor.js │ └── createTheme.js ├── package.json ├── stories └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | yarn-error.log 3 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | function loadStories() { 4 | require('../stories'); 5 | } 6 | 7 | configure(loadStories, module); 8 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (baseConfig) => { 2 | baseConfig.devtool = 'sourcemap'; 3 | 4 | return baseConfig; 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # styled-antd 2 | 3 | Proof of concept using styled-components with antd. 4 | 5 | ```bash 6 | $ npm install 7 | $ npm run storybook 8 | ``` 9 | 10 | -------------------------------------------------------------------------------- /components/button/button.js: -------------------------------------------------------------------------------- 1 | import styled, { css, keyframes } from 'styled-components'; 2 | import { Button } from 'antd'; 3 | import createTheme from '../theme/createTheme'; 4 | import mixin from './mixin'; 5 | 6 | const prefix = 'ant-btn'; 7 | 8 | const buttonEffect = keyframes` 9 | to { 10 | opacity: 0; 11 | top: -6px; 12 | left: -6px; 13 | bottom: -6px; 14 | right: -6px; 15 | border-width: 6px; 16 | } 17 | `; 18 | 19 | export default styled(Button)` 20 | ${(props) => { 21 | const theme = createTheme(props.theme); 22 | const mixins = mixin(theme); 23 | return css` 24 | ${mixins.btn()} 25 | ${mixins.btnDefault()} 26 | 27 | > i, 28 | > span { 29 | pointer-events: none; 30 | } 31 | 32 | &.${prefix}-primary { 33 | ${mixins.btnPrimary} 34 | 35 | .${prefix}-group &:not(:first-child):not(:last-child) { 36 | border-right-color: ${theme.btnGroupBorder}; 37 | border-left-color: ${theme.btnGroupBorder}; 38 | 39 | &:disabled { 40 | border-color: ${theme.btnDefaultBorder}; 41 | } 42 | } 43 | 44 | .${prefix}-group &:first-child { 45 | &:not(:last-child) { 46 | border-right-color: ${theme.btnGroupBorder}; 47 | &[disabled] { 48 | border-right-color: ${theme.btnDefaultBorder}; 49 | } 50 | } 51 | } 52 | 53 | .${prefix}-group &:last-child:not(:first-child), 54 | .${prefix}-group & + & { 55 | border-left-color: ${theme.btnGroupBorder}; 56 | &[disabled] { 57 | border-left-color: ${theme.btnDefaultBorder}; 58 | } 59 | } 60 | } 61 | 62 | &.${prefix}-ghost { 63 | ${mixins.btnGhost()} 64 | } 65 | 66 | &.${prefix}-dashed { 67 | ${mixins.btnDashed()} 68 | } 69 | 70 | &.${prefix}-danger { 71 | ${mixins.btnDanger()} 72 | } 73 | 74 | ${prefix}-circle, 75 | ${prefix}-circle-outline { 76 | ${mixins.btnCircle(prefix)} 77 | } 78 | 79 | &:before { 80 | position: absolute; 81 | top: -1px; 82 | left: -1px; 83 | bottom: -1px; 84 | right: -1px; 85 | background: #fff; 86 | opacity: 0.35; 87 | content: ''; 88 | border-radius: inherit; 89 | z-index: 1; 90 | transition: opacity .2s; 91 | pointer-events: none; 92 | display: none; 93 | } 94 | 95 | .${theme.iconfontCssPrefix} { 96 | transition: margin-left .3s ${theme.easeInOut}; 97 | } 98 | 99 | &.${prefix}-loading:before { 100 | display: block; 101 | } 102 | 103 | &.${prefix}-loading:not(.${prefix}-circle):not(.${prefix}-circle-outline) { 104 | padding-left: 29px; 105 | pointer-events: none; 106 | position: relative; 107 | .${theme.iconfontCssPrefix} { 108 | margin-left: -14px; 109 | } 110 | } 111 | 112 | .${prefix}-sm.${prefix}-loading:not(.${prefix}-circle):not(.${prefix}-circle-outline) { 113 | padding-left: 24px; 114 | .${theme.iconfontCssPrefix} { 115 | margin-left: -17px; 116 | } 117 | } 118 | 119 | .${prefix}-group { 120 | ${mixins.btnGroup(prefix)} 121 | } 122 | 123 | &:not(.${prefix}-circle):not(.${prefix}-circle-outline).${prefix}-icon-only { 124 | padding-left: 8px; 125 | padding-right: 8px; 126 | } 127 | 128 | // http://stackoverflow.com/a/21281554/3040605 129 | &:focus > span, 130 | &:active > span { 131 | position: relative; 132 | } 133 | 134 | > .${theme.iconfontCssPrefix} + span, 135 | > span + .${theme.iconfontCssPrefix} { 136 | margin-left: 0.5em; 137 | } 138 | 139 | &.${prefix}-clicked:after { 140 | content: ''; 141 | position: absolute; 142 | top: -1px; 143 | left: -1px; 144 | bottom: -1px; 145 | right: -1px; 146 | border-radius: inherit; 147 | border: 0 solid ${theme.primaryColor}; 148 | opacity: 0.4; 149 | animation: ${buttonEffect} .4s; 150 | display: block; 151 | } 152 | 153 | &.${prefix}-danger.${prefix}-clicked:after { 154 | border-color: ${theme.btnDangerColor}; 155 | } 156 | 157 | &.${prefix}-background-ghost { 158 | background: transparent !important; 159 | border-color: #fff; 160 | color: #fff; 161 | } 162 | 163 | &.${prefix}-background-ghost.${prefix}-primary { 164 | ${mixins.buttonVariantGhost(theme.primaryColor)} 165 | } 166 | 167 | &.${prefix}-background-ghost.${prefix}-danger { 168 | ${mixins.buttonVariantGhost(theme.btnDangerColor)} 169 | } 170 | `; 171 | }} 172 | ` 173 | -------------------------------------------------------------------------------- /components/button/index.js: -------------------------------------------------------------------------------- 1 | import Button from './button'; 2 | 3 | export default Button; 4 | -------------------------------------------------------------------------------- /components/button/mixin.js: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components'; 2 | import colorPalette from '../theme/color/colorPalete'; 3 | import { square } from '../style/mixins/size'; 4 | import { add } from '../style/mixins/font'; 5 | 6 | export default function mixin(theme) { 7 | const mixins = { 8 | // mixins for button 9 | // ------------------------ 10 | buttonSize(height, padding, fontSize, borderRadius) { 11 | return css` 12 | padding: ${padding}; 13 | font-size: ${fontSize}; 14 | border-radius: ${borderRadius}; 15 | height: ${height}; 16 | `; 17 | }, 18 | 19 | buttonDisabled() { 20 | return css` 21 | &.disabled, 22 | &[disabled] { 23 | &, 24 | &:hover, 25 | &:focus, 26 | &:active, 27 | &.active { 28 | ${mixins.buttonColor(theme.btnDisableColor, theme.btnDisableBg, theme.btnDisableBorder)} 29 | } 30 | } 31 | `; 32 | }, 33 | 34 | buttonVariantPrimary(color, background) { 35 | return css` 36 | ${mixins.buttonColor(color, background, background)}; 37 | 38 | &:hover, 39 | &:focus { 40 | ${mixins.buttonColor(color, colorPalette(background, 5), colorPalette(background, 5))}; 41 | } 42 | 43 | &:active, 44 | &.active { 45 | ${mixins.buttonColor(color, colorPalette(background, 7), colorPalette(background, 7))}; 46 | } 47 | 48 | ${mixins.buttonDisabled()}; 49 | `; 50 | }, 51 | 52 | buttonVariantOther(color, background, border) { 53 | return css` 54 | ${mixins.buttonColor(color, background, border)} 55 | 56 | &:hover, 57 | &:focus { 58 | ${mixins.buttonColor(theme.primaryColor, background, theme.primaryColor)} 59 | } 60 | 61 | &:active, 62 | &.active { 63 | ${mixins.buttonColor(theme.primary7, background, theme.primary7)} 64 | } 65 | 66 | ${mixins.buttonDisabled()} 67 | `; 68 | }, 69 | 70 | buttonVariantDanger(color, background, border) { 71 | return css` 72 | ${mixins.buttonColor(color, background, border)} 73 | 74 | &:hover, 75 | &:focus { 76 | ${mixins.buttonColor(theme.btnPrimaryColor, color, color)} 77 | } 78 | 79 | &:active, 80 | &.active { 81 | ${mixins.buttonColor(theme.btnPrimaryColor, colorPalette(color, 7), colorPalette(color, 7))} 82 | } 83 | 84 | ${mixins.buttonDisabled()} 85 | `; 86 | }, 87 | 88 | buttonVariantGhost(color) { 89 | return css` 90 | ${mixins.buttonColor(color, 'transparent', color)} 91 | 92 | &:hover, 93 | &:focus { 94 | ${mixins.buttonColor(colorPalette(color, 5), 'transparent', colorPalette(color, 5))} 95 | } 96 | 97 | &:active, 98 | &.active { 99 | ${mixins.buttonColor(colorPalette(color, 7), 'transparent', colorPalette(color, 7))} 100 | } 101 | 102 | ${mixins.buttonDisabled()} 103 | `; 104 | }, 105 | 106 | buttonColor(color, background, border) { 107 | return css` 108 | color: ${color}; 109 | background-color: ${background}; 110 | border-color: ${border}; 111 | // a inside Button which only work in Chrome 112 | // http://stackoverflow.com/a/17253457 113 | > a:only-child { 114 | color: currentColor; 115 | &:after { 116 | content: ''; 117 | position: absolute; 118 | top: 0; 119 | left: 0; 120 | bottom: 0; 121 | right: 0; 122 | background: transparent; 123 | } 124 | } 125 | `; 126 | }, 127 | 128 | buttonGroupBase(btnClassName) { 129 | return css` 130 | position: relative; 131 | display: inline-block; 132 | > .${btnClassName} { 133 | position: relative; 134 | z-index: 1; 135 | 136 | &:hover, 137 | &:focus, 138 | &:active, 139 | &.active { 140 | z-index: 2; 141 | } 142 | 143 | &:disabled { 144 | z-index: 0; 145 | } 146 | } 147 | 148 | // size 149 | &-lg > .${btnClassName} { 150 | ${mixins.buttonSize(theme.btnHeightLg, theme.btnPaddingLg, theme.btnFontSizeLg, theme.btnBorderRadiusBase)}; 151 | } 152 | 153 | &-sm > .${btnClassName} { 154 | ${mixins.buttonSize(theme.btnHeightSm, theme.btnPaddingSm, theme.fontSizeBase, theme.btnBorderRadiusSm)}; 155 | > .${theme.iconfontCssPrefix} { 156 | font-size: ${theme.fontSizeBase}; 157 | } 158 | } 159 | `; 160 | }, 161 | 162 | // Base styles of buttons 163 | // -------------------------------------------------- 164 | btn() { 165 | return css` 166 | display: inline-block; 167 | margin-bottom: 0; 168 | font-weight: ${theme.btnFontWeight}; 169 | text-align: center; 170 | touch-action: manipulation; 171 | cursor: pointer; 172 | background-image: none; 173 | border: ${theme.borderWidthBase} ${theme.borderStyleBase} transparent; 174 | white-space: nowrap; 175 | line-height: 1.15; 176 | 177 | ${mixins.buttonSize(theme.btnHeightBase, theme.btnPaddingBase, theme.fontSizeBase, theme.btnBorderRadiusBase)} 178 | 179 | user-select: none; 180 | transition: all .3s ${theme.easeInOut}; 181 | position: relative; 182 | 183 | > .${theme.iconfontCssPrefix} { 184 | line-height: 1; 185 | } 186 | 187 | &, 188 | &:active, 189 | &:focus { 190 | outline: 0; 191 | } 192 | 193 | &:not([disabled]):hover { 194 | text-decoration: none; 195 | } 196 | 197 | &:not([disabled]):active { 198 | outline: 0; 199 | transition: none; 200 | } 201 | 202 | &.disabled, 203 | &[disabled] { 204 | cursor: not-allowed; 205 | > * { 206 | pointer-events: none; 207 | } 208 | } 209 | 210 | &-lg { 211 | ${mixins.buttonSize(theme.btnHeightLg, theme.btnPaddingLg, theme.btnFontSizeLg, theme.btnBorderRadiusBase)} 212 | } 213 | 214 | &-sm { 215 | ${mixins.buttonSize(theme.btnHeightSm, theme.btnPaddingSm, theme.fontSizeBase, theme.btnBorderRadiusSm)} 216 | } 217 | `; 218 | }, 219 | 220 | // primary button style 221 | btnPrimary() { 222 | return css` 223 | ${mixins.buttonVariantPrimary(theme.btnPrimaryColor, theme.btnPrimaryBg)} 224 | `; 225 | }, 226 | 227 | // default button style 228 | btnDefault() { 229 | return css` 230 | ${mixins.buttonVariantOther(theme.btnDefaultColor, theme.btnDefaultBg, theme.btnDefaultBorder)} 231 | &:hover, 232 | &:focus, 233 | &:active, 234 | &.active { 235 | background: #fff; 236 | } 237 | `; 238 | }, 239 | 240 | // ghost button style 241 | btnGhost() { 242 | return css` 243 | ${mixins.buttonVariantOther(theme.textColor, 'transparent', theme.borderColorBase)} 244 | `; 245 | }, 246 | 247 | // dashed button style 248 | btnDashed() { 249 | return css` 250 | ${mixins.buttonVariantOther(theme.btnDefaultColor, theme.btnDefaultBg, theme.btnDefaultBorder)} 251 | border-style: dashed; 252 | `; 253 | }, 254 | 255 | // danger button style 256 | btnDanger() { 257 | return css` 258 | ${mixins.buttonVariantDanger(theme.btnDangerColor, theme.btnDangerBg, theme.btnDangerBorder)} 259 | `; 260 | }, 261 | 262 | // circle button: the content only contains icon 263 | btnCircle(btnClassName: btn) { 264 | return css` 265 | ${square(theme.btnCircleSize)} 266 | ${mixins.buttonSize(theme.btnCircleSize, 0, add(theme.fontSizeBase, '2px'), '50%')} 267 | 268 | &.${btnClassName}-lg { 269 | ${square(theme.btnCircleSizeLg)} 270 | ${mixins.buttonSize(theme.btnCircleSizeLg, 0, add(theme.btnFontSizeLg, '2px'), '50%')} 271 | } 272 | 273 | &.${btnClassName}-sm { 274 | ${square(theme.btnCircleSizeSm)} 275 | ${mixins.buttonSize(theme.btnCircleSizeSm, 0, theme.fontSizeBase, '50%')} 276 | } 277 | `; 278 | }, 279 | 280 | // Horizontal button groups styl 281 | // -------------------------------------------------- 282 | btnGroup(btnClassName: btn) { 283 | return css` 284 | ${mixins.buttonGroupBase(btnClassName)} 285 | 286 | .${btnClassName} + .${btnClassName}, 287 | .${btnClassName} + &, 288 | & + .${btnClassName}, 289 | & + & { 290 | margin-left: -1px; 291 | } 292 | 293 | .${btnClassName}:not(:first-child):not(:last-child) { 294 | border-radius: 0; 295 | padding-left: 8px; 296 | padding-right: 8px; 297 | } 298 | 299 | > .${btnClassName}:first-child { 300 | margin-left: 0; 301 | &:not(:last-child) { 302 | border-bottom-right-radius: 0; 303 | border-top-right-radius: 0; 304 | padding-right: 8px; 305 | } 306 | } 307 | 308 | > .${btnClassName}:last-child:not(:first-child) { 309 | border-bottom-left-radius: 0; 310 | border-top-left-radius: 0; 311 | padding-left: 8px; 312 | } 313 | 314 | & > & { 315 | float: left; 316 | } 317 | 318 | & > &:not(:first-child):not(:last-child) > .${btnClassName} { 319 | border-radius: 0; 320 | } 321 | 322 | & > &:first-child:not(:last-child) { 323 | > .${btnClassName}:last-child { 324 | border-bottom-right-radius: 0; 325 | border-top-right-radius: 0; 326 | padding-right: 8px; 327 | } 328 | } 329 | 330 | & > &:last-child:not(:first-child) > .${btnClassName}:first-child { 331 | border-bottom-left-radius: 0; 332 | border-top-left-radius: 0; 333 | padding-left: 8px; 334 | } 335 | `; 336 | }, 337 | }; 338 | 339 | return mixins; 340 | } 341 | -------------------------------------------------------------------------------- /components/style/mixins/color.js: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | export function fade(color, amount) { 4 | return new Color(color).fade(amount).string(); 5 | } 6 | -------------------------------------------------------------------------------- /components/style/mixins/font.js: -------------------------------------------------------------------------------- 1 | export function add(x, y) { 2 | const pattern = /([0-9]+)(.+)/; 3 | const [_, a, unit] = x.match(pattern)[1] 4 | const b = y.match(pattern)[1] 5 | return `${a + b}${unit}`; 6 | } 7 | -------------------------------------------------------------------------------- /components/style/mixins/size.js: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components'; 2 | // Sizing shortcuts 3 | 4 | export function size(width, height) { 5 | return css` 6 | width: ${width}; 7 | height: ${height}; 8 | `; 9 | } 10 | 11 | export function square(x) { 12 | return css` 13 | ${size(x, x)} 14 | `; 15 | } 16 | -------------------------------------------------------------------------------- /components/theme/color/bezierEasing.js: -------------------------------------------------------------------------------- 1 | var NEWTON_ITERATIONS = 4; 2 | var NEWTON_MIN_SLOPE = 0.001; 3 | var SUBDIVISION_PRECISION = 0.0000001; 4 | var SUBDIVISION_MAX_ITERATIONS = 10; 5 | 6 | var kSplineTableSize = 11; 7 | var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); 8 | 9 | var float32ArraySupported = typeof Float32Array === 'function'; 10 | 11 | function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } 12 | function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } 13 | function C (aA1) { return 3.0 * aA1; } 14 | 15 | // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. 16 | function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } 17 | 18 | // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. 19 | function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } 20 | 21 | function binarySubdivide (aX, aA, aB, mX1, mX2) { 22 | var currentX, currentT, i = 0; 23 | do { 24 | currentT = aA + (aB - aA) / 2.0; 25 | currentX = calcBezier(currentT, mX1, mX2) - aX; 26 | if (currentX > 0.0) { 27 | aB = currentT; 28 | } else { 29 | aA = currentT; 30 | } 31 | } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); 32 | return currentT; 33 | } 34 | 35 | function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { 36 | for (var i = 0; i < NEWTON_ITERATIONS; ++i) { 37 | var currentSlope = getSlope(aGuessT, mX1, mX2); 38 | if (currentSlope === 0.0) { 39 | return aGuessT; 40 | } 41 | var currentX = calcBezier(aGuessT, mX1, mX2) - aX; 42 | aGuessT -= currentX / currentSlope; 43 | } 44 | return aGuessT; 45 | } 46 | 47 | var BezierEasing = function (mX1, mY1, mX2, mY2) { 48 | if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { 49 | throw new Error('bezier x values must be in [0, 1] range'); 50 | } 51 | 52 | // Precompute samples table 53 | var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); 54 | if (mX1 !== mY1 || mX2 !== mY2) { 55 | for (var i = 0; i < kSplineTableSize; ++i) { 56 | sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); 57 | } 58 | } 59 | 60 | function getTForX (aX) { 61 | var intervalStart = 0.0; 62 | var currentSample = 1; 63 | var lastSample = kSplineTableSize - 1; 64 | 65 | for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { 66 | intervalStart += kSampleStepSize; 67 | } 68 | --currentSample; 69 | 70 | // Interpolate to provide an initial guess for t 71 | var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); 72 | var guessForT = intervalStart + dist * kSampleStepSize; 73 | 74 | var initialSlope = getSlope(guessForT, mX1, mX2); 75 | if (initialSlope >= NEWTON_MIN_SLOPE) { 76 | return newtonRaphsonIterate(aX, guessForT, mX1, mX2); 77 | } else if (initialSlope === 0.0) { 78 | return guessForT; 79 | } else { 80 | return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); 81 | } 82 | } 83 | 84 | return function BezierEasing (x) { 85 | if (mX1 === mY1 && mX2 === mY2) { 86 | return x; // linear 87 | } 88 | // Because JavaScript number are imprecise, we should guarantee the extremes are right. 89 | if (x === 0) { 90 | return 0; 91 | } 92 | if (x === 1) { 93 | return 1; 94 | } 95 | return calcBezier(getTForX(x), mY1, mY2); 96 | }; 97 | }; 98 | 99 | const colorEasing = BezierEasing(0.26, 0.09, 0.37, 0.18); 100 | 101 | export default colorEasing; 102 | -------------------------------------------------------------------------------- /components/theme/color/colorPalete.js: -------------------------------------------------------------------------------- 1 | import colorEasing from './bezierEasing'; 2 | import tinycolor from './tinyColor'; 3 | 4 | var warmDark = 0.5; // warm color darken radio 5 | var warmRotate = -26; // warm color rotate degree 6 | var coldDark = 0.55; // cold color darken radio 7 | var coldRotate = 10; // cold color rotate degree 8 | var getShadeColor = function(c) { 9 | var shadeColor = tinycolor(c); 10 | // warm and cold color will darken in different radio, and rotate in different degree 11 | // warmer color 12 | if (shadeColor.toRgb().r > shadeColor.toRgb().b) { 13 | return shadeColor.darken(shadeColor.toHsl().l * warmDark * 100).spin(warmRotate).toHexString(); 14 | } 15 | // colder color 16 | return shadeColor.darken(shadeColor.toHsl().l * coldDark * 100).spin(coldRotate).toHexString(); 17 | } 18 | var primaryEasing = colorEasing(0.6); 19 | 20 | export default function(color, index) { 21 | var currentEasing = colorEasing(index * 0.1); 22 | // return light colors after tint 23 | if (index <= 6) { 24 | return tinycolor.mix( 25 | '#ffffff', 26 | color, 27 | currentEasing * 100 / primaryEasing 28 | ).toHexString(); 29 | } 30 | return tinycolor.mix( 31 | getShadeColor(color), 32 | color, 33 | (1 - (currentEasing - primaryEasing) / (1 - primaryEasing)) * 100 34 | ).toHexString(); 35 | }; 36 | -------------------------------------------------------------------------------- /components/theme/color/colors.js: -------------------------------------------------------------------------------- 1 | import colorPalette from './colorPalete'; 2 | 3 | export const blue1 = colorPalette(blue6, 1); 4 | export const blue2 = colorPalette(blue6, 2); 5 | export const blue3 = colorPalette(blue6, 3); 6 | export const blue4 = colorPalette(blue6, 4); 7 | export const blue5 = colorPalette(blue6, 5); 8 | export const blue6 = '#108ee9'; 9 | export const blue7 = colorPalette(blue6, 7); 10 | export const blue8 = colorPalette(blue6, 8); 11 | export const blue9 = colorPalette(blue6, 9); 12 | export const blue10 = colorPalette(blue6, 10); 13 | 14 | export const purple1 = colorPalette(purple6, 1); 15 | export const purple2 = colorPalette(purple6, 2); 16 | export const purple3 = colorPalette(purple6, 3); 17 | export const purple4 = colorPalette(purple6, 4); 18 | export const purple5 = colorPalette(purple6, 5); 19 | export const purple6 = '#7265e6'; 20 | export const purple7 = colorPalette(purple6, 7); 21 | export const purple8 = colorPalette(purple6, 8); 22 | export const purple9 = colorPalette(purple6, 9); 23 | export const purple10 = colorPalette(purple6, 10); 24 | 25 | export const cyan1 = colorPalette(cyan6, 1); 26 | export const cyan2 = colorPalette(cyan6, 2); 27 | export const cyan3 = colorPalette(cyan6, 3); 28 | export const cyan4 = colorPalette(cyan6, 4); 29 | export const cyan5 = colorPalette(cyan6, 5); 30 | export const cyan6 = '#00a2ae'; 31 | export const cyan7 = colorPalette(cyan6, 7); 32 | export const cyan8 = colorPalette(cyan6, 8); 33 | export const cyan9 = colorPalette(cyan6, 9); 34 | export const cyan10 = colorPalette(cyan6, 10); 35 | 36 | export const green1 = colorPalette(green6, 1); 37 | export const green2 = colorPalette(green6, 2); 38 | export const green3 = colorPalette(green6, 3); 39 | export const green4 = colorPalette(green6, 4); 40 | export const green5 = colorPalette(green6, 5); 41 | export const green6 = '#00a854'; 42 | export const green7 = colorPalette(green6, 7); 43 | export const green8 = colorPalette(green6, 8); 44 | export const green9 = colorPalette(green6, 9); 45 | export const green10 = colorPalette(green6, 10); 46 | 47 | export const pink1 = colorPalette(pink6, 1); 48 | export const pink2 = colorPalette(pink6, 2); 49 | export const pink3 = colorPalette(pink6, 3); 50 | export const pink4 = colorPalette(pink6, 4); 51 | export const pink5 = colorPalette(pink6, 5); 52 | export const pink6 = '#f5317f'; 53 | export const pink7 = colorPalette(pink6, 7); 54 | export const pink8 = colorPalette(pink6, 8); 55 | export const pink9 = colorPalette(pink6, 9); 56 | export const pink10 = colorPalette(pink6, 10); 57 | 58 | export const red1 = colorPalette(red6, 1); 59 | export const red2 = colorPalette(red6, 2); 60 | export const red3 = colorPalette(red6, 3); 61 | export const red4 = colorPalette(red6, 4); 62 | export const red5 = colorPalette(red6, 5); 63 | export const red6 = '#f04134'; 64 | export const red7 = colorPalette(red6, 7); 65 | export const red8 = colorPalette(red6, 8); 66 | export const red9 = colorPalette(red6, 9); 67 | export const red10 = colorPalette(red6, 10); 68 | 69 | export const orange1 = colorPalette(orange6, 1); 70 | export const orange2 = colorPalette(orange6, 2); 71 | export const orange3 = colorPalette(orange6, 3); 72 | export const orange4 = colorPalette(orange6, 4); 73 | export const orange5 = colorPalette(orange6, 5); 74 | export const orange6 = '#f56a00'; 75 | export const orange7 = colorPalette(orange6, 7); 76 | export const orange8 = colorPalette(orange6, 8); 77 | export const orange9 = colorPalette(orange6, 9); 78 | export const orange10 = colorPalette(orange6, 10); 79 | 80 | export const yellow1 = colorPalette(yellow6, 1); 81 | export const yellow2 = colorPalette(yellow6, 2); 82 | export const yellow3 = colorPalette(yellow6, 3); 83 | export const yellow4 = colorPalette(yellow6, 4); 84 | export const yellow5 = colorPalette(yellow6, 5); 85 | export const yellow6 = '#ffbf00'; 86 | export const yellow7 = colorPalette(yellow6, 7); 87 | export const yellow8 = colorPalette(yellow6, 8); 88 | export const yellow9 = colorPalette(yellow6, 9); 89 | export const yellow10 = colorPalette(yellow6, 10); 90 | -------------------------------------------------------------------------------- /components/theme/color/tinyColor.js: -------------------------------------------------------------------------------- 1 | // TinyColor v1.4.1 2 | // https://github.com/bgrins/TinyColor 3 | // 2016-07-07, Brian Grinstead, MIT License 4 | var trimLeft = /^\s+/, 5 | trimRight = /\s+$/, 6 | tinyCounter = 0, 7 | mathRound = Math.round, 8 | mathMin = Math.min, 9 | mathMax = Math.max, 10 | mathRandom = Math.random; 11 | 12 | function tinycolor (color, opts) { 13 | 14 | color = (color) ? color : ''; 15 | opts = opts || { }; 16 | 17 | // If input is already a tinycolor, return itself 18 | if (color instanceof tinycolor) { 19 | return color; 20 | } 21 | // If we are called as a function, call using new instead 22 | if (!(this instanceof tinycolor)) { 23 | return new tinycolor(color, opts); 24 | } 25 | 26 | var rgb = inputToRGB(color); 27 | this._originalInput = color, 28 | this._r = rgb.r, 29 | this._g = rgb.g, 30 | this._b = rgb.b, 31 | this._a = rgb.a, 32 | this._roundA = mathRound(100*this._a) / 100, 33 | this._format = opts.format || rgb.format; 34 | this._gradientType = opts.gradientType; 35 | 36 | // Don't let the range of [0,255] come back in [0,1]. 37 | // Potentially lose a little bit of precision here, but will fix issues where 38 | // .5 gets interpreted as half of the total, instead of half of 1 39 | // If it was supposed to be 128, this was already taken care of by inputToRgb 40 | if (this._r < 1) { this._r = mathRound(this._r); } 41 | if (this._g < 1) { this._g = mathRound(this._g); } 42 | if (this._b < 1) { this._b = mathRound(this._b); } 43 | 44 | this._ok = rgb.ok; 45 | this._tc_id = tinyCounter++; 46 | } 47 | 48 | tinycolor.prototype = { 49 | isDark: function() { 50 | return this.getBrightness() < 128; 51 | }, 52 | isLight: function() { 53 | return !this.isDark(); 54 | }, 55 | isValid: function() { 56 | return this._ok; 57 | }, 58 | getOriginalInput: function() { 59 | return this._originalInput; 60 | }, 61 | getFormat: function() { 62 | return this._format; 63 | }, 64 | getAlpha: function() { 65 | return this._a; 66 | }, 67 | getBrightness: function() { 68 | //http://www.w3.org/TR/AERT#color-contrast 69 | var rgb = this.toRgb(); 70 | return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; 71 | }, 72 | getLuminance: function() { 73 | //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef 74 | var rgb = this.toRgb(); 75 | var RsRGB, GsRGB, BsRGB, R, G, B; 76 | RsRGB = rgb.r/255; 77 | GsRGB = rgb.g/255; 78 | BsRGB = rgb.b/255; 79 | 80 | if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);} 81 | if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);} 82 | if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);} 83 | return (0.2126 * R) + (0.7152 * G) + (0.0722 * B); 84 | }, 85 | setAlpha: function(value) { 86 | this._a = boundAlpha(value); 87 | this._roundA = mathRound(100*this._a) / 100; 88 | return this; 89 | }, 90 | toHsv: function() { 91 | var hsv = rgbToHsv(this._r, this._g, this._b); 92 | return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; 93 | }, 94 | toHsvString: function() { 95 | var hsv = rgbToHsv(this._r, this._g, this._b); 96 | var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); 97 | return (this._a == 1) ? 98 | "hsv(" + h + ", " + s + "%, " + v + "%)" : 99 | "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; 100 | }, 101 | toHsl: function() { 102 | var hsl = rgbToHsl(this._r, this._g, this._b); 103 | return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; 104 | }, 105 | toHslString: function() { 106 | var hsl = rgbToHsl(this._r, this._g, this._b); 107 | var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); 108 | return (this._a == 1) ? 109 | "hsl(" + h + ", " + s + "%, " + l + "%)" : 110 | "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; 111 | }, 112 | toHex: function(allow3Char) { 113 | return rgbToHex(this._r, this._g, this._b, allow3Char); 114 | }, 115 | toHexString: function(allow3Char) { 116 | return '#' + this.toHex(allow3Char); 117 | }, 118 | toHex8: function(allow4Char) { 119 | return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char); 120 | }, 121 | toHex8String: function(allow4Char) { 122 | return '#' + this.toHex8(allow4Char); 123 | }, 124 | toRgb: function() { 125 | return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; 126 | }, 127 | toRgbString: function() { 128 | return (this._a == 1) ? 129 | "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : 130 | "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; 131 | }, 132 | toPercentageRgb: function() { 133 | return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a }; 134 | }, 135 | toPercentageRgbString: function() { 136 | return (this._a == 1) ? 137 | "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : 138 | "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; 139 | }, 140 | toName: function() { 141 | if (this._a === 0) { 142 | return "transparent"; 143 | } 144 | 145 | if (this._a < 1) { 146 | return false; 147 | } 148 | 149 | return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; 150 | }, 151 | toFilter: function(secondColor) { 152 | var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a); 153 | var secondHex8String = hex8String; 154 | var gradientType = this._gradientType ? "GradientType = 1, " : ""; 155 | 156 | if (secondColor) { 157 | var s = tinycolor(secondColor); 158 | secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a); 159 | } 160 | 161 | return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; 162 | }, 163 | toString: function(format) { 164 | var formatSet = !!format; 165 | format = format || this._format; 166 | 167 | var formattedString = false; 168 | var hasAlpha = this._a < 1 && this._a >= 0; 169 | var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name"); 170 | 171 | if (needsAlphaFormat) { 172 | // Special case for "transparent", all other non-alpha formats 173 | // will return rgba when there is transparency. 174 | if (format === "name" && this._a === 0) { 175 | return this.toName(); 176 | } 177 | return this.toRgbString(); 178 | } 179 | if (format === "rgb") { 180 | formattedString = this.toRgbString(); 181 | } 182 | if (format === "prgb") { 183 | formattedString = this.toPercentageRgbString(); 184 | } 185 | if (format === "hex" || format === "hex6") { 186 | formattedString = this.toHexString(); 187 | } 188 | if (format === "hex3") { 189 | formattedString = this.toHexString(true); 190 | } 191 | if (format === "hex4") { 192 | formattedString = this.toHex8String(true); 193 | } 194 | if (format === "hex8") { 195 | formattedString = this.toHex8String(); 196 | } 197 | if (format === "name") { 198 | formattedString = this.toName(); 199 | } 200 | if (format === "hsl") { 201 | formattedString = this.toHslString(); 202 | } 203 | if (format === "hsv") { 204 | formattedString = this.toHsvString(); 205 | } 206 | 207 | return formattedString || this.toHexString(); 208 | }, 209 | clone: function() { 210 | return tinycolor(this.toString()); 211 | }, 212 | 213 | _applyModification: function(fn, args) { 214 | var color = fn.apply(null, [this].concat([].slice.call(args))); 215 | this._r = color._r; 216 | this._g = color._g; 217 | this._b = color._b; 218 | this.setAlpha(color._a); 219 | return this; 220 | }, 221 | lighten: function() { 222 | return this._applyModification(lighten, arguments); 223 | }, 224 | brighten: function() { 225 | return this._applyModification(brighten, arguments); 226 | }, 227 | darken: function() { 228 | return this._applyModification(darken, arguments); 229 | }, 230 | desaturate: function() { 231 | return this._applyModification(desaturate, arguments); 232 | }, 233 | saturate: function() { 234 | return this._applyModification(saturate, arguments); 235 | }, 236 | greyscale: function() { 237 | return this._applyModification(greyscale, arguments); 238 | }, 239 | spin: function() { 240 | return this._applyModification(spin, arguments); 241 | }, 242 | 243 | _applyCombination: function(fn, args) { 244 | return fn.apply(null, [this].concat([].slice.call(args))); 245 | }, 246 | analogous: function() { 247 | return this._applyCombination(analogous, arguments); 248 | }, 249 | complement: function() { 250 | return this._applyCombination(complement, arguments); 251 | }, 252 | monochromatic: function() { 253 | return this._applyCombination(monochromatic, arguments); 254 | }, 255 | splitcomplement: function() { 256 | return this._applyCombination(splitcomplement, arguments); 257 | }, 258 | triad: function() { 259 | return this._applyCombination(triad, arguments); 260 | }, 261 | tetrad: function() { 262 | return this._applyCombination(tetrad, arguments); 263 | } 264 | }; 265 | 266 | // If input is an object, force 1 into "1.0" to handle ratios properly 267 | // String input requires "1.0" as input, so 1 will be treated as 1 268 | tinycolor.fromRatio = function(color, opts) { 269 | if (typeof color == "object") { 270 | var newColor = {}; 271 | for (var i in color) { 272 | if (color.hasOwnProperty(i)) { 273 | if (i === "a") { 274 | newColor[i] = color[i]; 275 | } 276 | else { 277 | newColor[i] = convertToPercentage(color[i]); 278 | } 279 | } 280 | } 281 | color = newColor; 282 | } 283 | 284 | return tinycolor(color, opts); 285 | }; 286 | 287 | // Given a string or object, convert that input to RGB 288 | // Possible string inputs: 289 | // 290 | // "red" 291 | // "#f00" or "f00" 292 | // "#ff0000" or "ff0000" 293 | // "#ff000000" or "ff000000" 294 | // "rgb 255 0 0" or "rgb (255, 0, 0)" 295 | // "rgb 1.0 0 0" or "rgb (1, 0, 0)" 296 | // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" 297 | // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" 298 | // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" 299 | // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" 300 | // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" 301 | // 302 | function inputToRGB(color) { 303 | 304 | var rgb = { r: 0, g: 0, b: 0 }; 305 | var a = 1; 306 | var s = null; 307 | var v = null; 308 | var l = null; 309 | var ok = false; 310 | var format = false; 311 | 312 | if (typeof color == "string") { 313 | color = stringInputToObject(color); 314 | } 315 | 316 | if (typeof color == "object") { 317 | if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) { 318 | rgb = rgbToRgb(color.r, color.g, color.b); 319 | ok = true; 320 | format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; 321 | } 322 | else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) { 323 | s = convertToPercentage(color.s); 324 | v = convertToPercentage(color.v); 325 | rgb = hsvToRgb(color.h, s, v); 326 | ok = true; 327 | format = "hsv"; 328 | } 329 | else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) { 330 | s = convertToPercentage(color.s); 331 | l = convertToPercentage(color.l); 332 | rgb = hslToRgb(color.h, s, l); 333 | ok = true; 334 | format = "hsl"; 335 | } 336 | 337 | if (color.hasOwnProperty("a")) { 338 | a = color.a; 339 | } 340 | } 341 | 342 | a = boundAlpha(a); 343 | 344 | return { 345 | ok: ok, 346 | format: color.format || format, 347 | r: mathMin(255, mathMax(rgb.r, 0)), 348 | g: mathMin(255, mathMax(rgb.g, 0)), 349 | b: mathMin(255, mathMax(rgb.b, 0)), 350 | a: a 351 | }; 352 | } 353 | 354 | // Conversion Functions 355 | // -------------------- 356 | 357 | // rgbToHsl, rgbToHsv, hslToRgb, hsvToRgb modified from: 358 | // 359 | 360 | // rgbToRgb 361 | // Handle bounds / percentage checking to conform to CSS color spec 362 | // 363 | // *Assumes:* r, g, b in [0, 255] or [0, 1] 364 | // *Returns:* { r, g, b } in [0, 255] 365 | function rgbToRgb(r, g, b){ 366 | return { 367 | r: bound01(r, 255) * 255, 368 | g: bound01(g, 255) * 255, 369 | b: bound01(b, 255) * 255 370 | }; 371 | } 372 | 373 | // rgbToHsl 374 | // Converts an RGB color value to HSL. 375 | // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] 376 | // *Returns:* { h, s, l } in [0,1] 377 | function rgbToHsl(r, g, b) { 378 | 379 | r = bound01(r, 255); 380 | g = bound01(g, 255); 381 | b = bound01(b, 255); 382 | 383 | var max = mathMax(r, g, b), min = mathMin(r, g, b); 384 | var h, s, l = (max + min) / 2; 385 | 386 | if(max == min) { 387 | h = s = 0; // achromatic 388 | } 389 | else { 390 | var d = max - min; 391 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 392 | switch(max) { 393 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 394 | case g: h = (b - r) / d + 2; break; 395 | case b: h = (r - g) / d + 4; break; 396 | } 397 | 398 | h /= 6; 399 | } 400 | 401 | return { h: h, s: s, l: l }; 402 | } 403 | 404 | // hslToRgb 405 | // Converts an HSL color value to RGB. 406 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] 407 | // *Returns:* { r, g, b } in the set [0, 255] 408 | function hslToRgb(h, s, l) { 409 | var r, g, b; 410 | 411 | h = bound01(h, 360); 412 | s = bound01(s, 100); 413 | l = bound01(l, 100); 414 | 415 | function hue2rgb(p, q, t) { 416 | if(t < 0) t += 1; 417 | if(t > 1) t -= 1; 418 | if(t < 1/6) return p + (q - p) * 6 * t; 419 | if(t < 1/2) return q; 420 | if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; 421 | return p; 422 | } 423 | 424 | if(s === 0) { 425 | r = g = b = l; // achromatic 426 | } 427 | else { 428 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 429 | var p = 2 * l - q; 430 | r = hue2rgb(p, q, h + 1/3); 431 | g = hue2rgb(p, q, h); 432 | b = hue2rgb(p, q, h - 1/3); 433 | } 434 | 435 | return { r: r * 255, g: g * 255, b: b * 255 }; 436 | } 437 | 438 | // rgbToHsv 439 | // Converts an RGB color value to HSV 440 | // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] 441 | // *Returns:* { h, s, v } in [0,1] 442 | function rgbToHsv(r, g, b) { 443 | 444 | r = bound01(r, 255); 445 | g = bound01(g, 255); 446 | b = bound01(b, 255); 447 | 448 | var max = mathMax(r, g, b), min = mathMin(r, g, b); 449 | var h, s, v = max; 450 | 451 | var d = max - min; 452 | s = max === 0 ? 0 : d / max; 453 | 454 | if(max == min) { 455 | h = 0; // achromatic 456 | } 457 | else { 458 | switch(max) { 459 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 460 | case g: h = (b - r) / d + 2; break; 461 | case b: h = (r - g) / d + 4; break; 462 | } 463 | h /= 6; 464 | } 465 | return { h: h, s: s, v: v }; 466 | } 467 | 468 | // hsvToRgb 469 | // Converts an HSV color value to RGB. 470 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] 471 | // *Returns:* { r, g, b } in the set [0, 255] 472 | function hsvToRgb(h, s, v) { 473 | 474 | h = bound01(h, 360) * 6; 475 | s = bound01(s, 100); 476 | v = bound01(v, 100); 477 | 478 | var i = Math.floor(h), 479 | f = h - i, 480 | p = v * (1 - s), 481 | q = v * (1 - f * s), 482 | t = v * (1 - (1 - f) * s), 483 | mod = i % 6, 484 | r = [v, q, p, p, t, v][mod], 485 | g = [t, v, v, q, p, p][mod], 486 | b = [p, p, t, v, v, q][mod]; 487 | 488 | return { r: r * 255, g: g * 255, b: b * 255 }; 489 | } 490 | 491 | // rgbToHex 492 | // Converts an RGB color to hex 493 | // Assumes r, g, and b are contained in the set [0, 255] 494 | // Returns a 3 or 6 character hex 495 | function rgbToHex(r, g, b, allow3Char) { 496 | 497 | var hex = [ 498 | pad2(mathRound(r).toString(16)), 499 | pad2(mathRound(g).toString(16)), 500 | pad2(mathRound(b).toString(16)) 501 | ]; 502 | 503 | // Return a 3 character hex if possible 504 | if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { 505 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); 506 | } 507 | 508 | return hex.join(""); 509 | } 510 | 511 | // rgbaToHex 512 | // Converts an RGBA color plus alpha transparency to hex 513 | // Assumes r, g, b are contained in the set [0, 255] and 514 | // a in [0, 1]. Returns a 4 or 8 character rgba hex 515 | function rgbaToHex(r, g, b, a, allow4Char) { 516 | 517 | var hex = [ 518 | pad2(mathRound(r).toString(16)), 519 | pad2(mathRound(g).toString(16)), 520 | pad2(mathRound(b).toString(16)), 521 | pad2(convertDecimalToHex(a)) 522 | ]; 523 | 524 | // Return a 4 character hex if possible 525 | if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) { 526 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0); 527 | } 528 | 529 | return hex.join(""); 530 | } 531 | 532 | // rgbaToArgbHex 533 | // Converts an RGBA color to an ARGB Hex8 string 534 | // Rarely used, but required for "toFilter()" 535 | function rgbaToArgbHex(r, g, b, a) { 536 | 537 | var hex = [ 538 | pad2(convertDecimalToHex(a)), 539 | pad2(mathRound(r).toString(16)), 540 | pad2(mathRound(g).toString(16)), 541 | pad2(mathRound(b).toString(16)) 542 | ]; 543 | 544 | return hex.join(""); 545 | } 546 | 547 | // equals 548 | // Can be called with any tinycolor input 549 | tinycolor.equals = function (color1, color2) { 550 | if (!color1 || !color2) { return false; } 551 | return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); 552 | }; 553 | 554 | tinycolor.random = function() { 555 | return tinycolor.fromRatio({ 556 | r: mathRandom(), 557 | g: mathRandom(), 558 | b: mathRandom() 559 | }); 560 | }; 561 | 562 | // Modification Functions 563 | // ---------------------- 564 | // Thanks to less.js for some of the basics here 565 | // 566 | 567 | function desaturate(color, amount) { 568 | amount = (amount === 0) ? 0 : (amount || 10); 569 | var hsl = tinycolor(color).toHsl(); 570 | hsl.s -= amount / 100; 571 | hsl.s = clamp01(hsl.s); 572 | return tinycolor(hsl); 573 | } 574 | 575 | function saturate(color, amount) { 576 | amount = (amount === 0) ? 0 : (amount || 10); 577 | var hsl = tinycolor(color).toHsl(); 578 | hsl.s += amount / 100; 579 | hsl.s = clamp01(hsl.s); 580 | return tinycolor(hsl); 581 | } 582 | 583 | function greyscale(color) { 584 | return tinycolor(color).desaturate(100); 585 | } 586 | 587 | function lighten (color, amount) { 588 | amount = (amount === 0) ? 0 : (amount || 10); 589 | var hsl = tinycolor(color).toHsl(); 590 | hsl.l += amount / 100; 591 | hsl.l = clamp01(hsl.l); 592 | return tinycolor(hsl); 593 | } 594 | 595 | function brighten(color, amount) { 596 | amount = (amount === 0) ? 0 : (amount || 10); 597 | var rgb = tinycolor(color).toRgb(); 598 | rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); 599 | rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); 600 | rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); 601 | return tinycolor(rgb); 602 | } 603 | 604 | function darken (color, amount) { 605 | amount = (amount === 0) ? 0 : (amount || 10); 606 | var hsl = tinycolor(color).toHsl(); 607 | hsl.l -= amount / 100; 608 | hsl.l = clamp01(hsl.l); 609 | return tinycolor(hsl); 610 | } 611 | 612 | // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. 613 | // Values outside of this range will be wrapped into this range. 614 | function spin(color, amount) { 615 | var hsl = tinycolor(color).toHsl(); 616 | var hue = (hsl.h + amount) % 360; 617 | hsl.h = hue < 0 ? 360 + hue : hue; 618 | return tinycolor(hsl); 619 | } 620 | 621 | // Combination Functions 622 | // --------------------- 623 | // Thanks to jQuery xColor for some of the ideas behind these 624 | // 625 | 626 | function complement(color) { 627 | var hsl = tinycolor(color).toHsl(); 628 | hsl.h = (hsl.h + 180) % 360; 629 | return tinycolor(hsl); 630 | } 631 | 632 | function triad(color) { 633 | var hsl = tinycolor(color).toHsl(); 634 | var h = hsl.h; 635 | return [ 636 | tinycolor(color), 637 | tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), 638 | tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) 639 | ]; 640 | } 641 | 642 | function tetrad(color) { 643 | var hsl = tinycolor(color).toHsl(); 644 | var h = hsl.h; 645 | return [ 646 | tinycolor(color), 647 | tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), 648 | tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), 649 | tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) 650 | ]; 651 | } 652 | 653 | function splitcomplement(color) { 654 | var hsl = tinycolor(color).toHsl(); 655 | var h = hsl.h; 656 | return [ 657 | tinycolor(color), 658 | tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), 659 | tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) 660 | ]; 661 | } 662 | 663 | function analogous(color, results, slices) { 664 | results = results || 6; 665 | slices = slices || 30; 666 | 667 | var hsl = tinycolor(color).toHsl(); 668 | var part = 360 / slices; 669 | var ret = [tinycolor(color)]; 670 | 671 | for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { 672 | hsl.h = (hsl.h + part) % 360; 673 | ret.push(tinycolor(hsl)); 674 | } 675 | return ret; 676 | } 677 | 678 | function monochromatic(color, results) { 679 | results = results || 6; 680 | var hsv = tinycolor(color).toHsv(); 681 | var h = hsv.h, s = hsv.s, v = hsv.v; 682 | var ret = []; 683 | var modification = 1 / results; 684 | 685 | while (results--) { 686 | ret.push(tinycolor({ h: h, s: s, v: v})); 687 | v = (v + modification) % 1; 688 | } 689 | 690 | return ret; 691 | } 692 | 693 | // Utility Functions 694 | // --------------------- 695 | 696 | tinycolor.mix = function(color1, color2, amount) { 697 | amount = (amount === 0) ? 0 : (amount || 50); 698 | 699 | var rgb1 = tinycolor(color1).toRgb(); 700 | var rgb2 = tinycolor(color2).toRgb(); 701 | 702 | var p = amount / 100; 703 | 704 | var rgba = { 705 | r: ((rgb2.r - rgb1.r) * p) + rgb1.r, 706 | g: ((rgb2.g - rgb1.g) * p) + rgb1.g, 707 | b: ((rgb2.b - rgb1.b) * p) + rgb1.b, 708 | a: ((rgb2.a - rgb1.a) * p) + rgb1.a 709 | }; 710 | 711 | return tinycolor(rgba); 712 | }; 713 | 714 | // Readability Functions 715 | // --------------------- 716 | // false 735 | // tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false 736 | tinycolor.isReadable = function(color1, color2, wcag2) { 737 | var readability = tinycolor.readability(color1, color2); 738 | var wcag2Parms, out; 739 | 740 | out = false; 741 | 742 | wcag2Parms = validateWCAG2Parms(wcag2); 743 | switch (wcag2Parms.level + wcag2Parms.size) { 744 | case "AAsmall": 745 | case "AAAlarge": 746 | out = readability >= 4.5; 747 | break; 748 | case "AAlarge": 749 | out = readability >= 3; 750 | break; 751 | case "AAAsmall": 752 | out = readability >= 7; 753 | break; 754 | } 755 | return out; 756 | 757 | }; 758 | 759 | // mostReadable 760 | // Given a base color and a list of possible foreground or background 761 | // colors for that base, returns the most readable color. 762 | // Optionally returns Black or White if the most readable color is unreadable. 763 | // *Example* 764 | // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" 765 | // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" 766 | // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3" 767 | // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff" 768 | tinycolor.mostReadable = function(baseColor, colorList, args) { 769 | var bestColor = null; 770 | var bestScore = 0; 771 | var readability; 772 | var includeFallbackColors, level, size ; 773 | args = args || {}; 774 | includeFallbackColors = args.includeFallbackColors ; 775 | level = args.level; 776 | size = args.size; 777 | 778 | for (var i= 0; i < colorList.length ; i++) { 779 | readability = tinycolor.readability(baseColor, colorList[i]); 780 | if (readability > bestScore) { 781 | bestScore = readability; 782 | bestColor = tinycolor(colorList[i]); 783 | } 784 | } 785 | 786 | if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) { 787 | return bestColor; 788 | } 789 | else { 790 | args.includeFallbackColors=false; 791 | return tinycolor.mostReadable(baseColor,["#fff", "#000"],args); 792 | } 793 | }; 794 | 795 | // Big List of Colors 796 | // ------------------ 797 | // 798 | var names = tinycolor.names = { 799 | aliceblue: "f0f8ff", 800 | antiquewhite: "faebd7", 801 | aqua: "0ff", 802 | aquamarine: "7fffd4", 803 | azure: "f0ffff", 804 | beige: "f5f5dc", 805 | bisque: "ffe4c4", 806 | black: "000", 807 | blanchedalmond: "ffebcd", 808 | blue: "00f", 809 | blueviolet: "8a2be2", 810 | brown: "a52a2a", 811 | burlywood: "deb887", 812 | burntsienna: "ea7e5d", 813 | cadetblue: "5f9ea0", 814 | chartreuse: "7fff00", 815 | chocolate: "d2691e", 816 | coral: "ff7f50", 817 | cornflowerblue: "6495ed", 818 | cornsilk: "fff8dc", 819 | crimson: "dc143c", 820 | cyan: "0ff", 821 | darkblue: "00008b", 822 | darkcyan: "008b8b", 823 | darkgoldenrod: "b8860b", 824 | darkgray: "a9a9a9", 825 | darkgreen: "006400", 826 | darkgrey: "a9a9a9", 827 | darkkhaki: "bdb76b", 828 | darkmagenta: "8b008b", 829 | darkolivegreen: "556b2f", 830 | darkorange: "ff8c00", 831 | darkorchid: "9932cc", 832 | darkred: "8b0000", 833 | darksalmon: "e9967a", 834 | darkseagreen: "8fbc8f", 835 | darkslateblue: "483d8b", 836 | darkslategray: "2f4f4f", 837 | darkslategrey: "2f4f4f", 838 | darkturquoise: "00ced1", 839 | darkviolet: "9400d3", 840 | deeppink: "ff1493", 841 | deepskyblue: "00bfff", 842 | dimgray: "696969", 843 | dimgrey: "696969", 844 | dodgerblue: "1e90ff", 845 | firebrick: "b22222", 846 | floralwhite: "fffaf0", 847 | forestgreen: "228b22", 848 | fuchsia: "f0f", 849 | gainsboro: "dcdcdc", 850 | ghostwhite: "f8f8ff", 851 | gold: "ffd700", 852 | goldenrod: "daa520", 853 | gray: "808080", 854 | green: "008000", 855 | greenyellow: "adff2f", 856 | grey: "808080", 857 | honeydew: "f0fff0", 858 | hotpink: "ff69b4", 859 | indianred: "cd5c5c", 860 | indigo: "4b0082", 861 | ivory: "fffff0", 862 | khaki: "f0e68c", 863 | lavender: "e6e6fa", 864 | lavenderblush: "fff0f5", 865 | lawngreen: "7cfc00", 866 | lemonchiffon: "fffacd", 867 | lightblue: "add8e6", 868 | lightcoral: "f08080", 869 | lightcyan: "e0ffff", 870 | lightgoldenrodyellow: "fafad2", 871 | lightgray: "d3d3d3", 872 | lightgreen: "90ee90", 873 | lightgrey: "d3d3d3", 874 | lightpink: "ffb6c1", 875 | lightsalmon: "ffa07a", 876 | lightseagreen: "20b2aa", 877 | lightskyblue: "87cefa", 878 | lightslategray: "789", 879 | lightslategrey: "789", 880 | lightsteelblue: "b0c4de", 881 | lightyellow: "ffffe0", 882 | lime: "0f0", 883 | limegreen: "32cd32", 884 | linen: "faf0e6", 885 | magenta: "f0f", 886 | maroon: "800000", 887 | mediumaquamarine: "66cdaa", 888 | mediumblue: "0000cd", 889 | mediumorchid: "ba55d3", 890 | mediumpurple: "9370db", 891 | mediumseagreen: "3cb371", 892 | mediumslateblue: "7b68ee", 893 | mediumspringgreen: "00fa9a", 894 | mediumturquoise: "48d1cc", 895 | mediumvioletred: "c71585", 896 | midnightblue: "191970", 897 | mintcream: "f5fffa", 898 | mistyrose: "ffe4e1", 899 | moccasin: "ffe4b5", 900 | navajowhite: "ffdead", 901 | navy: "000080", 902 | oldlace: "fdf5e6", 903 | olive: "808000", 904 | olivedrab: "6b8e23", 905 | orange: "ffa500", 906 | orangered: "ff4500", 907 | orchid: "da70d6", 908 | palegoldenrod: "eee8aa", 909 | palegreen: "98fb98", 910 | paleturquoise: "afeeee", 911 | palevioletred: "db7093", 912 | papayawhip: "ffefd5", 913 | peachpuff: "ffdab9", 914 | peru: "cd853f", 915 | pink: "ffc0cb", 916 | plum: "dda0dd", 917 | powderblue: "b0e0e6", 918 | purple: "800080", 919 | rebeccapurple: "663399", 920 | red: "f00", 921 | rosybrown: "bc8f8f", 922 | royalblue: "4169e1", 923 | saddlebrown: "8b4513", 924 | salmon: "fa8072", 925 | sandybrown: "f4a460", 926 | seagreen: "2e8b57", 927 | seashell: "fff5ee", 928 | sienna: "a0522d", 929 | silver: "c0c0c0", 930 | skyblue: "87ceeb", 931 | slateblue: "6a5acd", 932 | slategray: "708090", 933 | slategrey: "708090", 934 | snow: "fffafa", 935 | springgreen: "00ff7f", 936 | steelblue: "4682b4", 937 | tan: "d2b48c", 938 | teal: "008080", 939 | thistle: "d8bfd8", 940 | tomato: "ff6347", 941 | turquoise: "40e0d0", 942 | violet: "ee82ee", 943 | wheat: "f5deb3", 944 | white: "fff", 945 | whitesmoke: "f5f5f5", 946 | yellow: "ff0", 947 | yellowgreen: "9acd32" 948 | }; 949 | 950 | // Make it easy to access colors via hexNames[hex] 951 | var hexNames = tinycolor.hexNames = flip(names); 952 | 953 | // Utilities 954 | // --------- 955 | 956 | // { 'name1': 'val1' } becomes { 'val1': 'name1' } 957 | function flip(o) { 958 | var flipped = { }; 959 | for (var i in o) { 960 | if (o.hasOwnProperty(i)) { 961 | flipped[o[i]] = i; 962 | } 963 | } 964 | return flipped; 965 | } 966 | 967 | // Return a valid alpha value [0,1] with all invalid values being set to 1 968 | function boundAlpha(a) { 969 | a = parseFloat(a); 970 | 971 | if (isNaN(a) || a < 0 || a > 1) { 972 | a = 1; 973 | } 974 | 975 | return a; 976 | } 977 | 978 | // Take input from [0, n] and return it as [0, 1] 979 | function bound01(n, max) { 980 | if (isOnePointZero(n)) { n = "100%"; } 981 | 982 | var processPercent = isPercentage(n); 983 | n = mathMin(max, mathMax(0, parseFloat(n))); 984 | 985 | // Automatically convert percentage into number 986 | if (processPercent) { 987 | n = parseInt(n * max, 10) / 100; 988 | } 989 | 990 | // Handle floating point rounding errors 991 | if ((Math.abs(n - max) < 0.000001)) { 992 | return 1; 993 | } 994 | 995 | // Convert into [0, 1] range if it isn't already 996 | return (n % max) / parseFloat(max); 997 | } 998 | 999 | // Force a number between 0 and 1 1000 | function clamp01(val) { 1001 | return mathMin(1, mathMax(0, val)); 1002 | } 1003 | 1004 | // Parse a base-16 hex value into a base-10 integer 1005 | function parseIntFromHex(val) { 1006 | return parseInt(val, 16); 1007 | } 1008 | 1009 | // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 1010 | // 1011 | function isOnePointZero(n) { 1012 | return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; 1013 | } 1014 | 1015 | // Check to see if string passed in is a percentage 1016 | function isPercentage(n) { 1017 | return typeof n === "string" && n.indexOf('%') != -1; 1018 | } 1019 | 1020 | // Force a hex value to have 2 characters 1021 | function pad2(c) { 1022 | return c.length == 1 ? '0' + c : '' + c; 1023 | } 1024 | 1025 | // Replace a decimal with it's percentage value 1026 | function convertToPercentage(n) { 1027 | if (n <= 1) { 1028 | n = (n * 100) + "%"; 1029 | } 1030 | 1031 | return n; 1032 | } 1033 | 1034 | // Converts a decimal to a hex value 1035 | function convertDecimalToHex(d) { 1036 | return Math.round(parseFloat(d) * 255).toString(16); 1037 | } 1038 | // Converts a hex value to a decimal 1039 | function convertHexToDecimal(h) { 1040 | return (parseIntFromHex(h) / 255); 1041 | } 1042 | 1043 | var matchers = (function() { 1044 | 1045 | // 1046 | var CSS_INTEGER = "[-\\+]?\\d+%?"; 1047 | 1048 | // 1049 | var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; 1050 | 1051 | // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. 1052 | var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; 1053 | 1054 | // Actual matching. 1055 | // Parentheses and commas are optional, but not required. 1056 | // Whitespace can take the place of commas or opening paren 1057 | var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 1058 | var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 1059 | 1060 | return { 1061 | CSS_UNIT: new RegExp(CSS_UNIT), 1062 | rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), 1063 | rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), 1064 | hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), 1065 | hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), 1066 | hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), 1067 | hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), 1068 | hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1069 | hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, 1070 | hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1071 | hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ 1072 | }; 1073 | })(); 1074 | 1075 | // isValidCSSUnit 1076 | // Take in a single string / number and check to see if it looks like a CSS unit 1077 | // (see matchers above for definition). 1078 | function isValidCSSUnit(color) { 1079 | return !!matchers.CSS_UNIT.exec(color); 1080 | } 1081 | 1082 | // stringInputToObject 1083 | // Permissive string parsing. Take in a number of formats, and output an object 1084 | // based on detected format. Returns { r, g, b } or { h, s, l } or { h, s, v} 1085 | function stringInputToObject(color) { 1086 | 1087 | color = color.replace(trimLeft, '').replace(trimRight, '').toLowerCase(); 1088 | var named = false; 1089 | if (names[color]) { 1090 | color = names[color]; 1091 | named = true; 1092 | } 1093 | else if (color == 'transparent') { 1094 | return { r: 0, g: 0, b: 0, a: 0, format: "name" }; 1095 | } 1096 | 1097 | // Try to match string input using regular expressions. 1098 | // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] 1099 | // Just return an object and let the conversion functions handle that. 1100 | // This way the result will be the same whether the tinycolor is initialized with string or object. 1101 | var match; 1102 | if ((match = matchers.rgb.exec(color))) { 1103 | return { r: match[1], g: match[2], b: match[3] }; 1104 | } 1105 | if ((match = matchers.rgba.exec(color))) { 1106 | return { r: match[1], g: match[2], b: match[3], a: match[4] }; 1107 | } 1108 | if ((match = matchers.hsl.exec(color))) { 1109 | return { h: match[1], s: match[2], l: match[3] }; 1110 | } 1111 | if ((match = matchers.hsla.exec(color))) { 1112 | return { h: match[1], s: match[2], l: match[3], a: match[4] }; 1113 | } 1114 | if ((match = matchers.hsv.exec(color))) { 1115 | return { h: match[1], s: match[2], v: match[3] }; 1116 | } 1117 | if ((match = matchers.hsva.exec(color))) { 1118 | return { h: match[1], s: match[2], v: match[3], a: match[4] }; 1119 | } 1120 | if ((match = matchers.hex8.exec(color))) { 1121 | return { 1122 | r: parseIntFromHex(match[1]), 1123 | g: parseIntFromHex(match[2]), 1124 | b: parseIntFromHex(match[3]), 1125 | a: convertHexToDecimal(match[4]), 1126 | format: named ? "name" : "hex8" 1127 | }; 1128 | } 1129 | if ((match = matchers.hex6.exec(color))) { 1130 | return { 1131 | r: parseIntFromHex(match[1]), 1132 | g: parseIntFromHex(match[2]), 1133 | b: parseIntFromHex(match[3]), 1134 | format: named ? "name" : "hex" 1135 | }; 1136 | } 1137 | if ((match = matchers.hex4.exec(color))) { 1138 | return { 1139 | r: parseIntFromHex(match[1] + '' + match[1]), 1140 | g: parseIntFromHex(match[2] + '' + match[2]), 1141 | b: parseIntFromHex(match[3] + '' + match[3]), 1142 | a: convertHexToDecimal(match[4] + '' + match[4]), 1143 | format: named ? "name" : "hex8" 1144 | }; 1145 | } 1146 | if ((match = matchers.hex3.exec(color))) { 1147 | return { 1148 | r: parseIntFromHex(match[1] + '' + match[1]), 1149 | g: parseIntFromHex(match[2] + '' + match[2]), 1150 | b: parseIntFromHex(match[3] + '' + match[3]), 1151 | format: named ? "name" : "hex" 1152 | }; 1153 | } 1154 | 1155 | return false; 1156 | } 1157 | 1158 | function validateWCAG2Parms(parms) { 1159 | // return valid WCAG2 parms for isReadable. 1160 | // If input parms are invalid, return {"level":"AA", "size":"small"} 1161 | var level, size; 1162 | parms = parms || {"level":"AA", "size":"small"}; 1163 | level = (parms.level || "AA").toUpperCase(); 1164 | size = (parms.size || "small").toLowerCase(); 1165 | if (level !== "AA" && level !== "AAA") { 1166 | level = "AA"; 1167 | } 1168 | if (size !== "small" && size !== "large") { 1169 | size = "small"; 1170 | } 1171 | return {"level":level, "size":size}; 1172 | } 1173 | 1174 | export default tinycolor; 1175 | -------------------------------------------------------------------------------- /components/theme/createTheme.js: -------------------------------------------------------------------------------- 1 | import * as colors from './color/colors'; 2 | import colorPalette from './color/colorPalete'; 3 | import { add } from '../style/mixins/font'; 4 | import { fade } from '../style/mixins/color'; 5 | 6 | export default (custom) => { 7 | const theme = { 8 | primaryColor: colors.blue6, 9 | infoColor: colors.blue6, 10 | successColor: colors.green6, 11 | errorColor: colors.red6, 12 | highlightColor: colors.red6, 13 | warningColor: colors.yellow6, 14 | normalColor: '#d9d9d9', 15 | 16 | // by default to control hover and active backgrounds and for 17 | // backgrounds. 18 | get primary1() { 19 | return colorPalette(theme.primaryColor, 1); 20 | }, // replace tint(@primary-color, 90%) 21 | get primary2() { 22 | return colorPalette(theme.primaryColor, 2); 23 | }, // replace tint(@primary-color, 80%) 24 | get primary3() { 25 | return colorPalette(theme.primaryColor, 3); 26 | }, // unused 27 | get primary4() { 28 | return colorPalette(theme.primaryColor, 4); 29 | }, // unused 30 | get primary5() { 31 | return colorPalette(theme.primaryColor, 5); 32 | }, // color used to control the text color in many active and hover states, replace tint(@primary-color, 20%) 33 | get primary6() { 34 | return theme.primaryColor; 35 | }, // color used to control the text color of active buttons, don't use, use @primary-color 36 | get primary7() { 37 | return colorPalette(theme.primaryColor, 7); 38 | }, // replace shade(@primary-color, 5%) 39 | get primary8() { 40 | return colorPalette(theme.primaryColor, 8); 41 | }, // unused 42 | get primary9() { 43 | return colorPalette(theme.primaryColor, 9); 44 | }, // unused 45 | get primary10() { 46 | return colorPalette(theme.primaryColor, 10); 47 | }, // unused 48 | 49 | // Base Scaffolding Variables 50 | // --- 51 | 52 | // Background color for `` 53 | bodyBackground: '#fff', 54 | // Base background color for most components 55 | componentBackground: '#fff', 56 | fontFamilyNoNumber: 57 | '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif', 58 | fontFamily: 'Helvetica Neue For Number", @font-family-no-number', 59 | codeFamily: 'Consolas, Menlo, Courier, monospace', 60 | headingColor: fade('#000', 0.15), 61 | textColor: fade('#000', 0.35), 62 | textColorSecondary: fade('#000', 0.57), 63 | headingColorDark: fade('#fff', 0.03), 64 | textColorDark: fade('#fff', 0.09), 65 | textColorSecondaryDark: fade('#fff', 0.33), 66 | fontSizeBase: '14px', 67 | get fontSizeLg() { 68 | return add(theme.fontSizeBase, '2px'); 69 | }, 70 | lineHeightBase: 1.5, 71 | borderRadiusBase: '4px', 72 | borderRadiusSm: '2px', 73 | 74 | // The background colors for active and hover states for things like 75 | // list items or table cells. 76 | get itemActiveBg() { 77 | return theme.primary1; 78 | }, 79 | get itemHoverBg() { 80 | return theme.primary1; 81 | }, 82 | 83 | // ICONFONT 84 | iconfontCssPrefix: 'anticon', 85 | iconUrl: 'https://at.alicdn.com/t/font_148784_1vkh7de5j6ecdi', 86 | 87 | // LINK 88 | get linkColor() { 89 | return theme.primaryColor; 90 | }, 91 | get linkHoverColor() { 92 | return theme.primary5; 93 | }, 94 | get linkActiveColor() { 95 | return theme.primary7; 96 | }, 97 | linkHoverDecoration: 'none', 98 | 99 | // Animation 100 | easeOut: 'cubic-bezier(0.215, 0.61, 0.355, 1)', 101 | easeIn: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', 102 | easeInOut: 'cubic-bezier(0.645, 0.045, 0.355, 1)', 103 | easeOutBack: 'cubic-bezier(0.12, 0.4, 0.29, 1.46)', 104 | easeInBack: 'cubic-bezier(0.71, -0.46, 0.88, 0.6)', 105 | easeInOutBack: 'cubic-bezier(0.71, -0.46, 0.29, 1.46)', 106 | easeOutCirc: 'cubic-bezier(0.08, 0.82, 0.17, 1)', 107 | easeInCirc: 'cubic-bezier(0.6, 0.04, 0.98, 0.34)', 108 | easeInOutCirc: 'cubic-bezier(0.78, 0.14, 0.15, 0.86)', 109 | easeOutQuint: 'cubic-bezier(0.23, 1, 0.32, 1)', 110 | easeInQuint: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)', 111 | easeInOutQuint: 'cubic-bezier(0.86, 0, 0.07, 1)', 112 | 113 | // Border color 114 | borderColorBase: '#d9d9d9', // base border outline a component 115 | borderColorSplit: '#e9e9e9', // split border inside a component 116 | borderWidthBase: '1px', // width of the border for a component 117 | borderStyleBase: 'solid', // style of a components border 118 | 119 | // Outline 120 | outlineBlurSize: 0, 121 | outlineWidth: '2px', 122 | get outlineColor() { 123 | return theme.primaryColor; 124 | }, 125 | 126 | // Default background color for disabled states, Collapse wrappers, 127 | // and several active and hover states. 128 | backgroundColorBase: '#f7f7f7', 129 | backgroundColorActive: '#eee', 130 | 131 | // Disabled states 132 | disabledColor: fade('#000', 0.75), 133 | get disabledBg() { 134 | return theme.backgroundColorBase; 135 | }, 136 | disabledColorDark: fade('#fff', 0.65), 137 | 138 | // Shadow 139 | shadowColor: 'rgba(0, 0, 0, .2)', 140 | get boxShadowBase() { 141 | return theme.shadow1Down; 142 | }, 143 | get shadow1Up() { 144 | return `0 -1px 6px ${theme.shadowColor}`; 145 | }, 146 | get shadow1Down() { 147 | return `0 1px 6px ${theme.shadowColor}`; 148 | }, 149 | get shadow1Left() { 150 | return `-1px 0 6px ${theme.shadowColor}`; 151 | }, 152 | get shadow1Right() { 153 | return `1px 0 6px ${theme.shadowColor}`; 154 | }, 155 | get shadow2() { 156 | return `0 2px 8px ${theme.shadowColor}`; 157 | }, 158 | 159 | // Buttons 160 | btnFontWeight: 500, 161 | get btnBorderRadiusBase() { 162 | return theme.borderRadiusBase; 163 | }, 164 | get btnBorderRadiusSm() { 165 | return theme.borderRadiusBase; 166 | }, 167 | 168 | btnPrimaryColor: '#fff', 169 | get btnPrimaryBg() { 170 | return theme.primaryColor; 171 | }, 172 | 173 | get btnDefaultColor() { 174 | return theme.textColor; 175 | }, 176 | btnDefaultBg: '#fff', 177 | get btnDefaultBorder() { 178 | return theme.borderColorBase; 179 | }, 180 | 181 | get btnDangerColor() { 182 | return theme.errorColor; 183 | }, 184 | get btnDangerBg() { 185 | return theme.backgroundColorBase; 186 | }, 187 | get btnDangerBorder() { 188 | return theme.borderColorBase; 189 | }, 190 | 191 | get btnDisableColor() { 192 | return theme.disabledColor; 193 | }, 194 | get btnDisableBg() { 195 | return theme.disabledBg; 196 | }, 197 | get btnDisableBorder() { 198 | return theme.borderColorBase; 199 | }, 200 | 201 | btnPaddingBase: '0 15px', 202 | get btnFontSizeLg() { 203 | return theme.fontSizeLg; 204 | }, 205 | get btnPaddingLg() { 206 | return theme.btnPaddingBase; 207 | }, 208 | btnPaddingSm: '0 7px', 209 | 210 | btnHeightBase: '28px', 211 | btnHeightLg: '32px', 212 | btnHeightSm: '22px', 213 | 214 | get btnCircleSize() { 215 | return theme.btnHeightBase; 216 | }, 217 | get btnCircleSizeLg() { 218 | return theme.btnHeightLg; 219 | }, 220 | get btnCircleSizeSm() { 221 | return theme.btnHeightSm; 222 | }, 223 | 224 | get btnGroupBorder() { 225 | return theme.primary7; 226 | }, 227 | 228 | ...custom, 229 | } 230 | return theme; 231 | } 232 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "styled-antd", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "storybook": "start-storybook -p 6006", 8 | "build-storybook": "build-storybook", 9 | "publish-gh-pages": "npm run build-storybook && cd storybook-static && git init && git commit --allow-empty -m 'Update' && git checkout -b gh-pages && git add . && git commit -am 'Update' && git push git@github.com:yesmeck/styled-antd gh-pages --force" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "antd": "^2.13.1", 16 | "color": "^2.0.0", 17 | "react": "^15.6.1", 18 | "react-dom": "^15.6.1", 19 | "styled-components": "^2.1.2" 20 | }, 21 | "devDependencies": { 22 | "@storybook/addon-actions": "^3.2.6", 23 | "@storybook/addon-links": "^3.2.6", 24 | "@storybook/react": "^3.2.8" 25 | }, 26 | "description": "" 27 | } 28 | -------------------------------------------------------------------------------- /stories/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { storiesOf } from '@storybook/react'; 4 | import { action } from '@storybook/addon-actions'; 5 | import { linkTo } from '@storybook/addon-links'; 6 | import styled, { ThemeProvider } from 'styled-components'; 7 | 8 | import Button from '../components/button'; 9 | 10 | const Block = styled.div` 11 | button { 12 | margin-right: 8px; 13 | margin-bottom: 12px; 14 | } 15 | `; 16 | 17 | storiesOf('Button', module) 18 | .add('default', () => 19 | 20 | 21 | 22 | 23 | 24 | 25 | ) 26 | .add('green theme', () => 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | --------------------------------------------------------------------------------