├── .github └── workflows │ └── next.yaml ├── .gitignore ├── .prettierrc ├── README.md ├── components ├── corner.js ├── footer.js ├── github.js ├── header.js ├── layout.js ├── linkedin.js ├── support-tweet.js ├── tweet-ids.js └── twitter.js ├── context ├── courseInfoContext.js └── headerContext.js ├── course.json ├── data ├── course.js └── lesson.js ├── lessons ├── 01-welcome-to-the-class │ ├── A-intro.md │ ├── B-get-set-up.md │ └── C-what-are-you-going-to-learn.md ├── 02-html │ ├── A-tags.md │ ├── B-types-of-tags.md │ ├── C-attributes.md │ ├── D-organizing-html.md │ ├── E-head-and-meta-tags.md │ ├── F-html-project.md │ └── meta.json ├── 03-css │ ├── A-rules.md │ ├── B-selectors-and-the-cascade.md │ ├── C-pseudoclasses-and-pseudoelements.md │ ├── D-layout-css.md │ ├── E-flex.md │ ├── F-grid.md │ ├── G-animations.md │ ├── H-putting-it-together.md │ ├── I-project.md │ └── meta.json ├── 04-javascript │ ├── A-intro.md │ ├── B-numbers-strings-and-booleans.md │ ├── C-control-flow.md │ ├── D-loops.md │ ├── E-exercise.md │ ├── F-functions.md │ ├── G-scope.md │ ├── H-builtins.md │ ├── I-objects.md │ ├── J-context.md │ ├── K-arrays.md │ └── meta.json ├── 05-putting-it-all-together │ ├── A-the-dom.md │ ├── B-events-and-listeners.md │ ├── C-project.md │ └── meta.json ├── 06-talking-to-servers │ ├── A-json.md │ ├── B-ajax.md │ ├── C-async-await.md │ ├── D-project.md │ └── meta.json └── 07-other-stuff-you-should-know │ ├── A-using-third-party-libraries.md │ ├── B-git-and-github.md │ ├── C-things-to-do-next.md │ ├── D-conclusion.md │ └── meta.json ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── index.js └── lessons │ └── [section] │ └── [slug].js ├── public ├── .nojekyll ├── images │ ├── BRAND-WHearts.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── author.jpg │ ├── brian.jpg │ ├── coffee_masters.png │ ├── coffee_masters_logo.png │ ├── commit.png │ ├── course-icon.png │ ├── error.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── git.png │ ├── plus-repo.png │ ├── remote.png │ ├── social-share-cover.jpg │ └── the_news_times.png ├── js │ └── klipse │ │ ├── FiraCode │ │ ├── eot │ │ │ ├── FiraCode-Bold.eot │ │ │ ├── FiraCode-Light.eot │ │ │ ├── FiraCode-Medium.eot │ │ │ └── FiraCode-Regular.eot │ │ ├── fira_code.css │ │ ├── otf │ │ │ ├── FiraCode-Bold.otf │ │ │ ├── FiraCode-Light.otf │ │ │ ├── FiraCode-Medium.otf │ │ │ ├── FiraCode-Regular.otf │ │ │ └── FiraCode-Retina.otf │ │ ├── ttf │ │ │ ├── FiraCode-Bold.ttf │ │ │ ├── FiraCode-Light.ttf │ │ │ ├── FiraCode-Medium.ttf │ │ │ ├── FiraCode-Regular.ttf │ │ │ └── FiraCode-Retina.ttf │ │ ├── woff │ │ │ ├── FiraCode-Bold.woff │ │ │ ├── FiraCode-Light.woff │ │ │ ├── FiraCode-Medium.woff │ │ │ └── FiraCode-Regular.woff │ │ └── woff2 │ │ │ ├── FiraCode-Bold.woff2 │ │ │ ├── FiraCode-Light.woff2 │ │ │ ├── FiraCode-Medium.woff2 │ │ │ └── FiraCode-Regular.woff2 │ │ ├── codemirror.css │ │ ├── javascript.inc.js │ │ ├── klipse_plugin.js │ │ ├── klipse_plugin.min.js │ │ └── pretty_format.js ├── project-files │ ├── blog │ │ ├── about.html │ │ └── index.html │ ├── calculator.css │ ├── calculator.html │ ├── calculator.js │ ├── coffee │ │ ├── index.html │ │ └── style.css │ ├── news.css │ ├── news.html │ ├── word-masters.css │ ├── word-masters.html │ └── word-masters.js └── webfonts │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff2 │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff2 │ ├── fa-v4compatibility.ttf │ └── fa-v4compatibility.woff2 └── styles ├── courses.css ├── footer.css └── variables.css /.github/workflows/next.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy NextJS Course Site to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@master 13 | - name: npm install, export 14 | run: | 15 | npm install 16 | npm run export 17 | - name: Deploy site to gh-pages branch 18 | uses: crazy-max/ghaction-github-pages@v2 19 | with: 20 | target_branch: gh-pages 21 | build_dir: out 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.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 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

code logo

