├── .babelrc
├── .gitignore
├── package-lock.json
├── package.json
├── readme.md
└── src
├── assets
└── logo_dube.png
├── components
├── Button.jsx
├── Card.jsx
├── Container.jsx
├── Flex.jsx
├── Heading.jsx
└── Loader.jsx
├── index.html
├── index.js
└── workspace
├── Application
├── GlobalStyle.jsx
├── Header.jsx
├── Routes.jsx
└── index.jsx
└── Components
├── Button.jsx
├── Card.jsx
├── Container.jsx
├── Flex.jsx
├── Heading.jsx
└── index.jsx
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"],
3 | "plugins": ["react-hot-loader/babel", "babel-plugin-styled-components"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .git/
3 | .cache/
4 | dist/
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "component-system",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel src/index.html"
8 | },
9 | "author": "Lukas Gisder-Dubé",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "@babel/core": "^7.2.2",
13 | "@babel/preset-env": "^7.2.3",
14 | "@babel/preset-react": "^7.0.0",
15 | "babel-plugin-styled-components": "^1.10.0",
16 | "parcel-bundler": "^1.11.0",
17 | "react-hot-loader": "^4.6.3"
18 | },
19 | "dependencies": {
20 | "faker": "^4.1.0",
21 | "react": "^16.7.0",
22 | "react-dom": "^16.7.0",
23 | "react-router": "^4.3.1",
24 | "react-router-dom": "^4.3.1",
25 | "styled-components": "^4.1.3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # React Component System
2 |
3 | This repository contains the code for the [accompanying article](https://levelup.gitconnected.com/building-a-reusable-component-system-with-react-js-and-styled-components-4e9f1018a31c)
4 |
5 | To run:
6 |
7 | 1. `npm install`
8 | 2. `npm run start`
9 |
--------------------------------------------------------------------------------
/src/assets/logo_dube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gisderdube/react-component-system/7f7fbf2ac1c99cef0353d0dc6d61d58f76708d2c/src/assets/logo_dube.png
--------------------------------------------------------------------------------
/src/components/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled, { css } from 'styled-components'
3 | import Loader from './Loader'
4 |
5 | const StyledButton = styled.button`
6 | border-radius: 5px;
7 | background-color: ${props => (props.secondary ? '#F7A072' : '#a1cdf1')};
8 | color: #fff;
9 | padding: 10px 15px;
10 | font-size: ${props => {
11 | if (props.big) return '20px'
12 | return '16px'
13 | }};
14 | outline: none;
15 | border: none;
16 | cursor: pointer;
17 | margin: 15px;
18 | border: 2px solid ${props => (props.secondary ? '#F7A072' : '#a1cdf1')};
19 |
20 | ${props => {
21 | return (
22 | props.inverse &&
23 | css`
24 | background-color: #fff;
25 | color: #a1cdf1;
26 | `
27 | )
28 | }}
29 | `
30 |
31 | const Button = ({ secondary, big, inverse, loading, children, ...props }) => {
32 | return (
33 |
34 | {loading ? : children}
35 |
36 | )
37 | }
38 |
39 | export default Button
40 |
--------------------------------------------------------------------------------
/src/components/Card.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled, { css, keyframes } from 'styled-components'
3 |
4 | const animatedCss = css`
5 | opacity: 1;
6 | transform: translateY(0);
7 | `
8 |
9 | const primaryCss = css`
10 | background-color: #008bf8;
11 | color: #fff;
12 | `
13 |
14 | const StyledCard = styled.div`
15 | width: ${props => (props.big ? '450px' : '300px')};
16 | padding: 15px;
17 | opacity: 0;
18 | transform: translateY(50px);
19 | transition: 500ms all ease-in-out;
20 | margin: ${props => (props.noMargin ? 0 : '15px')};
21 | box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 1);
22 | border-radius: 5px;
23 |
24 | ${props => props.animated && animatedCss}
25 | ${props => props.primary && primaryCss}
26 | `
27 |
28 | class Card extends React.Component {
29 | constructor(props) {
30 | super(props)
31 | this.state = {
32 | animated: false,
33 | }
34 | }
35 |
36 | componentDidMount() {
37 | setTimeout(() => {
38 | this.setState(() => {
39 | return { animated: true }
40 | })
41 | }, this.props.delay)
42 | }
43 |
44 | render() {
45 | const { delay = 0, noAnimation, primary, noMargin, big, ...props } = this.props
46 | return (
47 |
56 | )
57 | }
58 | }
59 |
60 | export default Card
61 |
--------------------------------------------------------------------------------
/src/components/Container.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | padding-left: ${props => {
6 | if (props.full) return 0
7 | return 'calc((100vw - 960px) / 2)'
8 | }};
9 |
10 | padding-right: ${props => {
11 | if (props.full) return 0
12 | return 'calc((100vw - 960px) / 2)'
13 | }};
14 |
15 | padding-top: ${props => {
16 | if (props.fullVertical) return 0
17 | if (props.small) return '15px'
18 | return '25px'
19 | }};
20 |
21 | padding-bottom: ${props => {
22 | if (props.fullVertical) return 0
23 | if (props.small) return '15px'
24 | return '25px'
25 | }};
26 | `
27 |
28 | export default Container
29 |
--------------------------------------------------------------------------------
/src/components/Flex.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Flex = styled.div`
5 | display: flex;
6 | flex-wrap: ${props => {
7 | if (props.wrapReverse) return 'wrap-reverse'
8 | else if (props.noWrap) return 'nowrap'
9 | return 'wrap'
10 | }};
11 | justify-content: ${props => {
12 | if (props.justifyContent) return props.justifyContent
13 | if (props.justifyCenter) return 'center'
14 | else if (props.justifyAround) return 'space-around'
15 | else if (props.justifyBetween) return 'space-between'
16 | else if (props.justifyEnd) return 'flex-end'
17 | return 'flex-start'
18 | }};
19 | align-items: ${props => {
20 | if (props.alignItems) return props.alignItems
21 | else if (props.alignStretch) return 'stretch'
22 | else if (props.alignEnd) return 'flex-end'
23 | if (props.alignCenter) return 'center'
24 | else if (props.alignBaseline) return 'baseline'
25 | return 'flex-start'
26 | }};
27 | align-content: ${props => {
28 | if (props.alignContent) return props.content
29 | else if (props.contentStart) return 'flex-start'
30 | else if (props.contentEnd) return 'flex-end'
31 | else if (props.contentCenter) return 'center'
32 | else if (props.contentBetween) return 'space-between'
33 | else if (props.contentAround) return 'contentAround'
34 | return 'stretch'
35 | }};
36 | flex-direction: ${props => (props.column ? 'column' : 'row')};
37 | `
38 |
39 | export const Column = styled.div`
40 | width: ${props => {
41 | if (props.three) return '33.33%'
42 | if (props.four) return '25%'
43 | return '50%'
44 | }};
45 | padding: ${props => (props.noPadding ? 0 : '15px')}};
46 | `
47 |
48 | export default Flex
49 |
--------------------------------------------------------------------------------
/src/components/Heading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled, { css } from 'styled-components'
3 |
4 | const baseStyle = css`
5 | margin-bottom: ${props => props.noMargin && '0'};
6 | color: #202020;
7 | font-family: 'Poppins', sans-serif;
8 | font-weight: 600;
9 | margin-top: 0;
10 | text-align: ${props => {
11 | if (props.center) return 'center'
12 | if (props.right) return 'right'
13 | return 'left'
14 | }};
15 |
16 | max-width: 100%;
17 | `
18 |
19 | const HeadingOne = styled.h1`
20 | font-size: 42px;
21 | font-weight: bold;
22 | margin-bottom: 25px;
23 | ${baseStyle};
24 |
25 | @media (max-width: 480px) {
26 | font-size: 40px;
27 | }
28 | `
29 |
30 | const HeadingTwo = styled.h2`
31 | font-size: 36px;
32 | font-weight: bold;
33 | margin-bottom: 20px;
34 | ${baseStyle};
35 | `
36 |
37 | const HeadingThree = styled.h3`
38 | font-size: 28px;
39 | font-weight: bold;
40 | margin-bottom: 15px;
41 | ${baseStyle};
42 | `
43 |
44 | const HeadingFour = styled.h4`
45 | font-size: 22px;
46 | font-weight: bold;
47 | margin-bottom: 10px;
48 | ${baseStyle};
49 | `
50 |
51 | const HeadingFive = styled.h5`
52 | font-size: 18px;
53 | font-weight: bold;
54 | margin-bottom: 5px;
55 | ${baseStyle};
56 | `
57 |
58 | const Heading = ({ h2, h3, h4, h5, noMargin, right, center, ...props }) => {
59 | if (h2) return
60 | if (h3) return
61 | if (h4) return
62 | if (h5) return
63 | return
64 | }
65 |
66 | export default Heading
67 |
--------------------------------------------------------------------------------
/src/components/Loader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled, { keyframes } from 'styled-components'
3 |
4 | const Bounce = keyframes`
5 | 0%, 80%, 100% {
6 | transform: scale(0);
7 | } 40% {
8 | transform: scale(1.0);
9 | }
10 | `
11 |
12 | const StyledDotsLoader = styled.div`
13 | display: inline-block;
14 | `
15 |
16 | const Dot = styled.span`
17 | width: ${props => (props.big ? '20px' : '12px')};
18 | height: ${props => (props.big ? '20px' : '12px')};
19 | background-color: ${props => (props.white ? '#FFF' : '#000')};
20 | border-radius: 100%;
21 | display: inline-block;
22 | animation: ${Bounce} 1s infinite ease-in-out both;
23 | &:first-child {
24 | animation-delay: -0.32s;
25 | }
26 |
27 | &:nth-child(2) {
28 | animation-delay: -0.16s;
29 | }
30 | `
31 |
32 | const Loader = props => {
33 | return (
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | export default Loader
43 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | React Component System by dube.io
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | import Application from './workspace/Application'
5 |
6 | ReactDOM.render( , document.getElementById('app'))
7 |
--------------------------------------------------------------------------------
/src/workspace/Application/GlobalStyle.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createGlobalStyle } from 'styled-components'
3 |
4 | const GlobalStyle = createGlobalStyle`
5 | * {
6 | box-sizing: border-box;
7 | }
8 |
9 | html, body {
10 | margin: 0;
11 | font-size: 20px;
12 | font-family: 'Work Sans', sans-serif;
13 | font-weight: 300;
14 | }
15 | `
16 |
17 | export default GlobalStyle
18 |
--------------------------------------------------------------------------------
/src/workspace/Application/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import styled from 'styled-components'
4 | import Container from '../../components/Container'
5 |
6 | const StyledHeader = styled(Container)`
7 | display: flex;
8 | justify-content: space-between;
9 | align-items: center;
10 | `
11 |
12 | const StyledLink = styled(Link)`
13 | color: #04e762;
14 | font-weight: 500;
15 | text-decoration: none;
16 | font-family: 'Work Sans', sans-serif;
17 | display: inline-block;
18 | margin-right: 25px;
19 | `
20 |
21 | const Header = () => {
22 | return (
23 |
24 |
25 |
26 | Components
27 |
28 |
29 |
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | export default Header
37 |
--------------------------------------------------------------------------------
/src/workspace/Application/Routes.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, Switch } from 'react-router-dom'
3 | import Components from '../Components'
4 |
5 | class Routes extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 | }
14 |
15 | export default Routes
16 |
--------------------------------------------------------------------------------
/src/workspace/Application/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Routes from './Routes'
3 | import Header from './Header'
4 | import GlobalStyle from './GlobalStyle'
5 | import { BrowserRouter } from 'react-router-dom'
6 |
7 | const Application = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default Application
20 |
--------------------------------------------------------------------------------
/src/workspace/Components/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Container from '../../components/Container'
3 | import Heading from '../../components/Heading'
4 | import Button from '../../components/Button'
5 |
6 | const ButtonSample = () => {
7 | return (
8 |
9 |
10 | Button
11 |
12 |
13 | Primary Button
14 | Secondary Button
15 | Inverse Button
16 | Big Primary Button
17 | Primary Loading Button
18 |
19 |
20 | )
21 | }
22 |
23 | export default ButtonSample
24 |
--------------------------------------------------------------------------------
/src/workspace/Components/Card.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import faker from 'faker'
3 | import Container from '../../components/Container'
4 | import Heading from '../../components/Heading'
5 | import Flex from '../../components/Flex'
6 | import Card from '../../components/Card'
7 |
8 | const randomCards = []
9 |
10 | for (let i = 0; i < 9; i++) {
11 | randomCards.push({
12 | title: faker.lorem.words(2),
13 | copy: faker.lorem.sentences(3),
14 | })
15 | }
16 |
17 | const CardSample = () => {
18 | return (
19 |
20 |
21 | Card
22 |
23 |
24 |
25 |
26 | Normal Card
27 |
28 | Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi
29 | porta gravida at eget metus. Donec sed odio dui.
30 |
31 |
32 |
33 | Big Card
34 |
35 | Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed
36 | odio dui. Donec id elit non mi porta gravida at eget metus.
37 |
38 |
39 |
40 |
41 | Primary Delayed Card
42 |
43 |
44 | Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi
45 | porta gravida at eget metus. Donec sed odio dui.
46 |
47 |
48 |
49 |
50 |
51 | Card List
52 |
53 | {randomCards.map((el, index) => {
54 | return (
55 |
56 |
57 | {el.title}
58 |
59 | {el.copy}
60 |
61 | )
62 | })}
63 |
64 |
65 |
66 | )
67 | }
68 |
69 | export default CardSample
70 |
--------------------------------------------------------------------------------
/src/workspace/Components/Container.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Container from '../../components/Container'
3 | import Heading from '../../components/Heading'
4 |
5 | const ContainerSample = () => {
6 | return (
7 |
8 |
9 | Container
10 |
11 |
12 |
13 | Default Container
14 |
15 |
16 |
17 |
18 | Full Vertical Container
19 |
20 |
21 |
22 |
23 | Full Small Container
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default ContainerSample
31 |
--------------------------------------------------------------------------------
/src/workspace/Components/Flex.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Container from '../../components/Container'
3 | import Heading from '../../components/Heading'
4 | import Flex, { Column } from '../../components/Flex'
5 |
6 | const FlexSample = () => {
7 | return (
8 |
9 |
10 | Flex & Column
11 |
12 |
13 |
14 |
15 |
23 |
31 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | )
61 | }
62 |
63 | export default FlexSample
64 |
--------------------------------------------------------------------------------
/src/workspace/Components/Heading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Container from '../../components/Container'
3 | import Heading from '../../components/Heading'
4 |
5 | const CardSample = () => {
6 | return (
7 |
8 |
9 | Heading
10 |
11 |
12 | h1 - Heading 1
13 | h2 - Heading 2
14 | h3 - Heading 3
15 | h4 - Heading 4
16 | h5 - Heading 5
17 |
18 |
19 | )
20 | }
21 |
22 | export default CardSample
23 |
--------------------------------------------------------------------------------
/src/workspace/Components/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Container from '../../components/Container'
3 | import ContainerSample from './Container'
4 | import Flex from './Flex'
5 | import Button from './Button'
6 | import Card from './Card'
7 | import Heading from './Heading'
8 |
9 | const Components = () => {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default Components
24 |
--------------------------------------------------------------------------------