diff --git a/packages/roleypoly-design/.gitignore b/packages/roleypoly-design/.gitignore
new file mode 100644
index 0000000..1eaff4a
--- /dev/null
+++ b/packages/roleypoly-design/.gitignore
@@ -0,0 +1,3 @@
+lib
+node_modules
+*.log
\ No newline at end of file
diff --git a/packages/roleypoly-design/.storybook/addons.js b/packages/roleypoly-design/.storybook/addons.js
new file mode 100644
index 0000000..6aed412
--- /dev/null
+++ b/packages/roleypoly-design/.storybook/addons.js
@@ -0,0 +1,2 @@
+import '@storybook/addon-actions/register';
+import '@storybook/addon-links/register';
diff --git a/packages/roleypoly-design/.storybook/config.js b/packages/roleypoly-design/.storybook/config.js
new file mode 100644
index 0000000..1f94f9f
--- /dev/null
+++ b/packages/roleypoly-design/.storybook/config.js
@@ -0,0 +1,8 @@
+import { configure } from '@storybook/react'
+const req = require.context('../src', true, /\.stor\bies|y\b\.[tj]sx?$/)
+
+function loadStories() {
+ req.keys().forEach(req)
+}
+
+configure(loadStories, module)
diff --git a/packages/roleypoly-design/.storybook/webpack.config.js b/packages/roleypoly-design/.storybook/webpack.config.js
new file mode 100644
index 0000000..1609a56
--- /dev/null
+++ b/packages/roleypoly-design/.storybook/webpack.config.js
@@ -0,0 +1,16 @@
+module.exports = ({ config }) => {
+ config.module.rules.push({
+ test: /\.(ts|tsx)$/,
+ use: [
+ {
+ loader: require.resolve('awesome-typescript-loader'),
+ },
+ // Optional
+ {
+ loader: require.resolve('react-docgen-typescript-loader'),
+ },
+ ],
+ });
+ config.resolve.extensions.push('.ts', '.tsx');
+ return config;
+};
\ No newline at end of file
diff --git a/packages/roleypoly-design/.stylelintrc b/packages/roleypoly-design/.stylelintrc
new file mode 100644
index 0000000..7bde962
--- /dev/null
+++ b/packages/roleypoly-design/.stylelintrc
@@ -0,0 +1,9 @@
+{
+ "processors": [
+ "stylelint-processor-styled-components"
+ ],
+ "extends": [
+ "stylelint-config-recommended",
+ "stylelint-config-styled-components"
+ ]
+}
\ No newline at end of file
diff --git a/packages/roleypoly-design/package.json b/packages/roleypoly-design/package.json
new file mode 100644
index 0000000..29fbe5a
--- /dev/null
+++ b/packages/roleypoly-design/package.json
@@ -0,0 +1,42 @@
+{
+ "private": true,
+ "name": "@roleypoly/design",
+ "version": "2.0.0",
+ "dependencies": {
+ "react": "^16.8.6",
+ "react-dom": "^16.8.6",
+ "styled-components": "^4.2.0"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "@babel/core": "^7.4.4",
+ "@storybook/addon-actions": "^5.0.11",
+ "@storybook/addon-info": "^5.0.11",
+ "@storybook/addon-knobs": "^5.0.11",
+ "@storybook/addon-links": "^5.0.11",
+ "@storybook/addons": "^5.0.11",
+ "@storybook/react": "^5.0.11",
+ "@types/jest": "^24.0.13",
+ "@types/node": "^12.0.2",
+ "@types/react": "^16.8.17",
+ "@types/react-dom": "^16.8.4",
+ "@types/storybook__addon-actions": "^3.4.2",
+ "@types/storybook__addon-knobs": "^5.0.0",
+ "@types/storybook__addon-links": "^3.3.4",
+ "@types/storybook__react": "^4.0.1",
+ "@types/styled-components": "4.1.8",
+ "awesome-typescript-loader": "^5.2.1",
+ "babel-loader": "^8.0.6",
+ "react-docgen-typescript-loader": "^3.1.0",
+ "stylelint": "^10.0.1",
+ "tslint": "^5.16.0",
+ "tslint-config-standard": "^8.0.1",
+ "typescript": "^3.4.5"
+ },
+ "scripts": {
+ "storybook": "start-storybook -p 6006",
+ "build-storybook": "build-storybook"
+ }
+}
\ No newline at end of file
diff --git a/packages/roleypoly-design/src/button/Button.story.tsx b/packages/roleypoly-design/src/button/Button.story.tsx
new file mode 100644
index 0000000..eb9bd8d
--- /dev/null
+++ b/packages/roleypoly-design/src/button/Button.story.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react'
+import { storiesOf } from '@storybook/react'
+
+import Button from './Button'
+
+const s = storiesOf('Button', module)
+
+s.add('Default', () => )
+s.add('Disabled', () => )
+s.add('Primary', () => )
+s.add('Secondary', () => )
+s.add('Loading', () => )
\ No newline at end of file
diff --git a/packages/roleypoly-design/src/button/Button.tsx b/packages/roleypoly-design/src/button/Button.tsx
new file mode 100644
index 0000000..4362022
--- /dev/null
+++ b/packages/roleypoly-design/src/button/Button.tsx
@@ -0,0 +1,10 @@
+import * as React from 'react'
+import {
+ StyledButton
+} from './styled-components'
+
+const Button = ({ children, ...rest }: { children: React.ReactChild | React.ReactChild[] }) =>
+ {children}
+
+
+export default Button
diff --git a/packages/roleypoly-design/src/button/index.ts b/packages/roleypoly-design/src/button/index.ts
new file mode 100644
index 0000000..fd8e2db
--- /dev/null
+++ b/packages/roleypoly-design/src/button/index.ts
@@ -0,0 +1,5 @@
+export { default as default } from './Button'
+
+export {
+ StyledButton
+} from './styled-components'
diff --git a/packages/roleypoly-design/src/button/styled-components.tsx b/packages/roleypoly-design/src/button/styled-components.tsx
new file mode 100644
index 0000000..814a08d
--- /dev/null
+++ b/packages/roleypoly-design/src/button/styled-components.tsx
@@ -0,0 +1,58 @@
+// import * as React from 'react'
+import styled from 'styled-components'
+
+export const StyledButton = styled.button`
+ /* reset some styles */
+ box-shadow: none;
+ appearance: none;
+ outline: none;
+ cursor: pointer;
+ background-color: rgba(0,0,0,0.1);
+
+ /* real styles */
+ position: relative;
+ transition: all 0.05s ease-in-out;
+ padding: 1em 1.4em;
+ font-weight: bold;
+ border: 1px solid rgba(0,0,0,0.1);
+
+ &::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+
+ /* transparent by default */
+ background-color: transparent;
+
+ transition: background-color 0.05s ease-in-out;
+
+ /* put the overlay behind the text */
+ z-index: -1;
+ }
+
+ /* on hover, raise, brighten and shadow */
+ &:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
+
+ &::after {
+ background-color: rgba(255, 255, 255, 0.3);
+ }
+ }
+
+ /* on click, lower and darken */
+ &:active {
+ outline: none;
+ box-shadow: none;
+ border-color: rgba(0,0,0,0.2);
+
+ transform: translateY(0);
+
+ &::after {
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ }
+`
diff --git a/packages/roleypoly-design/src/helpers/deep-merge.ts b/packages/roleypoly-design/src/helpers/deep-merge.ts
new file mode 100644
index 0000000..002c3b7
--- /dev/null
+++ b/packages/roleypoly-design/src/helpers/deep-merge.ts
@@ -0,0 +1,34 @@
+/*
+Copyright (c) 2018-2019 Uber Technologies, Inc.
+This source code is licensed under the MIT license found in the
+LICENSE file in the root directory of this source tree.
+*/
+
+export default function deepMerge (
+ target?: {},
+ ...sources: Array
+): {} {
+ target = target || {}
+ const len = sources.length
+ let obj
+ let value
+ for (let i = 0; i < len; i++) {
+ obj = sources[i] || {}
+ for (let key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ value = obj[key]
+ if (isCloneable(value)) {
+ target[key] = deepMerge(target[key] || {}, value)
+ } else {
+ target[key] = value
+ }
+ }
+ }
+ }
+ return target
+}
+
+/* eslint-disable-next-line flowtype/no-weak-types */
+function isCloneable(obj: Object) {
+ return Array.isArray(obj) || {}.toString.call(obj) === '[object Object]'
+}
diff --git a/packages/roleypoly-design/src/helpers/overrides.ts b/packages/roleypoly-design/src/helpers/overrides.ts
new file mode 100644
index 0000000..e0e67f7
--- /dev/null
+++ b/packages/roleypoly-design/src/helpers/overrides.ts
@@ -0,0 +1,151 @@
+/*
+Copyright (c) 2018-2019 Uber Technologies, Inc.
+This source code is licensed under the MIT license found in the
+LICENSE file in the root directory of this source tree.
+*/
+import * as React from 'react'
+import deepMerge from './deep-merge'
+
+export type StyleOverrideT = {} | (({ }) => {} | undefined)
+
+export type OverrideObjectT = {
+ component?: React.ComponentType,
+ props?: {},
+ style?: StyleOverrideT
+}
+
+export type OverrideT = OverrideObjectT | React.ComponentType
+
+export type OverridesT = {
+ [x: string]: OverrideT
+}
+
+/**
+ * Given an override argument, returns the component implementation override if it exists
+ */
+// eslint-disable-next-line flowtype/no-weak-types
+export function getOverride (override: any): any {
+ // Check if override is OverrideObjectT
+ if (override && typeof override === 'object') {
+ // Remove this 'any' once this flow issue is fixed:
+ // https://github.com/facebook/flow/issues/6666
+ // eslint-disable-next-line flowtype/no-weak-types
+ return override.component
+ }
+ // Otherwise it must be a component type (function or class) or null/undefined
+ return override
+}
+
+/**
+ * Given an override argument, returns the override props that should be passed
+ * to the component when rendering it.
+ */
+export function getOverrideProps (override?: OverrideT) {
+ if (override && typeof override === 'object') {
+ return {
+ // $FlowFixMe
+ ...override.props,
+ // $FlowFixMe
+ $style: override.style
+ }
+ }
+ return {}
+}
+
+/**
+ * Coerces an override argument into an override object
+ * (sometimes it is just an override component)
+ */
+export function toObjectOverride (
+ override: OverrideT
+): OverrideObjectT {
+ if (typeof override === 'function') {
+ return {
+ component: (override)
+ }
+ }
+ // Flow can't figure out that typeof 'function' above will
+ // catch React.StatelessFunctionalComponent
+ // (probably related to https://github.com/facebook/flow/issues/6666)
+ // eslint-disable-next-line flowtype/no-weak-types
+ return (((override || {}) as any) as OverrideObjectT)
+}
+
+/**
+ * Get a convenient override array that will always have [component, props]
+ */
+/* eslint-disable flowtype/no-weak-types */
+export function getOverrides (
+ override: any,
+ defaultComponent: React.ComponentType
+): [React.ComponentType, {}] {
+ const component = getOverride(override) || defaultComponent
+ const props = getOverrideProps(override)
+ return [component, props]
+}
+/* eslint-enable flowtype/no-weak-types */
+
+/**
+ * Merges two overrides objects – this is useful if you want to inject your own
+ * overrides into a child component, but also accept further overrides from
+ * from upstream. See `mergeOverride` below.
+ */
+export function mergeOverrides (
+ target: OverridesT | undefined = {},
+ source: OverridesT | undefined = {}
+): OverridesT {
+ const allIdentifiers = Object.keys({ ...target, ...source })
+ return allIdentifiers.reduce((acc, name) => {
+ acc[name] = mergeOverride(
+ toObjectOverride(target[name]),
+ toObjectOverride(source[name])
+ )
+ return acc
+ }, {})
+}
+
+/**
+ * Merges two override objects using the following behavior:
+ * - Component implementation from the source (parent) replaces target
+ * - Props and styles are both deep merged
+ */
+export function mergeOverride (
+ target: OverrideObjectT,
+ source: OverrideObjectT
+): OverrideObjectT {
+ // Shallow merge should handle `component`
+ const merged = { ...target, ...source }
+ // Props just use deep merge
+ if (target.props && source.props) {
+ merged.props = deepMerge({}, target.props, source.props)
+ }
+ // Style overrides need special merging since they may be functions
+ if (target.style && source.style) {
+ merged.style = mergeStyleOverrides(target.style, source.style)
+ }
+ return merged
+}
+
+/**
+ * Since style overrides can be an object *or* a function, we need to handle
+ * the case that one of them is a function. We do this by returning a new
+ * function that deep merges the result of each style override
+ */
+export function mergeStyleOverrides (
+ target: StyleOverrideT,
+ source: StyleOverrideT
+): StyleOverrideT {
+ // Simple case of both objects
+ if (typeof target === 'object' && typeof source === 'object') {
+ return deepMerge({}, target, source)
+ }
+
+ // At least one is a function, return a new composite function
+ return (...args) => {
+ return deepMerge(
+ {},
+ typeof target === 'function' ? target(...args) : target,
+ typeof source === 'function' ? source(...args) : source
+ )
+ }
+}
diff --git a/packages/roleypoly-design/tsconfig.json b/packages/roleypoly-design/tsconfig.json
new file mode 100644
index 0000000..69d92b9
--- /dev/null
+++ b/packages/roleypoly-design/tsconfig.json
@@ -0,0 +1,38 @@
+{
+ "compilerOptions": {
+ "outDir": "./lib",
+ "module": "commonjs",
+ "target": "es2018",
+ "lib": [
+ "es5",
+ "es6",
+ "es7",
+ "es2017",
+ "dom"
+ ],
+ "sourceMap": true,
+ "allowJs": false,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "rootDirs": [
+ "src"
+ ],
+ "baseUrl": "src",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "suppressImplicitAnyIndexErrors": true,
+ "noUnusedLocals": true,
+ "declaration": true,
+ "allowSyntheticDefaultImports": true,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true
+ },
+ "exclude": [
+ "node_modules",
+ "build",
+ "scripts"
+ ]
+}
\ No newline at end of file
diff --git a/packages/roleypoly-design/tslint.json b/packages/roleypoly-design/tslint.json
new file mode 100644
index 0000000..7967051
--- /dev/null
+++ b/packages/roleypoly-design/tslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": "tslint-config-standard"
+}
\ No newline at end of file