├── src ├── react-app-env.d.ts ├── types.d.ts ├── components │ ├── sandbox.tsx │ ├── modal.tsx │ ├── customize.tsx │ ├── thumbnail.tsx │ ├── grid.tsx │ ├── home.tsx │ ├── formats.tsx │ ├── complete.tsx │ ├── review.tsx │ ├── details.tsx │ ├── header.tsx │ └── carousel.tsx ├── index.tsx ├── data.ts └── styles.scss ├── public ├── robots.txt ├── banner.png ├── favicon.ico ├── logo192.png ├── logo512.png ├── images │ ├── 1.jpg │ ├── 10.jpg │ ├── 11.jpg │ ├── 12.jpg │ ├── 13.jpg │ ├── 14.jpg │ ├── 15.jpg │ ├── 16.jpg │ ├── 17.jpg │ ├── 18.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.jpg │ └── 9.jpg ├── chevron-left.png ├── chevron-right.png ├── manifest.json ├── index.html ├── eye.svg └── star.svg ├── .prettierrc ├── .gitignore ├── README.md ├── tsconfig.json └── package.json /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/banner.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/1.jpg -------------------------------------------------------------------------------- /public/images/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/10.jpg -------------------------------------------------------------------------------- /public/images/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/11.jpg -------------------------------------------------------------------------------- /public/images/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/12.jpg -------------------------------------------------------------------------------- /public/images/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/13.jpg -------------------------------------------------------------------------------- /public/images/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/14.jpg -------------------------------------------------------------------------------- /public/images/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/15.jpg -------------------------------------------------------------------------------- /public/images/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/16.jpg -------------------------------------------------------------------------------- /public/images/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/17.jpg -------------------------------------------------------------------------------- /public/images/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/18.jpg -------------------------------------------------------------------------------- /public/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/2.jpg -------------------------------------------------------------------------------- /public/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/3.jpg -------------------------------------------------------------------------------- /public/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/4.jpg -------------------------------------------------------------------------------- /public/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/5.jpg -------------------------------------------------------------------------------- /public/images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/6.jpg -------------------------------------------------------------------------------- /public/images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/7.jpg -------------------------------------------------------------------------------- /public/images/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/8.jpg -------------------------------------------------------------------------------- /public/images/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/images/9.jpg -------------------------------------------------------------------------------- /public/chevron-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/chevron-left.png -------------------------------------------------------------------------------- /public/chevron-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koss-lebedev/nordic-pixels/HEAD/public/chevron-right.png -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare type Picture = { 2 | url: string 3 | views: number 4 | rating: number 5 | } 6 | 7 | declare type OrderField = 'views' | 'rating' 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "printWidth": 70 8 | } 9 | -------------------------------------------------------------------------------- /src/components/sandbox.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Sandbox = () => { 4 | return ( 5 |
6 |
7 |
8 | ) 9 | } 10 | 11 | export default Sandbox 12 | -------------------------------------------------------------------------------- /src/components/modal.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react' 2 | 3 | type Props = { 4 | onClose: () => void 5 | } 6 | 7 | const Modal: FC = ({ children, onClose }) => { 8 | return ( 9 | <> 10 |
onClose()} /> 11 |
{children}
12 | 13 | ) 14 | } 15 | 16 | export default Modal 17 | -------------------------------------------------------------------------------- /.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 | 25 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Framer-motion course 4 | 5 |

6 | 7 |

8 | NordicPixels 9 |

