diff --git a/.storybook/main.js b/.storybook/main.js index 23cae2d8..75bbfec7 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,6 +1,12 @@ module.exports = { stories: ['../client/**/*.stories.(jsx|mdx)'], - addons: ['@storybook/addon-actions', '@storybook/addon-docs', '@storybook/addon-knobs', '@storybook/addon-links'], + addons: [ + '@storybook/addon-actions', + '@storybook/addon-docs', + '@storybook/addon-knobs', + '@storybook/addon-links', + 'storybook-addon-theme-playground/dist/register' + ], webpackFinal: async config => { // do mutation to the config diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 00000000..e63fad69 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { addDecorator, addParameters } from '@storybook/react'; +import { withKnobs } from "@storybook/addon-knobs"; +import { withThemePlayground } from 'storybook-addon-theme-playground'; +import { ThemeProvider } from "styled-components"; + +import theme, { Theme } from '../client/theme'; + +addDecorator(withKnobs); + +const themeConfigs = Object.values(Theme).map( + name => { + return { name, theme: theme[name] }; + } +); + +addDecorator(withThemePlayground({ + theme: themeConfigs, + provider: ThemeProvider +})); + +// addDecorator(storyFn => {storyFn()}); diff --git a/client/components/Button/index.js b/client/components/Button/index.js index 89e9d3b7..ea7afd36 100644 --- a/client/components/Button/index.js +++ b/client/components/Button/index.js @@ -1,12 +1,28 @@ import React from "react"; import PropTypes from "prop-types"; import styled from 'styled-components'; +import { remSize, prop } from '../../theme'; const StyledButton = styled.button` + background-color: ${prop('buttonColorBackground')}; + color: ${prop('buttonColor')}; + cursor: pointer; + border: 2px solid ${prop('buttonBorderColor')}; + border-radius: 2px; + padding: ${remSize(8)} ${remSize(25)}; + line-height: 1; margin: 0; - padding: 0; - border: none; - background: none; + + &:hover:not(:disabled) { + color: ${prop('buttonHoverColor')}; + background-color: ${prop('buttonHoverColorBackground')}; + } + + &:disabled { + color: ${prop('buttonDisabledColor')}; + background-color: ${prop('buttonDisabledColorBackground')}; + cursor: not-allowed; + } `; /** @@ -24,7 +40,7 @@ Button.propTypes = { /** If the button can be clicked or not */ - disabled: PropTypes.boolean, + disabled: PropTypes.bool, /* * An ARIA Label used for accessibility */ diff --git a/client/index.jsx b/client/index.jsx index 09f6eba0..140ef596 100644 --- a/client/index.jsx +++ b/client/index.jsx @@ -3,8 +3,11 @@ import { render } from 'react-dom'; import { hot } from 'react-hot-loader/root'; import { Provider } from 'react-redux'; import { Router, browserHistory } from 'react-router'; +import { ThemeProvider } from 'styled-components'; + import configureStore from './store'; import routes from './routes'; +import theme, { Theme } from './theme'; require('./styles/main.scss'); @@ -16,10 +19,14 @@ const initialState = window.__INITIAL_STATE__; const store = configureStore(initialState); +const currentTheme = Theme.light; + const App = () => ( - - - + + + + + ); const HotApp = hot(App); diff --git a/client/theme.js b/client/theme.js new file mode 100644 index 00000000..1d90e8fc --- /dev/null +++ b/client/theme.js @@ -0,0 +1,54 @@ +export const Theme = { + contrast: 'contrast', + dark: 'dark', + light: 'light', +}; + +export const colors = { + p5pink: '#ed225d', + yellow: '#f5dc23', +}; + +export const common = { + baseFontSize: 12 +}; + +export const remSize = size => `${size / common.baseFontSize}rem`; + +export const prop = key => props => props.theme[key]; + +export default { + [Theme.light]: { + colors, + ...common, + primaryTextColor: '#333', + + buttonColor: '#f10046', + buttonColorBackground: '#fff', + buttonBorderColor: '#b5b5b5', + buttonHoverColor: '#fff', + buttonHoverColorBackground: colors.p5pink, + }, + [Theme.dark]: { + colors, + ...common, + primaryTextColor: '#FFF', + + buttonColor: '#f10046', + buttonColorBackground: '#fff', + buttonBorderColor: '#b5b5b5', + buttonHoverColor: '#fff', + buttonHoverColorBackground: colors.p5pink, + }, + [Theme.contrast]: { + colors, + ...common, + primaryTextColor: '#F5DC23', + + buttonColor: '#333', + buttonColorBackground: colors.yellow, + buttonBorderColor: '#b5b5b5', + buttonHoverColor: '#333', + buttonHoverColorBackground: '#fff', + }, +}; diff --git a/package-lock.json b/package-lock.json index 5dceb528..66ae17b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27914,6 +27914,15 @@ "integrity": "sha512-tWEpK0snS2RPUq1i3R6OahfJNjWCQYNxq0+by1amCSuw0mXtymJpzmZIeYpA1UAa+7B0grCpNYIbDcd7AgTbFg==", "dev": true }, + "storybook-addon-theme-playground": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/storybook-addon-theme-playground/-/storybook-addon-theme-playground-1.2.0.tgz", + "integrity": "sha512-9eAgRhTdAqSPQfHPSBxk8ttvZ7fH0tI++/1xvZpyrJwDnSoWrpVlSNVIk748bH9mq6aCyUCi92KzJOhSy+nPxQ==", + "dev": true, + "requires": { + "react-color": "^2.17.3" + } + }, "storybook-chromatic": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/storybook-chromatic/-/storybook-chromatic-2.2.2.tgz", @@ -28563,6 +28572,11 @@ } } }, + "styled-theming": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/styled-theming/-/styled-theming-2.2.0.tgz", + "integrity": "sha1-MITkPUDqq0vBHrr9PeBONiL+434=" + }, "stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", diff --git a/package.json b/package.json index 3e584e4d..b8195517 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "react-test-renderer": "^16.6.0", "rimraf": "^2.6.3", "sass-loader": "^6.0.7", + "storybook-addon-theme-playground": "^1.2.0", "style-loader": "^1.0.0", "terser-webpack-plugin": "^1.4.1", "webpack-cli": "^3.3.7", @@ -185,6 +186,7 @@ "slugify": "^1.3.4", "srcdoc-polyfill": "^0.2.0", "styled-components": "^5.0.0", + "styled-theming": "^2.2.0", "url": "^0.11.0", "webpack": "^4.39.2", "webpack-dev-middleware": "^2.0.6",