├── .gitignore ├── .new-component-config.json ├── LICENSE.md ├── README.md ├── docs ├── footer-fix.png ├── mobile-variant.gif ├── mockup.png ├── overflow.gif ├── scroll.gif ├── short-window.png └── sizes.png ├── package-lock.json ├── package.json ├── public ├── favicon.png └── index.html └── src ├── App.js ├── components ├── ButtonRow │ ├── ButtonRow.js │ ├── ButtonRow.module.css │ └── index.js ├── Character │ ├── Character.js │ ├── Character.module.css │ ├── constants.js │ └── index.js ├── CharacterEditor │ ├── CharacterEditor.helpers.js │ ├── CharacterEditor.js │ ├── CharacterEditor.module.css │ └── index.js ├── ControlPane │ ├── ControlPane.js │ ├── ControlPane.module.css │ └── index.js ├── Footer │ ├── Footer.js │ ├── Footer.module.css │ └── index.js ├── MaxWidthWrapper │ ├── MaxWidthWrapper.js │ ├── MaxWidthWrapper.module.css │ └── index.js └── ToggleButton │ ├── ToggleButton.js │ ├── ToggleButton.module.css │ └── index.js ├── constants.js ├── index.css ├── index.js └── utils.js /.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 | .eslintcache 26 | -------------------------------------------------------------------------------- /.new-component-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "functional", 3 | "prettierConfig": { 4 | "semi": true, 5 | "singleQuote": true, 6 | "trailingComma": "es5" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Josh's Course Materials License 2 | 3 | Version 1, November 2020 4 | Copyright (c) Josh Comeau, 2020 5 | 6 | The files in this repository are meant to be used as part of a paid course, and are not intended for public distribution. They're open-source because it's the simplest form of distribution, and provides the best experience for students enrolled in the course. 7 | 8 | All are welcome to create personal copies of this repository, and modify its contents for educational use. Please experiment with the code, and see what you can build! 9 | 10 | It is forbidden to use these contents in any sort of commercial endeavour, including but not limited to: 11 | 12 | • Reselling its contents as part of a different course 13 | • Incorporating the code into a pre-existing business or project 14 | • Selling your solution to students enrolled in the course 15 | 16 | Exemptions can be made, on a case-by-case basis. Contact Josh Comeau (me@joshwcomeau.com) for more information. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Character Creation Workshop 2 | 3 | In this workshop, you'll build a Sims-style character creation screen. 4 | 5 | ## Mockups 6 | 7 | Desktop: 8 | 9 | Desktop-sized screenshot of the character creation screen 10 | 11 | For this workshop, we're only targeting desktops (although if you're so inclined, creating a mobile variant would make a great stretch goal!). 12 | 13 | > Protip: If you're reading this in VS Code, open the Command Palette (Cmd + Shift + P) and select “Markdown: Open Preview to the Side”. That way, you can see all the images in this document! 14 | 15 | ## Setup Instructions 16 | 17 | This project uses create-react-app. 18 | 19 | Start by installing dependencies: 20 | 21 | ``` 22 | npm install 23 | ``` 24 | 25 | Boot up a dev server: 26 | 27 | ``` 28 | npm run start 29 | ``` 30 | 31 | You should be able to access the application at `http://localhost:3000`. 32 | 33 | ## Troubleshooting 34 | 35 | If you run into problems running a local development server, check out our [Troubleshooting Guide](https://courses.joshwcomeau.com/troubleshooting) on the course platform. 36 | 37 | This guide addresses the common `Digital Envelope Routine` error you may have seen. 38 | 39 | ## Project structure and context 40 | 41 | This project is built with React. 42 | 43 | **All of the functionality has already been built.** Your job is to add the CSS. You're also allowed to tweak the JSX (HTML) as-needed. But you shouldn't need to fuss with any advanced React stuff. 44 | 45 | This project uses **CSS Modules**. CSS modules are ultimately very similar to vanilla CSS, but the classes are applied in JS. Here's an example: 46 | 47 | ```css 48 | /* Something.module.css */ 49 | .wrapper { 50 | width: 500px; 51 | } 52 | ``` 53 | 54 | ```js 55 | /* Something.js */ 56 | import styles from './Something.module.css'; 57 | 58 | function Something() { 59 | return ( 60 |
61 | I'll be 500px wide! 62 |
63 | ); 64 | } 65 | ``` 66 | 67 | Additionally, a few global styles can be found in `src/index.css`. 68 | 69 | ## Exercises 70 | 71 | ### Exercise 1: Fix footer links 72 | 73 | Let's start with a small detail: The footer links are unreadable: 74 | 75 | Side-by-side comparison of the current footer vs. the ideal one 76 | 77 | ### Exercise 2: Layout adjustments 78 | 79 | Next, let's tackle the biggest visual issue: the layout. 80 | 81 | We have a `MaxWidthWrapper` constraining the max width, but everything is super wide within it. 82 | 83 | Our header should be 65% of the available width, and our control-panel column should be 50%. 84 | 85 | Annotated mockup showing the overall width at 1024px, the header occupying 65%, and the control panels occupying 50% 86 | 87 | The character (the big illustration) should use fixed positioning, and it should occupy the space cleared by the above width tweaks. 88 | 89 | Give the character a minimum height of 500px. On smaller windows, this means the character won't fit in the viewport: 90 | 91 | Screenshot of a shorter Chrome window, with the character truncated at the knees 92 | 93 | > NOTE: If you notice at some point that the character SVG disappears, it's likely because it needs to be given an explicit width/height. This is discussed in more depth on the “Solution” page, https://courses.joshwcomeau.com/css-for-js/02-rendering-logic-2/20-character-workshop-solution#collapsed-svgs 94 | 95 | ### Exercise 3: Overflow 96 | 97 | Each control panel features a number of customizations. For control panels with too many options, a horizontal scrollbar should be introduced: 98 | 99 | Close-up screen recording of the overflow area in the control-panel 100 | 101 | ### Exercise 4: Perspective decoration 102 | 103 | To help add a bit of perspective, a light gray bar should extend across the bottom 40% of the screen: 104 | 105 | Screenshot of the mockup, showing the light gray bar 106 | 107 | It should sit behind the avatar (and both the perspective bar and the character should sit below the footer): 108 | 109 | Screen recording, showing how the character and stripe don't move as the page is scrolled 110 | 111 | You can use the background color `hsl(195deg, 20%, 86%)`. 112 | 113 | For bonus points, solve this challenge without setting any z-indexes. 114 | 115 | ### Exercise 5 (Stretch): Implement a mobile variant 116 | 117 | On mobile devices, the cards should stack horizontally, and sit near the bottom of the screen, underneath the character: 118 | 119 | Screen recording, showing a mobile variant of the application 120 | 121 | **NOTE:** This is a challenging stretch goal! It may require some CSS features we haven't covered yet. This is meant as an extra challenge for advanced students. Feel free to skip it! 122 | 123 | ## Submissions 124 | 125 | **Workshops are submitted through the course platform.** Commit your changes, push them to your fork, and submit the link by clicking the "Complete lesson" button on the workshop page. 126 | 127 | If you're not comfortable with Git, you can upload a `.zip` file using Dropbox or Google Drive, and paste a link to the public file instead. 128 | -------------------------------------------------------------------------------- /docs/footer-fix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/footer-fix.png -------------------------------------------------------------------------------- /docs/mobile-variant.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/mobile-variant.gif -------------------------------------------------------------------------------- /docs/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/mockup.png -------------------------------------------------------------------------------- /docs/overflow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/overflow.gif -------------------------------------------------------------------------------- /docs/scroll.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/scroll.gif -------------------------------------------------------------------------------- /docs/short-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/short-window.png -------------------------------------------------------------------------------- /docs/sizes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/docs/sizes.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "character-creator", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "5.11.4", 7 | "@testing-library/react": "11.1.0", 8 | "@testing-library/user-event": "12.1.10", 9 | "react": "17.0.1", 10 | "react-dom": "17.0.1", 11 | "react-scripts": "4.0.1", 12 | "web-vitals": "0.2.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | }, 38 | "engines": { 39 | "npm": ">5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/css-for-js/character-creator/376c1980384fb586b41f172ff762a620bc03aa42/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 14 | 15 | Character Creator — CSS for JavaScript Developers 16 | 17 | 21 | 22 | 23 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import CharacterEditor from './components/CharacterEditor'; 4 | import Footer from './components/Footer'; 5 | 6 | function App() { 7 | return ( 8 | <> 9 | 10 |