2 | 3 | [![Frontend Masters](https://static.frontendmasters.com/assets/brand/logos/full.png)][fem] 4 | 5 |

6 | Build your first web app with little code experience with industry veteran Brian Holt. 7 |

8 | 9 | [Course code icon created by Freepik - Flaticon](https://www.flaticon.com/free-icons/code) 10 | 11 | ## License 12 | 13 | The **code** is this repo is licensed under the Apache 2.0 license. 14 | 15 | The **content** is licensed under CC-BY-NC-4.0. 16 | 17 | [fem]: https://frontendmasters.com/courses/web-development-v3/ 18 | -------------------------------------------------------------------------------- /components/corner.js: -------------------------------------------------------------------------------- 1 | export default function Corner() { 2 | return ( 3 |
4 | 11 | 12 | 13 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 42 | 43 | 44 | 45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /components/footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Gh from "./github"; 3 | import Tw from "./twitter"; 4 | import Li from "./linkedin"; 5 | 6 | export default function Footer({ twitter, linkedin, github }) { 7 | return ( 8 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /components/github.js: -------------------------------------------------------------------------------- 1 | export default function GitHub() { 2 | return ( 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 28 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /components/header.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import Link from "next/link"; 3 | import { Context as HeaderContext } from "../context/headerContext"; 4 | import { Context as CourseContext } from "../context/courseInfoContext"; 5 | 6 | export default function Header(props) { 7 | const [{ section, title, icon }] = useContext(HeaderContext); 8 | const { frontendMastersLink } = useContext(CourseContext); 9 | return ( 10 |
11 |

12 | {props.title} 13 |

14 |
15 | {frontendMastersLink ? ( 16 | 17 | Watch on Frontend Masters 18 | 19 | ) : null} 20 | {section ? ( 21 |

22 | {section} {title} 23 |

24 | ) : null} 25 |
26 |
27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /components/layout.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Script from "next/script"; 3 | 4 | import Footer from "./footer"; 5 | import Header from "./header"; 6 | import getCourseConfig from "../data/course"; 7 | import { Provider as HeaderProvider } from "../context/headerContext"; 8 | import { Provider as CourseInfoProvider } from "../context/courseInfoContext"; 9 | 10 | function Layout({ children }) { 11 | const courseInfo = getCourseConfig(); 12 | const headerHook = useState({}); 13 | return ( 14 | 15 | 16 |
17 |
18 |
19 |
{children}
20 |
21 |
26 |
27 | 37 | 44 |
45 |
46 | ); 47 | } 48 | 49 | export default function App({ children }) { 50 | return {children}; 51 | } 52 | -------------------------------------------------------------------------------- /components/linkedin.js: -------------------------------------------------------------------------------- 1 | export default function LinkedIn() { 2 | return ( 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /components/support-tweet.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import TweetEmbed from "react-tweet-embed"; 3 | import getTweetId from "./tweet-ids"; 4 | 5 | const SupportTweet = () => { 6 | const [tweet, setTweet] = useState(""); 7 | 8 | useEffect(() => { 9 | setTweet(getTweetId()); 10 | }, []); 11 | 12 | if (!tweet) { 13 | return

loading …

; 14 | } 15 | 16 | return ( 17 |
18 |

Some words of encouragement!

19 | 20 |
21 | ); 22 | }; 23 | 24 | export default SupportTweet; 25 | -------------------------------------------------------------------------------- /components/tweet-ids.js: -------------------------------------------------------------------------------- 1 | const ids = [ 2 | "1524470018414579713", 3 | "1524513578333749257", 4 | "1524442213224095744", 5 | "1524426191859752960", 6 | "1524488808623865856", 7 | "1524427387139551234", 8 | "1524426604864098305", 9 | "1524478073265139713", 10 | "1524569707696766976", 11 | "1524465419154640897", 12 | "1524465979782094848", 13 | "1524471407106686976", 14 | "1524468277048934401", 15 | "1524426338190381062", 16 | "1524779828687388672", 17 | "1524429165453316096", 18 | "1524514835802865665", 19 | "1524715495777316870", 20 | "1524667998413357056", 21 | "1524996834199883776", 22 | "1524763533052891136", 23 | "1524636366767808512", 24 | "1524546652547084290", 25 | "1524586166728761344", 26 | "1524473716901437441", 27 | "1524569427206807554", 28 | "1524435522491322369", 29 | "1524473325887631360", 30 | "1524468553566629890", 31 | "1524441428696481794", 32 | "1524577567625293825", 33 | "1524456627906412544", 34 | "1524432273159901190", 35 | "1524481845097734144", 36 | "1524468511120437248", 37 | "1524461144475328513", 38 | "1524618777870098432", 39 | "1524573585163132928", 40 | "1524483014272507904", 41 | "1524442759121186816", 42 | "1524456882986958852", 43 | "1524577004275724288", 44 | "1524554417550331904", 45 | "1524561638841540608", 46 | "1524459516611469314", 47 | "1524568446586945536", 48 | "1524505864899641346", 49 | "1524921983460319232", 50 | "1524624305241444353", 51 | "1524467514096463873", 52 | "1524469735081103364", 53 | "1524467027536011264", 54 | "1525272834783580160", 55 | "1524447692805402624", 56 | "1524587047431802881", 57 | "1524439791068086273", 58 | "1524477829429141505", 59 | "1524567523647279108", 60 | "1524481932108746753", 61 | "1524439239676633090", 62 | "1524427970999308288", 63 | "1524673111291043841", 64 | "1524578378384175104", 65 | "1524570828838420480", 66 | "1524571309786775552", 67 | "1524573919352696833", 68 | "1524589147922571264", 69 | "1524429101867712513", 70 | "1524573602137395201", 71 | "1524555221866790918", 72 | "1524427772050886656", 73 | "1524465780108185603", 74 | "1524454367092482048", 75 | "1524543915235606530", 76 | "1524577386586689537", 77 | "1524765369809973250", 78 | "1524454922690912256", 79 | "1524429625111236608", 80 | "1524430531559047168", 81 | "1524562559336034305", 82 | "1524756723994853377", 83 | "1524429278967910402", 84 | "1524727199579918337", 85 | "1524456583857532928", 86 | "1524794032555180032", 87 | "1524425600835854336", 88 | "1524437286166822912", 89 | "1524427350368473088", 90 | "1524636893853503490", 91 | "1524617148898988033", 92 | "1524579332429070338", 93 | "1524446074706055169", 94 | "1524584311638224897", 95 | "1524605481364471809", 96 | "1524656750049255424", 97 | "1524637121553833984", 98 | "1524755790061441026", 99 | "1524642616930885632", 100 | "1524579798865129472", 101 | "1524765411417460738", 102 | ]; 103 | const KEY = "support-tweet-index"; 104 | 105 | function getTweetId() { 106 | let index = +localStorage.getItem(KEY); 107 | 108 | if (Number.isNaN(index)) { 109 | index = Math.floor(Math.random() * ids.length); 110 | } 111 | 112 | const newIndex = index + 1 >= ids.length ? 0 : index + 1; 113 | console.log("old", index, "new", newIndex); 114 | localStorage.setItem(KEY, newIndex); 115 | 116 | return ids[index]; 117 | } 118 | 119 | export default getTweetId; 120 | -------------------------------------------------------------------------------- /components/twitter.js: -------------------------------------------------------------------------------- 1 | export default function Twitter() { 2 | return ( 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /context/courseInfoContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | const courseInfoContext = createContext([{}, () => {}]); 4 | 5 | export const Provider = courseInfoContext.Provider; 6 | export const Consumer = courseInfoContext.Consumer; 7 | export const Context = courseInfoContext; 8 | -------------------------------------------------------------------------------- /context/headerContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | const headerContext = createContext([{}, () => {}]); 4 | 5 | export const Provider = headerContext.Provider; 6 | export const Consumer = headerContext.Consumer; 7 | export const Context = headerContext; 8 | -------------------------------------------------------------------------------- /course.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Brian Holt", 4 | "company": "Stripe" 5 | }, 6 | "title": "Complete Intro to Web Dev v3", 7 | "subtitle": "Learn to write websites with HTML, CSS, and JavaScript", 8 | "frontendMastersLink": "https://holt.fyi/web-dev", 9 | "social": { 10 | "linkedin": "btholt", 11 | "github": "btholt", 12 | "twitter": "holtbt" 13 | }, 14 | "description": "Learn to be a web developer from nothing from industry veteran Brian Holt", 15 | "keywords": ["web dev", "web developer", "css", "javascript", "html", "visual studio code", "learn to code"], 16 | "productionBaseUrl": "/complete-intro-to-web-dev-v3" 17 | } 18 | -------------------------------------------------------------------------------- /data/course.js: -------------------------------------------------------------------------------- 1 | import config from "../course.json"; 2 | 3 | const DEFAULT_CONFIG = { 4 | author: { 5 | name: "An Author", 6 | company: "An Author's Company", 7 | }, 8 | title: "A Superb Course", 9 | subtitle: "That Teaches Nice Things", 10 | frontendMastersLink: "", 11 | description: "A nice course for nice people.", 12 | keywords: ["a nice course", "for people", "to learn", "nice things"], 13 | social: { 14 | linkedin: "btholt", 15 | github: "btholt", 16 | twitter: "holtbt", 17 | }, 18 | productionBaseUrl: "/", 19 | }; 20 | 21 | export default function getCourseConfig() { 22 | return Object.assign({}, DEFAULT_CONFIG, config); 23 | } 24 | -------------------------------------------------------------------------------- /data/lesson.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs/promises"; 3 | import * as matter from "gray-matter"; 4 | import { titleCase } from "title-case"; 5 | import { marked } from "marked"; 6 | import hljs from "highlight.js"; 7 | 8 | marked.setOptions({ 9 | baseUrl: process.env.BASE_URL ? process.env.BASE_URL + "/" : "/", 10 | highlight: function (code, inputLang) { 11 | if (["html", "js"].indexOf(inputLang) >= 0) { 12 | return code; 13 | } 14 | 15 | const lang = inputLang.replace("display-", ""); 16 | 17 | let language = hljs.getLanguage(lang) ? lang : "plaintext"; 18 | 19 | return hljs.highlight(code, { language }).value; 20 | }, 21 | langPrefix: "hljs language-", 22 | }); 23 | 24 | const DEFAULT_ICON = "info-circle"; 25 | const lessonsPath = path.join(process.env.ROOT, "lessons"); 26 | 27 | function getTitle(slug, override) { 28 | let title = override; 29 | if (!title) { 30 | title = titleCase(slug.split("-").join(" ")); 31 | } 32 | 33 | return title; 34 | } 35 | 36 | async function getMeta(section) { 37 | let meta = {}; 38 | try { 39 | const file = await fs.readFile( 40 | path.join(lessonsPath, section, "meta.json") 41 | ); 42 | meta = JSON.parse(file.toString()); 43 | } catch (e) { 44 | // no meta.json, nothing to do 45 | } 46 | 47 | return meta; 48 | } 49 | 50 | function slugify(inputPath) { 51 | const pathParts = inputPath.split("-"); 52 | const pathOrder = pathParts.shift(); 53 | const pathSlug = pathParts.join("-"); 54 | return { 55 | slug: pathSlug, 56 | order: pathOrder, 57 | title: titleCase(pathParts.join(" ")), 58 | }; 59 | } 60 | 61 | export async function getLessons() { 62 | const dir = await fs.readdir(lessonsPath); 63 | const sections = []; 64 | 65 | for (let dirFilename of dir) { 66 | const dirStats = await fs.lstat(path.join(lessonsPath, dirFilename)); 67 | 68 | if (dirStats.isFile()) { 69 | continue; 70 | } 71 | 72 | const lessonsDir = await fs.readdir(path.join(lessonsPath, dirFilename)); 73 | 74 | let { 75 | title: sectionTitle, 76 | order: sectionOrder, 77 | slug: sectionSlug, 78 | } = slugify(dirFilename); 79 | 80 | let icon = DEFAULT_ICON; 81 | 82 | const meta = await getMeta(dirFilename); 83 | if (meta.title) { 84 | sectionTitle = meta.title; 85 | } 86 | if (meta.icon) { 87 | icon = meta.icon; 88 | } 89 | 90 | const lessons = []; 91 | for (let lessonFilename of lessonsDir) { 92 | if (lessonFilename.slice(-3) !== ".md") { 93 | continue; 94 | } 95 | 96 | const filePath = path.join(lessonsPath, dirFilename, lessonFilename); 97 | 98 | const file = await fs.readFile(filePath); 99 | const { data } = matter(file.toString()); 100 | let slug = lessonFilename.replace(/\.md$/, ""); 101 | 102 | const slugParts = slug.split("-"); 103 | const lessonOrder = slugParts.shift(); 104 | 105 | slug = slugParts.join("-"); 106 | 107 | const title = getTitle(slug, data.title); 108 | 109 | lessons.push({ 110 | slug, 111 | fullSlug: `/lessons/${sectionSlug}/${slug}`, 112 | title, 113 | order: `${sectionOrder}${lessonOrder}`, 114 | path: filePath, 115 | }); 116 | } 117 | 118 | sections.push({ 119 | icon, 120 | title: sectionTitle, 121 | slug: sectionSlug, 122 | lessons, 123 | order: sectionOrder, 124 | }); 125 | } 126 | 127 | return sections; 128 | } 129 | 130 | export async function getLesson(targetDir, targetFile) { 131 | const dir = await fs.readdir(lessonsPath); 132 | 133 | for (let i = 0; i < dir.length; i++) { 134 | const dirPath = dir[i]; 135 | if (dirPath.endsWith(targetDir)) { 136 | const lessonDir = ( 137 | await fs.readdir(path.join(lessonsPath, dirPath)) 138 | ).filter((str) => str.endsWith(".md")); 139 | 140 | for (let j = 0; j < lessonDir.length; j++) { 141 | const slugPath = lessonDir[j]; 142 | if (slugPath.endsWith(targetFile + ".md")) { 143 | const filePath = path.join(lessonsPath, dirPath, slugPath); 144 | const file = await fs.readFile(filePath); 145 | const { data, content } = matter(file.toString()); 146 | const html = marked(content); 147 | const title = getTitle(targetFile, data.title); 148 | const meta = await getMeta(dirPath); 149 | 150 | const section = getTitle(targetDir, meta.title); 151 | const icon = meta.icon ? meta.icon : DEFAULT_ICON; 152 | 153 | let nextSlug; 154 | let prevSlug; 155 | 156 | // get next 157 | if (lessonDir[j + 1]) { 158 | // has next in section 159 | const { slug: next } = slugify(lessonDir[j + 1]); 160 | nextSlug = `${targetDir}/${next.replace(/\.md$/, "")}`; 161 | } else if (dir[i + 1]) { 162 | // has next in next section 163 | const nextDir = ( 164 | await fs.readdir(path.join(lessonsPath, dir[i + 1])) 165 | ).filter((str) => str.endsWith(".md")); 166 | const nextDirSlug = slugify(dir[i + 1]).slug; 167 | const nextLessonSlug = slugify(nextDir[0]).slug.replace( 168 | /\.md$/, 169 | "" 170 | ); 171 | nextSlug = `${nextDirSlug}/${nextLessonSlug}`; 172 | } else { 173 | // last section 174 | nextSlug = null; 175 | } 176 | 177 | // get prev 178 | if (lessonDir[j - 1]) { 179 | // has prev in section 180 | const { slug: prev } = slugify(lessonDir[j - 1]); 181 | prevSlug = `${targetDir}/${prev.replace(/\.md$/, "")}`; 182 | } else if (dir[i - 1]) { 183 | // has prev in prev section 184 | const prevDir = ( 185 | await fs.readdir(path.join(lessonsPath, dir[i - 1])) 186 | ).filter((str) => str.endsWith(".md")); 187 | const prevDirSlug = slugify(dir[i - 1]).slug; 188 | const prevLessonSlug = slugify( 189 | prevDir[prevDir.length - 1] 190 | ).slug.replace(/\.md$/, ""); 191 | prevSlug = `${prevDirSlug}/${prevLessonSlug}`; 192 | } else { 193 | // first section 194 | prevSlug = null; 195 | } 196 | 197 | const base = process.env.BASE_URL ? process.env.BASE_URL : "/"; 198 | 199 | return { 200 | attributes: data, 201 | html, 202 | slug: targetFile, 203 | title, 204 | section, 205 | icon, 206 | filePath, 207 | nextSlug: nextSlug ? path.join(base, "lessons", nextSlug) : null, 208 | prevSlug: prevSlug ? path.join(base, "lessons", prevSlug) : null, 209 | }; 210 | } 211 | } 212 | } 213 | } 214 | 215 | return false; 216 | } 217 | -------------------------------------------------------------------------------- /lessons/01-welcome-to-the-class/B-get-set-up.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | A good craftsperson needs reliable tools. Let's get you set up and going. I have opinions, and as a person who has been teaching people how to code for over a decade, I think I have pretty good opinions on what tools you should use. That said, I will give you a few options and you can choose. If you know what you are doing, feel free to stick with what you know. If you don't, I suggest at least for this class use the tools I am using and then later revisit what tools you're using. 6 | 7 | ## The Code Editor 8 | 9 | **Recommended**: [Visual Studio Code][code] 10 | 11 | I admit some bias here as I used to be a program manager on the VS Code team. It's a lovely tool that's free, has a great ecosystem of plugins, and works well across many platforms. I strongly suggest getting started here with VS Code. 12 | 13 | Alternatives: 14 | 15 | - [Sublime Text][st]: I used this editor for years. Similar in style to VS Code. It also has a great ecosystem and is a fast editor. It's free to evaluate (indefinitely as far as I know) and then they ask you to pay $99 every three years. IMO a fair price for such an important tool. 16 | - [WebStorm][ws]: Sublime and VS Code's MO is more "here's a useful editor and not much more" whereas WebStorm is much "here's a useful editor and the kitchen sink". Whereas with VS Code you will likely install a few plugins to get everything you need, WebStorm will likely ship already with everything you need. You'll need to decide which approach suits you better. $60/year to use. 17 | - [Nova][nova] - I confess I'm far less familiar with Nova but it seems worth talking about. It's a very new product that in my view fills the gap between VS Code and WebStorm. I'm interested to see how they evolve. $99/year. 18 | - [vim][vim] and [emacs][emacs] - Don't use these … Okay, now that I've said that I've either made you mad and you're going to do it anyway or you're going to take my advice and not do it. These two tools are very difficult to learn and if you're taking this class you're already learning some difficult concepts and you don't want to layer in more difficulty. If you're going to learn them, take the time to focus and learn just them. 19 | 20 | ## The Browser 21 | 22 | **Recommended**: [Google Chrome][chrome] 23 | 24 | The standard at the moment for web development. It has great dev tools and is the number one most used browser in the world. Lots of great developer-focused extensions 25 | 26 | Alternatives: 27 | 28 | - [Firefox][ff] - I confess this is the browser _I use_. Honestly, it's a great browser and in my opinion just as good as Chrome. Very valid choice here. I'm showing you Chrome because it is the industry standard and this course is about showing you the tools that best prepare you to be the best coder and not enforce my opinions. 29 | 30 | ## In-Browser Code Editor 31 | 32 | In this course we will use [CodePen][codepen] a few times. It's an in-browser code editing experience and it makes it easy for you to learn. I suggest you head [here][sign-up] and sign up for a free account so you fork the assignments to your own CodePen and you can refer to them later. 33 | 34 | ## The Resources 35 | 36 | Something really important is that you choose to learn from good sources. Just like it's important to get your news from quality sources, it's important to get your technical information from sound sources. Here are some of my personal favorites: 37 | 38 | - For anything to do with HTML, CSS, or JavaScript, Mozilla's [MDN][mdn] is my go-to. I literally have it open all the time. 39 | - [CSS Tricks][css-tricks] has fashioned itself into a premier development website. It has great content not just for CSS but for HTML and JavaScript too. If I want a tutorial, I'll head there. If I want more technical how-to info, I head to MDN. 40 | - For video content, you really can't beat the rest of the content on [Frontend Masters][fem]. I love it. 41 | - If I'm working with a library or a framework, it's a good idea to head directly to their [GitHub][gh] (we'll talk about GitHub later) page or their official documentation. It's best to head straight to the source. 42 | 43 | Frontend Masters also puts out a really awesome book every year called the [Frontend Handbook][handbook]. It's a good way to get an overview of the whole industry. 44 | 45 | [code]: https://code.visualstudio.com/ 46 | [ws]: https://www.jetbrains.com/webstorm/ 47 | [nova]: https://nova.app/ 48 | [st]: https://www.sublimetext.com/ 49 | [vim]: https://www.vim.org/ 50 | [emacs]: https://www.gnu.org/software/emacs/ 51 | [chrome]: https://www.google.com/chrome/index.html 52 | [ff]: https://www.firefox.com 53 | [codepen]: https://codepen.io 54 | [sign-up]: https://codepen.io/accounts/signup/user/free 55 | [gh]: https://github.com/ 56 | [fem]: https://frontendmasters.com/learn/beginner/ 57 | [css-tricks]: https://css-tricks.com/ 58 | [mdn]: https://developer.mozilla.org/en-US/ 59 | [handbook]: https://frontendmasters.com/guides/front-end-handbook/2019/ 60 | -------------------------------------------------------------------------------- /lessons/01-welcome-to-the-class/C-what-are-you-going-to-learn.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Let's talk about what you're going to learn throughout this course: how to write websites and web apps. In the process we'll teach how to use all the tools necessary to code, to do a light bit of design, and how to continue forward afterward. After the end of this course, you can reasonably expect to know how to code a website from scratch and be prepared to take more courses on the [Frontend Masters beginner path][fem]. 6 | 7 | ## What is HTML? 8 | 9 | HTML stands for hypertext markup language. You can think of it as the content on a webpage. As you're reading this content right now, the images and text are written in HTML. It's also the _structure_ of the content. You can group bits of HTML together. We'll get into this in a bit, but it's good to associate HTML with _structure_ and _content_. 10 | 11 | If we were building a car together, you could think of the HTML as the materials we used to build the car: the frame, the doors, the steering wheel, the tires, etc. By themselves, the materials are inert: they don't _do_ anything by themselves or look particularly. They need to be arranged into a coherent car (via CSS) and it needs an engine with all the wiring (via JavaScript) to actually do anything. 12 | 13 | ## What is CSS? 14 | 15 | CSS stands for cascading stylesheets. You can think of it as the _style_ of a website. What type of font is being used, what the background color is, what is bolded, what the spacing is, what the layout is, etc. CSS is basically just a series of rules that says "if an element matches this selector, apply this style." 16 | 17 | Continuing our car metaphor, the CSS would be what color the car is, what the various trimmings are, and all the styling details. You could even say it would define if it's left or right drive. 18 | 19 | ## What is JavaScript? 20 | 21 | You can have a complete webpage without any JavaScript, with just CSS and HTML. [Example.com][example] is an example of a totally valid website that has zero JavaScript on it. Lots of websites that don't need JavaScript do. We'll get into it later, but just know not everything needs JavaScript. 22 | 23 | JavaScript is the programming language we are going to use today. There are many programming languages like C++, Python, Go, PHP, and many, many more. We are choosing JavaScript for the exact reason that every browser (Chrome, Firefox, etc.) can run JavaScript on a web page (whereas it cannot run any other language.) JavaScript was specifically invented for the purpose of running on webpages but it has now grown beyond that and is being run in a great many places. The skills you will learn writing JavaScript will translate to other languages. It's like learning a foreign language. If you learn French it makes learning Spanish easier. While distinct languages with their own words and patterns, many of the same underlying grammar principles are unchanged or similar 24 | 25 | HTML and CSS describe a non-interactive webpage, like a page in a book. All the content, pictures, fonts, spacing, etc. are all there, and it can be read. The page in a book is not interactive though; if you try to touch your book like a touchscreen, well, it's not going to do much. Think of JavaScript being the piece that transforms a non-interactive page out of a book into a touchscreen app. Whereas you had text, font, colors, images, etc. all before, now you can have things move around, have pop-ups, refresh content, start animations, all sorts of stuff. 26 | 27 | This is a relatively simplistic take. There's overlap on these things e.g. both CSS and JS can do animations, just differently. But work under these definitions for now and then down the road you can explore the overlap. 28 | 29 | [fem]: https://frontendmasters.com/learn/beginner/ 30 | [example]: http://example.com 31 | -------------------------------------------------------------------------------- /lessons/02-html/A-tags.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "Brian teaches what tags are, the foundation unit of HTML." 3 | --- 4 | 5 | We're going to start building our very first website. At first, our website is going to be pretty ugly, but it's still going to be functional! We're going to be using the language HTML, or hypertext markup language. This isn't a programming language since it doesn't actually _do_ anything. It's like how English isn't a programming language either: you can't "run" English. Same idea with HTML: you can't "run" HTML. HTML is simply the language and pictures on the page. It's the static (which is another word for unchanging) content. 6 | 7 | ## Tags 8 | 9 | HTML's base building block is the **tag**. A tag is a building block. It describes what's inside it. Every tag has an opening and a closing tag (some tags open and close at the same point.) I think the easiest way to learn it is just to show a bunch of examples. 10 | 11 | ```html 12 |

This is the title to my document

13 | ``` 14 | 15 | You can see the `

` and the `

` surrounding the text "This is the title to my document". This is how HTML operates. You can have an opening tag which has information or more tags inside of it. In this case we have an `h1` tag which is a heading tag: it's used for the biggest title on the page, typically a title. If you rendered that using the browser, it looks like: 16 | 17 |

This is the title to my document

18 | 19 | It's bigger and bolder because that's what browsers do with h1s, it makes them look important on the page. However it does more than that too. Browsers aren't the only thing reading websites. Blind and people who can't see well will use screen readers to read web pages out loud to them; it uses things like headers to understand what information is important to read to the users. It's also how Google and Bing will understand the important details of your website. In other words, it's important which type of tag you use. More than just the visual aesthetic is using those tags. 20 | 21 | A tag, whether it's opening or closing, is surrounded by angle brackets, `<` and `>`. Closing tags always have a slash, `/`, after the opening angle bracket, so it looks like ``. There are things called "self-closing tags" or "void tags" that open and close themselves. These will look like this: `` (I'll explain in a sec what inputs are.) That slash at the end means it is self-closing. To make it more confusing, that last slash is optional, so `` (with no closing tag ever) is valid too since input tags are always self-closing. 22 | 23 | Tags are also opened and closed in a specific order too. The most recently opened tag must be the next closed tag. For example, if I have an h1 instead of a div, the h1 must be closed first. 24 | 25 | ```html 26 |
27 |

Hi

28 |
29 | ``` 30 | 31 | The above is correct. 32 | 33 | ```display-html 34 |
35 |

36 | Hi 37 |

38 | 39 | ``` 40 | 41 | The above is incorrect. I can't close the div _before_ I close the h1 since the h1 was the last one I opened. 42 | -------------------------------------------------------------------------------- /lessons/02-html/C-attributes.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Attributes allow you to modify behavior of an HTML tag. You've already seen a few of them but we'll go into a few more examples of them. 6 | 7 | A really good example we have seen already is the `href` attribute of the `` tag. By modifying the href we're modifying what the tag does. It's contextual as well: `href` only works on `a` tags. You can't add a `href` to a `div` and expect to suddenly work as a link. 8 | 9 | Another one we say is the `type` attribute on the `input`. By changing the type you're changing what sort of input is being show 10 | 11 | ```html 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ``` 22 | 23 | `href` and `type` are specific to a few tags. There are other attributes like `class` and `id` that are universal and can be applied to (nearly) all tags. These themselves are inert; they don't do anything, but allow other things to find them later (we'll cover them heavily in CSS and JS sections.) 24 | 25 | ```html 26 |

This one is red

27 |

This one is blue

28 | 29 | 39 | ``` 40 | 41 | Classes are more useful than IDs. You should use them 95% of the time. A class is reusable. Now I could create another tag (doesn't even have to be another h3) and that CSS style would be reapplied to it! You can also give multiple classes to one tag. 42 | 43 | ```html 44 |

This is red and bold

45 | 46 | 51 | ``` 52 | 53 | ## IDs 54 | 55 | Classes are far more useful. However there are IDs that you may need occasionally. A key thing is that an _ID is unique_. Whereas I've already used the `the-red-one` class twice, you can't do with IDs. They're designed to not be reusable; they're unique to the page. 56 | 57 | Again, in general, even if you happen to have just one of something, it's best to just use classes for everything and more-or-less pretend that IDs are not an option at all. You really only want to use IDs when something is unique and you want to make sure it stays unique. A good example is if you have a JavaScript app that needs to put its HTML inside a specific div, you could use an ID for that (you could still use a class and some people do.) 58 | 59 | ```html 60 |

My Site's Branding

61 | ``` 62 | 63 | For now just use classes. It'll make everything easier. 64 | -------------------------------------------------------------------------------- /lessons/02-html/D-organizing-html.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Organizing HTML" 3 | description: "" 4 | --- 5 | 6 | Let's talk about organizing HTML and how to make the most of it. This will help a ton once you get to the CSS section. 7 | 8 | Let's say you're making a social media website and you're going to make the "news feed" section where you can see all the updates from your friends. A user would expect see a bunch of posts. Each post could have an author, a date that it was posted, a profile picture, and the message itself the user posted. Let's model that as HTML 9 | 10 | ```html 11 |
12 |

@sassy_pants_mcgee

13 |

Posted 15m ago

14 | a doggy 15 |

16 | You ever think about barking out the window? What would happen if I didn't 17 | bark at them? Keeps me up at night. 18 |

19 |
20 | ``` 21 | 22 | Notice we keep everything together with a div. Like I said before, it's a container, a cardboard box. We have a post and keep it all together with an encapsulating div. We also could have used the `
` tag. What's the difference between article and div? As far as the browser is concerned, nothing. Keep in mind that you just write code once, you also have to go back later and maintain it. What if there's a bug later and you need to debug what's wrong with it? It's good to write code that makes it very obvious what you're trying to do. In this case, article lets your future self (or coworkers) know that this is a self-contained bit of content. Divs have no meaning. Like a cardboard box, it's what is inside that matters. An article is a cardbox with a label on it that says it's a self-contained bit of content. Both are valid here, but let's roll with divs (since you'll more commonly see that.) 23 | 24 | Let's make this better. Let's add classes. 25 | 26 | ```html 27 |
28 |

@sassy_pants_mcgee

29 |

Posted 15m ago

30 | a doggy 35 | 39 |
40 | ``` 41 | 42 | Notice it looks exactly the same. But now try reading the code we just wrote instead of the one from before. Everything is way clearer what it is. We took a label gun and labelled everything. This code is way easier to maintain. 43 | 44 | What's more, this is reusable now. We could have a full feed of these. 45 | 46 | ```html 47 |
48 | 61 | 74 | 84 |
85 | ``` 86 | 87 | Now I used a div to enclose the entire social feed, and then I reused our structure from above to remake a bunch of the same post. Pretty neat, huh? We build one component and then reuse it over and over again. I imagine then we'd put the `social-feed` div inside another `news` component and then that inside of our `app` page. Our `app` div might have a navbar, the news div, a footer, a sidebar, and other things. But we'd just do nested things like we did with our `social-feed`. 88 | 89 | Let's construct a navbar for fun. 90 | 91 | ```html 92 | 108 | ``` 109 | 110 | A few things here: 111 | 112 | - It obviously isn't styled. We'd expect to look like the navbar on this site: horizontal and fixed to the top. Don't worry about that for now, we fix all that with CSS. For now we're writing just HTML and we're just concerned about the _content_ and not the _style_. (CSS can basically make any content look like anything you want.) 113 | - Those links don't go anywhere if you click them. They're relative links. If you have an absolute link like `href="www.google.com"` that will actually take you to Google. But if your website were `www.example.com` and on there you had link to `href="/about"` it would take you to `www.example.com/about`. It's relative to the base URL. The leading slash means it's always `www.example.com/about` no matter where you are on the site. If you're `www.example.com/blah` and there's a link to `href="about"` with no slash, the link will take you to `www.example.com/blah/about` whereas `href="/about"` will take you to `www.example.com/about`. 114 | - We didn't get nav a class. If you only have one nav that could make sense. If you have many nav (maybe nav in the footer and one in the sidebar) then you'd want to give it a class. Not everything _has_ to have a class. 115 | - I chose to do this with a ul and li. You could have done divs inside of divs and just used classes to differentiate them. Both totally valid ways of doing it. 116 | - There's no magic way to name classes. I used `nav-list` but could have just as easy been `head-navigation`. It's up to what makes sense to you. Naming things is hard. 117 | 118 | There you go! HTML is 95% just what I've shown you. 119 | -------------------------------------------------------------------------------- /lessons/02-html/E-head-and-meta-tags.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | So far we've been discussing snippets of HTML, using things like divs to build up "components" (or a group of tags that represents a higher level concept like a post or a navigation bar). But let's talk about the overall structure of an HTML document. 6 | 7 | Open up VS Code, create a new document and save it in a new folder that we'll make our project. 8 | 9 | In that folder, make a file called index.html. Why index? By convention that is the root file of a website. If you were building example.com, then index.html would be the home page. If you made an about.html page, then that'd be example.com/about.html. If you made a directory called `dogs` inside of your folder and you added a luna.html file to that directory, the address would be example.com/dogs/luna.html. 10 | 11 | Okay, so now you have an index.html file, put this in there (this will be the start of your project in the next lesson.) 12 | 13 | ```display-html 14 | 15 | 16 | 17 | 18 | 19 | My Blog 20 | 21 | 22 | 23 | 24 | 25 | ``` 26 | 27 | Let's pick these tags apart one-by-one. 28 | 29 | - `` - There have been several revisions of HTML. You'll frequently see HTML5 referred to. As you may guess, this is the fifth major review of HTML. HTLM5 was release in 2008 and there's no HTML6 brewing so no worries about this initial tag changing. In any case, since your browser can understand several versions of HTML, put this at the top to signal to the browser that we're using the latest version. 30 | - `` - This tag is the absolute most root tag of an HTML document. Technically HTML is just a specific type of XML, this tag is necessary to signal to any reader this is actually an HTML doc (you never need to worry about that, just FYI.) Everything inside it will be interpreted as HTML. The `lang="en"` is the language you're writing your HTML in. Since this class is English, we're writing it in English. Specifically that's important to search engines so they know where to index your content. For our content we want English readers to see it. 31 | - `` - All of your web pages are going to need important meta data. They need to tell browsers how to handle browser resizings, what character sets your page uses, what the title is, what the favicon is (the little logo on your browser tab), where the CSS is located, etc. The `` tag (which is over the `body` tag, duh) contains all the important meta data. Nothing that gets _displayed_ to the user (like a div or an h1) will _ever_ be put in the head. It's _just_ meta data. 32 | - `` - Okay, so all these meta tags are giving the browser information of how to handle your documents 33 | - The `charset="UTF-8"` bit says that your document is written using the UTF-8 character set. This basically means you can use all character/letters/numbers/emojis/etc. that you expect to be able to use. Another example (that you would never use is) is ASCII (as in ASCII Art). ASCII is a much narrower set of characters that wouldn't have emojis for example. ASCII can't even do a lot of the characters you'd expect you'd be able to use like non-English accents. In other words, always include this line and never think about it. It's always UTF-8. 34 | - `name="viewport" content="width=device-width, initial-scale=1.0"` - This is letting the browser know how specifically to handle mobile devices like phones and small tablets. If you don't put this, the browser may just output a really zoomed out view of your website and it's very hard to read. This allows you to have mobile browsers treat your content better and more like you'd expect. Always put this too. 35 | - `` allows you to set the title that shows up on the browser tab. It will also be the title of the search result when people search for it on Google. 36 | - `` All of your visible HTML will go here. Your divs, spans, tables, h1s, etc. all here. 37 | 38 | This is the barebones skeleton of an HTML. You can literally just copy/paste this from project to project. Some of these things you can leave out but I don't recommend it. It makes the browser guess what you meants and that can vary how they guess from Safari to Edge to Chrome to Firefox, etc. Best to be explicit. 39 | 40 | Let's talk about one second having multiple HTML docs in one project. Put this tag in the index.html we created. 41 | 42 | ```display-html 43 | About 44 | ``` 45 | 46 | Then create a new file called about.html (must be in same directory) and put this in there: 47 | 48 | ```display-html 49 | 50 | 51 | 52 | 53 | 54 | About 55 | 56 | 57 |

About Me

58 | Go home 59 | 60 | 61 | ``` 62 | 63 | Now open index.html in your browser. You can either just drag the file onto your browser or you can use the CMD+O or CTRL+O shortcut to open the open dialog. Once you've done that, try clicking the links. You should it navigate between the two pages (if you put them in the same directory.) We'll take advantage of this in the project. 64 | -------------------------------------------------------------------------------- /lessons/02-html/F-html-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "HTML Project" 3 | description: "" 4 | --- 5 | 6 | Here is your first HTML project! 7 | 8 | We're making your personal blog. Feel free to use the project we started in the last lesson. Here are your requirements 9 | 10 | - Your blog has two pages: index.html and about.html 11 | - Your index.html needs a title and a quick intro. 12 | - Your index also needs a link to about.html. 13 | - You blog must have five posts on it (you can just keep it all on the same page). 14 | - Each post needs: 15 | - A title 16 | - An author 17 | - The date it was posted (feel free to make up times) 18 | - The text of the blog post. Bonus points if there are multiple paragraphs. Feel free to use [Lorem Ipsum][lorem] if you want placeholder text. 19 | - Bonus points for images. 20 | - Your about.html needs 21 | - The name of your blog 22 | - A few paragraphs about you 23 | - A list of your recent jobs/schools/accomplishments 24 | 25 | Quick note on how to do an image: put the image in the same directory as the index.html and about.html and then put `a text description of what the image is for sight-impaired folks` to render the image. 26 | 27 | This will mostly be black and white and that's okay. You can come back and restyle this later with CSS. 28 | 29 | Here is my example: [Brian's Blog][html] (you'll have to right click and say "View Source") 30 | 31 | [lorem]: https://loremipsum.io 32 | [html]: https://btholt.github.io/complete-intro-to-web-dev-v3/project-files/blog/index.html 33 | -------------------------------------------------------------------------------- /lessons/02-html/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": "code", 3 | "title": "HTML" 4 | } -------------------------------------------------------------------------------- /lessons/03-css/C-pseudoclasses-and-pseudoelements.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | ## Pseudoclasses 6 | 7 | Some times we want to change how elements look based on certain events that happen in the DOM. One of the most common ones is we want to change an element when someone hovers their mouse over it. Like this box: 8 | 9 | The CSS we used for this is this: 10 | 11 | ```html 12 | 25 |
Hover your mouse over me
26 | ``` 27 | 28 | The `:hover` part selects that element **only when that condition is true**. There are are many [CSS pseudo classes][pseudoclasses] like being able to only select the first element of something like this: 29 | 30 | The CSS for this is: 31 | 32 | ```html 33 | 41 |
    42 |
  1. First
  2. 43 |
  3. Second
  4. 44 |
  5. Third
  6. 45 |
46 | ``` 47 | 48 | This only selects the element if it is the _first element_ inside of a tag. Otherwise it won't select it. There are numerous other CSS classes; check out the CSS-Tricks article if you want learn more. 49 | 50 | ## Pseudoelements 51 | 52 | We're not going to dwell too much on pseudoelements as they are a bit of an advance concept. [Read here][pseudoelements] for a more in-depth dive on it. But let's have a quick example. 53 | 54 | ```html 55 |
This is a chapter of my book.
56 | 57 |
This is a second chapter of my book.
58 | 59 | 70 | ``` 71 | 72 | Do you remember at the end of a chapter of some books, they have a little embellishment to let you know you've finished a chapter? That's called an end mark. Many use one called a fleuron and that's what the ❦ character is. Let's say we wanted to write CSS to automatically insert that character after every chapter class in our book 73 | 74 | (By the way you can totally use CSS for print layouts like books and newspapers.) 75 | 76 | Every element has a `::before` and an `::after`. You can use these _pseudoelements_ to insert things there like we did with the fleuron. 77 | 78 | Again, not the most common thing to do, just wanted you to be aware of it. You will in old code and documentation see `:before` and `:after` but these are old and now are `::before` and `::after` so that pseudoclasses (like `:hover`) and pseudoelements (like `::after`) can be disambiguated by syntax. 79 | 80 | [pseudoclasses]: https://css-tricks.com/pseudo-class-selectors/ 81 | [pseudoelements]: https://css-tricks.com/almanac/pseudo-selectors/b/after-and-before/ 82 | -------------------------------------------------------------------------------- /lessons/03-css/F-grid.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Let's do a quick intro to grid, the latest-and-greatest way to do layouts with CSS. Grid shares a bit of similarities with flex but it's more oriented to making grids layouts as opposed to flex which is more flexible in terms of what you can get it to do. In short, grid is more useful for laying out a page (which are frequently grid-like) whereas flex is more flexible (if a bit more complicated) and is more useful when you have more unique layouts to achieve. 6 | 7 | ```html 8 | 21 | 22 |
23 | a doggy 28 | a doggy 33 | a doggy 38 | a doggy 43 |
44 | ``` 45 | 46 | This is a very basic grid where we were making a 2x2 grid. 47 | 48 | - `row-gap` and `column-gap` are how you do gutters in grid i.e. the space between items in the grid 49 | - `grid-template-column` allows you to set up how the grid is constructed. You can do it in percentages, pixels, or fr which stands for fractions. With two `1fr` we made it so each piece will take 50% of the space minus the gutters which it will calculate for you. If we did `1fr 2fr 1fr` the first column would take 25%, the second 50%, the third 25%. The math works like `flex-grow`. 50 | - Just like block and flex, there is an `inline-grid` too. 51 | 52 | This was a basic example. Let's do a more complicated example. Let's say you're laying out your whole webpage using grid (a common use case for it.) 53 | 54 | ```html 55 | 88 |
89 |
the header
90 |
the body
91 |
the sidebar
92 |
the footer
93 |
94 | ``` 95 | 96 | - `grid-area` allows you to name a section and refer to it later as that. It doesn't need to match the class name (but it can, they have nothing to do with each other.) 97 | - We used `grid-template-columns` before. `grid-template-areas` allows you to do a similar thing but to refer to the columns by name. As you can see here, it also allows us to have columns that reach across rows. Pretty neat. 98 | - The `.` here just refers to an empty cell. 99 | - You don't have to do the lined-up spacing like I did (I normally don't) but I wanted it to be clear for you. 100 | - The padding and color here are for you to visualize the grid better. 101 | 102 | This is a very quick intro to a powerful feature. There's a lot more to what grid can do. To read more [see here from CSS Tricks][css-tricks]. To watch more, [watch Jen's class here][fem]. 103 | 104 | [css-tricks]: https://css-tricks.com/snippets/css/complete-guide-grid/ 105 | [fem]: https://frontendmasters.com/courses/css-grid-flexbox-v2/ 106 | -------------------------------------------------------------------------------- /lessons/03-css/G-animations.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | CSS animations are a deep pool of knowledge and we're just going to look at a few of the techniques. [You can achieve some truly amazing things with _just_ CSS animations][solar-system]. You can also use JavaScript with CSS to make anything you can imagine. 6 | 7 | ```html 8 | 20 |
🤢
21 | ``` 22 | 23 | - The `@keyframes` part allows you to define a reusable animation. We could throw `spin` on anything now. 24 | - We just definte `to` which is what you want the end state of your animation state to be. We could define `from` too which is where you want your animation to start from. Implicitly if you don't define a from, your animation starts from where it is already. In this case, the `spinny-boi` isn't rotated at all, or `transform: rotate(0deg)`. So it will rotate from 0 t0 360, or a full revolution. Feel free to play with it. 25 | - `animation` is the property to _use_ a defined keyframe. Here we just say "do what spin defines, the whole animation should take a half second, do it infinitely (aka never stop), and do it in a linear fashion (so there's no speed-up or speed-down in the animation). This is a short hand. You can define the properties separately. [See the MDN here][mdn]. 26 | 27 | ## Easing 28 | 29 | Animation implies some sort of change over time. You're going from 0 to 1 in some capacity. The rate of change can be manipulated to achieve some cool effects. In the previous example you saw a linear function (these effects are called functions) where it's just a smooth line between 0 and 1. Let's check out the other possibilities. 30 | 31 | ```html 32 | 79 | 80 | 88 | ``` 89 | 90 | Notice all of them some sort of what we'd call "rubber-banding" effect where it speeds up and slows down in some way. Imagine we could use ease-out to simulate the gravity of a bouncing ball or something like that. 91 | 92 | The last one, cubic-bezier allows you to define your own curve via mathematics that I don't understand well enough to explain to you. But I wanted to show you if you have exact needs that one of the functions doesn't fit you can accomplish with cubic-bezier. 93 | 94 | [Read more on the MDN here][mdn] 95 | 96 | ## Beyond Positioning 97 | 98 | I've been showing you how to do this with positioning since it's the easiest to visualize. _Many_ CSS properties are able to be animated so let's see one example here. 99 | 100 | ```html 101 | 146 |
Rainbow
147 | ``` 148 | 149 | - Similar idea but with color. Lots of things can be animated, like size and position. 150 | - `from` is an alias for `0%` and `to` is an alias for `100%`. You can define percentages like we did here. 151 | 152 | [solar-system]: https://codepen.io/juliangarnier/pen/krNqZO 153 | [mdn]: https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function 154 | -------------------------------------------------------------------------------- /lessons/03-css/H-putting-it-together.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | So far I've shown you a lot of various techniques but we haven't really put it together yet. So let's do that now, let's put together a new paper front page. 6 | 7 | Here's the design: 8 | 9 | ![The News Times design](/complete-intro-to-web-dev-v3/images/the_news_times.png) 10 | 11 | Here are the product requirements: 12 | 13 | - Implement however you see fit. There are many right ways to do this. 14 | - The nav links at the tops are indeed links. It should underline whenever a user hovers on it. 15 | - You can put any image there but I used `http://pets-images.dev-apis.com/pets/dog25.jpg` 16 | - I'll give you the HTML (since in this section we're just worried about CSS.) [Grab the HTML here][html] (you'll have to right-click and click "View Source"). You don't necessarily have to modify the HTML but you can if you want to. 17 | 18 | [See my version of the CSS here][my-version] once you've tried yourself. It's okay to struggle. This is putting a lot of hard stuff together. Give it a shot before looking at how I did it. A learning is doing while you struggle. 19 | 20 | [html]: /complete-intro-to-web-dev-v3/project-files/news.html 21 | [my-version]: /complete-intro-to-web-dev-v3/project-files/news.css 22 | -------------------------------------------------------------------------------- /lessons/03-css/I-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Let's do a project together. 6 | 7 | This will stretch you a bit and make you use everything we learned together. It's a bit advance but you have the skills to do it. 8 | 9 | We're going to make the checkout page for a coffee app. It looks like this: 10 | 11 | ![Coffee app design](/complete-intro-to-web-dev-v3/images/coffee_masters.png) 12 | 13 | Some notes: 14 | 15 | - You get to do the HTML and the CSS here. 16 | - [The logo for Coffee Masters is here.](/complete-intro-to-web-dev-v3/images/coffee_masters_logo.png) 17 | - We didn't talk about how to make rounded corners. It's with [border-radius][border-radius]. 18 | - There are many ways to do this. Flex and grid would work well, just don't go overboard with grid, it'd be hard to 100% handle this layout in just grid. 19 | - HTML inputs can be styled with HTML just as you expect. 20 | - We also didn't talk about box shadows. You can give it the nice little shadow effect with [the CSS property box-shadow][box-shadow]. 21 | - I took about 100 lines of HTML and 150 lines of CSS. Again, you _do not need to do it the way I did_. There are infinite correct ways to do this. 22 | - I ended up using grid and flex in different places. Neither is required but can make some parts easier. 23 | - You don't have to match the design _perfectly_. Just get the general style close. 24 | 25 | ## Adding a web font 26 | 27 | You can add a font using Google Web Fonts (lots of ways to do it, Google's is easy) by adding this tag in the `` 28 | 29 | ```display-html 30 | 34 | ``` 35 | 36 | and by adding `font-family: 'Open Sans', sans-serif;` to whatever you want to change the font to be. You don't have to but it's pretty cool! [Check out Google Fonts][font] if you're curious. 37 | 38 | ## Overlayed Input 39 | 40 | ```display-html 41 |
42 | 43 | 44 |
45 | ``` 46 | 47 | ```html 48 | 70 | No HTML to display here 71 | ``` 72 | 73 |
74 | 75 | 76 |
77 | 78 | Just a quick example to show you how to overlay a label over an input's border 79 | 80 | - We're using a containing div with `position: relative` set on it. This is so the `position: absolute` of the input label is absolute relative to to the containing div, and not the whole page. Try taking off the relative position of the container and watch the label fly somewhere else. 81 | - Labels allow users to click on the label and have that click select the input. Hence the `for=""` attribute and how it matches the `id` of the input. 82 | - I then just eyeballed the best left and top values to use. 83 | 84 | ## Good Luck! 85 | 86 | This time I'm going to make you write both the HTML and the CSS. Consider this a capstone project for the first two sections! 87 | 88 | Design credits for Coffee Masters to [Alex Danielson][alex] (he also did this website!) 89 | 90 | This is a hard one! Give it your best shot and tweet me the results! 91 | 92 | - [Here is my HTML (you'll need to view source)][html] 93 | - [Here is my CSS][css] 94 | 95 | [html]: https://btholt.github.io/complete-intro-to-web-dev-v3/project-files/coffee/index.html 96 | [css]: https://btholt.github.io/complete-intro-to-web-dev-v3/project-files/coffee/style.css 97 | [border-radius]: https://css-tricks.com/almanac/properties/b/border-radius/ 98 | [box-shadow]: https://css-tricks.com/almanac/properties/b/box-shadow/ 99 | [alex]: https://www.alexdanielson.com/ 100 | [fonts]: https://fonts.google.com/ 101 | -------------------------------------------------------------------------------- /lessons/03-css/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": "palette", 3 | "title": "CSS" 4 | } -------------------------------------------------------------------------------- /lessons/04-javascript/A-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | This is a tough section to teach because the needs of the student vary quite widely here. Some of you are programmers from other languages who are coming here to learn front end coding. Some of you are totally new to coding and JavaScript will be your first programming language. Just like if you know Spanish already, learning Italian becomes a whole lot easier since you take the same concepts and express them with minor variances. This workshop is optimizing for those totally new to coding and thus we assume no prior knowledge. If you do know another progamming language, I invite you instead to skim this section in familiarize with how JavaScript looks. The next sections will aimed also at you. 6 | 7 | ## What is code? 8 | 9 | A dumb question but its answer may surprise you. Code is for humans first and computers second. You can think of writing code as essentially writing notes on how to solve a particular problem that just happens to be in a way that computer can understand it. 10 | 11 | Wait, why? The why is because you or someone else will have to go back and re-read that code some time in the future, maybe tomorrow or maybe in ten years (I've worked on code older than 10 years old, it's not fun.) As such, it's important to write code in such a way that it can understood as quickly as is reasonable. Think of it like a textbook: you don't want to read the entire textbook everytime you want to review a concept, you want to jump right to the page and learn just the thing you're looking for. Same with code: you want to jump right to the bit of code in question and understand it at a glance without re-reading the whole codebase. 12 | 13 | You will spend far longer maintaining this code than you will writing it the first time. Be explicit. Be deliberate. The point here is not to be clever but to be simple and to communicate clearly. Code is communication. 14 | 15 | Okay, given this, let's frame how this code works then. When you write code, the computer breaks it down into smaller pieces it can understand and then executes those one bit at a time. With JavaScript, only one thing is ever happening at a time (this is called being _single threaded_ but that is not a term you need to know.) In general, this means it executes line 1, then line 2, then line 3, etc. Let's that in action: 16 | 17 | ```javascript 18 | const monthlyRent = 500; 19 | 20 | const yearlyRent = monthlyRent * 12; 21 | console.log(yearlyRent); 22 | ``` 23 | 24 | The first thing that happens above is that we declare a variable, `monthlyRent`. The `const` keyword is how we let JavaScript know we're declaring a variable. Variable names always have to know no spaces in them, which is why we squish the words "monthly rent" together. In order to make this more readable, we use what's called _camel casing_, so-called because the capital letters in the middle make it look like humps on a camel. You can also use other styles of captialization, there's no rule it must be camel case in JavaScript; everyone just happens to do camel casing in JavaScript. 25 | 26 | Notice the `;` at the end of every line. This semi colon lets JavaScript know you've completed your thought. Think of this as the period/full-stop of the the programming world. 27 | 28 | Variable can be called _almost_ anything. You **can't** use keywords. An example would be `const const = 15`. `const` is a keyword so it can't be used as a variable name. You do want to give your variables good names, even if they end up being long sometimes. Imagine we have a huge file and 200 lines below we see the variable named `monthlyRent`: we'll know instantly what this variable does and we won't have to go try to read other parts of the code to figure it out. Always, always, always use good variable names. Seriously. Put time into it. Naming things is hard and it's a big part of your job. 29 | 30 | Okay, so after line one, I have a variable named `monthlyRent` that I can use as much as I want. In this case, it represents the number of `500` but it also semantically represents monthlyRent. Imagine if I had 1000 lines between where `monthlyRent` is declared and where `yearlyRent` is calculated. I could have just put `500` directly in `yearlyRent` but I don't because I now understand how that's calculated just by reading the code. Use variables. Use them everywhere. It makes your code way easier to read. Also, later, if my monthly rent changes, I can change it one place and everywhere I reference `monthlyRent` and `yearlyRent` get updated automatically. Powerful stuff. 31 | 32 | Okay, I want to calculate `yearlyRent`. I use the `*` to represent multiplication. I'm also mixing variables and numbers which is just fine. I also could have said `const yearlyRent = monthlyRent * monthsInAYear;` (assuming I put `const monthsInAYear = 12;` somewhere else) too and that would be a good idea. I would argue the two are roughly the same since it's obvious there are 12 months in a year. But you do what you think is most clear. That's your job. 33 | 34 | `console.log(yearlyRent);` is going to print whatever is stored in `yearlyRent` to the JavaScript console. The JavaScript Console is a part of the dev tools. If you need help finding them, [see here][devtools]. We'll explain how it works in a bit but for now just know that anything you put between the parenthesises gets logged out to your JavaScript console. 35 | 36 | Let's get this little snippet working in our browser. Make a new folder (I'll just a put it on my desktop) and add an index.html file with the following in it: 37 | 38 | ```display-html 39 | 40 | 41 | 42 | JavaScript Experiments 43 | 44 | 45 |

JavaScript Experiments!

46 | 47 | 48 | 49 | ``` 50 | 51 | That `script` tag is going to let us load JavaScript code into out HTML page. So make another file in the same folder called `experiments.js` (it really can be called anything as long as the script tag matches it.) Then in the that JS file put our code from above: 52 | 53 | ```display-javascript 54 | const monthlyRent = 500; 55 | 56 | const yearlyRent = monthlyRent * 12; 57 | console.log(yearlyRent); 58 | ``` 59 | 60 | Now, if you open your **HTML** file, not the JS file, in your browser and open your console, you should see the number `6000` being printed. Congrats! You just wrote your first code! 61 | 62 | [devtools]: https://webmasters.stackexchange.com/questions/8525/how-do-i-open-the-javascript-console-in-different-browsers 63 | -------------------------------------------------------------------------------- /lessons/04-javascript/B-numbers-strings-and-booleans.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | title: "Numbers, Strings, and Booleans" 4 | --- 5 | 6 | So far we've just dealt with numbers. Let's go further and start working with words and characters. In programming, we refer to these things are strings, as in a string of one-letter characters. An example of this would be 7 | 8 | ```javascript 9 | const myName = "Brian Holt"; 10 | console.log(myName); 11 | ``` 12 | 13 | You can see I use the " (double quote) to enclose everything I want to be in the string. In JavaScript you can also use ' (single quote) and ` (back tick) as well to demarcate strings. 14 | 15 | Strings, as you may imagine, are everywhere in programming. We're constantly keeping track of names, addresses, names of products, cities, etc. and thus constantly need strings. 16 | 17 | Let's go further. Strings let you connect them together through string concatenation. If I want to be able to greet someone based on their name, I might have something like this: 18 | 19 | ```javascript 20 | const firstName = "Brian"; 21 | const lastName = "Holt"; 22 | 23 | const sentence = "Hello " + firstName + " " + lastName + "! How are you!?"; 24 | const sentenceWithTemplate = `Hello ${firstName} ${lastName}! How are you!?`; 25 | 26 | console.log(sentence); 27 | console.log(sentenceWithTemplate); 28 | ``` 29 | 30 | The first way is the old way. We can use the `+` to tell JavaScript to connect two strings. Notice how we have insert the space between `firstName` and `lastName`. The computer only does exactly what you tell it to do. If you don't insert that space, it doesn't get put there. 31 | 32 | The second line is the new way of doing this. JavaScript got a large update in 2015 and it made things a lot easier. Now you can use the back tick (notice the first uses a double quote, you must use back ticks to do template strings) to do template strings. If you do that, anything inside of `${yourVariableHere}` gets output in the string. Cool, right? 33 | 34 | ## Booleans 35 | 36 | Sometimes you just need a simple true or false. These are where booleans are useful. Something like a light switch's state is best represented by a boolean. A light is either on (true) or off (false). You'd have something like `const lightIsOn = true;`. Useful and you'll see them everywhere. 37 | 38 | ## Number 39 | 40 | Some languages separate integers (whole numbers, like 1, 2, 3, 4, 500, 1000) and floats (1.2, 3.14159, 14.01, etc.) differently but not JavaScript. JavaScript just has one type of number, Number. A number is a number. 41 | -------------------------------------------------------------------------------- /lessons/04-javascript/C-control-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Sometimes I want to modify the flow of how my program works, or in other words, some time I only want to run code if some condition is true. This is where `if` statements are very useful. Imagine if we tried this. 6 | 7 | ```javascript 8 | const skyIsBlue = true; 9 | 10 | if (skyIsBlue) { 11 | console.log("The sky is blue!"); 12 | } else { 13 | console.log("The sky is … not blue?"); 14 | } 15 | ``` 16 | 17 | In the above example, the condition inside of the parentheses is evaluated and if it's true, the first block is run and the second is skipped. If it is false, the second block is run and the first block is skipped. Paste that code into your experiments and play with it. You also do not have to have an else block. Okay, let's go further. 18 | 19 | ```javascript 20 | // if you see three lines, it's just three = in a row, ===. the font just combines them into one big character 21 | if (2 + 2 === 4) { 22 | console.log( 23 | "Oh thank god, the fundamental principles of mathematics still hold true." 24 | ); 25 | } else { 26 | console.log("Uh, panic?"); 27 | } 28 | ``` 29 | 30 | You can put any expression (a technical terms, means anything you can stick on the right side of an equal sign, we'll explore it more as we go) inside of the if statement. In this case, we are asking, is two plus two still equal to four. If this is true (I hope so) then again the first block will be run. If not, the second will be. 31 | 32 | Let's talk about `===` for a second. If you use just one `=` in JavaScript, it means **is assigned to**. So when we have `const isBrianCool = true;` you can verbalize that as "The variable isBrianCool is assigned to true". Thus we can't use that inside of the if statement because that's not what we mean. We're trying to ask a question, not assign something. We're trying to ask "is two plus two equal to four." Enter the triple equals. Triple equals is the same as asking "is this equal to that." We use the triple equals instead of the double equals because double equals does a lot of funny business that usually we don't want it to do. It does what's called coercion and we'll talk about that below. But in an example `2 == "2"` but it does not `2 === "2"`. String 2 is double equal to number 2 but string 2 is not triple equal to number 2. 33 | 34 | There's also `!==`. This is asking "is this not equal to that". Lastly you can ask with numbers `>` `>=` `<` `<=` as well to ask if numbers less than or greater than too. For another example: 35 | 36 | ```javascript 37 | const friendsAtYourParty = 10; 38 | 39 | if (friendsAtYourParty === 0) { 40 | console.log("Cool, now I have a lot of nachos to myself."); 41 | } else if (friendsAtYourParty <= 4) { 42 | console.log("Perfect amount to play some Mario Kart."); 43 | } else { 44 | console.log("Wooooo turn on the dance music!"); 45 | } 46 | ``` 47 | 48 | This also demonstrates the `else if` if you have more than just two different conditions. 49 | -------------------------------------------------------------------------------- /lessons/04-javascript/D-loops.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Okay so now what if I want do one thing multiple times? I could do something like this 6 | 7 | ```javascript 8 | let friendsAtYourParty = 0; 9 | friendsAtYourParty = friendsAtYourParty + 1; 10 | friendsAtYourParty = friendsAtYourParty + 1; 11 | friendsAtYourParty = friendsAtYourParty + 1; 12 | friendsAtYourParty = friendsAtYourParty + 1; 13 | friendsAtYourParty = friendsAtYourParty + 1; 14 | friendsAtYourParty = friendsAtYourParty + 1; 15 | friendsAtYourParty = friendsAtYourParty + 1; 16 | friendsAtYourParty = friendsAtYourParty + 1; 17 | friendsAtYourParty = friendsAtYourParty + 1; 18 | friendsAtYourParty = friendsAtYourParty + 1; 19 | console.log(friendsAtYourParty); 20 | ``` 21 | 22 | That's annoying though. I wish there was a better way. Before we explore that, let's chat about this example a tad more. 23 | 24 | We used `let` instead of `const`. Things that are `const` cannot be reassigned later. In general I find this be of minor help but others do not so I leave you to make your own judgement call. In general one should try to follow the "principle of least power." You should always choose the least powerful "thing" to accomplish whatever you're trying to do. Things with less power tend to be simpler and simple things are less prone to having or causing bugs. Why don't you cut your hair with garden shears? You could, it'd work, but it's way easier to screw it up and has worse consequences. Same general idea here. Right tool for the right job. 25 | 26 | We instead use `let` here because you can see on the subsequent lines we do reassign `friendsAtYourParty` to be a different number. If you used `const` your code would crash because `const` won't let you do that. Thus here we use `let`. There's another one called `var` that is the old way of doing JavaScript. There are differences but I don't see a reason to use `var` at all anymore. It behaves more similar to `let`. 27 | 28 | Okay, so, we want to do this better, let's explore a few ways to do that using loops. 29 | 30 | ```javascript 31 | let friendsAtYourParty = 0; 32 | while (friendsAtYourParty < 10) { 33 | friendsAtYourParty = friendsAtYourParty + 1; 34 | } 35 | console.log(friendsAtYourParty); 36 | ``` 37 | 38 | This is a while loop. The first part works similar to an `if` statement: as long as what's inside that statement is **true** it will continue running and re-running the body (what's between the `{ }`) until that statement is false. Once that statement is false, it'll break the loop and continue on. This case, we add 1 to `friendsAtYourParty` until it's 10, and then the next loop, when it's 10, it'll stop because 10 is not less than 10. 39 | 40 | Also, let's just show you a few shortcuts for adding one to a thing 41 | 42 | ```javascript 43 | let friendsAtYourParty = 0; 44 | friendsAtYourParty = friendsAtYourParty + 1; 45 | friendsAtYourParty += 1; 46 | friendsAtYourParty++; 47 | ++friendsAtYourParty; 48 | console.log(friendsAtYourParty); 49 | ``` 50 | 51 | Those four lines are equivalent. They all do the exact same thing: they add one to the existing total. The second one, the plus-equals line, you can put any number there and it'll add that amount to total, so `friendsAtYourParty += 15;` would add 15 to the total. It also works with `-=` (subtraction,) as well as `*=` (multiplication,) `/=` (division,) and `**=` (exponent.) Two last two lines (`++` before or after) just signify add one. They more-or-less mean the same thing (there's a subtle different of _when_ it adds one that should never matter to you) but suffice to say everyone in the JavaScript community _always_ does the `++` after; I've never seen anyone do it before in JavaScript. `--` Works as well to subtract one as well. 52 | 53 | Okay, so now let's see a second kind of loop to achieve the same effect as above. 54 | 55 | ```javascript 56 | let friendsAtYourParty = 0; 57 | for (let i = 0; i <= 10; i++) { 58 | friendsAtYourParty++; 59 | } 60 | console.log(friendsAtYourParty); 61 | ``` 62 | 63 | This is a for loop which is likely the most common kind of loop. Inside the parens are three statements and you need all of them. The `let i = 0;` is you defining your control variable that will control the loop. For some reason people always use `i`, not sure why. It's just that way. It really could be anything. The second statement `i <= 10` is just like the while loop, is that's the statement that is as soon as it's false it breaks the loop. The last statement, `i++` is that happens at the end of every loop. In our case, we increment the control variable `i` so that it creeps closer to the end of the loop each time. 64 | 65 | An important note: in coding, we start counting from 0. In English, we count `1, 2, 3, 4, 5, etc.` but in coding we count `0, 1, 2, 3, 4, etc.`. So the fifth element of a string is index 4 (where index is how we'd refer to where that item is in the string). Index 0 is the first element. It's weird but you get used to it and it makes a lot of things easier. 66 | 67 | Sometimes, if you mess up what's inside the control condition for the loop, you'll get a runaway loop that'll never complete. This is called an **infinite loop** and it'll lock up and crash your code. Something like this: 68 | 69 | ```display-javascript 70 | let friendsAtYourParty = 1; 71 | while (friendsAtYourParty > 0) { 72 | friendsAtYourParty = friendsAtYourParty + 1; 73 | } 74 | console.log(friendsAtYourParty); 75 | ``` 76 | 77 | Since you're adding one to `friendsAtYourParty` each time, and the loop will continue each time until it's less than zero, that condition will never happen. Thus it'll continue going until it crashes your code. Be careful of these. Nasty bugs. 78 | -------------------------------------------------------------------------------- /lessons/04-javascript/E-exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | In your experiments.js, 6 | 7 | - Write some code that declares two variables, character and timesToRepeat. 8 | - Using a loop, repeat that character that many times and then console.log it. 9 | - Example, if I had character = 'f' and timesToRepeat = 5, it'd console.log `'fffff'`. 10 | 11 | Try a few different combinations to make sure you got it right e.g. 'a' and 10, 'c' and 100, '🐶' and 3. 12 | 13 | ## Why? 14 | 15 | This is just an exercise to get you flex your newly-gained muscles a bit. This code itself wouldn't be super useful but it'll be useful for you to try to take an idea in words and translate that into workable code. 16 | 17 | Scroll down to see my answer. 18 | 19 |
20 | 21 | ```javascript 22 | const timesToRepeat = 100; 23 | const character = "🐩"; 24 | 25 | let word = ""; // start with an empty string 26 | for (let i = 0; i < timesToRepeat; i++) { 27 | word = word + character; 28 | } 29 | 30 | console.log(word); 31 | ``` 32 | -------------------------------------------------------------------------------- /lessons/04-javascript/F-functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | A function is a bit of re-usable code. Just how we like to re-use CSS classes, we love to re-use code. Let's start with an example: 6 | 7 | ```javascript 8 | function addTwo(number) { 9 | return number + 2; 10 | } 11 | 12 | const finalAnswer = addTwo(5); 13 | console.log(finalAnswer); 14 | ``` 15 | 16 | This isn't super useful but hopefully it shows you the mechanics of how a function works. We created a function called `addTwo`. This function takes in one parameter, `number` and it returns that number with 2 added to it. We can now use that `addTwo` function as much as we want! Let's make a something a bit more useful. 17 | 18 | ```javascript 19 | function greet(firstName, lastName, honorific, greeting) { 20 | return `${greeting} ${honorific} ${lastName}! I’m extremely pleased you could join us, ${firstName}! I hope you enjoy your stay, ${honorific} ${lastName}.`; 21 | } 22 | 23 | console.log(greet("Brian", "Holt", "Lord", "Salutations")); 24 | console.log(greet("Jack", "Sparrow", "Captain", "A-hoy")); 25 | ``` 26 | 27 | Now we rather than have to repeat ourselves over-and-over again with that long string, we can just call greet with the appropriate parameters. Here we use four parameters. The order is important that we send in the parameters because this will be the order function receives these parameters. You can have as many or as few parameters as you like. 28 | 29 | The way to **call** a function is you add parens to the end of it, like this: `someFunctionName()`. If you see parens after a variable name, you instantly know that that's a function. Inside of the parens go the parameters. These variables will be passed to the function that is being called in the order that you put them there. These input variables are called **parameters**. Example: 30 | 31 | ```javascript 32 | const myHomeCity = "Seattle"; 33 | const myHomeState = "Washington"; 34 | const myHomeCountry = "USA"; 35 | 36 | function logOutYourHome(city, state, country) { 37 | console.log(`You are from ${city}, ${state} ${country}.`); 38 | } 39 | 40 | logOutYourHome(myHomeCity, myHomeState, myHomeCountry); 41 | ``` 42 | 43 | ## Various Ways of Writing Functions 44 | 45 | There are a few ways to write functions that are mostly the same (there are some differences but for now don't worry about it.) 46 | 47 | ```javascript 48 | function bark() { 49 | console.log("woof"); 50 | } 51 | 52 | const meow = function () { 53 | console.log("meeeeeeeow"); 54 | }; 55 | 56 | // the => is just = > put together, the font just combines them to one glyph 57 | const chirp = () => { 58 | console.log("chirp chirp"); 59 | }; 60 | 61 | bark(); 62 | meow(); 63 | chirp(); 64 | ``` 65 | 66 | All of these are functions and work as such. There are very subtle differences in how they work but for now it's enough to know "those are functions". Most of the time they work exactly the same (as you see here.) 67 | -------------------------------------------------------------------------------- /lessons/04-javascript/G-scope.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | We'll talk about scope multiple times but we'll start off here with it. Every time you call a function, it has its own scope. Other things can't peek into it; it just has its own little workspace for it work with. Once its done, any variable that you haven't explicitly held on to or returned at the end is discarded. For example: 6 | 7 | ```javascript 8 | function addFive(number) { 9 | const someVariable = "you can't see me outside this function"; 10 | return number + 5; 11 | } 12 | 13 | addFive(10); 14 | console.log(someVariable); 15 | ``` 16 | 17 | This is not going to work. `someVariable` is inside of the `addFive` scope and once `addFive` completes, it throws `someVariable` away since it's now out-of-scope. 18 | 19 | ```javascript 20 | let friendsAtYourParty = 0; 21 | for (let i = 0; i <= 10; i++) { 22 | friendsAtYourParty++; 23 | } 24 | console.log(i); 25 | ``` 26 | 27 | Even this doesn't work since `i` is only in scope for the loop and then after that it's thrown away. This can be a difficult one to deal with as someone new to coding because you'll go to log something or use a variable and it's out of scope so it's not there. Just know if that happens, this is probably the problem. 28 | 29 | Scope is hard. And scope is particularly strange in JavaScript (it varies by programming language.) If it feels hard it's because it is. A general, imperfect way for you to think about it right now is that a variable is "alive" (in scope) in between whatever the closest `{` is until that `{` closes its corresponding `}`. A few examples below, see if you can get it right. Keep in mind that the variable will stay in scope as long as any scope it exists in still exists. If I declare a variable in an outter scope and modify a variable in an inner scope, that variable will survive as long as the outter scope does. **It matters where the variable is declared.** 30 | 31 | ```display-javascript 32 | const A = "A"; 33 | let F; 34 | 35 | function doStuff(B) { 36 | console.log(B); 37 | const C = "C"; 38 | let H = "H"; 39 | if (1 + 1 === 2) { 40 | const D = "D"; 41 | H = "something else"; 42 | } 43 | console.log(D); 44 | console.log(H); 45 | F = "F"; 46 | } 47 | 48 | let E = 0; 49 | while (E < 3) { 50 | E++; 51 | console.log(A); 52 | const G = "G"; 53 | } 54 | console.log(E); 55 | console.log(G); 56 | 57 | doStuff("B"); 58 | console.log(B); 59 | console.log(C); 60 | console.log(F); 61 | ``` 62 | 63 | This is pretty convulated example but see what you think. Once your ready, the next block will be the answers. 64 | 65 | ```display-javascript 66 | const A = "A"; 67 | let F; 68 | 69 | function doStuff(B) { 70 | console.log(B); // works, B parameter is still in scope 71 | const C = "C"; 72 | let H = "H"; 73 | if (1 + 1 === 2) { 74 | const D = "D"; 75 | H = "something else"; 76 | } 77 | console.log(D); // does not work, D was declared in that if statement block 78 | console.log(H); // works, H was declared outside the if statement 79 | F = "F"; 80 | } 81 | 82 | let E = 0; 83 | while (E < 3) { 84 | E++; 85 | console.log(A); // works, the outter block (called the global scope) is still in scope 86 | const G = "G"; 87 | } 88 | console.log(E); // works, E was declared outside the whie loop 89 | console.log(G); // does not work, declared inside the while loop and it's over 90 | 91 | doStuff("B"); 92 | console.log(B); // does not work, the B parameter expires after the function call 93 | console.log(C); // does not work, C was declared inside the function and the function is over 94 | console.log(F); // works, F was declared in the global scope 95 | ``` 96 | -------------------------------------------------------------------------------- /lessons/04-javascript/H-builtins.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | ## Builtins 6 | 7 | Lots of functions already exist for you! Smart people have created this commonly-used functions for things we often need. For example, say you have a string and you want to make everything lowercase, you can do this: 8 | 9 | ```javascript 10 | const sentence = "ThIs HaS wEiRd CaSiNg On It"; 11 | const lowerCaseSentence = sentence.toLowerCase(); 12 | console.log(lowerCaseSentence); 13 | ``` 14 | 15 | Always be looking for the parens. And the best place to look all this stuff up is from our friends at Mozilla (makers of Firefox): [the MDN][mdn]. MDN used to stand for "Mozilla Developer Network" I think but now it's just synonmous with the documentation for the web. I literally look at this website several times a day. As I said before, you are not expected to remember everything. Looking things up on the MDN is **not** cheating. 16 | 17 | There are so many builtins there's no way we could ever cover all of them. Here's just a few examples. The rest you'll learn as you go. 18 | 19 | ```javascript 20 | // want to round a number? use Math! 21 | const number = 5.3; 22 | const roundedNumber = Math.round(number); 23 | console.log(number); 24 | ``` 25 | 26 | --- 27 | 28 | --- 29 | 30 | ```javascript 31 | // want to see if a string contains another string? 32 | const testStringOne = "The quick brown fox jumps over the lazy dog"; 33 | const testStringTwo = 34 | "Mirror, mirror on the wall, don't say it cause I know I'm cute"; 35 | const stringToLookFor = "cute"; 36 | 37 | console.log(testStringOne.includes(stringToLookFor)); 38 | console.log(testStringTwo.includes(stringToLookFor)); 39 | ``` 40 | 41 | --- 42 | 43 | --- 44 | 45 | ```javascript 46 | // want to know how many milliseconds have elapsed since Jan 1 1970? 47 | console.log(Date.now()); 48 | ``` 49 | 50 | [mdn]: https://developer.mozilla.org/en-US/ 51 | -------------------------------------------------------------------------------- /lessons/04-javascript/I-objects.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | So far we've talked about having one variable at a time: one first name, one last name, one price, etc. What if we have a collection of data? It'd be nice to group together like data. Good news! You can! 6 | 7 | ```javascript 8 | const person = { 9 | name: "Brian Holt", 10 | city: "Seattle", 11 | state: "WA", 12 | favoriteFood: "🌮", 13 | wantsTacosRightNow: true, 14 | numberOfTacosWanted: 100, 15 | }; 16 | console.log(person); 17 | console.log(person.name); 18 | console.log(person["name"]); // same as the line above; prefer using the other one 19 | ``` 20 | 21 | This is called an object. They're extremely useful in JavaScript; they're how you'll group together like-information so that they can be used together. They contain a bunch of **keys** and **values**. The keys are on the left side of the `:` and represent how you get that piece of data out of the object. `name` is one such key, and the way I get the name of the 22 | 23 | Used in conjunction with functions they're very powerful. Take this example: 24 | 25 | ```javascript 26 | const person1 = { 27 | name: "Angie", 28 | ageRange: "25-35", 29 | }; 30 | const person2 = { 31 | name: "Francesca", 32 | ageRange: "65-75", 33 | }; 34 | 35 | function suggestMusic(person) { 36 | if (person.ageRange === "25-35") { 37 | console.log("We think you will like Daft Punk."); 38 | } else if (person.ageRange === "65-75") { 39 | console.log("You are obviously going to like Johnny Cash."); 40 | } else { 41 | console.log( 42 | "Uh, maybe try David Bowie? Everyone likes David Bowie, right?" 43 | ); 44 | } 45 | } 46 | 47 | suggestMusic(person1); 48 | suggestMusic(person2); 49 | ``` 50 | 51 | Now we're able to pass all this information as one package which makes it easy to keep track of since we're just passing one variable. You'll see this become even more useful as we start integrating with servers and APIs. 52 | 53 | Objects can even have their functions! Let's see that. 54 | 55 | ```javascript 56 | const dog = { 57 | name: "dog", 58 | speak() { 59 | console.log("woof woof"); 60 | }, 61 | }; 62 | 63 | dog.speak(); 64 | ``` 65 | 66 | Objects can as well have nested objects inside of them. 67 | 68 | ```javascript 69 | const me = { 70 | name: { 71 | first: "Brian", 72 | last: "Holt", 73 | }, 74 | location: { 75 | city: "Seattle", 76 | state: "WA", 77 | country: "USA", 78 | }, 79 | }; 80 | 81 | console.log(me.name.first); 82 | console.log(me.location.state); 83 | ``` 84 | -------------------------------------------------------------------------------- /lessons/04-javascript/J-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Given an object with a person's address, wouldn't it be nice if we could use a function to nicely print out a properly formatted shipping address? 6 | 7 | ```javascript 8 | const me = { 9 | name: { 10 | first: "Brian", 11 | last: "Holt", 12 | }, 13 | location: { 14 | streetNumber: 500, 15 | street: "Fakestreet", 16 | city: "Seattle", 17 | state: "WA", 18 | zipCode: 55555, 19 | country: "USA", 20 | }, 21 | getAddress() { 22 | return `${this.name.first} ${this.name.last} 23 | ${this.location.streetNumber} ${this.location.street} 24 | ${this.location.city}, ${this.location.state} ${this.location.zipCode} 25 | ${this.location.country}`; 26 | }, 27 | }; 28 | 29 | console.log(me.getAddress()); 30 | ``` 31 | 32 | This is our first time seeing the weird `this` keyword. This is a strange, complicated, and difficult concept in JavaScript known as context and trips up all sorts of people, new and old to the language. If you decide to pursue a career as a developer, interviewers will often ask questions about context in JavaScript. It's worth investment to understand how it works eventually (not now, focus just focus on basic JS for now.) 33 | 34 | In the simplest form, anywhere you are in JavaScript you have a context you are in. You can reference that context by using `this`. If I just reference `this` from the outtermost layer, it'll be the global object, which in the browser is something called `window`. `window` already has a bunch of stuff on it. For example: 35 | 36 | ```javascript 37 | console.log(this === window); 38 | console.log(this.scrollY); 39 | console.log(window.scrollY); 40 | ``` 41 | 42 | As you can see from the first line, you can see that in this context, window is the `this` at that time. However, in the example above when we're doing the address, the `this` is the object since when I **call the function**, it's created inside of an object. That object then becomes `this` when `getAddress` is called. As soon as the function completes, the context is destroyed and the context goes back to being what it was before, in this case `window`. 43 | 44 | A good rule of thumb (that is unfortunately not always true) is that if you're inside an object of some sort, the `this` will be that object. If not, it'll be the global object, `window`. There are crazy exceptions to this and you can even manipulate it yourself. For now, operate with that definition. It's a deep-and-dark rabbit hole to go down so let's continue and you can take [Kyle Simpson's course][kyle] later where he goes in depth on it. 45 | 46 | [kyle]: https://frontendmasters.com/courses/getting-started-javascript-v2/ 47 | -------------------------------------------------------------------------------- /lessons/04-javascript/K-arrays.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | Objects are un-ordered collections of data using keys and values. Arrays, in contrast, are **ordered collections of data**. If you put something in an array, it has an order. For example, you might a list of the days of the week. 6 | 7 | ```javascript 8 | const daysOfTheWeek = [ 9 | "Monday", 10 | "Tuesday", 11 | "Wednesday", 12 | "Thursday", 13 | "Friday", 14 | "Saturday", 15 | "Sunday", 16 | ]; 17 | console.log(daysOfTheWeek); 18 | console.log(daysOfTheWeek[0]); 19 | console.log(daysOfTheWeek[1]); 20 | console.log(daysOfTheWeek[6]); 21 | ``` 22 | 23 | You first can see how we declare an array, using `[ ]`. Inside of an array, you can store anything you can store in a variable. You can have an array of numbers, an array of strings, an array of objects, an array of arrays, an array of arrays of arrays, etc. 24 | 25 | You can also see above how we access individual elements in an array: we use square brackets again and then we reference the number that we want to access. Again, remember, the numbering starts at 0. So the first element is index 0. 26 | 27 | Arrays also have many methods (another word for functions that live on an object) and properties (another word for key/value pairs) that live on them. Let's see some of those: 28 | 29 | ```javascript 30 | const primeNumbers = [1, 2, 3, 5, 7, 11, 13, 17]; 31 | console.log(primeNumbers.length); 32 | console.log(primeNumbers.join(" | ")); 33 | ``` 34 | 35 | `primeNumbers.length` gives you back an number that is how long the array is. In this case there are eight elements in the array so it gives us back `8`. `primeNumbers.join(" | "))` takes your whole array and makes it into one string. The `" | "` paramenter I'm passing is what I want put between each element, so you end up with the string `"1 | 2 | 3 | 5 | 7 | 11 | 13 | 17"`. 36 | 37 | So what if I want to add an element to the array after I've created. Use `push`! 38 | 39 | ```javascript 40 | const courses = [ 41 | { teacher: "Will Sentance", course: "JavaScript: The Hard Parts" }, 42 | { teacher: "Sarah Drasner", course: "Intro to Vue" }, 43 | { teacher: "Brian Holt", course: "Complete Intro to React" }, 44 | { teacher: "Steve Kinney", course: "Build Your Own Programming Language" }, 45 | { teacher: "Scott Moss", course: "Intro to Node.js" }, 46 | ]; 47 | 48 | courses.push({ teacher: "Jen Kramer", course: "Getting Started with CSS" }); 49 | 50 | console.log(courses); 51 | 52 | courses[2] = { teacher: "Brian Holt", course: "Complete Intro to Databases" }; 53 | 54 | console.log(courses); 55 | ``` 56 | 57 | The first thing we do is add an element to the end using the push function that arrays have. It "pushes" the element on the end. 58 | 59 | Below that, we're overriding index 2 with a new course. This will throw away what was there before and set it to be what we've set it to be. 60 | 61 | Okay, now, given that, what if we wanted to `console.log` everything in the array? You already have all the tools to do that? Let's see to do it. 62 | 63 | ```javascript 64 | const cities = [ 65 | "Seattle", 66 | "San Francisco", 67 | "Salt Lake City", 68 | "Amsterdam", 69 | "Hong Kong", 70 | ]; 71 | 72 | // method 1 73 | for (let i = 0; i < cities.length; i++) { 74 | console.log(cities[i]); 75 | } 76 | 77 | // method 2 78 | cities.forEach(function (city) { 79 | console.log(city); 80 | }); 81 | ``` 82 | 83 | The first way, using a for loop, we're using that `i` control variable which gets incremented each loop. We use that `i` to access each item in the array on each iteration of the loop. We have the loop stop when `i` gets equal to the `length` of cities. Very useful pattern. You'll see it a lot. 84 | 85 | The second way is using a function that arrays have called `forEach`. This `forEach` method takes in a function and that function will be called once on each item of the array. It will pass that item into the function, which is what `city` is in this situation. Both are useful patterns to know. You'll use both frequently. While you're getting started, just use the one you feel comfortable with. They have different things that make them preferable in different situations but usually you can use either. Method 2 may be a bit more advance but I don't think you should be scared of it. For now prefer method 1. I just wanted you to see method 2. 86 | -------------------------------------------------------------------------------- /lessons/04-javascript/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": "bolt", 3 | "title": "JavaScript" 4 | } -------------------------------------------------------------------------------- /lessons/05-putting-it-all-together/A-the-dom.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | title: "The DOM" 4 | --- 5 | 6 | So far we've been writing code pretty well in a vacuum. We've been using `console.log` as the output mechanism. We haven't really done anything in JavaScript that couldn't be done in any other language. Now we're going to start using JavaScript to interact with your webpage. This is a unique feature to JavaScript (mostly, let's not explore that at the moment.) 7 | 8 | Let's first chat about what a browser is and how your code gets from you writing it to being on run in a browser. 9 | 10 | In a typical circumstance. 11 | 12 | 1. You write code in your editor (like VSCode) 13 | 1. You put your code on a server so that other people can get it 14 | 1. Someone visits your website 15 | 1. (Lots of stuff happens here. For now we're not going to talk about it) 16 | 1. Their browser makes a request to your server for your index.html 17 | 1. Your server sends them a copy of the html 18 | 1. The browser reads the HTML, sees you have a `my-script.js` script tag on there 19 | 1. Browsers makes another request for `my-script.js` from your server 20 | 1. Your server sends them a copy of `my-script.js` 21 | 1. The browser reads the JavaScript code and begins executing the code 22 | 23 | Same process happens with CSS too. 24 | 25 | Okay, so this is how it works if you have put your code on some server like in a cloud like Microsoft Azure, Amazon Web Services or other places like Bluehost or GoDaddy. So how are we doing it locally, without a server, just on our computers? Your computer is basically faking this process. It's acting as both the server and the client so that it's easier for you to write code. When you open a file in your browser from your computer, your hard drive is the server. This was a point of confusion for me when starting so I'm sharing it with you. 26 | 27 | Right now our computer isn't doing anything other than serving up the file unmodified. A real server can do a lot of things like read from databases, call other servers, or modify in-flight requests. We're not doing any of that. Right now it's just giving you back the raw file unmodified. 28 | 29 | Okay, now we've gotten that out of the way, let's begin making another project. 30 | 31 | ## The DOM 32 | 33 | The way that JavaScript and HTML/CSS interact with each other is a thing called the DOM, the Document Object Model. The DOM is basically a bunch of objects and methods that you can call from JavaScript to interact with the HTML/CSS of the page. 34 | 35 | Note: for these code samples, I'll show you the HTML and the CSS first (which won't be editable) and then I'll show you the JavaScript afterwards (which will be editable.) 36 | 37 | Let's see an example. 38 | 39 | ```display-html 40 |
41 | ``` 42 | 43 | ```css 44 | .red-square { 45 | color: crimson; 46 | width: 100px; 47 | height: 100px; 48 | } 49 | ``` 50 | 51 | 58 |
59 | 60 | ```javascript 61 | const redSquare = document.querySelector(".red-square"); 62 | redSquare.style.backgroundColor = "limegreen"; 63 | ``` 64 | 65 | Notice that, despite the CSS class dictating that the `div` should be `crimson` colored, it's actually `limegreen`. This is because we used JavaScript to change the color of it. So let's break it down. 66 | 67 | - We called a method on `document`. `document` is a globally available variable in the browser that you use to interact with the HTML and CSS. It has a lot of methods that you can use. In this case, we're using the `querySelector` in which you pass in a CSS selector and it returns to you the **first** one of that matches selector that it finds (if you have many of them on the page, you get just the first one.) 68 | - From there, we have a JavaScript pointer to the `div.red-square` tag stored in the `redSquare` variable which means we can start manipulating it. 69 | - We then use the `style` object which represents all the CSS styles that are being applied to that object at that time. 70 | - We then set the `backgroundColor` of that element. Notice it is `backgroundColor` and not `background-color` (camelCasing vs kebab-casing). This is how you interact with CSS via JavaScript. Anything that's kebab-cased like `padding-right` becomes camelCased, like `paddingRight`. While annoying, it'd be even more annoying if they didn't switch it since everything in JavaScript is camelCased. 71 | - We then just assign that to be whatever value we want. This works with any CSS property, eg: `tag.style.marginBottom = '50px'`. 72 | 73 | There's a lot more you can do with an element than just modifying its style. You can add more HTML into it, remove it, change the text, search for different elements inside of it, get its position on the page, clone it, and a lot more. 74 | 75 | Okay, so what if we had multiple elements we wanted to modify all at once. We have the tools to do that too! 76 | 77 | ```display-html 78 | 86 | ``` 87 | 88 | 96 | 97 | ```javascript 98 | const elementsToChange = document.querySelectorAll(".js-target"); 99 | for (let i = 0; i < elementsToChange.length; i++) { 100 | const currentElement = elementsToChange[i]; 101 | currentElement.innerText = "Modified by JavaScript!"; 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /lessons/05-putting-it-all-together/B-events-and-listeners.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | We've been able to modify HTML and CSS using JavaScript using `document`. Awesome! We're going to go one step further and start involving the user. Web sites are meant to be reactive to users. In order to be reactive to them, we need to wait for them to do stuff, like click a button or type in an input. The way we do that is we wait for **events** to happen. An event is created every time certain events happens like when a user clicks something or when they type something. We respond to these events by having what are called **event listeners**. We give an event listener a function to run whenever an event happens. Let's take a look at responding to a click when a user clicks a button. 6 | 7 | > NOTE: Since it's possible for a single tag to have multiple listeners, every time you type into the coding box, it's adding more event listeners to that same button. Therefore you'll have multiple alerts. 8 | 9 | ```display-html 10 | 11 | ``` 12 | 13 | 14 | 15 | ```javascript 16 | const button = document.querySelector(".event-button"); 17 | button.addEventListener("click", function () { 18 | alert("Hey there!"); 19 | }); 20 | ``` 21 | 22 | Let's break it down. 23 | 24 | - We grab the button via `querySelector` and store it in the JavaScript variable `button`. 25 | - We then call the `addEventListener` method on the button. This takes two parameters (no need to memorize this, you can always look it up): the name of the event you want respond to, which in this case is the `click` event, and a function that is called whenever that event happens. This function is often called a **callback** because it gets called back whenever the event happens. 26 | - We then call a function called `alert`. `alert` is a super, super annoying function that pops up a dialog window with whatever you call it with. 27 | - People often get confused seeing `});` on the last line. The first `}` is closing the function, the second `)` is closing the function call of `addEventListener`, and the `;` ends the statement. 28 | 29 | > `alert` comes from the DOM. It's technically `window.alert` but when you're working in a browser you can always leave out `window` since it's implied. This pretty much only ever happens with `window` so don't worry too much about it. Only becomes a problem when you start writing Node.js which is beyond the scope of this class. 30 | 31 | Let's do another example with an `input` tag. 32 | 33 | ```display-html 34 | 35 |

Nothing has happened yet.

36 | ``` 37 | 38 | 39 |

Nothing has happened yet.

40 | 41 | ```javascript 42 | const input = document.querySelector(".input-to-copy"); 43 | const paragraph = document.querySelector(".p-to-copy-to"); 44 | 45 | input.addEventListener("keyup", function () { 46 | paragraph.innerText = input.value; 47 | }); 48 | ``` 49 | 50 | Try typing into the input. You'll see whatever text you type into the input will instantly be reflected in the `p` tag. Pretty cool, right? 51 | 52 | - We're now using the `keyup` event. This event happens whenever you release a key after pressing it. As you may guess, there is a `keydown` event too that is fired whenever you press a key. We're using `keyup` because `keydown` happens _before_ a key actually registers which means we would always be one key behind. 53 | - We're reference `input.value`. The value property of an input reflects whatever the user has typed into the input. 54 | - We're taking whatever is in `input.value` and passing that directly into the `paragraph.innerText`. Since that function is called every time a user types into the input, it keeps the two in sync! 55 | 56 | One more example and then we'll move on. 57 | 58 | ```css 59 | .color-box { 60 | background-color: limegreen; 61 | width: 100px; 62 | height: 100px; 63 | } 64 | ``` 65 | 66 | ```display-html 67 |
68 | 69 | ``` 70 | 71 | 78 |
79 | 80 | 81 | ```javascript 82 | const input = document.querySelector(".color-input"); 83 | const paragraph = document.querySelector(".color-box"); 84 | 85 | input.addEventListener("change", function () { 86 | paragraph.style.backgroundColor = input.value; 87 | }); 88 | ``` 89 | 90 | Similar to above. The key difference here is that we're listening for `change` events. `change` events happen whenever a user types something in the input and then unfocuses the input by clicking somewhere else or hitting tab to change the focus. Try typing "red" and then clicking somewhere else. Also, try something that isn't a color. Notice that if you give it an invalid color it just doesn't change anything. 91 | 92 | ## Event Delegation 93 | 94 | If you have a bunch of elements that you need to listen for events on, you could attach an event listener to each but that's a bit tedious to do. Instead what is sometimes easier to do is to use what's called **event bubbling**. When event fires on an element, after that "bubbles" up to its parent, and then its parent, and its parent, etc. until it's at the root element. 95 | 96 | ```display-html 97 |
98 | 99 | 100 | 101 | 102 | 103 |
104 | ``` 105 | 106 |
107 | 108 | 109 | 110 | 111 | 112 |
113 | 114 | ```javascript 115 | document 116 | .querySelector(".button-container") 117 | .addEventListener("click", function (event) { 118 | alert(`You clicked on button ${event.target.innerText}`); 119 | }); 120 | ``` 121 | 122 | You can see that we only bound event listener, and that was the div above it. Then, when we click the button, we're using the `event` parameter that is being passed into the callback. You may be wondering where that came from. It was always there, we just ignoring it. An event listener's first parameter is always an event object. There's lots of information on the event object but we're most concerned with `event.target`. `target` is the tag that the event originated from. In this case it'll be the button that caused the event. And we know that with tags you can use the `innerText` property to get the text inside of them. That's how we able to alert the correct number. Cool, right? 123 | -------------------------------------------------------------------------------- /lessons/05-putting-it-all-together/C-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | 11 | 12 | Project time! 13 | 14 | We are going to rebuild the calculator on iOS! If you've never seen that before, here it is: 15 | 16 | 17 | 18 | My implementation of it is embedded here on the page so feel free to play with it. 19 | 20 | Let's going over the requirements: 21 | 22 | - The calculator should look like the above image 23 | - The calculator should function like a normal calculator 24 | - **Do not** implement `%` or `.`. You can assume everything will be an integer. 25 | - `C` means clear. When a user clicks it, it should clear everything and go back to the first state it was in when the page loaded. 26 | - Doing the back arrow is extra credit. It's like pressing backspace; it'll delete the last character typed. If it's clicked when there's only one digit, it sets the current number to be `0`. 27 | - Don't worry about if the number is too long for the screen. 28 | - Calculators tend to have some special behavior when you hit equals: if you type another number it erases the results and starts over. Feel free to do that but also free free (like me) to just treat it normally and make the user hit `C` if they want to clear it. Let's keep it simple. 29 | 30 | Okay, now that you have requirements, let's go over some tips and hints. 31 | 32 | ## HTML and CSS Tips and Hints 33 | 34 | - Programming is all about taking large problems and breaking them into smaller problems. If you're trying to tackle too much at once, break it into two smaller problems and try to solve one of those. 35 | - Personally, I wrote the HTML and CSS first. Once that's all taken care of, then I do the JavaScript. 36 | - For the font of the "result screen" I'd just use `monospace`. 37 | - There are so many ways to write this. There is no one right way. My solution is not the only nor is it the best solution. Experiment. Try. Fail. Succeed. It's all about learning here. 38 | - Good idea to use `` for the buttons. You have to deal with some extra styling stuff but it will make your code work pretty much automatically for disabled people. In general when writing HTML, if something serves the function of a button, make it a ``. 39 | - I used multiple rows of flex layed out divs for the button. You could do it all in one div using the `flex-wrap` property. 40 | - The secret to getting equal gutters (which is what you call the black space between buttons): you can set width to be `24.5%` (so four of them fit on a line) and then use `justify-content: space-between` to evenly space them. That'll give them a gutter of roughly `.5%` of the whole width. The problem with using percentages in conjuections with heights: your heights and widths are different. 5% of height is not the same of 5% of width, and that'll make the gutters look weird. You want the bottom gutters to be the same size as the side gutters. `margin-bottom` to the rescue! If you give the row a `margin-bottom` of `.5%` (if you're using my same numbers) then that'll work since margin is always measured as a function of width (just one of those things you have to know!) Hopefully that helps. 41 | - Sometimes I do the math to get things right. Sometimes I just guess-and-check to see if it looks okay. 42 | - You can add a class to get the orange buttons. Or you could try `:last-child` (assuming you have row div.) 43 | 44 | ## JavaScript Tips and Hints 45 | 46 | - Again, no wrong way to do this. I wrote about 80 lines of JavaScript to finish the project (not including empty lines.) I say that so you have an idea of how much code you should be writing. If you're writing 200+ lines of code, you may want to rethink some of your solutions. Don't feel like you're going for the smallest possible answer. You're just going for correct. 47 | - I use `console.log` everywhere while I'm writing code. Just remember to take them out at the end. 48 | - Many small functions is _very_ preferable to one large function. Have each function do one thing well as opposed to having giant functions that do everything. If you find a function doing too, break it into smaller pieces. I solved it with eight different functions. 49 | - You'll need to keep track of several variables. Make sure these variables are stored in a place where they stay in scope. 50 | - You can add an event listener to each button individually, or you can use one listener at the root of the button. I did the latter but it's up to you. 51 | 52 | ## Types 53 | 54 | A brief note on what is called **types** in JavaScript. We've danced the idea already and I want to make it a little more concrete for you. Strings, booleans, objects, arrays, numbers, these are different types of types (lol). JavaScript is a language where you don't have to concern yourself _a lot_ with types since it doesn't strictly enforce them (other languages do) but in this problem you are definitely going to have to deal with it. 55 | 56 | Whatever you put into the DOM and whatever you get out of it are going to be strings, every time. If I do: 57 | 58 |
59 | 60 | ```javascript 61 | const num = 10; 62 | const div = document.querySelector(".number-target"); // the div right above this block 63 | console.log(num, typeof num); // this is a number here 64 | div.innerText = num; 65 | console.log(div.innerText, typeof div.innerText); // it's a string here 66 | ``` 67 | 68 | Since you're doing math here, you'll need the numbers to actually be of the number type. Otherwise you'll get `"5" + "5" = "55"`. There's a function called `parseInt(string)` that will turn a string of a number (`"5"`) to a number (`5`). 69 | 70 | You'll also see that we used the `typeof` operator. `typeof` tells whatever the type of the thing that comes right after it is. This is useful to quickly see what's happening in your code. Be careful because `typeof` is not always useful, but it is useful for telling numbers and strings apart. 71 | 72 | ## Answer 73 | 74 | - [The HTML](/complete-intro-to-web-dev-v3/project-files/calculator.html) (you can view source on it) 75 | - [The JavaScript](/complete-intro-to-web-dev-v3/project-files/calculator.js) 76 | - [The CSS](/complete-intro-to-web-dev-v3/project-files/calculator.css) 77 | -------------------------------------------------------------------------------- /lessons/05-putting-it-all-together/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": "arrows-to-dot" 3 | } -------------------------------------------------------------------------------- /lessons/06-talking-to-servers/A-json.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JSON" 3 | description: "" 4 | --- 5 | 6 | Some times you want to request additional data from the server after your page has loaded. Imagine like scrolling through your Facebook or Twitter feeds: when you reach the bottom it requests more data from the server to keep your infinite doomscroll going. 7 | 8 | Requesting data from the server after the page has loaded is called AJAX. AJAX is an old acronym that has been around for a while and actually now doesn't make any sense but we still use it. It stands for asynchronous JavaScript and XML (we don't typically use XML anymore.) However the name AJAX stuck so that's what it means. 9 | 10 | Before we hop into the AJAX portion, let's spend a bit with JSON. We need some standard language that your frontend website can speak with your backend, someway to encode messages. Think of it like morse code: we need some way that both the sender and receiver of messages can encode their messages so it's understood by both. 11 | 12 | This is what JSON is. It stands for JavaScript Object Notation and it looks a lot like, surprise-surpise, JavaScript objects. 13 | 14 | ```JSON 15 | { 16 | "name": "Luna", 17 | "age": 10, 18 | "breed": "Havanese", 19 | "location": { 20 | "city": "Seattle", 21 | "state": "WA" 22 | } 23 | } 24 | ``` 25 | 26 | Looks like valid JavaScript, right? That's because it is! We could literally copy-and-paste that code straight into our JavaScript code and it'd just work. That's because 99.999999% of all JSON is valid JavaScript ([there are some corner cases][json] but I'd bet most devs didn't even know that.) 27 | 28 | So let's try it out. Let's pretend we submitted a request to a server and got back a response. The response will always come back as a string so we have to convert it to an object. Luckily JavaScript can do that for us. 29 | 30 | ```javascript 31 | // pretend this came from a server instead of me just declaring it here. 32 | const responseFromServer = `{"name": "Luna","age": 10,"breed": "Havanese","location": {"city":"Seattle","state": "WA"}}`; 33 | 34 | console.log(responseFromServer); // a string 35 | 36 | const responseObject = JSON.parse(responseFromServer); 37 | 38 | console.log(responseObject.name); // just the name 39 | console.log(responseObject.location.city); // just the city 40 | console.log(responseObject); // the whole object 41 | ``` 42 | 43 | That's what the JSON builtin object is for, handling data to and from JSON. 44 | 45 | ## Escape Characters 46 | 47 | You probably see a lot of `\` being added. These are called escape characters. Notice it's showing the string between `"` but the JSON uses `"` inside of it. The way you prevent that `"` from ending the string is using `\"` to signify that you're wanting to use the `"` inside the string. So if you wanted to have a string of just one `"` you'd need to do `const quotes = "\""`. Or you could just do `'"'` too!. 48 | 49 | What if you want to have a `\` in the string? You'd use `\\` to signify one `\`. 50 | 51 | ## JSON.stringify 52 | 53 | Let's use to go in the opposite direction: 54 | 55 | ```javascript 56 | const dog = { 57 | name: "Luna", 58 | age: 10, 59 | breed: "Havanese", 60 | location: { 61 | city: "Seattle", 62 | state: "WA", 63 | }, 64 | }; 65 | 66 | const objString = JSON.stringify(dog); 67 | console.log(objString); 68 | ``` 69 | 70 | Yes, the function name is stringify. This takes an object and encodes it as valid JSON. Very useful. 71 | 72 | One more thing to do. Let's say you have a big object and you want to print it out in a useful way. Let me show you how to do that. 73 | 74 | We'll use this HTML. 75 | 76 | ```display-html 77 |
 78 |   
 79 | 
80 | ``` 81 | 82 |
 83 |   
 84 | 
85 | 86 | ```javascript 87 | const dog = { 88 | name: "Luna", 89 | age: 10, 90 | breed: "Havanese", 91 | location: { 92 | city: "Seattle", 93 | state: "WA", 94 | }, 95 | }; 96 | 97 | const el = document.getElementById("code-block"); 98 | el.innerText = JSON.stringify(dog, null, 4); 99 | ``` 100 | 101 | - `
` means pre-formatted. It means respect whitespace and don't mess with the formatting
102 | - `` means a block of code
103 | - The two tags together mean you can have a nicely formatted code block. Useful for quick debugging when you can't use console.log.
104 | - `JSON.stringify(dog, null, 4)`
105 |   - The `null` you can ignore. You can give it a function that will _replace_ things in your object. I don't even know how it works
106 |   - The `4` is how much indentation you want. If you give it `0` it puts not space in. If you put `8` you'll get a lot of whitespace.
107 | 
108 | [json]: https://stackoverflow.com/questions/23752156/are-all-json-objects-also-valid-javascript-objects
109 | [dog]: https://dog.ceo/dog-api/
110 | [api]: https://github.com/toddmotto/public-apis
111 | [breeds]: https://dog.ceo/dog-api/documentation/
112 | [docs]: https://dog.ceo/dog-api/documentation/random
113 | [pic]: https://dog.ceo/dog-api/documentation/breed
114 | 


--------------------------------------------------------------------------------
/lessons/06-talking-to-servers/B-ajax.md:
--------------------------------------------------------------------------------
  1 | ---
  2 | description: ""
  3 | title: "AJAX"
  4 | ---
  5 | 
  6 | What is an API? An application programming interface is a URL that you can make requests to get information back from. It's like website but only for machines. It's a method that one computer can request information from another.
  7 | 
  8 | > Note that API is also used to describe how something is used. If I wrote a dog object in JavaScript and gave it two methods: `eat()` and `bark()`, you could call those two methods its "API". A similar but slightly different meaning for the word. I just point that out because it can be confusing. For the rest of this course, we're talking about endpoints that we can call to get data from.
  9 | 
 10 | Now that we speak the language of APIs, let's dive into making some server requests. Let's walk through the hypothetical process.
 11 | 
 12 | Let's say you're making a weather page where the user enters their zip code and gets back their forecast for the day.
 13 | 
 14 | 1. The user navigates to your page and the page loads.
 15 | 1. The user types `98109` into the search bar and hits enter
 16 | 1. Your app makes a request to `api.example.com/weather?zip=98109`
 17 | 1. The API response `{ "temperature": 75, units: "F" }`
 18 | 1. Your app decodes the string to an object using `JSON.parse`
 19 | 1. Your app updates the page to say "The current weather is 75ºF"
 20 | 
 21 | So let's dive into a few things:
 22 | 
 23 | - Who is makes the API? It depends. It could be you; your frontend could be requesting against a server you yourself wrote (a topic for a different class.) Or it could be against a myriad of public and free APIs. Or it could be a service you pay for.
 24 | - [Check out these publically available APIs][apis]. [dog.ceo][dog] is a favorite of mine.
 25 | - In our case, we're going to make requests an API that Frontend Masters maintains for us. [See here][pets] to see an example request.
 26 | - Whats is the `?zip=98109` part of the request. This is called a query string. It allows us to send parameters to a request (like sending parameters to a function) that it can use. In that case, we're sending the zip code we want the weather for. If you want multiple queries, they are separated by an `&` e.g. `example.com/weather?zip=98109&day=tomorrow`.
 27 | - What variables you send via querystring will be determined by the API.
 28 | 
 29 | Okay, so let's make our first request to the dog API. I'm going to be attaching this to a button so we don't hammer our poor friend's APIs. Running these APIs isn't free.
 30 | 
 31 | > I don't take responsibility for what images this gives back. They should all just be cute pups.
 32 | 
 33 | ```display-html
 34 | 
 35 | 
36 | ``` 37 | 38 | 39 | 40 |
41 | 42 | ```javascript 43 | const DOG_URL = "https://dog.ceo/api/breeds/image/random"; 44 | 45 | const doggos = document.getElementById("dog-target"); 46 | 47 | function addNewDoggo() { 48 | const promise = fetch(DOG_URL); 49 | promise 50 | .then(function (response) { 51 | const processingPromise = response.text(); 52 | return processingPromise; 53 | }) 54 | .then(function (processedResponse) { 55 | const dogObject = JSON.parse(processedResponse); 56 | const img = document.createElement("img"); 57 | img.src = dogObject.message; 58 | img.alt = "Cute doggo"; 59 | doggos.appendChild(img); 60 | }); 61 | } 62 | 63 | document.getElementById("dog-btn").addEventListener("click", addNewDoggo); 64 | ``` 65 | 66 | Let's dissect this. 67 | 68 | Requests take time. The process of calling out to the Internet, reaching the server, the server processing, responding, and coming back takes time. It could be very fast. It could be a minute. You don't know until it happens. You need to have the ability to _wait_ in your code. This is called async code, the A in AJAX. 69 | 70 | JavaScript has several ways of dealing with this. We're using a way called promises. A promise represents a future value. With a promise, you can give it a function with its `then` function to run whenever it gets its answer back. In this case, we say "hey, you're going to get some text back from this API, so we need that text. 71 | 72 | `fetch` is a function (builtin for the browser) that allows you to give it a URL like `https://dog.ceo/api/breeds/image/random` and it will try to get information from that API. In this case we're asking for a random image of a dog. 73 | 74 | `fetch` gives you back a promise. Then we need to tell it what we think the API is going to send us back. In our case we're saying it's going to be `text()` from the API. There are others (and I'll show you one in a sec.) 75 | 76 | You can do what's called **promise chaining**. This allows you to do one async action after another. In our case, we don't know how long it will take to transform our response into text (it's basically instant and usually is but it could not be). In any case, by **returning the promise** at the end of the first `then`, we then can use its data in the second `then`. That's why that's weird that way. 77 | 78 | In the second `then` we read the `message` which has the URL, we create an `img` tag, and append it to the DOM. The result is we get a cool dog pic! 79 | 80 | Okay, let's tweak it minorly. 81 | 82 | ```display-html 83 | 84 |
85 | ``` 86 | 87 | 88 | 89 |
90 | 91 | ```javascript 92 | const DOG_URL = "https://dog.ceo/api/breeds/image/random"; 93 | 94 | const doggos = document.getElementById("dog-target2"); 95 | 96 | function addNewDoggo() { 97 | const promise = fetch(DOG_URL); 98 | promise 99 | .then(function (response) { 100 | const processingPromise = response.json(); // json instead of text 101 | return processingPromise; 102 | }) 103 | .then(function (processedResponse) { 104 | // we get to skip this line since it'll already be an object 105 | // const dogObject = JSON.parse(processedResponse); 106 | 107 | const img = document.createElement("img"); 108 | img.src = processedResponse.message; 109 | img.alt = "Cute doggo"; 110 | doggos.appendChild(img); 111 | }); 112 | } 113 | 114 | document.getElementById("dog-btn2").addEventListener("click", addNewDoggo); 115 | ``` 116 | 117 | A slight difference, but if we know we're going to get JSON back from the API, fetch can do the parsing directly for you with the `json()` function. 118 | 119 | [apis]: https://github.com/toddmotto/public-apis 120 | [dog]: https://dog.ceo/dog-api/ 121 | [pets]: http://pets-v2.dev-apis.com/pets 122 | -------------------------------------------------------------------------------- /lessons/06-talking-to-servers/C-async-await.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | title: "async/await" 4 | --- 5 | 6 | Promises make code hard to read. Before them we did everything with callbacks (similar to how we're doing event listeners, you just give a function to be run later) and it was even worse. We used to deal with what we called the "pyramid of doom" or just simply "callback hell" where we had to deal with functions inside of functions. As you saw with promise chaining, at least it's linear and not terrible to follow. 7 | 8 | What if it could be better? It can! With recent versions of JavaScript we got the ability to do what's called async/await. It allows us to make code even easier to read. 9 | 10 | ```display-html 11 | 12 |
13 | ``` 14 | 15 | 16 | 17 |
18 | 19 | ```javascript 20 | const DOG_URL = "https://dog.ceo/api/breeds/image/random"; 21 | 22 | const doggos = document.getElementById("dog-target3"); 23 | 24 | async function addNewDoggo() { 25 | const promise = await fetch(DOG_URL); 26 | const processedResponse = await promise.json(); 27 | const img = document.createElement("img"); 28 | img.src = processedResponse.message; 29 | img.alt = "Cute doggo"; 30 | doggos.appendChild(img); 31 | } 32 | 33 | document.getElementById("dog-btn3").addEventListener("click", addNewDoggo); 34 | ``` 35 | 36 | How much easier is that to read!? So much easier. 37 | 38 | Okay, so what's going on with this black magic? 39 | 40 | First, you can only use the magic `await` keyword inside of `async` functions. This can be confusing to folks but just be aware of that `await` can only be used in `async` functions. 41 | 42 | All `await` does is tell your code "pause execution on this function until this promise resolves." So on the line `const promise = await fetch(DOG_URL);` the function stops executing until your API call finishes and you have a response back. It then picks back up where it was. It makes the code read very linearly which is great. 43 | 44 | One thing to note about async functions: they _always_ return promises themselves. 45 | 46 | ```javascript 47 | async function getName() { 48 | return "Brian"; 49 | } 50 | 51 | console.log("a promise", getName()); 52 | 53 | getName().then(function (name) { 54 | console.log("the actual name", name); 55 | }); 56 | ``` 57 | 58 | Despite the fact we're not doing any awaiting in getName, because it's async it returns a promise. I show you this because it can catch people off guard. That's how `async` functions work and why `await` does work: they're async and therefore one may have to wait when you call them. 59 | 60 | Shouldn't be a thing to worry too much about though, only matters if you' care about what your `async` function returns at the end. 61 | -------------------------------------------------------------------------------- /lessons/06-talking-to-servers/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "icon": "code-compare" 3 | } -------------------------------------------------------------------------------- /lessons/07-other-stuff-you-should-know/B-git-and-github.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Git and GitHub" 3 | description: "" 4 | --- 5 | 6 | Git is a tool that allows you to store, version, and share your code. Imagine you're writing a book with a hundred other people. How would you share the in-progress edits of the book? Email? Chat? A chaotic Google Doc? All sound pretty bad, right? 7 | 8 | Imagine a company like Facebook with thousands of engineers. They all work on the same codebase and it's not feasible for them to edit the same copy (one person could break the code and break it everybody.) That's why we have a tool like Git. Git allows everyone to checkout their own copy of the code, make edits, and make change-requests that someone else can review before being merged in. That's what Git does. 9 | 10 | Git itself is a command-line tool and it's worth your time to learn it. [Nina Zakharenko][nina] has a great course on it for Frontend Masters. But we're going to do a fast way. Make a new folder on your desktop and open it with Visual Studio Code. On the left bar, there's a logo that should look something like this: 11 | 12 | ![git logo in VS Code](/complete-intro-to-web-dev-v3/images/git.png) 13 | 14 | Click on that. Click initialize repo. 15 | 16 | A repo (short for repository) is a project in Git parlance. Every project you do will be one repo. Right now it's an empty repo. 17 | 18 | Create a new file called `my-file.txt` and put "Hi my name is Brian" in that file. In your Git tab, you'll see a new file show up under changes. These are changes that happened since you last "saved". 19 | 20 | > You may see a file called .DS_Store if you're on macOS. This is a file that macOS creates about how you organize files in your folders. You shouldn't commit this file. Go ahead and delete it if you see it. 21 | 22 | Click the `+` next you the file. This file is now **staged**. This means it hasn't been saved yet but it is ready to be. Why? Because we may want to group multiple files into one larger save. We therefore stage all the files we want to save and then do one big save action. Keep in mind you stage a file and then change it after, you need to "stage" it again. When you stage something, it captures how it is at that moment. You stage and unstage things freely. 23 | 24 | Okay, so now you've staged multiple things and you want to save it. We're going to commit your staged changes. When I saying "save" before I really meant "commit" as that's the word Git uses. A commit creates a new entry in your repo with all your new changes. Write a little message about you did in the message bar (like "added my name text file", enough to remind yourself later what it was you did) and click the ✓ at the top of the Git panel in VS Code. 25 | 26 | ![git logo in VS Code](/complete-intro-to-web-dev-v3/images/commit.png) 27 | 28 | That's the whole flow: make your changes and then commit them. Different people have different thoughts on when, how, and how often to commit but I'll let you decide that yourself. It's a tool for you to learn and get opinions for yourself. 29 | 30 | There's _a lot_ more to Git. This is just the most basic flow. [Watch Nina's course][nina] to learn more. 31 | 32 | ## GitHub 33 | 34 | If you don't have an account [click here to sign up][github]. This is a public place to store your code, to share and learn from others, and the place in general to store their code. It becomes a bit of a defacto resume for you. Employers will definitely look to see if you're GitHub and how you write code. It'll become a professional profile for you. [See mine here][my-gh]. 35 | 36 | Once you've signed up, click the plus in the top right and create a new repo. 37 | 38 | ![git logo in VS Code](/complete-intro-to-web-dev-v3/images/plus-repo.png) 39 | 40 | Call it my-first-repo or whatever you want to. You can ignore the other options. Nice. From here, grab the URL on the page. It'll be something like `https://github.com//my-first-repo.git`. Copy that. Add it to VS Code via the submenu. 41 | 42 | ![git logo in VS Code](/complete-intro-to-web-dev-v3/images/remote.png) 43 | 44 | Call the remote origin. It's a standard practice to call the copy of the repo the origin that you're working off of. 99.9999% of the time you'll only need the one remote. Because Git is distributed, in theory you could all have your own copy of Git and push to each other's repos but no one works that way. So yeah, just call it origin. 45 | 46 | It may ask you if you want git fetch periodically. Go ahead and say yes. This means VS Code will keep itself updated with your GitHub repo. It's a nice feature. 47 | 48 | Okay, now click publish branch! There may be a bunch of prompts about authing with GitHub and allowing VSCode to open URLs. Just click yes through those. 49 | 50 | > We're not getting into branches but Git has a concept of branches. You can make a lot commits in a branch to keep all those changes together and then eventually merge those changes into the the main/master branch. We're not going to talk about that but that's what they're talking about. You just published your main/master branch for the first time. 51 | 52 | Now check GitHub and see it's up there! That's it! That's how you share code! 53 | 54 | Now go create a new file or modify your existing file. Stage the changes. Commit. And click sync changes to push to GitHub. That's it! Congrats! That's most of what's useful about Git. 55 | 56 | ![git logo in VS Code](/complete-intro-to-web-dev-v3/images/error.png) 57 | 58 | You may see an error like the above. In which case just copy and paste that first line into your command line. It needs a strategy for how to merge when there are conflicts. 59 | 60 | [github]: https://github.com/signup 61 | [nina]: https://frontendmasters.com/courses/git-in-depth/ 62 | [my-gh]: https://github.com/btholt 63 | -------------------------------------------------------------------------------- /lessons/07-other-stuff-you-should-know/C-things-to-do-next.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | You have reached the end of this course! Congratulations!! 6 | 7 | Here are some suggestions on courses to take next. [Or follow the lovely learning paths on Frontend Masters.][paths] 8 | 9 | - How to use a CLI, bash and Linux. [Luckily for you I already have a course on it!][linux]. 10 | - How to use Git well. [See Nina's course.][nina] 11 | - Deploy your code so you can see it from the web. [I like Jem's course][jem]. 12 | - Learn some Node.js to understand how servers work. [Scott's course is amazing][scott]. 13 | - Try a framework. Good ones to try are [React (my course)][react], [Vue][vue], [Svelte][svelte], or [Angular][angular]. 14 | - Take a computer science course (not now, but in the nearish future, have some practice before going here.) [I have a course on that too.][cs] 15 | 16 | [linux]: https://frontendmasters.com/courses/linux-command-line/ 17 | [nina]: https://frontendmasters.com/courses/git-in-depth/ 18 | [paths]: https://frontendmasters.com/learn/ 19 | [cs]: https://frontendmasters.com/courses/computer-science-v2/ 20 | [jem]: https://frontendmasters.com/courses/fullstack-v2/ 21 | [scott]: https://frontendmasters.com/courses/node-js-v2/ 22 | [react]: https://frontendmasters.com/courses/complete-react-v7/ 23 | [vue]: https://frontendmasters.com/courses/vue-3/ 24 | [angular]: https://frontendmasters.com/courses/angular-13/ 25 | [svelte]: https://frontendmasters.com/courses/svelte/ 26 | -------------------------------------------------------------------------------- /lessons/07-other-stuff-you-should-know/D-conclusion.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: "" 3 | --- 4 | 5 | # ❤️ Thank you 6 | 7 | I hope you enjoyed the course! [Tweet at me][tweet] what you thought or let me know on a post on LinkedIn and tag me! Thank you and Frontend Masters for going through this journey and best of luck on your continued path through tech!! 8 | 9 | [tweet]: https://twitter.com/holtbt 10 | -------------------------------------------------------------------------------- /lessons/07-other-stuff-you-should-know/meta.json: -------------------------------------------------------------------------------- 1 | { "icon" : "map" } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const course = require("./course.json"); 2 | const BASE_URL = course?.productionBaseUrl || ""; 3 | 4 | module.exports = { 5 | basePath: BASE_URL, 6 | env: { 7 | ROOT: __dirname, 8 | BASE_URL, 9 | }, 10 | async redirects() { 11 | if (BASE_URL) { 12 | return [ 13 | { 14 | source: "/", 15 | destination: BASE_URL, 16 | basePath: false, 17 | permanent: false, 18 | }, 19 | ]; 20 | } 21 | return []; 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "(CC-BY-NC-4.0 OR Apache-2.0)", 4 | "author": "Brian Holt ", 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "export": "next build && next export", 9 | "start": "next start" 10 | }, 11 | "dependencies": { 12 | "@fortawesome/fontawesome-free": "^6.1.1", 13 | "codemirror": "^5.65.2", 14 | "gray-matter": "^4.0.3", 15 | "highlight.js": "^11.4.0", 16 | "klipse": "^7.11.4", 17 | "marked": "^4.0.9", 18 | "next": "^12.0.7", 19 | "popmotion": "^11.0.3", 20 | "react": "^18.0.0", 21 | "react-dom": "^18.0.0", 22 | "react-tweet-embed": "^2.0.0", 23 | "title-case": "^3.0.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import "@fortawesome/fontawesome-free/css/all.css"; 3 | 4 | import "highlight.js/styles/a11y-light.css"; 5 | import "../styles/variables.css"; 6 | import "../styles/footer.css"; 7 | import "../styles/courses.css"; 8 | import "klipse/dist/codemirror.css"; 9 | import Layout from "../components/layout"; 10 | 11 | globalThis.klipse_settings = { 12 | selector_eval_js: ".language-javascript", 13 | selector_eval_html: ".language-html", 14 | codemirror_options_in: { 15 | indentUnit: 2, 16 | lineWrapping: true, 17 | lineNumbers: true, 18 | autoCloseBrackets: true, 19 | }, 20 | codemirror_options_out: { 21 | lineWrapping: true, 22 | lineNumbers: true, 23 | }, 24 | }; 25 | 26 | export default function App({ Component, pageProps }) { 27 | return ( 28 | 29 | 30 | 35 | 41 | 47 | 53 | 58 | 59 | 60 | 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Link from "next/link"; 3 | 4 | import { getLessons } from "../data/lesson"; 5 | 6 | import Corner from "../components/corner"; 7 | import getCourseConfig from "../data/course"; 8 | 9 | export default function Lessons({ sections }) { 10 | const courseInfo = getCourseConfig(); 11 | return ( 12 | <> 13 | 14 | {courseInfo.title} 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |

{courseInfo.title}

30 |

{courseInfo.subtitle}

31 |
32 |
33 | author image 38 |
39 |
40 |
{courseInfo.author.name}
41 |
{courseInfo.author.company}
42 |
43 |
44 |
45 |
46 |
47 | course icon 51 |
52 |
53 | {courseInfo.frontendMastersLink ? ( 54 | 55 | Watch on Frontend Masters 56 | 57 | ) : null} 58 |
59 |

Table of Contents

60 |
61 |
    62 | {sections.map((section) => ( 63 |
  1. 64 |
    65 |
    66 | 67 |
    68 |
    69 |

    {section.title}

    70 |
      71 | {section.lessons.map((lesson) => ( 72 |
    1. 73 | {lesson.title} 74 |
    2. 75 | ))} 76 |
    77 |
    78 | 79 |
    80 |
  2. 81 | ))} 82 |
83 |
84 |
85 |
86 | 87 | ); 88 | } 89 | 90 | export async function getStaticProps() { 91 | const sections = await getLessons(); 92 | return { 93 | props: { 94 | sections, 95 | }, 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /pages/lessons/[section]/[slug].js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from "react"; 2 | import Head from "next/head"; 3 | import { getLesson, getLessons } from "../../../data/lesson"; 4 | import getCourseConfig from "../../../data/course"; 5 | import Corner from "../../../components/corner"; 6 | import SupportTweet from "../../../components/support-tweet"; 7 | import { Context } from "../../../context/headerContext"; 8 | 9 | import * as popmotion from "popmotion"; 10 | 11 | globalThis.popmotion = popmotion; 12 | 13 | export default function LessonSlug({ post }) { 14 | const courseInfo = getCourseConfig(); 15 | const [_, setHeader] = useContext(Context); 16 | useEffect(() => { 17 | window.klipse.plugin.init(klipse.run.plugin_prod.plugin.settings()); 18 | setHeader({ 19 | section: post.section, 20 | title: post.title, 21 | icon: post.icon, 22 | }); 23 | return () => setHeader({}); 24 | }, []); 25 | 26 | const title = post.title 27 | ? `${post.title} – ${courseInfo.title}` 28 | : courseInfo.title; 29 | const description = post.description 30 | ? post.description 31 | : courseInfo.description; 32 | 33 | return ( 34 | <> 35 | 36 | {title} 37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 |
48 |
49 |
53 |
54 | {post.prevSlug ? ( 55 | 56 | ← Previous 57 | 58 | ) : null} 59 | {post.nextSlug ? ( 60 | 61 | Next → 62 | 63 | ) : null} 64 |
65 |
66 | 67 |
68 | 69 | 70 | ); 71 | } 72 | 73 | export async function getStaticProps({ params }) { 74 | const post = await getLesson(params.section, params.slug); 75 | return { 76 | props: { 77 | post, 78 | }, 79 | }; 80 | } 81 | 82 | export async function getStaticPaths() { 83 | const sections = await getLessons(); 84 | const lessons = sections.map((section) => section.lessons); 85 | const slugs = lessons.flat().map((lesson) => lesson.fullSlug); 86 | 87 | return { paths: slugs, fallback: false }; 88 | } 89 | -------------------------------------------------------------------------------- /public/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/.nojekyll -------------------------------------------------------------------------------- /public/images/BRAND-WHearts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/BRAND-WHearts.png -------------------------------------------------------------------------------- /public/images/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/images/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/apple-touch-icon.png -------------------------------------------------------------------------------- /public/images/author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/author.jpg -------------------------------------------------------------------------------- /public/images/brian.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/brian.jpg -------------------------------------------------------------------------------- /public/images/coffee_masters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/coffee_masters.png -------------------------------------------------------------------------------- /public/images/coffee_masters_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/coffee_masters_logo.png -------------------------------------------------------------------------------- /public/images/commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/commit.png -------------------------------------------------------------------------------- /public/images/course-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/course-icon.png -------------------------------------------------------------------------------- /public/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/error.png -------------------------------------------------------------------------------- /public/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/favicon-16x16.png -------------------------------------------------------------------------------- /public/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/favicon-32x32.png -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/favicon.ico -------------------------------------------------------------------------------- /public/images/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/git.png -------------------------------------------------------------------------------- /public/images/plus-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/plus-repo.png -------------------------------------------------------------------------------- /public/images/remote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/remote.png -------------------------------------------------------------------------------- /public/images/social-share-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/social-share-cover.jpg -------------------------------------------------------------------------------- /public/images/the_news_times.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/images/the_news_times.png -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/eot/FiraCode-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/eot/FiraCode-Bold.eot -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/eot/FiraCode-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/eot/FiraCode-Light.eot -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/eot/FiraCode-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/eot/FiraCode-Medium.eot -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/eot/FiraCode-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/eot/FiraCode-Regular.eot -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/fira_code.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Fira Code'; 3 | src: url('eot/FiraCode-Light.eot'); 4 | src: url('eot/FiraCode-Light.eot') format('embedded-opentype'), 5 | url('woff2/FiraCode-Light.woff2') format('woff2'), 6 | url('woff/FiraCode-Light.woff') format('woff'), 7 | url('ttf/FiraCode-Light.ttf') format('truetype'); 8 | font-weight: 300; 9 | font-style: normal; 10 | } 11 | 12 | @font-face{ 13 | font-family: 'Fira Code'; 14 | src: url('eot/FiraCode-Regular.eot'); 15 | src: url('eot/FiraCode-Regular.eot') format('embedded-opentype'), 16 | url('woff2/FiraCode-Regular.woff2') format('woff2'), 17 | url('woff/FiraCode-Regular.woff') format('woff'), 18 | url('ttf/FiraCode-Regular.ttf') format('truetype'); 19 | font-weight: 400; 20 | font-style: normal; 21 | } 22 | 23 | @font-face{ 24 | font-family: 'Fira Code'; 25 | src: url('eot/FiraCode-Medium.eot'); 26 | src: url('eot/FiraCode-Medium.eot') format('embedded-opentype'), 27 | url('woff2/FiraCode-Medium.woff2') format('woff2'), 28 | url('woff/FiraCode-Medium.woff') format('woff'), 29 | url('ttf/FiraCode-Medium.ttf') format('truetype'); 30 | font-weight: 500; 31 | font-style: normal; 32 | } 33 | 34 | @font-face{ 35 | font-family: 'Fira Code'; 36 | src: url('eot/FiraCode-Bold.eot'); 37 | src: url('eot/FiraCode-Bold.eot') format('embedded-opentype'), 38 | url('woff2/FiraCode-Bold.woff2') format('woff2'), 39 | url('woff/FiraCode-Bold.woff') format('woff'), 40 | url('ttf/FiraCode-Bold.ttf') format('truetype'); 41 | font-weight: 700; 42 | font-style: normal; 43 | } -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/otf/FiraCode-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/otf/FiraCode-Bold.otf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/otf/FiraCode-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/otf/FiraCode-Light.otf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/otf/FiraCode-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/otf/FiraCode-Medium.otf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/otf/FiraCode-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/otf/FiraCode-Regular.otf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/otf/FiraCode-Retina.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/otf/FiraCode-Retina.otf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/ttf/FiraCode-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/ttf/FiraCode-Bold.ttf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/ttf/FiraCode-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/ttf/FiraCode-Light.ttf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/ttf/FiraCode-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/ttf/FiraCode-Medium.ttf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/ttf/FiraCode-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/ttf/FiraCode-Regular.ttf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/ttf/FiraCode-Retina.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/ttf/FiraCode-Retina.ttf -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff/FiraCode-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff/FiraCode-Bold.woff -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff/FiraCode-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff/FiraCode-Light.woff -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff/FiraCode-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff/FiraCode-Medium.woff -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff/FiraCode-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff/FiraCode-Regular.woff -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff2/FiraCode-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff2/FiraCode-Bold.woff2 -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff2/FiraCode-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff2/FiraCode-Light.woff2 -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff2/FiraCode-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff2/FiraCode-Medium.woff2 -------------------------------------------------------------------------------- /public/js/klipse/FiraCode/woff2/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/js/klipse/FiraCode/woff2/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /public/project-files/blog/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Brian's Blog | About Me 7 | 8 | 9 | 14 |
15 |

About Me

16 |

17 | My name is Brian Holt. I am a resident of Seattle, Washington and live 18 | here with my wife, son, and dog. 19 |

20 |

21 | Currently I work at Stripe as a product manager of developer tools. 22 | Before I worked at Microsoft, LinkedIn, Netflix, Reddit, and other 23 | smaller companies. I am currently working my masters of business 24 | administration degree at Seattle Univeristy. 25 |

26 |

Here are some interesting accomplishments about me:

27 |
    28 |
  • 29 | I once won a gaming tournament when I was six. We played Sonic & 30 | Knuckles. 31 |
  • 32 |
  • 33 | I ate burritos with the band Sugarcult. I don't even like Sugarcult. 34 | The burritos are the highlight of this story. 35 |
  • 36 |
  • 37 | I am one of the very few people to never graduate college nor ever 38 | take the standardized test GMAT and yet still be accepted into a 39 | master's program. 40 |
  • 41 |
42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /public/project-files/blog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Brian's Blog 7 | 8 | 9 | 13 |
14 |

Intro

15 |

16 | My name is Brian Holt. I am a resident of Seattle, Washington and live 17 | here with my wife, son, and dog. 18 |

19 |

20 | Currently I work at Stripe as a product manager of developer tools. 21 | Before I worked at Microsoft, LinkedIn, Netflix, Reddit, and other 22 | smaller companies. I am currently working my masters of business 23 | administration degree at Seattle Univeristy. 24 |

25 |
26 |
27 |

Blog Posts

28 |
29 |

Lorem, ipsum dolor.

30 |

Brian Holt

31 |

May 05

32 | dog 1 37 |

38 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Perferendis 39 | hic dignissimos, perspiciatis excepturi facilis minima vero omnis 40 | natus quaerat delectus unde fugit, quod error non mollitia amet quia 41 | quasi laudantium. 42 |

43 |

44 | Vitae fugiat repellat veniam quas modi officia, amet corporis itaque 45 | voluptatibus sint deleniti ad natus autem voluptate veritatis 46 | similique expedita reprehenderit debitis odit quos? Sint totam libero 47 | expedita temporibus illum. 48 |

49 |

50 | Dolore laboriosam ad porro harum quaerat sunt velit sit, corrupti fuga 51 | laborum sapiente aliquam inventore eligendi perspiciatis ipsum nulla 52 | quo praesentium! Pariatur rem ratione, provident dolore non modi nisi 53 | laudantium. 54 |

55 |
56 |
57 |

Voluptates, sit illo!

58 |

Brian Holt

59 |

May 04

60 | dog 2 65 |

66 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Maiores 67 | voluptas earum nihil quasi eveniet iure est tempore suscipit doloribus 68 | asperiores illum temporibus fuga deserunt, facere ipsa libero possimus 69 | dolores aliquid! 70 |

71 |

72 | Adipisci a ipsam error voluptatum ullam tempore voluptatibus facere 73 | incidunt quos sequi molestiae reiciendis enim praesentium molestias, 74 | odit eos nihil sint minima. Sunt itaque commodi debitis possimus. Et, 75 | dicta labore! 76 |

77 |

78 | Consectetur ad quos quidem tempore similique nam beatae, cum autem 79 | dolorum placeat fuga officiis rem nisi quam, commodi architecto! 80 | Voluptate autem suscipit dolorum laboriosam corrupti aut nulla sed 81 | distinctio odio. 82 |

83 |
84 |
85 |

Corporis, consectetur maiores.

86 |

Brian Holt

87 |

May 03

88 | dog 3 93 |

94 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eaque, 95 | facilis eveniet quis nulla facere fugiat assumenda incidunt delectus 96 | neque minima fugit repudiandae consequatur eligendi odit nam repellat 97 | quibusdam dolorem. Impedit? 98 |

99 |

100 | Totam assumenda, porro officia, corporis veniam obcaecati sit 101 | quibusdam ipsam nam, sapiente explicabo corrupti voluptatum? Quo non 102 | quos obcaecati tempore harum ipsam reiciendis? Quis enim sed eligendi 103 | ad dolor sunt. 104 |

105 |

106 | Repellendus aliquam quisquam vero enim? Consequuntur minima quos natus 107 | accusantium soluta cum molestias. Non ipsum in corporis, officia autem 108 | sequi. Nisi ut labore deserunt illo? Libero id cumque omnis porro! 109 |

110 |
111 |
112 |

Est, sit corporis.

113 |

Brian Holt

114 |

May 02

115 | dog 4 120 |

121 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed autem 122 | culpa quam, dicta dolorem cupiditate quas, ea itaque dolore error, aut 123 | sequi debitis id ducimus. Itaque distinctio dolores facilis hic? 124 |

125 |

126 | Possimus eum minus, distinctio unde harum praesentium voluptatibus 127 | numquam est tempore, ipsum nemo non repellat vero vel porro delectus! 128 | Quia, veritatis perferendis fugiat perspiciatis praesentium in animi 129 | minima temporibus fugit. 130 |

131 |

132 | Doloremque pariatur consequuntur rem neque, architecto iste officiis 133 | aperiam. Deleniti dolorum, dolores itaque amet, repudiandae nam 134 | provident ut odio debitis quisquam ratione unde nulla eius eligendi 135 | doloribus nemo error nobis. 136 |

137 |
138 |
139 |

Pariatur, aliquam voluptates.

140 |

Brian Holt

141 |

May 01

142 | dog 5 147 |

148 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aperiam iure 149 | odio quis eveniet minima ducimus deserunt, fuga aspernatur voluptates, 150 | excepturi, enim officia neque modi aliquid. Hic quisquam quas quod 151 | eaque? 152 |

153 |

154 | Cum repellendus nulla dignissimos tempore iste, blanditiis iure 155 | aspernatur totam. Soluta voluptates perspiciatis repellat officiis vel 156 | quidem laudantium? Illum debitis nobis repudiandae odio! Quaerat, 157 | architecto. Fugit consectetur consequatur accusamus nisi? 158 |

159 |

160 | Repudiandae repellat aut ea iure, optio exercitationem dolorem quos 161 | obcaecati libero fugit quas explicabo, magnam quidem sit corrupti 162 | earum cumque, dolores odit. Ea deserunt excepturi corrupti eaque, 163 | aspernatur nemo odit! 164 |

165 |
166 |
167 | 168 | 169 | -------------------------------------------------------------------------------- /public/project-files/calculator.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | padding: 0; 7 | margin: 0; 8 | } 9 | 10 | .calc { 11 | width: 400px; 12 | background-color: black; 13 | color: white; 14 | } 15 | 16 | .screen { 17 | color: white; 18 | font-size: 40px; 19 | font-family: "Courier New", Courier, monospace; 20 | text-align: right; 21 | padding: 20px 5px; 22 | } 23 | 24 | .calc-button { 25 | background-color: #d8d9db; 26 | color: black; 27 | height: 100px; 28 | width: 24.5%; 29 | border: none; 30 | border-radius: 0; 31 | font-size: 40px; 32 | cursor: pointer; 33 | } 34 | 35 | .calc-button:hover { 36 | background-color: #ebebeb; 37 | } 38 | 39 | .calc-button:active { 40 | background-color: #bbbcbe; 41 | } 42 | 43 | .double { 44 | width: 49.7%; 45 | } 46 | 47 | .triple { 48 | width: 74.8%; 49 | } 50 | 51 | .calc-button:last-child { 52 | background-color: #df974c; 53 | color: white; 54 | } 55 | 56 | .calc-button:last-child:hover { 57 | background-color: #dfb07e; 58 | } 59 | 60 | .calc-button:last-child:active { 61 | background-color: #dd8d37; 62 | } 63 | 64 | .calc-button-row { 65 | display: flex; 66 | align-content: stretch; 67 | justify-content: space-between; 68 | margin-bottom: 0.5%; 69 | } 70 | 71 | .calc-button-row:last-child { 72 | padding-bottom: 0; 73 | } 74 | -------------------------------------------------------------------------------- /public/project-files/calculator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Calculator 5 | 6 | 7 | 8 | 9 |
10 |
0
11 | 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 | -------------------------------------------------------------------------------- /public/project-files/calculator.js: -------------------------------------------------------------------------------- 1 | let runningTotal = 0; 2 | let buffer = "0"; 3 | let previousOperator; 4 | const screen = document.querySelector(".screen"); 5 | 6 | function buttonClick(value) { 7 | if (isNaN(parseInt(value))) { 8 | handleSymbol(value); 9 | } else { 10 | handleNumber(value); 11 | } 12 | rerender(); 13 | } 14 | 15 | function handleNumber(value) { 16 | if (buffer === "0") { 17 | buffer = value; 18 | } else { 19 | buffer += value; 20 | } 21 | } 22 | 23 | function handleMath(value) { 24 | if (buffer === "0") { 25 | // do nothing 26 | return; 27 | } 28 | 29 | const intBuffer = parseInt(buffer); 30 | if (runningTotal === 0) { 31 | runningTotal = intBuffer; 32 | } else { 33 | flushOperation(intBuffer); 34 | } 35 | 36 | previousOperator = value; 37 | 38 | buffer = "0"; 39 | } 40 | 41 | function flushOperation(intBuffer) { 42 | if (previousOperator === "+") { 43 | runningTotal += intBuffer; 44 | } else if (previousOperator === "-") { 45 | runningTotal -= intBuffer; 46 | } else if (previousOperator === "×") { 47 | runningTotal *= intBuffer; 48 | } else { 49 | runningTotal /= intBuffer; 50 | } 51 | } 52 | 53 | function handleSymbol(value) { 54 | switch (value) { 55 | case "C": 56 | buffer = "0"; 57 | runningTotal = 0; 58 | break; 59 | case "=": 60 | if (previousOperator === null) { 61 | // need two numbers to do math 62 | return; 63 | } 64 | flushOperation(parseInt(buffer)); 65 | previousOperator = null; 66 | buffer = +runningTotal; 67 | runningTotal = 0; 68 | break; 69 | case "←": 70 | if (buffer.length === 1) { 71 | buffer = "0"; 72 | } else { 73 | buffer = buffer.substring(0, buffer.length - 1); 74 | } 75 | break; 76 | case "+": 77 | case "-": 78 | case "×": 79 | case "÷": 80 | handleMath(value); 81 | break; 82 | } 83 | } 84 | 85 | function rerender() { 86 | screen.innerText = buffer; 87 | } 88 | 89 | function init() { 90 | document 91 | .querySelector(".calc-buttons") 92 | .addEventListener("click", function (event) { 93 | buttonClick(event.target.innerText); 94 | }); 95 | } 96 | 97 | init(); 98 | -------------------------------------------------------------------------------- /public/project-files/coffee/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Coffee Masters 7 | 8 | 9 | 10 | 14 | 15 | 16 | 26 |
27 |
28 |

ITEMS

29 |
    30 |
  • 31 |
    1x
    32 |
    Cappucino
    33 |
    $6.00
    34 |
      35 |
    • Light Foam
    • 36 |
    37 |
  • 38 |
  • 39 |
    1x
    40 |
    Cappucino
    41 |
    $7.90
    42 |
      43 |
    • Almond Milk
    • 44 |
    • Double Espresso Shot
    • 45 |
    46 |
  • 47 |
  • 48 |
    1x
    49 |
    Croissant
    50 |
    $5.37
    51 |
      52 |
    • Butter
    • 53 |
    54 |
  • 55 |
56 |
57 |
58 |
59 |

NAME

60 |
61 | 62 | 63 |
64 |
65 |
66 |

PAYMENT

67 |
68 | 69 | 70 |
71 |
72 | 73 | 74 |
75 |
76 |
77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
Subtotal:$19.27
Tax:$0.96
Total:$20.23
94 | 95 |
96 |
97 | 98 | 99 | -------------------------------------------------------------------------------- /public/project-files/coffee/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | padding: 0; 8 | background-color: #efefef; 9 | font-family: 'Open Sans', sans-serif; 10 | } 11 | 12 | .page { 13 | display: grid; 14 | row-gap: 30px; 15 | column-gap: 30px; 16 | width: 100%; 17 | padding: 30px; 18 | margin: 0 auto; 19 | grid-template-areas: 20 | "order payment" 21 | "total total"; 22 | grid-template-columns: 1fr 1fr;; 23 | } 24 | 25 | .main-nav { 26 | background-color: #43281c; 27 | color: #fbf2c0; 28 | font-size: 16px; 29 | display: flex; 30 | align-items: center; 31 | justify-content: center; 32 | font-weight: normal; 33 | } 34 | 35 | .logo { 36 | height: 25px; 37 | position: relative; 38 | top: 3px; 39 | } 40 | 41 | .box { 42 | background-color: #eee1d5; 43 | border-radius: 5px; 44 | box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); 45 | padding: 15px; 46 | margin-bottom: 20px; 47 | width: 100%; 48 | } 49 | 50 | .box-title { 51 | color: #885a2d; 52 | font-weight: normal; 53 | font-size: 16px; 54 | } 55 | 56 | .order { 57 | grid-area: order; 58 | } 59 | 60 | .payment { 61 | grid-area: payment; 62 | } 63 | 64 | .total { 65 | grid-area: total; 66 | } 67 | 68 | .items { 69 | list-style: none; 70 | padding: 0; 71 | margin: 0; 72 | } 73 | 74 | .item { 75 | border-bottom: 2px solid #DDB892; 76 | display: grid; 77 | 78 | padding: 10px 0; 79 | 80 | grid-template-columns: 1fr 8fr 1fr; 81 | grid-template-areas: 82 | "quantity title price" 83 | "quantity options price"; 84 | 85 | row-gap: 8px; 86 | 87 | } 88 | 89 | .item:last-child { 90 | border-bottom: none; 91 | } 92 | 93 | .item-quantity { 94 | grid-area: quantity; 95 | } 96 | 97 | .item-title { 98 | grid-area: title; 99 | font-weight: bold; 100 | } 101 | 102 | .item-price { 103 | grid-area: price; 104 | font-weight: bold; 105 | } 106 | 107 | .item-options-list { 108 | grid-area: options; 109 | color: #B08968; 110 | list-style: none; 111 | margin: 0; 112 | padding: 0; 113 | font-size: 14px; 114 | } 115 | 116 | .item-option { 117 | margin-bottom: 5px; 118 | } 119 | 120 | .input-group { 121 | position: relative; 122 | margin-bottom: 20px; 123 | } 124 | 125 | .user-input { 126 | background-color: rgba(0,0,0,0); 127 | border: 2px #885a2d solid; 128 | padding: 8px; 129 | border-radius: 100px; 130 | width: 100%; 131 | text-indent: 18px; 132 | font-size: 18px; 133 | } 134 | 135 | .user-input-label { 136 | color: #885a2d; 137 | background-color: #eee1d5; 138 | font-size: 11px; 139 | position: absolute; 140 | left: 25px; 141 | top: -7px; 142 | padding: 0 2px; 143 | } 144 | 145 | .short-input { 146 | max-width: 200px; 147 | } 148 | 149 | .total { 150 | display: flex; 151 | flex-direction: column; 152 | align-items: center; 153 | justify-content: center; 154 | } 155 | 156 | .totals-table { 157 | width: 100%; 158 | max-width: 500px; 159 | margin-bottom: 30px; 160 | } 161 | 162 | .totals-table td { 163 | padding: 10px; 164 | } 165 | 166 | .totals-table td:last-child { 167 | text-align: right; 168 | } 169 | 170 | .final-totals { 171 | font-weight: bold; 172 | } 173 | 174 | .checkout { 175 | background-color: #deb993; 176 | border: none; 177 | padding: 10px 30px; 178 | width: 100%; 179 | border-radius: 100px; 180 | font-size: 16px; 181 | } -------------------------------------------------------------------------------- /public/project-files/news.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | padding: 30px; 7 | } 8 | 9 | .brand { 10 | font-family: "Snell Roundhand"; 11 | font-weight: normal; 12 | font-size: 60px; 13 | text-align: center; 14 | } 15 | 16 | .brand-header { 17 | grid-area: header; 18 | } 19 | 20 | .nav-links { 21 | grid-area: nav; 22 | } 23 | 24 | .story-1 { 25 | grid-area: story-1; 26 | } 27 | 28 | .story-2 { 29 | grid-area: story-2; 30 | } 31 | 32 | .story-3 { 33 | grid-area: story-3; 34 | } 35 | 36 | .story-4 { 37 | grid-area: story-4; 38 | } 39 | 40 | .story-5 { 41 | grid-area: story-5; 42 | } 43 | 44 | .page { 45 | display: grid; 46 | grid-template-areas: 47 | "header header header" 48 | "nav nav nav" 49 | "story-1 story-2 story-3" 50 | "story-4 story-5 story-5"; 51 | } 52 | 53 | .article-title { 54 | font-size: 30px; 55 | text-align: center; 56 | font-weight: normal; 57 | font-style: italic; 58 | } 59 | 60 | .article-img { 61 | display: block; 62 | margin: 0 auto; 63 | max-height: 250px; 64 | } 65 | 66 | .story { 67 | padding: 10px; 68 | border-left: 3px solid black; 69 | border-bottom: 3px solid black; 70 | } 71 | 72 | .story-3, .story-5 { 73 | border-right: 3px solid black; 74 | } 75 | 76 | .nav-links { 77 | display: flex; 78 | justify-content: space-around; 79 | align-items: center; 80 | list-style: none; 81 | border: 3px solid black; 82 | margin: 0; 83 | padding: 30px; 84 | } 85 | 86 | .section-link { 87 | color: black; 88 | text-decoration: none; 89 | font-size: 30px; 90 | font-weight: bold; 91 | font-family: "Futura"; 92 | color: #666; 93 | } 94 | 95 | .section-link:hover { 96 | text-decoration: underline; 97 | } -------------------------------------------------------------------------------- /public/project-files/news.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The News Times 7 | 8 | 9 | 10 | 11 |
12 |
13 |

The News Times

14 |
15 | 16 | 33 | 34 |
35 |

Student Learns HTML

36 |

37 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Hic ad in 38 | quas alias non! Expedita similique et dolore illum doloremque vel 39 | accusantium ut eos tempora sequi, doloribus vitae mollitia praesentium 40 | ex ullam? Dolorem labore fugiat neque nostrum porro vero fugit. 41 |

42 |

43 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aliquam 44 | corrupti, eius odio neque incidunt esse fuga veritatis ipsum, eligendi 45 | mollitia, porro quasi provident nisi sit! Doloribus, eligendi nemo, ad 46 | laudantium cupiditate aspernatur eveniet asperiores modi praesentium 47 | voluptas eos sunt odio. 48 |

49 |
50 |
51 |

BREAKING: Luna is Adorable

52 | Luna in a suitcase 57 |

58 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quos, 59 | dolores? Illo dolores, rerum iste aut porro doloribus fugit itaque 60 | voluptas. 61 |

62 |

63 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laudantium 64 | incidunt ullam ea magni ipsam perferendis ratione pariatur enim 65 | repellendus quia? 66 |

67 |
68 |
69 |

CSS Is Apparently a Thing

70 |

71 | Lorem, ipsum dolor sit amet consectetur adipisicing elit. Hic ad in 72 | quas alias non! Expedita similique et dolore illum doloremque vel 73 | accusantium ut eos tempora sequi, doloribus vitae mollitia praesentium 74 | ex ullam? Dolorem labore fugiat neque nostrum porro vero fugit. 75 |

76 |

77 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aliquam 78 | corrupti, eius odio neque incidunt esse fuga veritatis ipsum, eligendi 79 | mollitia, porro quasi provident nisi sit! Doloribus, eligendi nemo, ad 80 | laudantium cupiditate aspernatur eveniet asperiores modi praesentium 81 | voluptas eos sunt odio. 82 |

83 |
84 |
85 |

86 | Between the Angle Brackets: The Brian Holt Scandal 87 |

88 |

89 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque, 90 | perferendis! Accusamus asperiores quod vitae architecto natus alias 91 | fugiat aliquam unde sint expedita repellat rerum, obcaecati hic 92 | placeat recusandae quaerat. Dolores excepturi earum minus magni! Animi 93 | aperiam eligendi molestias necessitatibus ducimus. 94 |

95 |

96 | Voluptate, ut, cupiditate ducimus vitae blanditiis ex impedit debitis 97 | est tempora dolores nam sed. Esse nobis ea inventore qui enim quia 98 | beatae ab commodi laboriosam quam aliquam aut perspiciatis fuga nam 99 | rerum temporibus voluptatum explicabo voluptate, pariatur ullam 100 | laudantium eligendi. 101 |

102 |
103 | 104 |
105 |

106 | Scientist Invents New Miracle Drink: Coffee 107 |

108 |

109 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 110 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Adipiscing 111 | at in tellus integer feugiat scelerisque varius morbi enim. Interdum 112 | consectetur libero id faucibus nisl tincidunt eget. In tellus integer 113 | feugiat scelerisque varius morbi enim. Sit amet purus gravida quis 114 | blandit turpis. Nec ultrices dui sapien eget mi proin sed. Platea 115 | dictumst quisque sagittis purus sit. Augue interdum velit euismod in. 116 | Bibendum ut tristique et egestas quis ipsum suspendisse ultrices 117 | gravida. Feugiat sed lectus vestibulum mattis ullamcorper. Accumsan 118 | lacus vel facilisis volutpat est. 119 |

120 |

121 | Elit sed vulputate mi sit amet mauris commodo. Enim eu turpis egestas 122 | pretium aenean pharetra. Sed viverra tellus in hac habitasse platea 123 | dictumst. Pretium fusce id velit ut tortor pretium viverra 124 | suspendisse. Neque ornare aenean euismod elementum nisi quis eleifend 125 | quam adipiscing. Orci a scelerisque purus semper eget duis at tellus 126 | at. Amet nulla facilisi morbi tempus iaculis urna. Id leo in vitae 127 | turpis massa sed. In tellus integer feugiat scelerisque varius morbi. 128 | Fusce id velit ut tortor pretium. Fusce id velit ut tortor. Turpis 129 | cursus in hac habitasse platea dictumst. Adipiscing elit duis 130 | tristique sollicitudin nibh sit amet. 131 |

132 |

133 | Vel orci porta non pulvinar neque laoreet. Velit euismod in 134 | pellentesque massa placerat duis ultricies. Eget nunc scelerisque 135 | viverra mauris in aliquam sem fringilla. Nisl rhoncus mattis rhoncus 136 | urna neque viverra justo nec. In dictum non consectetur a erat nam at 137 | lectus. Dignissim convallis aenean et tortor at risus viverra 138 | adipiscing at. Pharetra sit amet aliquam id diam maecenas ultricies 139 | mi. Faucibus pulvinar elementum integer enim neque volutpat. Nibh 140 | tellus molestie nunc non blandit massa enim nec. Rhoncus aenean vel 141 | elit scelerisque mauris. Fames ac turpis egestas sed tempus urna. 142 | Aenean euismod elementum nisi quis eleifend quam adipiscing. Iaculis 143 | at erat pellentesque adipiscing commodo elit. 144 |

145 |
146 |
147 | 148 | 149 | -------------------------------------------------------------------------------- /public/project-files/word-masters.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | /* a nice font that uses a user's built in font */ 7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 8 | 9 | color: #333; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | .navbar { 15 | border-bottom: 2px solid #333; 16 | padding: 15px; 17 | text-align: center; 18 | margin-bottom: 0; 19 | } 20 | 21 | .brand { 22 | font-size: 30px; 23 | } 24 | 25 | .scoreboard { 26 | max-width: 295px; 27 | width: 100%; 28 | margin: 10px auto; 29 | display: grid; 30 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr; 31 | row-gap: 10px; 32 | column-gap: 5px; 33 | } 34 | 35 | .scoreboard-letter { 36 | height: 45px; 37 | width: 45px; 38 | font-size: 30px; 39 | text-transform: uppercase; 40 | border: 3px solid #ccc; 41 | font-weight: bold; 42 | display: flex; 43 | align-items: center; 44 | justify-content: center; 45 | } 46 | 47 | .correct { 48 | background-color: darkgreen; 49 | color: white; 50 | } 51 | 52 | .close { 53 | background-color: goldenrod; 54 | color: white; 55 | } 56 | 57 | .wrong { 58 | background-color: #888; 59 | color: white; 60 | } 61 | 62 | .invalid { 63 | animation: flash 1s; 64 | } 65 | 66 | /* flashes red border and then fades back to gray */ 67 | @keyframes flash { 68 | 5% { 69 | border-color: crimson; 70 | } 71 | 72 | 100% { 73 | border-color: #ccc; 74 | } 75 | } 76 | 77 | .spiral { 78 | font-size: 40px; 79 | animation: spin 1.5s linear infinite; 80 | } 81 | 82 | /* rotates clockwise indefinitely */ 83 | @keyframes spin { 84 | to { 85 | transform: rotate(360deg); 86 | } 87 | } 88 | 89 | /* visibility hidden means the item is still there and taking up space 90 | but just not shown. display: none doesn't take up space */ 91 | .hidden { 92 | visibility: hidden; 93 | } 94 | 95 | .info-bar { 96 | display: flex; 97 | align-items: center; 98 | justify-content: center; 99 | } 100 | 101 | 102 | /* winner animation */ 103 | 104 | @keyframes rainbow { 105 | 100%, 106 | 0% { 107 | color: rgb(255, 0, 0); 108 | } 109 | 8% { 110 | color: rgb(255, 127, 0); 111 | } 112 | 16% { 113 | color: rgb(255, 255, 0); 114 | } 115 | 25% { 116 | color: rgb(127, 255, 0); 117 | } 118 | 33% { 119 | color: rgb(0, 255, 0); 120 | } 121 | 41% { 122 | color: rgb(0, 255, 127); 123 | } 124 | 50% { 125 | color: rgb(0, 255, 255); 126 | } 127 | 58% { 128 | color: rgb(0, 127, 255); 129 | } 130 | 66% { 131 | color: rgb(0, 0, 255); 132 | } 133 | 75% { 134 | color: rgb(127, 0, 255); 135 | } 136 | 83% { 137 | color: rgb(255, 0, 255); 138 | } 139 | 91% { 140 | color: rgb(255, 0, 127); 141 | } 142 | } 143 | 144 | .winner { 145 | animation: rainbow 4s infinite linear; 146 | } -------------------------------------------------------------------------------- /public/project-files/word-masters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Word Masters 7 | 8 | 9 | 10 | 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 |
46 |
47 |
48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /public/project-files/word-masters.js: -------------------------------------------------------------------------------- 1 | const ANSWER_LENGTH = 5; 2 | const ROUNDS = 6; 3 | const letters = document.querySelectorAll(".scoreboard-letter"); 4 | const loadingDiv = document.querySelector(".info-bar"); 5 | 6 | // I like to do an async init function so I can use "await" 7 | async function init() { 8 | // the state for the app 9 | let currentRow = 0; 10 | let currentGuess = ""; 11 | let done = false; 12 | let isLoading = true; 13 | 14 | // nab the word of the day 15 | const res = await fetch("https://words.dev-apis.com/word-of-the-day"); 16 | const { word: wordRes } = await res.json(); 17 | const word = wordRes.toUpperCase(); 18 | const wordParts = word.split(""); 19 | isLoading = false; 20 | setLoading(isLoading); 21 | 22 | // user adds a letter to the current guess 23 | function addLetter(letter) { 24 | if (currentGuess.length < ANSWER_LENGTH) { 25 | currentGuess += letter; 26 | } else { 27 | current = currentGuess.substring(0, currentGuess.length - 1) + letter; 28 | } 29 | 30 | letters[currentRow * ANSWER_LENGTH + currentGuess.length - 1].innerText = 31 | letter; 32 | } 33 | 34 | // user tries to enter a guess 35 | async function commit() { 36 | if (currentGuess.length !== ANSWER_LENGTH) { 37 | // do nothing 38 | return; 39 | } 40 | 41 | // check the API to see if it's a valid word 42 | // skip this step if you're not checking for valid words 43 | isLoading = true; 44 | setLoading(isLoading); 45 | const res = await fetch("https://words.dev-apis.com/validate-word", { 46 | method: "POST", 47 | body: JSON.stringify({ word: currentGuess }), 48 | }); 49 | const { validWord } = await res.json(); 50 | isLoading = false; 51 | setLoading(isLoading); 52 | 53 | // not valid, mark the word as invalid and return 54 | if (!validWord) { 55 | markInvalidWord(); 56 | return; 57 | } 58 | 59 | const guessParts = currentGuess.split(""); 60 | const map = makeMap(wordParts); 61 | let allRight = true; 62 | 63 | // first pass just finds correct letters so we can mark those as 64 | // correct first 65 | for (let i = 0; i < ANSWER_LENGTH; i++) { 66 | if (guessParts[i] === wordParts[i]) { 67 | // mark as correct 68 | letters[currentRow * ANSWER_LENGTH + i].classList.add("correct"); 69 | map[guessParts[i]]--; 70 | } 71 | } 72 | 73 | // second pass finds close and wrong letters 74 | // we use the map to make sure we mark the correct amount of 75 | // close letters 76 | for (let i = 0; i < ANSWER_LENGTH; i++) { 77 | if (guessParts[i] === wordParts[i]) { 78 | // do nothing 79 | } else if (map[guessParts[i]] && map[guessParts[i]] > 0) { 80 | // mark as close 81 | allRight = false; 82 | letters[currentRow * ANSWER_LENGTH + i].classList.add("close"); 83 | map[guessParts[i]]--; 84 | } else { 85 | // wrong 86 | allRight = false; 87 | letters[currentRow * ANSWER_LENGTH + i].classList.add("wrong"); 88 | } 89 | } 90 | 91 | currentRow++; 92 | currentGuess = ""; 93 | if (allRight) { 94 | // win 95 | alert("you win"); 96 | document.querySelector(".brand").classList.add("winner"); 97 | done = true; 98 | } else if (currentRow === ROUNDS) { 99 | // lose 100 | alert(`you lose, the word was ${word}`); 101 | done = true; 102 | } 103 | } 104 | 105 | // user hits backspace, if the the length of the string is 0 then do 106 | // nothing 107 | function backspace() { 108 | currentGuess = currentGuess.substring(0, currentGuess.length - 1); 109 | letters[currentRow * ANSWER_LENGTH + currentGuess.length].innerText = ""; 110 | } 111 | 112 | // let the user know that their guess wasn't a real word 113 | // skip this if you're not doing guess validation 114 | function markInvalidWord() { 115 | for (let i = 0; i < ANSWER_LENGTH; i++) { 116 | letters[currentRow * ANSWER_LENGTH + i].classList.remove("invalid"); 117 | 118 | // long enough for the browser to repaint without the "invalid class" so we can then add it again 119 | setTimeout( 120 | () => letters[currentRow * ANSWER_LENGTH + i].classList.add("invalid"), 121 | 10 122 | ); 123 | } 124 | } 125 | 126 | // listening for event keys and routing to the right function 127 | // we listen on keydown so we can catch Enter and Backspace 128 | document.addEventListener("keydown", function handleKeyPress(event) { 129 | if (done || isLoading) { 130 | // do nothing; 131 | return; 132 | } 133 | 134 | const action = event.key; 135 | 136 | if (action === "Enter") { 137 | commit(); 138 | } else if (action === "Backspace") { 139 | backspace(); 140 | } else if (isLetter(action)) { 141 | addLetter(action.toUpperCase()); 142 | } else { 143 | // do nothing 144 | } 145 | }); 146 | } 147 | 148 | // a little function to check to see if a character is alphabet letter 149 | // this uses regex (the /[a-zA-Z]/ part) but don't worry about it 150 | // you can learn that later and don't need it too frequently 151 | function isLetter(letter) { 152 | return /^[a-zA-Z]$/.test(letter); 153 | } 154 | 155 | // show the loading spinner when needed 156 | function setLoading(isLoading) { 157 | loadingDiv.classList.toggle("hidden", !isLoading); 158 | } 159 | 160 | // takes an array of letters (like ['E', 'L', 'I', 'T', 'E']) and creates 161 | // an object out of it (like {E: 2, L: 1, T: 1}) so we can use that to 162 | // make sure we get the correct amount of letters marked close instead 163 | // of just wrong or correct 164 | function makeMap(array) { 165 | const obj = {}; 166 | for (let i = 0; i < array.length; i++) { 167 | if (obj[array[i]]) { 168 | obj[array[i]]++; 169 | } else { 170 | obj[array[i]] = 1; 171 | } 172 | } 173 | return obj; 174 | } 175 | 176 | init(); 177 | -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btholt/complete-intro-to-web-dev-v3/015db804e3271e8e6f039e3183abab17f2fd6d51/public/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /styles/footer.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | width: 100%; 3 | padding: 50px 15px; 4 | background-color: var(--primary); 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | color: var(--text-footer); 9 | } 10 | 11 | .socials { 12 | display: flex; 13 | align-items: center; 14 | max-width: 900px; 15 | width: 100%; 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | .social { 21 | display: inline-block; 22 | list-style: none; 23 | margin-right: 40px; 24 | } 25 | 26 | .social img:hover { 27 | opacity: 0.4; 28 | } 29 | 30 | .social img { 31 | transition: opacity 0.25s; 32 | width: 30px; 33 | } 34 | 35 | .terms { 36 | font-size: 10px; 37 | } 38 | 39 | .terms p { 40 | margin: 3px; 41 | } 42 | 43 | .footer a { 44 | color: inherit; 45 | text-decoration: underline; 46 | } 47 | 48 | .social svg { 49 | transition: opacity 0.25s; 50 | } 51 | 52 | .social svg:hover { 53 | opacity: 0.4; 54 | } 55 | -------------------------------------------------------------------------------- /styles/variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary: lavender; 3 | --secondary: #281750; 4 | --highlight: #281750; 5 | 6 | --text-header: var(--primary); 7 | --text-main-headers: var(--highlight); 8 | --text-links: #51178c; 9 | --text-footer: #333; 10 | 11 | --bg-main: white; 12 | --bg-dots: var(--text-links); 13 | --bg-lesson: white; 14 | 15 | --nav-buttons: var(--highlight); 16 | --nav-buttons-text: white; 17 | 18 | --corner-active: var(--highlight); 19 | --corner-inactive: #f4f4f4; 20 | --icons: var(--primary); 21 | --footer-icons: var(--highlight); 22 | 23 | --emphasized-bg: #dce8ff; 24 | --emphasized-border: #aab6d2; 25 | } 26 | --------------------------------------------------------------------------------