10 | 11 | This is a starter application for [framer-motion course](https://octocourses.com/framer-motion/). 12 | 13 | ### Getting started 14 | 15 | To get started, run: 16 | 17 | yarn 18 | yarn start 19 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { Route } from 'wouter' 4 | import Home from './components/home' 5 | import Sandbox from './components/sandbox' 6 | import './styles.scss' 7 | 8 | const App = () => ( 9 | <> 10 | 11 | 12 | 13 | ) 14 | 15 | ReactDOM.render(, document.getElementById('root')) 16 | -------------------------------------------------------------------------------- /src/components/customize.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Carousel from './carousel' 3 | 4 | type Props = { 5 | onComplete: () => void 6 | picture: Picture 7 | } 8 | 9 | const Customize = ({ onComplete, picture }: Props) => { 10 | return ( 11 |
12 | 13 | 16 |
17 | ) 18 | } 19 | 20 | export default Customize 21 | -------------------------------------------------------------------------------- /src/components/thumbnail.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | type Props = { 4 | picture: Picture 5 | onClick: (picture: Picture) => void 6 | } 7 | 8 | const Thumbnail = ({ picture, onClick }: Props) => { 9 | return ( 10 |
onClick(picture)}> 11 |
17 |
18 | ) 19 | } 20 | 21 | export default Thumbnail 22 | -------------------------------------------------------------------------------- /src/components/grid.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Thumbnail from './thumbnail' 3 | 4 | type Props = { 5 | pictures: readonly Picture[] 6 | onPictureClick: (picture: Picture) => void 7 | } 8 | 9 | const Grid = ({ pictures, onPictureClick }: Props) => { 10 | return ( 11 |
12 | {pictures.map(picture => ( 13 | 18 | ))} 19 |
20 | ) 21 | } 22 | 23 | export default Grid 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | NordicPixels 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import sortBy from 'lodash.sortby' 3 | import Header from './header' 4 | import Grid from './grid' 5 | import Details from './details' 6 | import pictures from '../data' 7 | 8 | const Home = () => { 9 | const [order, setOrder] = useState('rating') 10 | const [selected, setSelected] = useState(null) 11 | 12 | return ( 13 |
14 |
15 | 19 | {selected && ( 20 |
setSelected(null)} 22 | picture={selected} 23 | /> 24 | )} 25 |
26 | ) 27 | } 28 | 29 | export default Home 30 | -------------------------------------------------------------------------------- /src/components/formats.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | type Props = { 4 | src: string 5 | } 6 | 7 | const Square = ({ src }: Props) => ( 8 |
16 | ) 17 | 18 | const Portrait = ({ src }: Props) => ( 19 |
27 | ) 28 | 29 | const Landscape = ({ src }: Props) => ( 30 |
38 | ) 39 | 40 | export default { 41 | Square, 42 | Portrait, 43 | Landscape, 44 | } 45 | -------------------------------------------------------------------------------- /src/components/complete.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | type Props = { 4 | onComplete: () => void 5 | } 6 | 7 | const Complete = ({ onComplete }: Props) => { 8 | return ( 9 |
10 | 11 | 12 | 20 | 27 | 28 | 29 | 30 |

31 | Thank you for
your order 32 |

33 |
34 | ) 35 | } 36 | 37 | export default Complete 38 | -------------------------------------------------------------------------------- /src/components/review.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | type Props = { 4 | onComplete: () => void 5 | } 6 | 7 | const props: ReadonlyArray<{ name: string; value: string }> = [ 8 | { name: 'SKU', value: 'Printed photo' }, 9 | { name: 'Delivery type', value: 'expedited' }, 10 | { name: 'Price', value: '$5.99' }, 11 | ] 12 | 13 | const Review = ({ onComplete }: Props) => { 14 | return ( 15 |
16 |
17 |

Review your order

18 |
    19 | {props.map(prop => ( 20 |
  • 21 | {prop.name} 22 | {prop.value} 23 |
  • 24 | ))} 25 |
26 |
27 | 30 |
31 | ) 32 | } 33 | 34 | export default Review 35 | -------------------------------------------------------------------------------- /src/components/details.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import Modal from './modal' 3 | import Customize from './customize' 4 | import Review from './review' 5 | import Complete from './complete' 6 | 7 | type Props = { 8 | picture: Picture 9 | onClose: () => void 10 | } 11 | 12 | type Step = 'customize' | 'review' | 'complete' 13 | 14 | const Details = ({ onClose, picture }: Props) => { 15 | const [step, setStep] = useState('customize') 16 | 17 | return ( 18 | 19 | {step === 'customize' && ( 20 | setStep('review')} 23 | /> 24 | )} 25 | {step === 'review' && ( 26 | setStep('complete')} /> 27 | )} 28 | {step === 'complete' && } 29 | 30 | ) 31 | } 32 | 33 | export default Details 34 | -------------------------------------------------------------------------------- /src/components/header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | type Props = { 4 | order: OrderField 5 | onOrderChange: (newOrder: OrderField) => void 6 | } 7 | 8 | const Header = ({ onOrderChange, order }: Props) => { 9 | return ( 10 |
11 |
12 |
13 | NordicPixels 14 |
15 |
16 | 24 | 32 |
33 |
34 |
35 | ) 36 | } 37 | 38 | export default Header 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nordic-pixels", 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 | "@types/jest": "^24.0.0", 10 | "@types/node": "^12.0.0", 11 | "@types/react": "^16.9.0", 12 | "@types/react-dom": "^16.9.0", 13 | "lodash.sortby": "^4.7.0", 14 | "node-sass": "^4.13.0", 15 | "react": "^16.12.0", 16 | "react-dom": "^16.12.0", 17 | "react-scripts": "3.4.0", 18 | "typescript": "~3.7.2", 19 | "wouter": "^2.4.0" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": "react-app" 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "@types/lodash.sortby": "^4.7.6" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /public/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/data.ts: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Photos from https://unsplash.com by 4 | Justin Kauffman, Alexander Sinn, Reiseuhu, Carl Cerstrand, Simon Migaj, 5 | Jelle van Leest, Torbjorn Sandbakk, Kym Ellis, Callum Stewart, Kym Ellis, 6 | Mikita Karasiou, John O'Nolan, Yuriy Garnaev, Geran de Klerk 7 | 8 | */ 9 | 10 | const pictures: readonly Picture[] = [ 11 | { url: '/images/1.jpg', views: 403, rating: 3.7 }, 12 | { url: '/images/2.jpg', views: 435, rating: 3.9 }, 13 | { url: '/images/3.jpg', views: 408, rating: 4.0 }, 14 | { url: '/images/4.jpg', views: 379, rating: 3.2 }, 15 | { url: '/images/5.jpg', views: 579, rating: 4.7 }, 16 | { url: '/images/6.jpg', views: 429, rating: 4.1 }, 17 | { url: '/images/7.jpg', views: 508, rating: 4.5 }, 18 | { url: '/images/8.jpg', views: 485, rating: 3.0 }, 19 | { url: '/images/9.jpg', views: 351, rating: 4.9 }, 20 | { url: '/images/10.jpg', views: 302, rating: 3.7 }, 21 | { url: '/images/11.jpg', views: 469, rating: 5.0 }, 22 | { url: '/images/12.jpg', views: 367, rating: 4.5 }, 23 | { url: '/images/13.jpg', views: 558, rating: 5.0 }, 24 | { url: '/images/14.jpg', views: 582, rating: 4.2 }, 25 | { url: '/images/15.jpg', views: 449, rating: 3.8 }, 26 | { url: '/images/16.jpg', views: 431, rating: 3.8 }, 27 | { url: '/images/17.jpg', views: 571, rating: 4.4 }, 28 | { url: '/images/18.jpg', views: 514, rating: 4.8 }, 29 | ] 30 | 31 | export default pictures 32 | -------------------------------------------------------------------------------- /public/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/components/carousel.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import Formats from './formats' 3 | 4 | type Props = { 5 | picture: Picture 6 | } 7 | 8 | const wrap = (index: number, total: number) => { 9 | if (index < 0) { 10 | return total - 1 11 | } 12 | if (index === total) { 13 | return 0 14 | } 15 | return index 16 | } 17 | 18 | const options = [ 19 | { 20 | label: 'Square (1:1)', 21 | Component: Formats.Square, 22 | }, 23 | { 24 | label: 'Portrait (3:4)', 25 | Component: Formats.Portrait, 26 | }, 27 | { 28 | label: 'Landscape (7:4)', 29 | Component: Formats.Landscape, 30 | }, 31 | ] 32 | 33 | const Carousel = ({ picture }: Props) => { 34 | const [index, setIndex] = useState(0) 35 | 36 | const paginate = (newDirection: number) => { 37 | const newIndex = wrap(index + newDirection, options.length) 38 | setIndex(newIndex) 39 | } 40 | 41 | const Option = options[index] 42 | 43 | return ( 44 |
45 |

