├── .env ├── .gitignore ├── package.json ├── public ├── images │ ├── despicable-me.jpg │ ├── joker.jpg │ └── the-prestige.jpg └── index.html ├── src ├── App.js ├── App.scss ├── components │ └── Card │ │ ├── Card.js │ │ ├── index.js │ │ └── styles │ │ └── Card.js ├── data.json └── index.js └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compound-components", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "classnames": "^2.2.6", 10 | "node-sass": "^4.13.0", 11 | "react": "^16.12.0", 12 | "react-dom": "^16.12.0", 13 | "react-scripts": "3.3.0", 14 | "styled-components": "^4.4.1" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/images/despicable-me.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlhadwen/compound-components/379dab5cd3f02c09096043d147b6654524332e14/public/images/despicable-me.jpg -------------------------------------------------------------------------------- /public/images/joker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlhadwen/compound-components/379dab5cd3f02c09096043d147b6654524332e14/public/images/joker.jpg -------------------------------------------------------------------------------- /public/images/the-prestige.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlhadwen/compound-components/379dab5cd3f02c09096043d147b6654524332e14/public/images/the-prestige.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Compound Components 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.scss'; 3 | import { Card } from './components/Card'; 4 | import movies from './data'; 5 | 6 | function App() { 7 | return ( 8 |
9 | {movies.map(movie => ( 10 | 11 | 12 | 13 | {movie.title} 14 | {movie.desc} 15 | {movie.ctaText} 16 | 17 | 18 | ))} 19 |
20 | ); 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /src/App.scss: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | COMPOUND COMPONENTS 3 | ========================================================================== */ 4 | @import url('https://fonts.googleapis.com/css?family=Roboto:400,700,900'); 5 | 6 | /* RESETS 7 | ============================================ */ 8 | 9 | html { 10 | -webkit-box-sizing: border-box; 11 | box-sizing: border-box; 12 | } 13 | *, *:before, *:after { 14 | -webkit-box-sizing: inherit; 15 | box-sizing: inherit; 16 | } 17 | 18 | body { 19 | margin: 20px 0; 20 | padding: 0; 21 | line-height: 1; 22 | font-family: 'Roboto', sans-serif; 23 | color: #202020; 24 | background-color: #fbfbfb; 25 | font-smooth: always; 26 | -webkit-font-smoothing: antialiased; 27 | -moz-osx-font-smoothing: grayscale; 28 | } 29 | 30 | main { 31 | display: flex; 32 | max-width: 1200px; 33 | margin: auto; 34 | justify-content: center; 35 | } 36 | 37 | .mr { 38 | margin-right: 20px; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/Card/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import { Container, Body, Title, Text, Image, Button } from './styles/Card'; 4 | 5 | export function Card({ classes, children, ...restProps }) { 6 | return ( 7 | 8 | {children} 9 | 10 | ); 11 | } 12 | 13 | Card.Body = function CardBody({ classes, children, ...restProps }) { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | Card.Title = function CardTitle({ classes, children, ...restProps }) { 22 | return ( 23 | 24 | {children} 25 | 26 | ); 27 | }; 28 | 29 | Card.Text = function CardText({ classes, children, ...restProps }) { 30 | return ( 31 | 32 | {children} 33 | 34 | ); 35 | }; 36 | 37 | Card.Image = function CardImage({ src, alt, classes, ...restProps }) { 38 | return ( 39 | {alt} 45 | ); 46 | }; 47 | 48 | Card.Button = function CardButton({ classes, children, ...restProps }) { 49 | return ( 50 | 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /src/components/Card/index.js: -------------------------------------------------------------------------------- 1 | import { Card } from './Card'; 2 | 3 | export { Card }; 4 | -------------------------------------------------------------------------------- /src/components/Card/styles/Card.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | min-width: 0; 7 | max-width: 250px; 8 | word-wrap: break-word; 9 | background-color: #fff; 10 | background-clip: border-box; 11 | border: 1px solid rgba(0, 0, 0, 0.125); 12 | border-radius: 0.25rem; 13 | box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); 14 | overflow: hidden; 15 | `; 16 | 17 | export const Body = styled.div` 18 | display: flex; 19 | flex-direction: column; 20 | flex: 1 1 auto; 21 | min-height: 1px; 22 | padding: 1.25rem; 23 | `; 24 | 25 | export const Title = styled.h1` 26 | font-size: 1.25rem; 27 | font-weight: bold; 28 | margin: 0; 29 | `; 30 | 31 | export const Text = styled.p` 32 | line-height: 25px; 33 | color: #6c757d; 34 | `; 35 | 36 | export const Image = styled.img` 37 | width: 100%; 38 | `; 39 | 40 | export const Button = styled.button` 41 | cursor: pointer; 42 | margin-top: auto; 43 | width: 100%; 44 | color: #fff; 45 | background-color: #28a745; 46 | border-color: #28a745; 47 | display: block; 48 | font-weight: bold; 49 | text-align: center; 50 | vertical-align: middle; 51 | user-select: none; 52 | border: 1px solid transparent; 53 | padding: 0.375rem 0.75rem; 54 | font-size: 1rem; 55 | line-height: 1.5; 56 | border-radius: 0.25rem; 57 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, 58 | border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 59 | 60 | &:hover { 61 | color: #fff; 62 | background-color: #218838; 63 | border-color: #1e7e34; 64 | } 65 | `; 66 | -------------------------------------------------------------------------------- /src/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "title": "Joker", 5 | "image": "/images/joker.jpg", 6 | "desc": "Forever alone in a crowd, failed comedian Arthur Fleck seeks connection as he walks the streets of Gotham City. Arthur wears two masks -- the one he paints for his day job as a clown.", 7 | "alt": "Joker Movie", 8 | "ctaText": "Watch", 9 | "rating": "9.2" 10 | }, 11 | { 12 | "id": 2, 13 | "title": "The Prestige", 14 | "image": "/images/the-prestige.jpg", 15 | "desc": "Two friends and fellow magicians become bitter enemies after a sudden tragedy. They make sacrifices that bring them fame but with terrible consequences.", 16 | "alt": "Joker Movie", 17 | "ctaText": "Watch", 18 | "rating": "7.9" 19 | }, 20 | { 21 | "id": 3, 22 | "title": "Despicable Me", 23 | "image": "/images/despicable-me.jpg", 24 | "desc": "Gru, a criminal mastermind, adopts three orphans as pawns to carry out the biggest heist in history. His life takes an unexpected turn when the little girls see him as their potential father.", 25 | "alt": "Despicable Me Movie", 26 | "ctaText": "Watch", 27 | "rating": "8.6" 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | --------------------------------------------------------------------------------