├── .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 |
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 |
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 |
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 |
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 |
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 |
106 |
107 | It should sit behind the avatar (and both the perspective bar and the character should sit below the footer):
108 |
109 |
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 |
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 |
11 | >
12 | );
13 | }
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/src/components/ButtonRow/ButtonRow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import styles from './ButtonRow.module.css';
4 |
5 | const ButtonRow = ({ children }) => {
6 | return