Choose your format

46 |
47 |
48 | 49 |
50 |
59 |
60 | {options[index].label} 61 |
62 |
63 | ) 64 | } 65 | 66 | export default Carousel 67 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | @mixin mq-small { 2 | @media (max-width: 52em) { 3 | @content; 4 | } 5 | } 6 | 7 | // GLOBALS 8 | 9 | html { 10 | font-size: 62.5%; 11 | } 12 | 13 | * { 14 | box-sizing: border-box; 15 | } 16 | 17 | body { 18 | margin: 0; 19 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 20 | 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 21 | 'Helvetica Neue', sans-serif; 22 | -webkit-font-smoothing: antialiased; 23 | -moz-osx-font-smoothing: grayscale; 24 | background: #f7f8fb; 25 | -webkit-touch-callout: none; 26 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 27 | color: #434343; 28 | 29 | @include mq-small { 30 | user-select: none; 31 | } 32 | } 33 | 34 | // COMPONENTS 35 | 36 | .button { 37 | font-size: 1.6rem; 38 | background: #3b96fa; 39 | border: none; 40 | border-radius: 5rem; 41 | color: #fff; 42 | padding: 2rem 3rem; 43 | box-shadow: 0 0.2rem 0.8rem -0.1rem rgba(39, 94, 254, 0.32); 44 | cursor: pointer; 45 | outline: none; 46 | font-weight: bold; 47 | align-self: end; 48 | margin: 2rem 4rem 4rem 4rem; 49 | width: -webkit-fill-available; 50 | 51 | &:hover { 52 | box-shadow: 0 0.4rem 2rem -0.2rem rgba(39, 94, 254, 0.5); 53 | } 54 | } 55 | 56 | .overlay { 57 | background: rgba(4, 15, 39, 0.8); 58 | width: 100%; 59 | height: 100vh; 60 | position: fixed; 61 | top: 0; 62 | left: 0; 63 | z-index: 3; 64 | backdrop-filter: blur(0.4rem); 65 | } 66 | 67 | .modal-panel { 68 | background: white; 69 | position: fixed; 70 | width: 40rem; 71 | top: 10rem; 72 | left: 0; 73 | right: 0; 74 | margin: 0 auto; 75 | z-index: 4; 76 | min-height: 20rem; 77 | border-radius: 1rem; 78 | box-shadow: 0 1rem 2rem 0 rgba(0, 0, 0, 0.2); 79 | user-select: none; 80 | cursor: pointer; 81 | 82 | &:active { 83 | cursor: grab; 84 | } 85 | 86 | @include mq-small { 87 | width: 100%; 88 | height: 80%; 89 | bottom: 0; 90 | top: unset; 91 | position: fixed; 92 | overflow: auto; 93 | border-radius: 1rem 1rem 0 0; 94 | box-shadow: none; 95 | } 96 | } 97 | 98 | .carousel { 99 | background: #e1e1e1; 100 | height: 30rem; 101 | position: relative; 102 | overflow: hidden; 103 | 104 | &-item { 105 | position: absolute; 106 | top: 0; 107 | left: 0; 108 | right: 0; 109 | bottom: 0; 110 | display: flex; 111 | align-items: center; 112 | justify-content: center; 113 | } 114 | 115 | &-label { 116 | background: white; 117 | border-radius: 0.5rem; 118 | padding: 1rem; 119 | box-shadow: 0 0 0.5rem 0 rgba(95, 95, 95, 0.3); 120 | font-size: 1.4rem; 121 | font-weight: bold; 122 | margin: 0 auto; 123 | width: 15rem; 124 | margin-top: -1.5rem; 125 | text-align: center; 126 | color: #4c4c4c; 127 | overflow: hidden; 128 | position: relative; 129 | } 130 | 131 | &-arrow { 132 | position: absolute; 133 | top: 0; 134 | bottom: 0; 135 | width: 3rem; 136 | background-color: transparent; 137 | background-repeat: no-repeat; 138 | background-position: center; 139 | border: none; 140 | outline: none; 141 | opacity: 0.5; 142 | 143 | &--left { 144 | left: 0; 145 | background-image: url(/chevron-left.png); 146 | } 147 | 148 | &--right { 149 | right: 0; 150 | background-image: url(/chevron-right.png); 151 | } 152 | } 153 | } 154 | 155 | // SCREENS 156 | 157 | .home-screen { 158 | .header { 159 | background: white; 160 | box-shadow: 0 0 0.8rem 1rem rgba(234, 234, 234, 0.35); 161 | position: sticky; 162 | top: 0; 163 | z-index: 1; 164 | 165 | &-inner { 166 | height: 11rem; 167 | max-width: 60rem; 168 | margin: 0 auto; 169 | 170 | .logo { 171 | text-align: center; 172 | padding: 2rem; 173 | font-weight: bold; 174 | font-size: 2rem; 175 | 176 | & > span { 177 | color: #3b96fa; 178 | } 179 | } 180 | } 181 | 182 | .sort-buttons { 183 | display: flex; 184 | 185 | .sort-button { 186 | font-size: 1.6rem; 187 | flex: 1; 188 | display: flex; 189 | align-items: center; 190 | justify-content: center; 191 | background: none; 192 | height: 4rem; 193 | width: 4rem; 194 | outline: none; 195 | border: none; 196 | color: #434343; 197 | font-weight: bold; 198 | opacity: 0.3; 199 | 200 | & > img { 201 | margin-right: 1rem; 202 | width: 1.6rem; 203 | } 204 | } 205 | 206 | .active { 207 | opacity: 1; 208 | } 209 | } 210 | } 211 | 212 | .thumbnails { 213 | display: grid; 214 | grid-template-columns: repeat(3, 1fr); 215 | max-width: 60rem; 216 | margin: 0 auto; 217 | row-gap: 0.5rem; 218 | column-gap: 0.5rem; 219 | padding: 0.5rem; 220 | 221 | .thumbnail { 222 | width: 100%; 223 | padding-top: 100%; 224 | position: relative; 225 | cursor: pointer; 226 | 227 | &-image { 228 | background-size: cover; 229 | position: absolute; 230 | top: 0; 231 | left: 0; 232 | right: 0; 233 | bottom: 0; 234 | border-radius: 0.5rem; 235 | } 236 | } 237 | } 238 | } 239 | 240 | .customize-screen { 241 | display: grid; 242 | grid-template-rows: 4fr 1fr; 243 | height: 100%; 244 | 245 | h3 { 246 | margin: 2.5rem auto; 247 | text-align: center; 248 | text-transform: uppercase; 249 | font-size: 1.4rem; 250 | } 251 | } 252 | 253 | .review-screen { 254 | display: flex; 255 | flex-direction: column; 256 | align-items: center; 257 | justify-content: space-between; 258 | height: 100%; 259 | 260 | h2 { 261 | margin-top: 7rem; 262 | font-size: 2.4rem; 263 | margin-bottom: 3rem; 264 | } 265 | 266 | ul { 267 | list-style: none; 268 | padding: 0; 269 | font-size: 1.6rem; 270 | 271 | li { 272 | display: flex; 273 | justify-content: space-between; 274 | margin-bottom: 1rem; 275 | font-weight: bold; 276 | 277 | span:first-child { 278 | color: #a8a8a8; 279 | font-weight: normal; 280 | } 281 | } 282 | } 283 | } 284 | 285 | .complete-screen { 286 | text-align: center; 287 | padding: 15rem 0; 288 | 289 | svg { 290 | width: 10rem; 291 | } 292 | 293 | h1 { 294 | color: #4f4f4f; 295 | font-size: 2.4rem; 296 | } 297 | } 298 | 299 | // SANDBOX 300 | 301 | .sandbox-screen { 302 | padding: 2rem; 303 | 304 | .box { 305 | width: 10rem; 306 | height: 10rem; 307 | background: #09f; 308 | margin-bottom: 1rem; 309 | } 310 | } 311 | --------------------------------------------------------------------------------