diff --git a/.eslintignore b/.eslintignore index 5a5fe8e..41629e0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,4 @@ -public -# config +dist node_modules -.cache -content \ No newline at end of file +.github +types.generated.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index f77bc9b..d3563cc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,37 +1,62 @@ +/** @type {import("eslint").Linter.Config} */ module.exports = { - parser: "@typescript-eslint/parser", - extends: ["airbnb-base", "prettier"], - plugins: ["react", "jsx-a11y", "import"], + env: { + node: true, + es2022: true, + browser: true, + }, + plugins: ["tailwindcss"], + extends: ["eslint:recommended", "plugin:astro/recommended", "plugin:tailwindcss/recommended"], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, rules: { - "arrow-parens": ["error", "as-needed"], - "no-console": "off", - "no-else-return": "off", - "no-plusplus": "off", - "no-use-before-define": ["error", { functions: false }], - "object-curly-newline": "off", - "operator-linebreak": ["error", "after"], - semi: "off", + "tailwindcss/migration-from-tailwind-2": "off", + "tailwindcss/no-custom-classname": "off", + "tailwindcss/classnames-order": "off", }, settings: { - // "import/core-modules": [], - "import/resolver": { - alias: [ - ["assets", "./src/assets/*"], - ["components", "./src/components/*"], - ["config", "./config"], - ["hooks", "./src/hooks"], - ["images", "./src/images"], - ["layout", "./src/layout"], - ["models", "./src/models"], - ["pages", "./src/pages"], - ["services", "./src/services"], - ["utils", "./src/utils"], - ], + tailwindcss: { + config: "./tailwind.config.cjs", }, }, - env: { - browser: true, - node: true, - es6: true, - }, + ignorePatterns: ["**/.astro/**"], + overrides: [ + { + files: ["*.js"], + rules: { + "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], + }, + }, + { + files: ["*.astro"], + parser: "astro-eslint-parser", + parserOptions: { + parser: "@typescript-eslint/parser", + extraFileExtensions: [".astro"], + }, + rules: { + "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], + }, + }, + { + files: ["*.ts"], + parser: "@typescript-eslint/parser", + extends: ["plugin:@typescript-eslint/recommended"], + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", destructuredArrayIgnorePattern: "^_" }, + ], + "@typescript-eslint/no-non-null-assertion": "off", + }, + }, + { + // Define the configuration for ` +--- + + \ No newline at end of file diff --git a/src/components/Head/Analytics/MicrosoftClarity.astro b/src/components/Head/Analytics/MicrosoftClarity.astro new file mode 100644 index 0000000..6f46dea --- /dev/null +++ b/src/components/Head/Analytics/MicrosoftClarity.astro @@ -0,0 +1,29 @@ +--- +type Props = { + siteId?: string +} + +const { siteId = "ijcwm97t6v" } = Astro.props + +const baseUrl = "https://www.clarity.ms/tag/" +// const siteId = "ijcwm97t6v" +--- + + diff --git a/src/components/Head/Analytics/Plausible.astro b/src/components/Head/Analytics/Plausible.astro new file mode 100644 index 0000000..f732952 --- /dev/null +++ b/src/components/Head/Analytics/Plausible.astro @@ -0,0 +1,14 @@ +--- +export type Props = { + domain?: string + src?: string +} + +const { domain = "jeffastor.com", src = "https://plausible.io/js/script.js" } = Astro.props + +// const dataDomain = "jeffastor.com" +// const scriptSource = `https://plausible.io/js/script.js` +--- + + + diff --git a/src/components/Head/BaseHead.astro b/src/components/Head/BaseHead.astro new file mode 100644 index 0000000..4835b76 --- /dev/null +++ b/src/components/Head/BaseHead.astro @@ -0,0 +1,125 @@ +--- +// Import the global.css file here so that it is included on +// all pages through the use of the component. +import "@/styles/variables.css" +import "@/styles/fonts.css" +import "@/styles/global.css" +// code blocks +import "@/styles/code/code-blocks.css" +import "@/styles/code/code-titles.css" +import "@/styles/code/rehype-pretty-code.css" +// commento - commento has been discontinued +// import "@/styles/commento.css" + +// import katex styles +import "katex/dist/katex.min.css" + +import Favicons from "./Favicons.astro" +// import MicrosoftClarityAnalytics from "./Analytics/MicrosoftClarity.astro" + +import { appendToMetaTitle, metaDescriptionOrDefault } from "@/lib/meta" + +type Props = { + title?: string + description?: string + image?: string + excludeOpenGraph?: boolean + excludeTwitter?: boolean +} + +const canonicalURL = new URL(Astro.url.pathname, Astro.site) + +const { + title, + description, + image = "/astor_teaching.jpg", + excludeOpenGraph = false, + excludeTwitter = false, +} = Astro.props + +const metaTitle = appendToMetaTitle(title) +const metaDescription = metaDescriptionOrDefault(description) +--- + + + + + + + + + + + + + + +{metaTitle} + + + + + + + + + + + +{ + excludeOpenGraph ? null : ( + <> + + + + + + + ) +} + + +{ + excludeTwitter ? null : ( + <> + + + + + + + ) +} + + + diff --git a/src/components/Head/Favicons.astro b/src/components/Head/Favicons.astro new file mode 100644 index 0000000..9e5ced4 --- /dev/null +++ b/src/components/Head/Favicons.astro @@ -0,0 +1,47 @@ +--- +// NOTE: +// This is the results of generating favicons from favycon + +// https://github.com/ruisaraiva19/favycon + +// OTHER SITE TO LOOK AT +// https://realfavicongenerator.net/ + +type Props = { + useFullFaviconSet?: boolean +} + +const { useFullFaviconSet = false } = Astro.props +--- + +{ + useFullFaviconSet ? ( + <> + + + + + + + + + + + + + + + + + + + + ) : ( + <> + + + + + + ) +} diff --git a/src/components/Head/PostSEO.astro b/src/components/Head/PostSEO.astro new file mode 100644 index 0000000..2db7e87 --- /dev/null +++ b/src/components/Head/PostSEO.astro @@ -0,0 +1,206 @@ +--- +import type { BlogPostWithMeta, IPostData } from "@/types/posts" + +import * as links from "@/lib/links" + +import { config } from "@/core/config" + +import TwitterTags from "./SEO/TwitterTags.astro" +import OpenGraphBasicTags from "./SEO/OpenGraphBasicTags.astro" +import OpenGraphArticleTags from "./SEO/OpenGraphArticleTags.astro" +import OpenGraphImageTags from "./SEO/OpenGraphImageTags.astro" +import OpenGraphOptionalTags from "./SEO/OpenGraphOptionalTags.astro" + +type Props = { + post: BlogPostWithMeta + globbedPost: Record | undefined +} + +const props = Astro.props + +const { post, globbedPost } = props as Props + +const { + site_url, + // sub_title, + site_title, + info, + // site_logo, + site_description, +} = config + +// const siteUrl = Astro.site ?? site_url +const canonicalURL = new URL(Astro.url.pathname, Astro.site) + +const relativePostUrl = links.formatBlogLinkFromSlug(post.slug) +const postUrl = new URL(relativePostUrl, Astro.site) + +const postData = post.data as IPostData + +// const isHighSchoolPost = postData.isHighSchoolLesson ?? false + +const twitter = info.contacts.twitter +const authorName = info.name +const postTitle = post.data.title + +// @ts-ignore +const postExcerpt = post?.excerpt as string | undefined + +const postDescription = postData.description ?? postExcerpt ?? null +// const description = postDescription ?? site_description // "A new post by Jeff Astor." +const description = site_description // "A new post by Jeff Astor." +const tags = postData.tags ?? [] +// turn tags into string like this: +// ;("tag1, tag2, tag3") +const stringifiedTags = tags.join(", ") +const postCategory = postData.category ?? undefined +const publishDate = postData.date +const updatedDate = postData.dateModified ?? null + +// const metaTitle = appendToMetaTitle(postTitle) +// const metaDescription = metaDescriptionOrDefault(description) + +const image = "/images/astor_teaching_512_x_512.jpg" +const logo = "/images/JeffAstor_Logo_512x512_rounded.png" + +// const imageUrl = new URL(image, Astro.url) +const imageUrl = new URL(image, Astro.site) +const logoUrl = new URL(logo, Astro.url) + +// const schemaOrgJSONLD = [] +// website schema item +const websiteSchemaItem = { + "@context": "http://schema.org", + "@type": "WebSite", + name: site_title, + url: site_url, + logo: logoUrl.toString(), + image: imageUrl.toString(), + // alternateName: sub_title, +} as const + +// breadcrumb list +const breadCrumbSchemaItem = { + "@context": "http://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + item: { + "@id": postUrl, + name: site_title, + image: imageUrl.toString(), + }, + }, + ], +} as const +const blogPostSchemaItem = { + "@context": "http://schema.org", + "@type": "BlogPosting", + // "@type": "TechArticle", + url: site_url, + name: postTitle, + alternateName: site_title, + headline: postTitle, + mainEntityOfPage: { + "@type": "WebSite", + "@id": site_url, + }, + // proficiencyLevel: isHighSchoolPost ? "Beginner" : "Expert", + image: { + "@type": "ImageObject", + "@id": imageUrl.toString(), + url: imageUrl.toString(), + height: "512", + width: "512", + }, + author: { + "@type": "Person", + name: authorName, + url: site_url, + image: { + "@type": "ImageObject", + "@id": imageUrl.toString(), + url: imageUrl.toString(), + height: "512", + width: "512", + }, + }, + description, + keywords: tags, + /* the schema expects Date or DateTime using ISO 8601 format. For Date that is yyyy-MM-dd */ + datePublished: publishDate.toISOString().substring(0, 10), + /* updateDate is optional frontmatter, so we conditionally add dateModified if it exists */ + ...(updatedDate ? { dateModified: updatedDate.toISOString().substring(0, 10) } : {}), +} as const + +const schemaOrgJSONLD = [websiteSchemaItem, breadCrumbSchemaItem, blogPostSchemaItem] as const + +const stringifiedSchema = JSON.stringify(schemaOrgJSONLD, null, 2) + +const twitterInfo = { + card: "summary_large_image" as const, + creator: twitter, + title: postTitle, + // description, + description: postDescription ?? description, + image: imageUrl.toString(), + // site: siteUrl.toString(), + section: postCategory, +} as const + +const openGraphInfo = { + article: { + section: postCategory, + datePublished: publishDate.toISOString().substring(0, 10), + ...(updatedDate ? { dateModified: updatedDate.toISOString().substring(0, 10) } : {}), + authors: [authorName], + tags, + }, + basic: { + title: postTitle, + type: "article" as const, + // url: postUrl.toString(), + url: canonicalURL, + image: imageUrl.toString(), + }, + image: { + // image: "", + height: 512, + width: 512, + alt: "Teaching former students", + type: "image/jpeg", + }, + optional: { + description: postDescription, + siteName: site_title, + }, +} as const + +const openGraphArticle = { ...openGraphInfo.article, authors: [...openGraphInfo.article.authors] } +const openGraphOptional = { ...openGraphInfo.optional, description: openGraphInfo.optional.description ?? undefined } + +/** + * NOTE: Commento has been discontinued + * + * Disabling comments until a decision has been made on what to migrate to + */ +--- + + + + + - - {/* OpenGraph tags */} - - {postSEO ? : null} - - - - {/* */} - - {/* Twitter Card tags */} - - - - - - - ) -} - -// const SEO = ({ postNode, postPath, postSEO, ...props }) => ( -// } -// /> -// ) - -// class SEO extends Component { -// render() { -// const { postNode, postPath, postSEO } = this.props -// let title -// let description -// let image = config.siteLogo -// let postURL - -// console.log(postNode) - -// if (postSEO) { -// const postMeta = postNode.frontmatter -// title = postMeta.title -// // ({ title } = postMeta) -// description = postMeta.description -// ? postMeta.description -// : postNode.excerpt -// // image = postMeta.image -// // postURL = urljoin(config.siteUrl, config.pathPrefix, postPath) -// postURL = `${config.siteUrl}/blog${postPath}` -// } else { -// title = config.siteTitle -// description = config.siteDescription -// // image = config.siteLogo -// } - -// // image = urljoin(config.siteUrl, config.pathPrefix, image); -// // image = config.siteUrl + config.pathPrefix + (!!image ? image : config.siteLogo) -// // image = -// // const blogURL = urljoin(config.siteUrl, config.pathPrefix) -// const blogURL = `${config.siteUrl}/` - -// console.log("blogURL", blogURL) -// console.log("postURL", postURL) -// console.log("description", description) -// console.log("title", title) -// const schemaOrgJSONLD = [ -// { -// '@context': 'http://schema.org', -// '@type': 'WebSite', -// url: blogURL, -// name: title, -// alternateName: config.siteTitleAlt ? config.siteTitleAlt : '' -// }, -// // author: { -// // "@type": "Person", -// // "name": "${author.name}", -// // "url": "${siteUrl}" -// // }, -// ] -// if (postSEO) { -// schemaOrgJSONLD.push( -// { -// '@context': 'http://schema.org', -// '@type': 'BreadcrumbList', -// itemListElement: [ -// { -// '@type': 'ListItem', -// position: 1, -// item: { -// '@id': postURL, -// name: title, -// image -// } -// } -// ] -// }, -// { -// '@context': 'http://schema.org', -// '@type': 'BlogPosting', -// url: blogURL, -// name: title, -// alternateName: config.siteTitleAlt ? config.siteTitleAlt : '', -// headline: title, -// // description: description, -// image: { -// '@type': 'ImageObject', -// url: image -// }, -// author: { -// "@type": "Person", -// "name": "${author.name}", -// url: blogURL, -// }, -// description -// } -// ) -// } - -// console.log('schemaOrgJSONLD', schemaOrgJSONLD) -// return ( -// -// {/* General tags */} -// -// - -// {/* Schema.org tags */} -// - -// {/* OpenGraph tags */} -// -// {postSEO ? : null} -// -// -// -// {/* */} - -// {/* Twitter Card tags */} -// -// -// -// -// -// -// ) -// } -// } - -export default SEO diff --git a/src/components/Socials/ContactIcon.astro b/src/components/Socials/ContactIcon.astro new file mode 100644 index 0000000..d8636bb --- /dev/null +++ b/src/components/Socials/ContactIcon.astro @@ -0,0 +1,92 @@ +--- +import ExternalLink from "@/components/Common/ExternalLink.astro" +import Codepen from "@/components/Icons/Codepen.astro" +import Envelope from "@/components/Icons/Envelope.astro" +import Facebook from "@/components/Icons/Facebook.astro" +import Github from "@/components/Icons/Github.astro" +import Instagram from "@/components/Icons/Instagram.astro" +import Linkedin from "@/components/Icons/Linkedin.astro" +import Twitter from "@/components/Icons/Twitter.astro" +import YouTube from "@/components/Icons/YouTube.astro" + +import { cn } from "@/utils/styles" + +const iconMapping = { + codepen: Codepen, + email: Envelope, + facebook: Facebook, + github: Github, + instagram: Instagram, + linkedin: Linkedin, + twitter: Twitter, + youtube: YouTube, +} as const + +const iconSizeToClassNamesMapping = { + sm: { + link: "w-3.5 h-3.5 rounded m-1 p-1.5", + icon: "max-w-5 max-h-5", + }, + md: { + link: "w-5 h-5 rounded-md m-2 p-3", + icon: "max-w-6 max-h-6", + }, + lg: { + link: "w-6 h-6 rounded-lg m-4 p-4", + icon: "max-w-12 max-h-12", + }, +} as const + +const iconVariantToClassNamesMapping = { + standard: { + stroke: "stroke-light text-light", + fill: "fill-light text-light", + }, + inverted: { + stroke: "stroke-dark text-dark", + fill: "fill-dark text-dark", + }, +} as const + +type Props = { + icon: keyof typeof iconMapping + path: string + size?: keyof typeof iconSizeToClassNamesMapping + variant?: keyof typeof iconVariantToClassNamesMapping + fillIcon?: boolean + className?: string +} + +const { icon, path, size, variant, fillIcon, className, ...props } = Astro.props + +const sizeStyles = iconSizeToClassNamesMapping[size ?? "sm"] +const variantStyles = iconVariantToClassNamesMapping[variant ?? "standard"][fillIcon ? "fill" : "stroke"] +const Icon = iconMapping[icon] + +const wrapperClassnames = cn(["flex flex-col items-center justify-center", className]) +const linkClassnames = cn([ + "flex-center flex items-center justify-center", + "text-white", + "group duration-200", + variantStyles, + sizeStyles.link, +]) +const iconClassnames = cn([ + sizeStyles.icon, + "flex-shrink-0", + "will-change-transform", + "group-hover:duration-300 group-hover:ease-op-ease-squish-5 group-hover:scale-125", +]) +--- + +
+ + + +
diff --git a/src/components/Socials/SocialIcons.astro b/src/components/Socials/SocialIcons.astro new file mode 100644 index 0000000..075d42e --- /dev/null +++ b/src/components/Socials/SocialIcons.astro @@ -0,0 +1,38 @@ +--- +import ContactIcon from "./ContactIcon.astro" +import type { SocialIconSize } from "@/lib/socials" +import { getSocials } from "@/lib/socials" +import { cn } from "@/utils/styles" + +type Props = { + size?: SocialIconSize + className?: string +} + +const props: Props = Astro.props + +const size = props.size ?? "sm" + +const socials = getSocials(size) + +const wrapperClassnames = cn([ + // + "my-2", + "-translate-x-3 lg:-translate-x-0", + props.className, +]) +--- + +
+ { + socials.map((social) => ( + + )) + } +
diff --git a/src/components/_Template.astro b/src/components/_Template.astro new file mode 100644 index 0000000..3de833e --- /dev/null +++ b/src/components/_Template.astro @@ -0,0 +1,28 @@ +--- +import { cn } from "@/utils/styles" + +type Props = { + className?: string +} + +const { className, ...props } = Astro.props + +const wrapperClassnames = cn([ + "flex", + "flex-col", + "items-center", + "justify-center", + "w-full", + "max-w-2xl", + "mx-auto", + "px-4", + "py-8", + "space-y-8", + className, + // +]) +--- + +
+ +
diff --git a/src/components/index.tsx b/src/components/index.tsx deleted file mode 100644 index e8ba2bf..0000000 --- a/src/components/index.tsx +++ /dev/null @@ -1,35 +0,0 @@ -export { default as About } from "./About/About" -export { default as AboutMyWork } from "./AboutMyWork/AboutMyWork" -export { default as Author } from "./Author/Author" -export { default as Blog } from "./Blog/Blog" -export { default as BlogCategorySVG } from "./BlogCategorySVG/BlogCategorySVG" -export { default as BlogTopics } from "./BlogTopics/BlogTopics" -export { default as ClipboardTrigger } from "./ClipboardTrigger/ClipboardTrigger" -export { default as CodeBlockTitle } from "./CodeBlockTitle/CodeBlockTitle" -export { default as CodesandboxDisplay } from "./CodesandboxDisplay/CodesandboxDisplay" -export { default as ConfirmationFade } from "./ConfirmationFade/ConfirmationFade" -export { default as ContactIcon } from "./ContactIcon/ContactIcon" -export { default as FastAPISeriesList } from "./FastAPISeriesList/FastAPISeriesList" -export { default as FourOhFour } from "./FourOhFour/FourOhFour" -export { default as Landing } from "./Landing/Landing" -export { default as MontyHall } from "./MontyHallViz/MontyHallViz" -export { default as Navbar } from "./Navbar/Navbar" -export { default as NextPrevPosts } from "./NextPrevPosts/NextPrevPosts" -export { default as PrePlaceholder } from "./PrePlaceholder/PrePlaceholder" -export { default as Post } from "./Post/Post" -export { default as PostActions } from "./PostActions/PostActions" -export { default as PostCodeBlock } from "./PostCodeBlock/PostCodeBlock" -export { default as PostComments } from "./PostComments/PostComments" -export { default as PostContent } from "./PostContent/PostContent" -export { default as PostListing } from "./PostListing/PostListing" -export { default as PostPreview } from "./PostPreview/PostPreview" -export { default as PostSeriesList } from "./PostSeriesList/PostSeriesList" -export { default as PostTags } from "./PostTags/PostTags" -export { default as SEO } from "./SEO/SEO" - -export * from "./Icons" - -// blog display stuff -export { default as CRUDEndpointsTable } from "./BlogDisplayElements/CRUDEndpointsTable" -export { default as DeprecatedNotification } from "./BlogDisplayElements/DeprecatedNotification" -export { default as RegistrationFlowImage } from "./BlogDisplayElements/RegistrationFlowImage" diff --git a/src/content/config.ts b/src/content/config.ts new file mode 100644 index 0000000..090c267 --- /dev/null +++ b/src/content/config.ts @@ -0,0 +1,37 @@ +import { defineCollection, z } from "astro:content" + +export const BlogPostSchemaAttrs = { + title: z.string(), + description: z.string().optional(), + category: z.string().optional(), + image: z.string().optional(), + date: z + .string() + .or(z.date()) + .transform((val) => new Date(val)), + dateModified: z + .string() + .optional() + .transform((str) => (str ? new Date(str) : undefined)), + // slug: z.string(), + series: z.string().optional(), + isDraft: z.boolean().optional().default(false), + published: z.enum(["true", "false"]).optional(), + deprecated: z.enum(["true", "false"]).optional(), + author: z.string().optional(), + rating: z + .string() + .optional() + .transform((str) => (str ? Number(str) : undefined)), + template: z.string().optional(), + tags: z.array(z.string()).optional(), + // isHighSchoolLesson: z.boolean().optional().default(false), +} +export const BlogPostSchema = z.object(BlogPostSchemaAttrs) + +const blogPostsCollection = defineCollection({ + type: "content", + schema: BlogPostSchema, +}) + +export const collections = { posts: blogPostsCollection } diff --git a/content/2018-07-20-Fast-ai-Deep-Learning/index.mdx b/src/content/posts/2018-07-20-Fast-ai-Deep-Learning.mdx similarity index 60% rename from content/2018-07-20-Fast-ai-Deep-Learning/index.mdx rename to src/content/posts/2018-07-20-Fast-ai-Deep-Learning.mdx index 6fcd6da..f03475e 100644 --- a/content/2018-07-20-Fast-ai-Deep-Learning/index.mdx +++ b/src/content/posts/2018-07-20-Fast-ai-Deep-Learning.mdx @@ -1,7 +1,8 @@ --- title: "Fast.ai - Practical Deep Learning for Coders" category: "Course Review" -date: 2018-07-20 +# image: /fast_ai.png +date: "07/20/2018" slug: "fast-ai-practical-deep-learning-for-coders" author: "Jeremy Howard" link: "https://course.fast.ai/" @@ -17,13 +18,10 @@ tags: Plain and simple, the best deep learning course on the Internet. Jeremy Howard has found a way to make State Of The Art (SOTA) deep learning accessible. If you want to truly understand how convolutional neural networks and recommendation engines work, take this course. He provides the best explanations I've found on both subjects. Before this, I wasn't a Pytorch user. Now there's no reason not to be one. - - ## Pros -Every lesson is jampacked with information I wanted to know. From beginning to end, I was hooked. Can't say that for many courses. Pytorch is a great framework and the way he mixes up high level abstractions and lower-level neural network construction is phenomenal. His top-down teaching approach makes learning significantly more enjoyable. From the very beginning, students are working with state of the art deep learning models. Then he digs down deeper into the mechanics of each concept using the products students have already built. Afterwards, he often builds them all the way back up from scratch. This methodology has the benefit of providing tangible, working products right from the start. The student is never in question about "why" they're learning a given topic. On top of that, the course flow helps the learners build the appropriate context needed to understand the more difficult concepts that come further down the road. It's clear that this is intentional on the instructor's part. Simply excellent. +Every lesson is jampacked with information I wanted to know. From beginning to end, I was hooked. Can't say that for many courses. Pytorch is a great framework and the way he mixes up high level abstractions and lower-level neural network construction is phenomenal. ## Cons The videos are long. I watched them at 1.5X speed, but still. 2 hour videos take time. Especially when you're coding along with the videos. Jeremy also spends time answering questions that I'm not interested in hearing answers to. Otherwise, don't have much to add on the negative side. - diff --git a/content/2019-02-28-How-to-Make-a-Website/index.md b/src/content/posts/2019-02-28-How-to-Make-a-Website.mdx similarity index 76% rename from content/2019-02-28-How-to-Make-a-Website/index.md rename to src/content/posts/2019-02-28-How-to-Make-a-Website.mdx index fda0ba0..4160492 100644 --- a/content/2019-02-28-How-to-Make-a-Website/index.md +++ b/src/content/posts/2019-02-28-How-to-Make-a-Website.mdx @@ -1,53 +1,62 @@ --- title: "How to Start Building Your Own Website" category: "Web Development" -date: 2019-02-28 +date: "02/28/2019" published: "true" slug: "how-to-make-a-website" tags: - - HTML - - CSS - - web development - - front-end - - beginner - - high school lesson + - HTML + - CSS + - web development + - front-end + - beginner + - high school lesson +# # isHighSchoolLesson: true --- +import Image from "@/components/MDX/MdxImage.astro" +import anatomyOfHTML from "../../assets/posts/anatomy-of-an-html-element.png" +import attributeSyntax from "../../assets/posts/attribute-syntax.png" +import cssLabels from "../../assets/posts/css-labels.png" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + This walkthrough should cover only the most foundational concepts in HTML and CSS. The purpose is not to make you a complete and total expert, but to bring you up to speed with the essential components necessary to start building a website from scratch. ## How to Build a Webpage Walkthrough The sequence is as follows: -* Part 1 - How HTML Works -* Part 2 - CSS and HTML Attributes -* Part 3 - Composing a Skeleton Website + +- Part 1 - How HTML Works +- Part 2 - CSS and HTML Attributes +- Part 3 - Composing a Skeleton Website ## Part 1 - How HTML Works **Tags and Elements**: When building an HTML webpage, the first thing to know is that the page is constructed entirely of HTML elements. Each element starts with a opening tag and ends with a closing tag. For example, if we wanted to create header text like the one at the top of this document, we would begin with the opening tag `

` and end with a closing tag `

`. As you might be able to tell, the difference between an opening and closing tag is that the closing tag has a forward slash ( / ) at the beginning of the tag. If we wanted to create paragraph text, we would use the `

` opening tag and the `

` closing tag. The text we put in between those tags is known as the content and is what appears on the page. -![Anatomy of an HTML Element](./anatomy-of-an-html-element.png) +Anatomy of an HTML element **Nesting Elements**: A big part of HTML is that we can place some elements inside of other elements. This is known as nesting. When we create a web page, we’ll nest elements inside of each other all the time. That will look like the code below. We call the tags on the outside the parent and the tags on the inside the child. We often use `
` tags as parent elements. Div elements are short for divider elements, and they section off different parts of the page. ```html
-

- This content is inside of the child tag, which is inside of the parent tag. Keep an eye on indentation to see which is the parent and which is the child! -

-
+

+ This content is inside of the child tag, which is inside of the parent tag. Keep an eye on indentation to see which + is the parent and which is the child! +

+ ``` -**Building a Skeleton Webpage**: When we start building a complete HTML webpage, we’ll need to use certain elements that let our browser know how to create and style the page. First, we indicate that we’ll be building an HTML document by placing this tag: `` at the beginning of the page. Next, we’ll use an `` element that will surround all the code on our page. Inside the opening and closing html tags, we’ll place two other important elements: `` and ``. The head tags hold all the code that goes on behind the scenes of our website. The body tag holds all the code that actually appears on our webpage. Every website has a basic layout that looks the same. Here’s how you should start EVERY webpage: +**Building a Skeleton Webpage**: When we start building a complete HTML webpage, we’ll need to use certain elements that let our browser know how to create and style the page. First, we indicate that we’ll be building an HTML document by placing this tag: `` at the beginning of the page. Next, we’ll use an `` element that will surround all the code on our page. Inside the opening and closing html tags, we’ll place two other important elements: `` and ``. The head tags hold all the code that goes on behind the scenes of our website. The body tag holds all the code that actually appears on our webpage. Every website has a basic layout that looks the same. Here’s how you should start EVERY webpage: ```html - + - - My Shiny New Website - - - + + My Shiny New Website + + ``` @@ -55,31 +64,31 @@ After you place those tags inside your html document, you can start placing elem ### Let's summarize how HTML works: -* HTML elements are composed of opening and closing tags with content in between them. We can nest HTML elements inside of each other to create -* `` - This tag tells your browser that the rest of text that is about to follow is apart of an HTML document. If you leave it out, some browsers won't 'know' what type of document they are looking at (computers are dumb), and the page will break. It's a little boring, but you should have it. -* `` - The most basic HTML tags are simply `` and ``, and all the content of your pages will be written inside these tags. -* `` - The `` contains information about your website, but not actual content that will show up on the page (think of it as the 'brains' of your webpage.) It will contain things like links to stylesheets and code that will make your page beautiful and interactive. -* The `` contains all the content of your page that will actually show up on the screen. 90% of the HTML you write will go inside the ``. +- HTML elements are composed of opening and closing tags with content in between them. We can nest HTML elements inside of each other to create +- `` - This tag tells your browser that the rest of text that is about to follow is apart of an HTML document. If you leave it out, some browsers won't 'know' what type of document they are looking at (computers are dumb), and the page will break. It's a little boring, but you should have it. +- `` - The most basic HTML tags are simply `` and ``, and all the content of your pages will be written inside these tags. +- `` - The `` contains information about your website, but not actual content that will show up on the page (think of it as the 'brains' of your webpage.) It will contain things like links to stylesheets and code that will make your page beautiful and interactive. +- The `` contains all the content of your page that will actually show up on the screen. 90% of the HTML you write will go inside the ``. ## Part 2 - Introducing Attributes and CSS **Common Elements**: There are a number of HTML elements that we use pretty frequently on any webpage. Here are a few common elements and the things they create. -* **Paragraph Text**: `

Hello!

` -* **Biggest Heading Text**: `

Biggest Header

` -* **Middle Heading Text**: `

Medium Sized Header

` -* **Small Heading Text**: `
Smallest Header
` -* **Links:** `Link to Google` -* **Images** `` -* **Horizontal Rule**: `
` -* **Divider** `
This element sections off part of the page
` -* **Ordered Lists**: `
` -* **Unordered Lists**: `
` -* **List Items**: `
  • A list item goes inside a list
  • ` +- **Paragraph Text**: `

    Hello!

    ` +- **Biggest Heading Text**: `

    Biggest Header

    ` +- **Middle Heading Text**: `

    Medium Sized Header

    ` +- **Small Heading Text**: `
    Smallest Header
    ` +- **Links:** `Link to Google` +- **Images** `` +- **Horizontal Rule**: `
    ` +- **Divider** `
    This element sections off part of the page
    ` +- **Ordered Lists**: `
    ` +- **Unordered Lists**: `
    ` +- **List Items**: `
  • A list item goes inside a list
  • ` You’ll notice that some of these elements have information inside of the tags. These are called **attributes**, and provide extra information to the element, like where a photo is, or where a link should direct users to. To give an element an attribute, place the name of the attribute inside the opening tag followed by an equal sign and the data needed inside quotations like so: -![html attributes syntax](https://s3.amazonaws.com/upperline/curriculum-assets/html/attribute-syntax.png) +html attributes syntax We’ll use attributes often to style elements and inside of images and links. @@ -87,7 +96,7 @@ We’ll use attributes often to style elements and inside of images and links. ```html -

    Click here for my favorite search engine

    +

    Click here for my favorite search engine

    ``` @@ -96,14 +105,14 @@ Be aware that you can also link to parts of the webpage using IDs, which we’ll **Adding Images**: Without any images, your website will be pretty boring. Let's add in an image or two. Use the `` tag (which doesn't need a closing tag) to add an image to your site. The src attribute links to the location of the image (either online or in your project directory). ```html - + ``` **Styling Elements with CSS**: CSS (which stands for cascading style sheets) is the fundamental way of adding style to your websites. Without it, your sites are just a bunch of text in ugly chunks. All the color, style, and generally cool things you see on the web require tons of CSS. Let’s see how that works. CSS is written using rules. Each rule is composed of a selector, properties, and values. For example, we can select all h1 elements and set their background color to blue. The h1 is the selector, background-color is the property, and blue is the value. -![Selector, Property, Value](https://s3.amazonaws.com/upperline/curriculum-assets/css/css-labels.png) +css selector property and value syntax There are many CSS properties to manipulate, such as height, width, font-size, font-family, color, text-align, border, margin, padding, display, position, and many others. Look a few up! @@ -112,9 +121,9 @@ There are many CSS properties to manipulate, such as height, width, font-size, f Classes and ids are types of attributes that we can add onto an element in order to be more specific with our CSS selectors. This is what they look like: ```html -

    +

    -

    +

    ``` Classes and IDs allow us to more finely select elements to style using CSS. If we have 10 `

    ` elements on our page, but only want to change the background color of a few of them, we'd reference their class instead of the tag type. If you wanted to only style the `

    ` on your page that is the main title, while leaving all other `

    ` elements alone, we'd reference the id we've given it. @@ -123,7 +132,7 @@ We reference a class by using a . (period) and the class name: ```css .animal-description { - background-color: #FF0000; + background-color: #ff0000; } ``` @@ -131,7 +140,7 @@ We reference an id by using a # (hashtag) and the id name: ```css #main-title { - background-color: #FF0000; + background-color: #ff0000; } ``` @@ -140,7 +149,7 @@ We reference an id by using a # (hashtag) and the id name: Let's say we have the following HTML elements, and we want to give them all a single blue background color, and want all of the elements to be centered in the page. ```html - +

    Francis Crick

    Nobel Prize winner and co-discoverer of the double-helix structure of DNA

    ``` @@ -149,7 +158,7 @@ We could work on individually styling each of these elements, but this gets old ```html
    - +

    James Watson

    Nobel Prize winner and co-discoverer of the double-helix structure of DNA

    @@ -172,13 +181,13 @@ For lists, use `
      ` and `
        `. There are unordered lists and ordered lists. T
        • First List Item
        • Second List Item
        • -
        • Third List Item
        • +
        • Third List Item
        1. First List Item
        2. Second List Item
        3. -
        4. Third List Item
        5. +
        6. Third List Item
        ``` @@ -189,13 +198,12 @@ Whew! That was a lot. I think we’re finally ready to build our own web page. **Getting Something On The Page** - Open https://www.codepen.io. Create a new pen and place the following code into the HTML section: ```html - + - - My Very Own Website - - - + + My Very Own Website + + ``` @@ -221,9 +229,9 @@ Great! Now you’re ready to build yourself a portfolio page. Most likely, you'r Want a more comprehensive overview of HTML and CSS? Want to expand your skills and practice what you've learned? Check out a few of these additional resources to get you going: -* [HTMLDOG](https://www.htmldog.com/guides/html/beginner/) - HTML Beginner Tutorial to show you the ropes of HTML. -* [Learn HTML & CSS](https://learn.shayhowe.com/html-css/) - Amazing tutorial series from Shay Howe on all the ins and outs of web development. Couldn't recommend this online book enough. Use it as a reference when going through other tutorial series. -* [FreeCodeCamp](https://www.freecodecamp.com) - Online platform with 300 hours of guided front-end web development challenges that help you master HTML and CSS in its entirety. -* [Khan Academy](https://www.khanacademy.com) - Khan academy has added a number of computer programming and computer science courses that are very structured and well lead. This is perfect for beginners and intermediate students alike. -* [Codecademy](https://www.codecademy.com) - Online platform that offers a number of free solid courses in web design and other disciplines -* [Quizlet HTMl & CSS Flash Cards](https://quizlet.com/5807800/csshtml-flash-cards/) - Set of flash cards on the basics of HTML, CSS, and the Internet. Used for an Intro to Web Design class. +- [HTMLDOG](https://www.htmldog.com/guides/html/beginner/) - HTML Beginner Tutorial to show you the ropes of HTML. +- [Learn HTML & CSS](https://learn.shayhowe.com/html-css/) - Amazing tutorial series from Shay Howe on all the ins and outs of web development. Couldn't recommend this online book enough. Use it as a reference when going through other tutorial series. +- [FreeCodeCamp](https://www.freecodecamp.com) - Online platform with 300 hours of guided front-end web development challenges that help you master HTML and CSS in its entirety. +- [Khan Academy](https://www.khanacademy.com) - Khan academy has added a number of computer programming and computer science courses that are very structured and well lead. This is perfect for beginners and intermediate students alike. +- [Codecademy](https://www.codecademy.com) - Online platform that offers a number of free solid courses in web design and other disciplines +- [Quizlet HTMl & CSS Flash Cards](https://quizlet.com/5807800/csshtml-flash-cards/) - Set of flash cards on the basics of HTML, CSS, and the Internet. Used for an Intro to Web Design class. diff --git a/content/2019-03-04-The-Box-Model-and-CSS-Positioning/index.md b/src/content/posts/2019-03-04-The-Box-Model-and-CSS-Positioning.mdx similarity index 70% rename from content/2019-03-04-The-Box-Model-and-CSS-Positioning/index.md rename to src/content/posts/2019-03-04-The-Box-Model-and-CSS-Positioning.mdx index 60ae121..6832432 100644 --- a/content/2019-03-04-The-Box-Model-and-CSS-Positioning/index.md +++ b/src/content/posts/2019-03-04-The-Box-Model-and-CSS-Positioning.mdx @@ -1,19 +1,32 @@ --- title: "The Box Model and CSS Positioning" category: "Web Development" -date: 2019-03-04 +date: "03/04/2019" published: "true" slug: "the-box-model-and-css-positioning" tags: - - HTML - - CSS - - box model - - positioning - - web development - - beginner - - high school lesson + - HTML + - CSS + - box model + - positioning + - web development + - beginner + - high school lesson +# # isHighSchoolLesson: true --- +import Image from "@/components/MDX/MdxImage.astro" +import wikipediaDevTools from "../../assets/posts/wikipedia-dev-tools.png" + +import boxModel1 from "../../assets/posts/box-model-1.png" +import boxModel2 from "../../assets/posts/box-model-2.png" +import boxModel3 from "../../assets/posts/box-model-3.png" +import boxModel4 from "../../assets/posts/box-model-4.png" +import boxModel5 from "../../assets/posts/box-model-5.png" +import boxModelGif from "../../assets/posts/box-model.gif" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + In the previous walkthrough, we focused on how to use HTML and CSS to get our content onto our website. This post will cover how to position elements on a page. By understanding the box model and CSS positioning, web developers can design visually appealing sites. Here's how they both work. @@ -24,21 +37,23 @@ The first thing you should know is that every single element on an HTML page act Open up [Wikipedia.com](http://www.wikipedia.com) and then inspect element on the page. -![Wikipedia Dev Tools](./wikipedia-dev-tools.png) +Wikipedia dev tools Inside the two head tags, there should be a style tag that looks like this ``. Open those tags in and insert the following code before everything else inside the style tags. ```css -*, *:before, *:after { - border: 1px solid red; +*, +*:before, +*:after { + border: 1px solid red; } ``` -We've just added a solid red border to every element on the page using the * wildcard selector. +We've just added a solid red border to every element on the page using the \* wildcard selector. -![Wikipedia with rectangles](https://s3.amazonaws.com/upperline/curriculum-assets/css/Wikipedia-box.png) +Wikipedia dev tools -Notice how everything on the page is surrounded by rectangles? There's even rectangles *inside* of other rectangles. **Rectangleception**. +Notice how everything on the page is surrounded by rectangles? There's even rectangles _inside_ of other rectangles. **Rectangleception**. This is the perfect way to get started with the box model. @@ -46,24 +61,33 @@ This is the perfect way to get started with the box model. The box model is the way we think about how to position HTML elements on a page using CSS. Every element lives inside its own box, and we can change the properties of the box to move elements around on a page. -One way to think of the box model is to imagine each element as a picture frame. +One way to think of the box model is to imagine each element as a picture frame. -![box model 1](./box-model-1.png) +Box model example Let's zoom in on the picture of me and use this frame as an example. What similarities do each of these picture frames share? -![box model 2](./box-model-2.png) -+ Well, each frame has content - in this case, the picture itself. The image also has a certain **width** and **height**. -![box model 3](./box-model-3.png) -+ Moving outward, we next have that white space, which we call **padding**. -![box model 4](./box-model-4.png) -+ After the padding, we hit the picture frame itself. In CSS we call this the **border**. -![box model 5](./box-model-5.png) -+ Last, we have the space beyond the frame that distances it from the other frames on the wall - this is the **margin**. -![box model 6](./box-model-6.png) + +Box model example + +Well, each frame has content - in this case, the picture itself. The image also has a certain **width** and **height**. + +Box model example + +Moving outward, we next have that white space, which we call **padding**. + +Box model example + +After the padding, we hit the picture frame itself. In CSS we call this the **border**. + +Box model example + +Last, we have the space beyond the frame that distances it from the other frames on the wall - this is the **margin**. + +Box model example As you'll learn shortly, we can move elements around on a page by modifying height, width, margin, border, and padding. -![box model gif](https://s3.amazonaws.com/upperline/curriculum-assets/css/BoxModel.gif) +Box model example ## Designing The Layout of Our website @@ -98,8 +122,9 @@ Still sound kind of confusing? The best way to understand the display property is to see examples of it being used. Let's look at a few inline elements and a few block elements: -* **Inline elements**: img, a, span, br, em, strong -* **Block elements**: p, h1, ul, li, almost everything else + +- **Inline elements**: img, a, span, br, em, strong +- **Block elements**: p, h1, ul, li, almost everything else
        @@ -115,7 +140,7 @@ Let's look at a few inline elements and a few block elements: ### The CSS Position Property -Many of my students who are new to web development overuse `position: absolute` to move elements around on the page. While this strategy *does* work, it's usually not the right long-term solution. +Many of my students who are new to web development overuse `position: absolute` to move elements around on the page. While this strategy _does_ work, it's usually not the right long-term solution. Using the `position: absolute` in CSS removes HTML elements from the normal flow of the page. As stated by [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/position): @@ -131,8 +156,8 @@ This is how elements appear on the page by default. You should only change this Here are the other two display property options for reference and what MDN has to say about them: -* **relative**: The element is positioned according to the normal flow of the document, and then offset relative to itself based on the values of `top`, `right`, `bottom`, and `left`. The offset does not affect the position of any other elements; thus, the space given for the element in the page layout is the same as if position were `static`. -* **fixed**: The element is removed from the normal document flow, and no space is created for the element in the page layout. It is positioned relative to the initial containing block established by the `viewport`... Its final position is determined by the values of `top`, `right`, `bottom`, and `left`. +- **relative**: The element is positioned according to the normal flow of the document, and then offset relative to itself based on the values of `top`, `right`, `bottom`, and `left`. The offset does not affect the position of any other elements; thus, the space given for the element in the page layout is the same as if position were `static`. +- **fixed**: The element is removed from the normal document flow, and no space is created for the element in the page layout. It is positioned relative to the initial containing block established by the `viewport`... Its final position is determined by the values of `top`, `right`, `bottom`, and `left`. You specify the position of your element via offset properties: auto (default), top, bottom, right, left. @@ -145,20 +170,21 @@ You specify the position of your element via offset properties: auto (default), height: 75px; background: dodgerblue; color: white; - box-shadow: 0 2px 4px 0 rgba(153,153,153,.75); + box-shadow: 0 2px 4px 0 rgba(153, 153, 153, 0.75); } ``` An element whose position property is set to absolute or fixed is called an absolutely positioned element. ## Learn Layout + To learn about different ways of laying out content, head to [Learn Layout](http://learnlayout.com/) and walk through their lessons. ## Resources -+ [KhanAcademy - CSS Layout](https://www.khanacademy.org/computing/computer-programming/html-css#css-layout-properties) -+ [CodeAcademy - CSS Positioning](https://www.codecademy.com/courses/web-beginner-en-6merh/0/1#) -+ [FreeCodeCamp - Introduction to Basic CSS](https://learn.freecodecamp.org/responsive-web-design/basic-css) -+ [Learn Layout](http://learnlayout.com/) -+ [CSS Tricks - The Box Model](https://css-tricks.com/the-css-box-model/) -+ [Shay Howe on the Box Model](http://learn.shayhowe.com/html-css/opening-the-box-model/) +- [KhanAcademy - CSS Layout](https://www.khanacademy.org/computing/computer-programming/html-css#css-layout-properties) +- [CodeAcademy - CSS Positioning](https://www.codecademy.com/courses/web-beginner-en-6merh/0/1#) +- [FreeCodeCamp - Introduction to Basic CSS](https://learn.freecodecamp.org/responsive-web-design/basic-css) +- [Learn Layout](http://learnlayout.com/) +- [CSS Tricks - The Box Model](https://css-tricks.com/the-css-box-model/) +- [Shay Howe on the Box Model](http://learn.shayhowe.com/html-css/opening-the-box-model/) diff --git a/content/2019-03-07-CSS-Flexbox-and-CSS-Grid/index.md b/src/content/posts/2019-03-07-CSS-Flexbox-and-CSS-Grid.mdx similarity index 58% rename from content/2019-03-07-CSS-Flexbox-and-CSS-Grid/index.md rename to src/content/posts/2019-03-07-CSS-Flexbox-and-CSS-Grid.mdx index 74f4893..f376dc2 100644 --- a/content/2019-03-07-CSS-Flexbox-and-CSS-Grid/index.md +++ b/src/content/posts/2019-03-07-CSS-Flexbox-and-CSS-Grid.mdx @@ -1,17 +1,26 @@ --- title: "CSS Flexbox and The Holy Grail Layout" category: "Web Development" -date: 2019-03-05 +date: "03/05/2019" published: "true" slug: "css-flexbox-and-the-holy-grail-layout" tags: - - CSS - - flexbox - - web development - - beginner - - high school lesson + - CSS + - flexbox + - web development + - beginner + - high school lesson +# # isHighSchoolLesson: true --- +import Image from "@/components/MDX/MdxImage.astro" +import cssFloatsBroken from "../../assets/posts/css-floats-broken-site.jpg" +import flexboxSvg from "../../assets/posts/css-tricks-guide-to-flexbox.svg" +import facebook from "../../assets/posts/facebook-holy-grail-layout.png" +import ExternalLink from "@/components/Common/ExternalLink.astro" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + In the previous post, we covered the CSS display and position properties - each of which helps give HTML elements a certain behavior in our website layout. This time, we'll cover one of the newest developments in modern CSS: flexbox. Flexbox is extremely powerful and solves many of the problems that web developers have struggled with before 2019. We won't cover how to do everything in flexbox - there are other articles that do this better. Instead, we'll focus on constructing a flexbox-based layout that is used across a number of high quality websites. @@ -24,13 +33,9 @@ When I first learned how to design websites, I was taught to use CSS floats. Tho Also, I can't lie - I sucked at them. Pretty much any time I worked with CSS I dreaded dealing with floats. -
        - -
        Stolen from r/ProgrammerHumor
        -
        -
        +floats broken -Fortunately, we now have flexbox and it's available on almost every modern browser. Check [here](http://caniuse.com/#feat=flexbox) to make sure I'm right. +Fortunately, we now have flexbox and it's available on almost every modern browser. Check [here](http://caniuse.com/#feat=flexbox) to make sure I'm right. ## Introduction to Flexbox @@ -38,24 +43,25 @@ Instead of going over each flexbox rule individually, we're going to look at the Let's start with a simple website. - + This is all code we've seen before. The h1 element displays block, so it takes up the entire row it lives on. That means the `
          ` is pushed to the next row. Each of the `
        • ` behaves in the same way. Now, this doesn't look horrible, but it's also not ideal. Let's use flexbox to style this navbar a bit. I'm going to write some code first, and then explain what it does. -First change the CSS code for your navbar by adding `display: flex` on line 7. +First change the CSS code for your navbar by adding `display: flex` to the `header` element with a class of `navbar` like so: -```css{numberLines: true} +```css .navbar { - background: rgb(40,50,60); + background: rgb(40, 50, 60); color: white; - font-family: Courier New; + font-family: 'Courier New'; padding: 0 20px; - height: 110px; + min-height: 110px; + // highlight-next-line display: flex; } ``` @@ -69,14 +75,19 @@ Next add the `display: flex` property to the `
            ` as well. Now, all the `
          • ` We're now ready to tell our navbar and ul where their child elements should be aligned. First, we'll need to understand the two axes that flexbox operates on. They are: -+ main axis -+ cross axis -
            - -
            Found on CSS tricks here
            -
            -
            +- main axis +- cross axis + +
            {" "} + +css tricks guide to flexbox +
            Found on CSS tricks here
            + + +{" "} + +
            {" "} The main axis points in the same direction that each element is laid out. So for our navbar, all elements get laid out from left to right. That is our **main axis**. Which means that our **cross axis** is running top to bottom. @@ -84,15 +95,17 @@ The justify-content property positions elements along the main axis and the alig For starters, we'll tell the navbar to `justify-content: space-between` and `align-items: center`. We'll also change the height of the navbar to 70px, as we no longer need as much space on the top and bottom. -```css{numberLines: true} +```css .navbar { - background: rgb(40,50,60); + background: rgb(40, 50, 60); color: white; font-family: Courier New; padding: 0 20px; height: 70px; display: flex; + // highlight-next-line justify-content: space-between; + // highlight-next-line align-items: center; } ``` @@ -101,7 +114,7 @@ Justifying our content as `space-between` maximizes the space between whatever e Aligning items in the center makes sure that they are directly in the center of the top and bottom of our navbar. This works on the cross-axis, or the axis perpendicular to the direction that elements are laid out in. -For both of these properties, we can choose `flex-start`, `flex-end`, `space-between`, `space-around`, `space-evenly`, or `stretch`. Try a few of them out to get a feel for how they work. +For both of these properties, we can choose `flex-start`, `flex-end`, `space-between`, `space-around`, `space-evenly`, or `stretch`. Try a few of them out to get a feel for how they work. Let's also add a little space to our list items like so: @@ -111,22 +124,20 @@ ul li { } ``` - And then our navbar should look like the codepen below. Not bad for only 20 lines of CSS. +And then our navbar should look like the codepen below. Not bad for only 20 lines of CSS. + +{" "} - + ## The Holy Grail Layout One of the most classic designs in website development is known as the Holy Grail. It involves a navbar, 3 columns, and a footer. Facebook does a variation on the standard Holy Grail layout, as they leave out a prominent footer. -
            - -
            Ignore my facebook feed please.
            -
            -
            +facebook holy grail layout We'll go ahead and recreate this layout using flexbox. First add the following html below the navbar: @@ -147,7 +158,7 @@ main { .col { min-height: 100vh; font-family: Courier New; - padding: 1em; + padding: 1rem; } .first { background: rgb(40, 50, 60); @@ -156,7 +167,7 @@ main { background: rgb(210, 220, 240); } .third { - background: rgb(40,50, 60); + background: rgb(40, 50, 60); } ``` @@ -167,41 +178,46 @@ Add this one line to the col class and watch what happens. ```css .col { min-height: 100vh; - font-family: Courier New; - padding: 1em; + font-family: Courier New; + padding: 1rem; + // highlight-next-line flex: 1; } ```
            - + + +
            Interesting. The flex property combines three different properties: flex-grow, flex-shrink, and flex-basis. Each of them controls the space an element occupies. -+ **flex-grow**: A unitless value that dictates the proportion of space an element takes up along the main axis. -+ **flex-shrink**: A value that determines how much of the element to take away when they are overflowing their container. -+ **flex-basis**: The minimum value an element can shrink down to. -By indicating `flex: 1`, we've only used the flex-grow property. Giving each column a value of 1 makes sure that each one takes up 1/3 of the space along the main axis (3 columns each with a flex of 1). +- **flex-grow**: A unitless value that dictates the proportion of space an element takes up along the main axis. +- **flex-shrink**: A value that determines how much of the element to take away when they are overflowing their container. +- **flex-basis**: The minimum value an element can shrink down to. + +By indicating `flex: 1`, we've only used the flex-grow property. Giving each column a value of `1` makes sure that each one takes up $1/3$ of the space along the main axis (3 columns each with a flex of `1`). -Let's change the middle column to have a flex of 2.2. +Let's change the middle column to have a flex of `2.2`. ```css .second { background: rgb(210, 220, 240); + // highlight-next-line flex: 2.2; } ``` You should see something like this: - + Play around with those flex values until you get column widths you like best. @@ -213,13 +229,13 @@ Your second column should look like this: ```html
            -

            Feed

            +

            Feed


            -
            +
            ``` @@ -227,7 +243,7 @@ Your second column should look like this: Then, add the following styles to your CSS section. ```css -h2 { +h2.feed-title { font-size: 2.5em; margin-bottom: 0.5em; } @@ -241,42 +257,45 @@ h2 { border-bottom: solid 1px slategray; border-radius: 8px; margin: 10px; + padding: 20px 10px; } ```
            - + And just as expected, all elements line up from left to right - along the main axis. That's not the ideal behavior for a feed though. We'd prefer if they would align themselves on an axis going from top to bottom. With flexbox, that's easy to do. Add one line of CSS to the div with a class of feed for the desired effect. ```css .feed { display: flex; + // highlight-next-line flex-direction: column; } ``` And now we get this: - + ## Resources and Wrapping Up We only looked at a few core pieces of flexbox and there is so much more to learn. Fortunately, there are a number of exhaustive resources that do a great job of covering the rest. In fact, there's two fantastic games that I use to teach my students flexbox every year. I recommend they follow this sequence: -+ **Flexbox Froggy** - Flexbox froggy is a fun game made by the folks at team treehouse that has over 20 interesting level of flexbox challenges. -+ **Flexbox Tower Defense** - Flexbox Tower Defense is another game that teaches flexbox from a slightly different approach. Also a ton of fun. -+ **The Complete CSS Flexbox Tutorial** - https://medium.com/@js_tut/the-complete-css-flex-box-tutorial-d17971950bdc -+ **CSS Tricks - A Guide to Flexbox** - https://css-tricks.com/snippets/css/a-guide-to-flexbox/ -+ **FreeCodeCamp** - Complete the code challenges provided by FreeCodeCamp when you feel you understand flexbox more fully. This will help prepare you to actually build things with flexbox. + +- **Flexbox Froggy** - Flexbox froggy is a fun game made by the folks at team treehouse that has over 20 interesting level of flexbox challenges. +- **Flexbox Tower Defense** - Flexbox Tower Defense is another game that teaches flexbox from a slightly different approach. Also a ton of fun. +- **The Complete CSS Flexbox Tutorial** - https://medium.com/@js_tut/the-complete-css-flex-box-tutorial-d17971950bdc +- **CSS Tricks - A Guide to Flexbox** - https://css-tricks.com/snippets/css/a-guide-to-flexbox/ +- **FreeCodeCamp** - Complete the code challenges provided by FreeCodeCamp when you feel you understand flexbox more fully. This will help prepare you to actually build things with flexbox. Start by completing every level from Flexbox Froggy using [MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/) - Mozilla Developer Network - as a guide. When you're finished, move on to Flexbox Tower Defense and read through the last two articles as a complete reference guide. When you're finished, complete all of the FreeCodeCamp challenges on flexbox. diff --git a/content/2019-03-10-A-Primer-on-Probability/index.md b/src/content/posts/2019-03-10-A-Primer-on-Probability.mdx similarity index 77% rename from content/2019-03-10-A-Primer-on-Probability/index.md rename to src/content/posts/2019-03-10-A-Primer-on-Probability.mdx index 28d8dcf..33c969a 100644 --- a/content/2019-03-10-A-Primer-on-Probability/index.md +++ b/src/content/posts/2019-03-10-A-Primer-on-Probability.mdx @@ -1,18 +1,21 @@ --- title: "A Primer on Probability in Python" category: "Data Science" -date: 2018-03-10 +date: "03/10/2019" published: "false" slug: "a-primer-on-probability-in-python" tags: - - python - - statistics - - set theory - - probability - - distributions + - python + - statistics + - set theory + - probability + - distributions +# # isHighSchoolLesson: true --- -Before I started my micro-masters in statistics and data science from MIT, I sat down and wrote out all the mathy things I needed to improve on. My strategy was to look over the courses in the micro-masters sequence and analyze the concepts taught in each chapter. Then I crossed out anything I already felt comfortable with. +> NOTE: This post was originally created as a lesson for my high school students. The target audience is math students who want to learn intro to stats. + +Before I started my micro-masters in statistics and data science from MIT, I sat down and wrote out all the _mathy_ things I needed to improve on. My strategy was to look over the courses in the micro-masters sequence and analyze the concepts taught in each chapter. Then I crossed out anything I already felt comfortable with. The remaining list was pretty long, but most of them fell into one of two categories: calculus or statistics. @@ -26,13 +29,20 @@ Stop 1 in this journey is **probability theory**, something I found dramatically Probability is simply how likely it is that an event will occur. -> Probability theory underlies a huge portion of artificial intelligence, machine learning, and statistics. - Jeremy Kun +
            + {" "} + Probability theory underlies a huge portion of artificial intelligence, machine learning, and statistics. - Jeremy Kun +
            -> Whenever we’re unsure about the outcome of an event, we can talk about the probabilities of certain outcomes—how likely they are. The analysis of events governed by probability is called statistics. - Khan Academy +
            + {" "} + Whenever we’re unsure about the outcome of an event, we can talk about the probabilities of certain outcomes—how likely + they are. The analysis of events governed by probability is called statistics. - Khan Academy{" "} +
            Khan Academy puts it nicely when they say: -> Probability of an event = (# of ways it can happen) / (total number of outcomes) +
            Probability of an event = (# of ways it can happen) / (total number of outcomes)
            There is a more formal, mathematical way to look at probability, and that involves examining set theory. We'll take that approach in this post and branch out our understanding from there. @@ -57,28 +67,35 @@ Mathematically, we define a set using $𝑆$. If element $x$ belongs to the set, If we think of rolling a single die, then $Ω = { 1, 2, 3, 4, 5, 6 }$ and the sample space, $𝑆$ would be the same. No other value is possible when rolling a die, so the sample space shows every conceivable outcome of the random experiment.
            - -
            Taken from this slideplayer deck.
            + +
            + Taken from this slideplayer deck. +
            The Laplace definition for probability states: -> If all the singular events in the event space (here denoted by $𝑆$) are equally as likely, then: ->> $𝑃(𝐸) = \frac{\#𝐸}{\#𝑆}$. +
            +If all the singular events in the event space (here denoted by $𝑆$) are equally as likely, then: + +
            $𝑃(𝐸) = \frac{\#𝐸}{\#𝑆}$.
            +
            Basically, the probability is the number of occurrences of the event divided by the number of total event occurrences in the sample space. To understand this even better, let's look at another way of writing this. Again, if we say each outcome in $𝑆$ is equally likely, then the probability of observing one particular outcome is: -> 𝑃(each outcome) = 1 / ∣𝑆∣ +
            𝑃(each outcome) = 1 / ∣𝑆∣
            where $∣𝑆∣$ is the **cardinality** of $𝑆$. In other words, the total number of different outcomes $𝑆$ can have. Thinking about the probability of $𝐸$: -> $𝑃(𝐸) = \frac{∣𝐸∣}{∣𝑆∣}$ +
            + $𝑃(𝐸) = \frac{∣𝐸∣}{∣𝑆∣}$ +
            And this takes us right back to the Khan Academy definition. -> P(A) = (# of ways A can happen) / (Total number of outcomes) +
            P(A) = (# of ways A can happen) / (Total number of outcomes)
            #### Example: Rolling Dice vs. Coin Flip @@ -102,16 +119,10 @@ The math for this is $P(A_1, A_2, \dots A_n) = \displaystyle\sum_{i=1}^n P(A_i)$ Another way to think about this is that if $A \cap B = \emptyset $, then $P(A\cup B) = P(A) + P(B)$. A result of this axiom is the **addition law or probability** or the **sum rule**. - $P(A\cup B) = P(A) + P(B) \, \text{\textemdash} \, P(A \cap B) $ - Essentially, the probability that $A$ or $B$ will happen is the sum of the probabilities that $A$ will happen and that $B$ will happen, minus the probability that $A$ and $B$ both happen. Again, this is assuming that the events are exclusive. That is often not the case. - - - - ## The Types of Probability The probability of any **random variable** being the outcome of a particular event depends on the nature of the event. Probability is seen in 3 distinct forms: marginal, joint, and conditional. @@ -122,7 +133,7 @@ The probability of any **random variable** being the outcome of a particular eve Example: the probability that a card drawn from a pack is red and has the value 4 is P(red and 4) = 2/52 = 1/26. (There are 52 cards in a pack of traditional playing cards and the 2 red ones are the hearts and diamonds). -**Conditional Probability**: When the outcome of event $A$ in one trial is somehow dependent on the outcome of event $B$ in a previous trial, we identify them as **dependent events**. Conditional probability - written as $P(A|B)$ - is the probability of $A$ occurring given that $B$ has already occurred. +**Conditional Probability**: When the outcome of event $A$ in one trial is somehow dependent on the outcome of event $B$ in a previous trial, we identify them as **dependent events**. Conditional probability - written as $P(A|B)$ - is the probability of $A$ occurring given that $B$ has already occurred. Events in a sample space can be either **dependent** or **independent**. @@ -130,10 +141,10 @@ Events in a sample space can be either **dependent** or **independent**. Consider following independent events: -+ Flip tails on a coin and have a 5 come up on a single die roll. -+ Pick a marble from a bag and flip heads on a coin +- Flip tails on a coin and have a 5 come up on a single die roll. +- Pick a marble from a bag and flip heads on a coin -Events A and B are independent if $𝑃(𝐴∩𝐵) = 𝑃(𝐴)𝑃(𝐵)$, and $𝑃(𝐴∪𝐵) = 𝑃(𝐴)+𝑃(𝐵) \, \text{\textemdash} \, 𝑃(𝐴∩𝐵)$. If they are independent, then the probability of event $A$ occurring does not affect the probability of event $B$ occurring. +Events A and B are independent if $𝑃(𝐴∩𝐵) = 𝑃(𝐴)𝑃(𝐵)$, and $𝑃(𝐴∪𝐵) = 𝑃(𝐴)+𝑃(𝐵) \, \text{\textemdash} \, 𝑃(𝐴∩𝐵)$. If they are independent, then the probability of event $A$ occurring does not affect the probability of event $B$ occurring. ### Dependent Events @@ -146,7 +157,10 @@ Let's say event $A$ is drawing a red or blue marble from the bag. In it, there a So the probability of getting a blue marble would initially be 2/5 and getting a red marble would be 3/5.
            - +
            Every time we draw a marble, we record the color and place it in our pocket. With one less marble in the bag, the chances of drawing either a red or blue marble in the next trial have changed. @@ -165,8 +179,8 @@ A **tree diagram** can be used to explain this for all possible events. Here are some more examples: -+ Determining if a user will like an audiobook based on their previous audiobook purchases. -+ Drawing a pair of aces in a hand of poker. +- Determining if a user will like an audiobook based on their previous audiobook purchases. +- Drawing a pair of aces in a hand of poker. We define conditional probability (probability of $A$ **given** $B$) as: @@ -182,9 +196,9 @@ We define conditional probability (probability of $A$ **given** $B$) as: And this is our conditional probability formula. There are three theorems that are a result of this formula. -+ Theorem 1 - Product Rule -+ Theorem 2 - Chain Rule -+ Theorem 3 - Bayes Rule +- Theorem 1 - Product Rule +- Theorem 2 - Chain Rule +- Theorem 3 - Bayes Rule We'll address the last two of these later on. For now, we'll only look at the **product rule**. @@ -192,25 +206,25 @@ The **product rule** is useful when the conditional probability is easy to compu The intersection of events $A$ and $B$ can be given by -$ P(A \cap B) = P(B) P(A|B) = P(A) P(B|A) $ +$ P(A \cap B) = P(B) P(A|B) = P(A) P(B|A) $ Here’s how to derive the conditional probability equation shown above using the product rule: Step 1: Write out the product rule: -+ $P(A \, and \, B) = P(B) * P(A|B)$ +- $P(A \, and \, B) = P(B) * P(A|B)$ Step 2: Divide both sides of the equation by $P(B)$: -+ $ \frac{P(A \, and \, B)}{P(B)} = \frac{P(B) * P(A|B)}{P(B)}$ +- $ \frac{P(A \, and \, B)}{P(B)} = \frac{P(B) \* P(A|B)}{P(B)}$ Step 3: Cancel $P(B)$ on the right side of the equation: -+ $ \frac{P(A \, and \, B)}{P(B)} = P(A|B)$ +- $ \frac{P(A \, and \, B)}{P(B)} = P(A|B)$ Step 4: Rewrite the equation: -+ $ P(A|B) = \frac{P(A \, and \, B)}{P(B)} $ +- $ P(A|B) = \frac{P(A \, and \, B)}{P(B)} $ In the situation that both of these events are independent of each other, then $P(A|B) = P(A)$ and $P(A \cap B) = P(A) P(B)$. @@ -272,13 +286,13 @@ Let's try our hand at a few examples to get a better grasp of what's going on he Say we have two bags of marbles. The first bag has 20 red marbles and 30 blue marbles. The second bag has 30 red marbles and 20 blue marbles. We run a simulation where we toss a coin and pick a marble from the first bag if it lands on heads. If it lands on tails, we pick a marble from the second bag. -+ **What is the probability of drawing a red marble?** +- **What is the probability of drawing a red marble?** ```python """ The probability of picking either bag is 0.5. -If we pick the first bag, the probability of drawing a red marble is 0.4. If we pick the second bag, the probability of drawing a red marble is 0.6 +If we pick the first bag, the probability of drawing a red marble is 0.4. If we pick the second bag, the probability of drawing a red marble is 0.6 """ (0.5 * 4/10) + (0.5 * 6/10) # should be exactly 50% @@ -286,13 +300,13 @@ If we pick the first bag, the probability of drawing a red marble is 0.4. If we Ok, another one with marbles. Say we have three bags with 100 marbles each. Bag 1 has 50 red and 50 blue marbles. Bag 2 has 45 red and 55 blue marbles. Bag 3 has 60 red and 40 blue marbles. You choose a bag at random and then pick a marble from the bag at random. -+ **What is the probability of drawing a red marble now?** +- **What is the probability of drawing a red marble now?** ```python """ The probability of picking either bag is 1/3 or 0.33333. -If we pick the first bag, the probability of drawing a red marble is 0.50. With the second bag 0.45 and with the third 0.60. +If we pick the first bag, the probability of drawing a red marble is 0.50. With the second bag 0.45 and with the third 0.60. """ (0.33333 * 0.5) + (0.33333 * 0.45) + (0.33333 * 0.6) @@ -302,16 +316,16 @@ If we pick the first bag, the probability of drawing a red marble is 0.50. With There's a lot of really great content freely available on each of these subjects. Some GMAT prep websites have great probability practice problems if you want to test yourself further. I won't include them here, but feel free to Google around and sign up for the freebies. -+ Analytics Vidhya - 40 Questions on Probability for Data Scientists. Great practice problems with solutions provided. https://www.analyticsvidhya.com/blog/2017/04/40-questions-on-probability-for-all-aspiring-data-scientists/ -+ Intro to Probability Slide Deck - Where most of the content for this article came from. https://slideplayer.com/slide/10865356/ -+ Jeremy Kun - Set Theory: A Primer. https://jeremykun.com/2011/07/09/set-theory-a-primer/ -+ Kolmogorov - https://en.wikipedia.org/wiki/Andrey_Kolmogorov -+ Probability Axioms - https://en.wikipedia.org/wiki/Probability_axioms -+ Jeremy Kun - Probability Theory: A Primer. https://jeremykun.com/2013/01/04/probability-theory-a-primer/ -+ Jeremy Kun - Conditional Probability: A Primer. https://jeremykun.com/2013/03/28/conditional-partitioned-probability-a-primer/ -+ Khan Academy - Probability. https://www.khanacademy.org/math/probability/probability-geometry -+ Math Hacks - The Airplane Probability Problem. https://medium.com/i-math/solving-an-advanced-probability-problem-with-virtually-no-math-5750707885f1 -+ Setosa.io - Conditional Probability. http://setosa.io/conditional/ -+ Multiple Probability Notes - http://home.avvanta.com/~math/mult_prob.html -+ DZone - Conditional Probability and Bayes Theorem. https://dzone.com/articles/conditional-probability-and-bayes-theorem -+ Sangakoo - Law of Total Probability. https://www.sangakoo.com/en/unit/law-of-total-probability +- Analytics Vidhya - 40 Questions on Probability for Data Scientists. Great practice problems with solutions provided. https://www.analyticsvidhya.com/blog/2017/04/40-questions-on-probability-for-all-aspiring-data-scientists/ +- Intro to Probability Slide Deck - Where most of the content for this article came from. https://slideplayer.com/slide/10865356/ +- Jeremy Kun - Set Theory: A Primer. https://jeremykun.com/2011/07/09/set-theory-a-primer/ +- Kolmogorov - https://en.wikipedia.org/wiki/Andrey_Kolmogorov +- Probability Axioms - https://en.wikipedia.org/wiki/Probability_axioms +- Jeremy Kun - Probability Theory: A Primer. https://jeremykun.com/2013/01/04/probability-theory-a-primer/ +- Jeremy Kun - Conditional Probability: A Primer. https://jeremykun.com/2013/03/28/conditional-partitioned-probability-a-primer/ +- Khan Academy - Probability. https://www.khanacademy.org/math/probability/probability-geometry +- Math Hacks - The Airplane Probability Problem. https://medium.com/i-math/solving-an-advanced-probability-problem-with-virtually-no-math-5750707885f1 +- Setosa.io - Conditional Probability. http://setosa.io/conditional/ +- Multiple Probability Notes - http://home.avvanta.com/~math/mult_prob.html +- DZone - Conditional Probability and Bayes Theorem. https://dzone.com/articles/conditional-probability-and-bayes-theorem +- Sangakoo - Law of Total Probability. https://www.sangakoo.com/en/unit/law-of-total-probability diff --git a/content/2019-03-11-CSS-Grid-and-Landing-Pages/index.md b/src/content/posts/2019-03-11-CSS-Grid-and-Landing-Pages.mdx similarity index 58% rename from content/2019-03-11-CSS-Grid-and-Landing-Pages/index.md rename to src/content/posts/2019-03-11-CSS-Grid-and-Landing-Pages.mdx index e5f79fa..3b536e9 100644 --- a/content/2019-03-11-CSS-Grid-and-Landing-Pages/index.md +++ b/src/content/posts/2019-03-11-CSS-Grid-and-Landing-Pages.mdx @@ -1,17 +1,23 @@ --- title: "Using CSS Grid For Landing Pages" category: "Web Development" -date: 2019-03-11 +date: "03/11/2019" published: "true" slug: "css-grid-and-landing-pages" tags: - - CSS - - css grid - - web development - - beginner - - high school lesson + - CSS + - css grid + - web development + - beginner + - high school lesson] +# isHighSchoolLesson: true --- +import Image from "@/components/MDX/MdxImage.astro" +import cssShades from "../../assets/posts/css-shades.gif" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + In the previous post, we discussed how to create the Holy Grail layout using CSS flexbox - one of the newest additions to CSS. Even newer than flexbox is CSS grid. While flexbox is perfect for laying elements out in 1D, grid helps with 2D layouts and works nicely in conjunction with flexbox. We'll get into the meat of grid in this post. ## CSS Grid @@ -22,11 +28,7 @@ While those who learned CSS before 2010 may have relied heavily on creating tabl Writing proper CSS can still be difficult, but hopefully the addition of grid will make designing layouts easier for newcomers to web development. This post will show you how to get started with grid by designing a real product landing page. -
            - -
            Stolen from r/ProgrammerHumor
            -
            -
            +CSS shades broke site We'll start with an introduction to grid, and quickly move into designing an actual layout for a fake product. The techniques leveraged in this article apply to all layouts, and should be useful for any developer. @@ -36,18 +38,38 @@ Let's get started. Just like flexbox, if you want to use CSS grid, you have to indicate that you'd like a container to `display: grid;`. Unlike flexbox, when you tell a container holding block-level elements to display grid, nothing happens. Check out these two codepens that prove my point. -Here's a codepen where the container does *not* display grid: - - -and here's the same container where it *does* display grid: - - Notice the difference? Me neither. (Ok there is a slight difference in spacing at the bottom, but that's unimportant for now.) @@ -68,7 +90,7 @@ Right now, our html is simply:
            -
            +
            ``` @@ -91,7 +113,7 @@ section div { ## Grid Template -Next we'll add a property to our grid-container that indicates **how many** columns we want and **how wide** we want each column to be. We do that by specifying the grid-template-columns property and the grid-column-gap property like so: +Next we'll add a property to our grid-container that indicates **how many** columns we want and **how wide** we want each column to be. We do that by specifying the `grid-template-columns` property and the `grid-column-gap` property like so: ```css .grid-container { @@ -99,18 +121,30 @@ Next we'll add a property to our grid-container that indicates **how many** colu width: 100%; min-height: 100vh; display: grid; + // highlight-next-line grid-template-columns: 200px auto 200px; + // highlight-next-line grid-column-gap: 20px; } ``` -We then see the layout split into three columns - two on the left and the right that are both exactly 200px wide, and a column in the middle that occupies whatever space is left in between (specifying auto handles this). The three columns are a result of indicating three sizes `200px auto 200px` separated by spaces in the grid-template-columns property. There is also a gap of 20px between any two columns that would otherwise be touching. +We then see the layout split into three columns - two on the left and the right that are both exactly `200px` wide, and a column in the middle that occupies whatever space is left in between (specifying auto handles this). The three columns are a result of indicating three sizes `200px auto 200px` separated by spaces in the `grid-template-columns` property. There is also a gap of `20px` between any two columns that would otherwise be touching. Play around with the following codepen to see this behavior in action. Resize the screen and watch the middle column change sizes. - While this is cool to visualize, it's not yet useful. Let's clean up our html and add content to the middle column. @@ -160,14 +194,24 @@ The `grid-column` property combines grid column start and grid column end, separ } ``` -
            -If you're wondering how to specify row size, you're in luck. It's just as easy. Update your CSS code for the grid container to look like so: +If you're wondering how to specify row size, you're in luck. It's just as easy. Update your CSS code for the grid container to look like so: ```css .grid-container { @@ -184,7 +228,6 @@ If you're wondering how to specify row size, you're in luck. It's just as easy. Just like columns, row sizes are specified by separating values with a space. We have also given each row a gap of 20px - the same as our columns. The shorthand notation for specifying the gaps of grid row and grid column gaps is just `grid-column-gap`. - Now that we've covered the basics, let's kick it up a notch and build a more complex layout. ## Repeat and fr @@ -202,9 +245,19 @@ Frameworks like [bootstrap](https://www.getbootstrap.com) come preloaded with a } ``` - Notice how that breaks up our page into 12 identically-sized columns? The repeat function takes in two values - the number of columns or rows and the amount of space each one should take up. The `1fr` value stands for 1 fraction, and means each column should occupy 1fr of the total 12 fractional units we've defined. If we had specified `repeat(10, 1fr)`, we'd have 10 equally sized columns. We don't always have to use fractional units. @@ -213,9 +266,19 @@ Sometimes pixel values make more sense. If we want 10 equally-sized columns of 1 Fractional units use cases extend beyond equally-sized rows or columns. They can make your life easier in a number of cases. As [Violet Peña](https://vgpena.github.io/using-css-grid-the-right-way/) puts it, "Fractional units are defined by the space not already used by something else. Fractional units split up this space relative to each other." This concept makes more sense if we have a concrete example. - Looking at our grid container, we see that the `grid-template-columns` property contains the value `1fr 3fr`. @@ -231,7 +294,7 @@ Looking at our grid container, we see that the `grid-template-columns` property } ``` -Essentially, the columns occupy a total of 4fr. The first column occupies 1 of those 4 fractions (25%), while the second column occupies 3 of the 4 fractions (75%). +Essentially, the columns occupy a total of `4fr``. The first column occupies 1 of those 4 fractions ($25%$), while the second column occupies 3 of the 4 fractions ($75%$). Neat. But we can do even better. @@ -248,8 +311,8 @@ Think of grid area as mapping to the class names. So, if we have some HTML looki
            -
            -
            +
            +
            ``` @@ -266,7 +329,7 @@ body { } .product_front { grid-area: pf; - background: rgb(241, 236, 226); + background: rgb(241, 236, 226); } .jumbo { grid-area: ju; @@ -279,7 +342,7 @@ body { } .product_top { grid-area: pt; - background: rgb(241, 236, 226); + background: rgb(241, 236, 226); } .description { grid-area: de; @@ -296,30 +359,40 @@ Change the CSS for the grid-container section to look like this: grid-template-columns: repeat(13, 1fr); grid-template-rows: 800px 600px 600px 1600px; grid-template-areas: - "pf pf pf pf pf pf pf ju ju ju ju ju ju" - "fl fl fl fl fl fl fl fl fl fl fl fl fl" - "hi hi hi hi hi hi pt pt pt pt pt pt pt" - "de de de de de de de de de de de de de"; + "pf pf pf pf pf pf pf ju ju ju ju ju ju" + "fl fl fl fl fl fl fl fl fl fl fl fl fl" + "hi hi hi hi hi hi pt pt pt pt pt pt pt" + "de de de de de de de de de de de de de"; } ``` -The first thing to recognize here is that we've created 13 equally-sized columns and 4 rows of varying heights. For the rows, the first one takes up 800px, the second and third 600px, and the final row 1600px. These values are an almost accurate representation of the heights seen in the dribbble post. +The first thing to recognize here is that we've created 13 equally-sized columns and 4 rows of varying heights. For the rows, the first one takes up `800px`, the second and third `600px`, and the final row `1600px`. These values are an almost accurate representation of the heights seen in the dribbble post. Our newly added property, `grid-template-areas`, allows us name the areas that should be assigned to each row/column location. Let's break it down by row. -For row 1 (800px), the div with the class of product_front (grid-area name pf) gets assigned to the first 7 fractions of the first row and the last 6 fractions are assigned to the jumbo div (grid-area name ju). +For row 1 (`800px`), the div with the class of `product_front` (`grid-area` name `pf`) gets assigned to the first 7 fractions of the first row and the last 6 fractions are assigned to the jumbo div (`grid-area` name `ju`). -For row 2 (600px), the full_length div (grid-area name fl) takes up the entire row (all 13 fractions). +For row 2 (`600px`), the `full_length` div (`grid-area` name `fl`) takes up the entire row (all 13 fractions). -For row 3 (600px), the highlight div (grid-area name hi) takes up 6/13 of the row and the product_top div takes up 7/13. +For row 3 (`600px`), the `highlight` div (`grid-area` name `hi`) takes up $6/13$ of the row and the `product_top` div takes up $7/13$. -For the final row (1600px), the description div (grid-area name de) also takes up the entire row. +For the final row (`1600px`), the `description` div (`grid-area` name `de`) also takes up the entire row. All that work, and what we get is this: - Not anything special until we had two new background images with a bit of CSS. Modify the `.product_front` and `.product_top` divs with the following code. @@ -340,35 +413,55 @@ Not anything special until we had two new background images with a bit of CSS. M /* ...other CSS */ ``` - And would you look at that. All we're missing is a few headers, a button, and lorem ipsum text before we have the first three rows sorted out. While you're welcome to try it out yourself, to save you the pain of implementing it, here's a codepen with much of it done. Note that it doesn't look very nice unless viewed as a full page. Also, the fonts are similar, but not identical. I'm pretty sure that the correct main header font is [Aller](https://fonts.adobe.com/fonts/aller), while the proper main font looks to be [Dunbar](https://fonts.adobe.com/fonts/dunbar). For this demo, I substituted them with PT Sans and Roboto, respectively. - -If you look through the code, you'll also notice we've added a number of media queries to deal with screens of different sizes. Most of them are pretty standard, but the very last one (with a max-width of 700px) has code that updated the `grid-template-columns`, `grid-template-rows`, and `grid-template-areas` properties of the `.grid-container`. +If you look through the code, you'll also notice we've added a number of media queries to deal with screens of different sizes. Most of them are pretty standard, but the very last one (with a max-width of `700px`) has code that updated the `grid-template-columns`, `grid-template-rows`, and `grid-template-areas` properties of the `.grid-container`. -To see what that's doing, resize the width of your browser to below 700px. See how each div now stacks on top of each other? By naming each grid area, we can explicitly determine the order that each section appears in. This my favorite part of CSS grid and the primary reason I recommend front-end developers use it. +To see what that's doing, resize the width of your browser to below `700px`. See how each div now stacks on top of each other? By naming each grid area, we can explicitly determine the order that each section appears in. This my favorite part of CSS grid and the primary reason I recommend front-end developers use it. For your next challenge, set the description div to display grid and see if you can recreate the rest of the dribbble mockup in CSS grid. Here are the images you'll need to get started. -+ [minicam mockup 1](https://storage.googleapis.com/jeffastor/minicam-mockup1.png) -+ [minicam mockup 2](https://storage.googleapis.com/jeffastor/minicam-mockup2.png) -+ [minicam mockup 3](https://storage.googleapis.com/jeffastor/minicam-mockup3.png) +- [minicam mockup 1](https://storage.googleapis.com/jeffastor/minicam-mockup1.png) +- [minicam mockup 2](https://storage.googleapis.com/jeffastor/minicam-mockup2.png) +- [minicam mockup 3](https://storage.googleapis.com/jeffastor/minicam-mockup3.png) For the description section, you'll need to get both of these images to overlap rows slightly. Here's screenshots of each section to make your life easier. -+ [minicam-3](https://storage.googleapis.com/jeffastor/minicam-3.png) -+ [minicam-4](https://storage.googleapis.com/jeffastor/minicam-4.png) -+ [minicam-5](https://storage.googleapis.com/jeffastor/minicam-5.png) +- [minicam-3](https://storage.googleapis.com/jeffastor/minicam-3.png) +- [minicam-4](https://storage.googleapis.com/jeffastor/minicam-4.png) +- [minicam-5](https://storage.googleapis.com/jeffastor/minicam-5.png) Good luck! @@ -378,14 +471,14 @@ That's it for now. Hope you enjoyed this walkthrough and learned enough about gr Here's some resources to get you going. -+ **CSS Grid Garden** - Get your feet wet in this interactive game by Team Treehouse that has 28 coding challenges all about CSS Grid. https://cssgridgarden.com/ -+ **CSS Tricks** - A Complete Guide to CSS Grid (Use as a reference): https://css-tricks.com/snippets/css/complete-guide-grid/ -+ **MDN CSS Grid** - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout -+ **Learn CSS Grid** - Visual guide to a number of common grid layouts: https://learncssgrid.com/ -+ **CSS Grid by Wes Bos** - Full course with 24 videos on how to use grid. https://cssgrid.io/ -+ **CSS Grid for Designers by NY Times** - This Open New York Times article gives clear examples on how grid can be applied to achieve high quality design layouts. https://open.nytimes.com/css-grid-for-designers-f74a883b98f5 -+ **Layout Labs by Jen Simmons** - One of the coolest resources available. Check out all the amazing uses of grid this website showcases: https://labs.jensimmons.com/ -+ **CSS Grid Playground** - Really cool tool that makes it easy to play around with grid layouts. https://www.cssgridplayground.com/ -+ **Grid by Example** - Amazing set of grid examples that match common use cases for layouts. https://gridbyexample.com/examples/ -+ **Grid.cc** - Site with a number of learning resources and grid tools to help get you going. -+ **FreeCodeCamp Grid Challenges** - And as always, FreeCodeCamp offers some sweet grid challenges that will get you up to speed in no time. +- **CSS Grid Garden** - Get your feet wet in this interactive game by Team Treehouse that has 28 coding challenges all about CSS Grid. https://cssgridgarden.com/ +- **CSS Tricks** - A Complete Guide to CSS Grid (Use as a reference): https://css-tricks.com/snippets/css/complete-guide-grid/ +- **MDN CSS Grid** - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout +- **Learn CSS Grid** - Visual guide to a number of common grid layouts: https://learncssgrid.com/ +- **CSS Grid by Wes Bos** - Full course with 24 videos on how to use grid. https://cssgrid.io/ +- **CSS Grid for Designers by NY Times** - This Open New York Times article gives clear examples on how grid can be applied to achieve high quality design layouts. https://open.nytimes.com/css-grid-for-designers-f74a883b98f5 +- **Layout Labs by Jen Simmons** - One of the coolest resources available. Check out all the amazing uses of grid this website showcases: https://labs.jensimmons.com/ +- **CSS Grid Playground** - Really cool tool that makes it easy to play around with grid layouts. https://www.cssgridplayground.com/ +- **Grid by Example** - Amazing set of grid examples that match common use cases for layouts. https://gridbyexample.com/examples/ +- **Grid.cc** - Site with a number of learning resources and grid tools to help get you going. +- **FreeCodeCamp Grid Challenges** - And as always, FreeCodeCamp offers some sweet grid challenges that will get you up to speed in no time. diff --git a/content/2019-03-19-Dice-Statistics-in-Python/index.md b/src/content/posts/2019-03-19-Dice-Statistics-in-Python.mdx similarity index 69% rename from content/2019-03-19-Dice-Statistics-in-Python/index.md rename to src/content/posts/2019-03-19-Dice-Statistics-in-Python.mdx index 60af30c..2ec204e 100644 --- a/content/2019-03-19-Dice-Statistics-in-Python/index.md +++ b/src/content/posts/2019-03-19-Dice-Statistics-in-Python.mdx @@ -1,17 +1,26 @@ --- title: "Using Python to Calculate Dice Statistics" category: "Statistics" -date: 2019-03-19 +date: "03/19/2019" published: "true" slug: "using-python-to-calculate-dice-statistics" tags: - - python - - statistics - - data science - - distributions - - visualizations + - python + - statistics + - data science + - distributions + - visualizations +# isHighSchoolLesson: true --- +import Image from "@/components/MDX/MdxImage.astro" +import python_dice_rolls_500 from "../../assets/posts/python-dice-rolls-500.png" +import python_dice_rolls_500_sums from "../../assets/posts/python-dice-rolls-500-sums.png" +import python_dice_rolls_10_000 from "../../assets/posts/python-dice-rolls-10000.png" +import python_dice_rolls_10_000_sums from "../../assets/posts/python-dice-rolls-10000-sums.png" + +> NOTE: This post was originally created as a lesson for my AP high school students. The target audience is proficient beginners with at least 1 year of programming experience and some math pre-reqs. + Every time I visited Las Vegas as a young kid, I would get kicked out of the casinos. I just wanted to play the games. But rules are rules, and I had to wait until I was 21. When I was finally of age, a few friends and I hopped in my car and drove the 4.5 hours from Los Angeles to Sin City. We checked into our hotel and I was immediately hooked. It was at that moment, that I understood how people descend into addiction. Few feelings are like the hit of dopamine you receive when you score big on the craps table. @@ -30,9 +39,9 @@ We'll start with a quick detour and then explore the statistics inherent to dice Two of my students, Xochitl and Jimmie, play a game where each takes a turn rolling two six-side dice. -Xochitl gets $1 if the sum of the numbers of the two dice is a prime number (the number 1 is not prime). +Xochitl gets `$1` if the sum of the numbers of the two dice is a prime number (the number 1 is not prime). -Jimmie gets $2 if the numbers on the two dice are the same (e.g. 1-1, 2-2, 3-3, etc). +Jimmie gets `$2` if the numbers on the two dice are the same (e.g. 1-1, 2-2, 3-3, etc). Who makes more money on average? @@ -74,7 +83,7 @@ On lines 10 and 11, we create numpy arrays that hold the sample space for our tw Line 13, 14, and 15 hold python lists where we'll store the outcomes for each of the two dice, and their sum. -Finally, we loop 500 times, picking a random number from the sample space for each dice and appending them to their respective arrays. We also append their sums, and call it a day. +Finally, we loop $500$ times, picking a random number from the sample space for each dice and appending them to their respective arrays. We also append their sums, and call it a day. After running this code, we should be able to plot the results. Here's the code: @@ -86,25 +95,19 @@ sns.countplot(dice_2, ax=ax2) And here's what we get: -
            - -
            dice_1 on the left and dice_2 on the right
            -
            +Python dice rolls 500 -Interesting. Looks like 500 rolls doesn't give us consistent results for our dice rolls. Die #1 rolled 5 significantly more times than die #2, among other identifiable differences. +Interesting. Looks like 500 rolls doesn't give us consistent results for our dice rolls. Die #1 rolled $5$ significantly more times than die #2, among other identifiable differences. Let's look at their sums. -
            - -
            -
            +Python dice rolls 500 sums This shows a clear sign. Sevens got rolled the most. ## The Law of Large Numbers -Now we didn't get results consistent with what we know to be true. For any fair dice, every number is equally likely to be rolled. However, we see clear discrepancies in our results because we only ran the trials 500 times. If we up that number to 5000 or 10000, we'll start to see our outcomes converge to their expected values. This is due to *the law of large numbers*, which states that as the number of samples increases, their respective probabilities converge to their true probabilities. +Now we didn't get results consistent with what we know to be true. For any fair dice, every number is equally likely to be rolled. However, we see clear discrepancies in our results because we only ran the trials $500$ times. If we up that number to $5,000$ or $10,000$, we'll start to see our outcomes converge to their expected values. This is due to _the law of large numbers_, which states that as the number of samples increases, their respective probabilities converge to their true probabilities. Let's re-run our experiment with 20x more trials and see how our results look. @@ -131,25 +134,19 @@ sns.countplot(dice_1, ax=ax1) sns.countplot(dice_2, ax=ax2) ``` -The only thing that's changed is on line 9. We've replaced 500 with 10,000. And voila! Look at how similar our outcomes look to their expected values. Still not exact, and that's fine. The universe is filled with inherent randomness. Some variation is unavoidable. +The only thing that's changed is on line 9. We've replaced $500$ with $10,000$. And voila! Look at how similar our outcomes look to their expected values. Still not exact, and that's fine. The universe is filled with inherent randomness. Some variation is unavoidable. -
            - -
            dice_1 on the left and dice_2 on the right
            -
            +Python dice rolls 10000 And when we look at the sums? -
            - -
            -
            +Python dice rolls 500 sums These values have also converged towards their expected outcomes, though the difference isn't as stark. ## The Rules of Craps -In craps, the main bet (Pass Line) is on whether the shooter (dice-roller) can throw the **"point"** number before a 7 is rolled. The shooter starts the game by throwing 2 dice. The sum becomes the **"point"** number, unless the shooter rolls a 7 or 11 on the come-out roll. Then everyone's bet on the pass line wins even money. If the shooter comes out with a 2, 3, or 12--this is called craps--everyone loses their Pass Line bets. +In craps, the main bet (Pass Line) is on whether the shooter (dice-roller) can throw the **"point"** number before a $7$ is rolled. The shooter starts the game by throwing $2$ dice. The sum becomes the **"point"** number, unless the shooter rolls a $7$ or $11$ on the come-out roll. Then everyone's bet on the pass line wins even money. If the shooter comes out with a $2$, $3$, or $12$ -- this is called craps -- everyone loses their Pass Line bets. If the shooter rolls any other number, this number becomes the point. @@ -178,12 +175,10 @@ Let's return to our discussion of probability from earlier in the post. Remember So what are all the possible combinations of dice rolls? - - - - +
            - Roll -
            + + + @@ -192,7 +187,7 @@ So what are all the possible combinations of dice rolls? + @@ -201,12 +196,14 @@ So what are all the possible combinations of dice rolls? + - + @@ -215,7 +212,9 @@ So what are all the possible combinations of dice rolls? - + @@ -224,7 +223,9 @@ So what are all the possible combinations of dice rolls? - + @@ -233,7 +234,9 @@ So what are all the possible combinations of dice rolls? - + @@ -242,7 +245,9 @@ So what are all the possible combinations of dice rolls? - + @@ -251,20 +256,22 @@ So what are all the possible combinations of dice rolls? - + - +
            Roll Dice2: 1 Dice2: 3 - Dice2: 4 Dice2: 6 -
            Dice1: 1 + Dice1: 1 + 2 3 47
            Dice1: 2 + Dice1: 2 + 3 4 58
            Dice1: 3 + Dice1: 3 + 4 5 69
            Dice1: 4 + Dice1: 4 + 5 6 710
            Dice1: 5 + Dice1: 5 + 6 7 811
            Dice1: 6 + Dice1: 6 + 7 8 9 10 11 12
            -Looking at the table, we can see that there are 6 different combinations that add up to seven. Since there are a total of 36 possible combinations, the probability of rolling a seven is 6 / 36, or 16.67%. +Looking at the table, we can see that there are $6$ different combinations that add up to seven. Since there are a total of $36$ possible combinations, the probability of rolling a seven is $6 / 36$, or $16.67%$. -There are 5 combinations that sum to 8, and 5 combinations that sum to 6. The chances of rolling either of those numbers is 5 / 36, or 13.89%. If we calculate the probabilities for the rest of the outcomes, we can start to see why Las Vegas will always make money in the long run. +There are 5 combinations that sum to $8$, and $5$ combinations that sum to $6$. The chances of rolling either of those numbers is $5 / 36$, or $13.89%$. If we calculate the probabilities for the rest of the outcomes, we can start to see why Las Vegas will always make money in the long run. Maybe I'll reconsider my dice-playing ways the next time I visit the casinos. @@ -286,7 +293,7 @@ for i in range(len(dice_1)): if sums[i] in prime_numbers: primes += 1 -same_perc = same / len(dice_1) +same_perc = same / len(dice_1) print(same_perc) # 16.66666 prime_perc = primes / len(dice_1) print(prime_perc) # 41.59 diff --git a/content/2019-03-25-CSS-Colors-Fonts-and-Design-Principles/index.md b/src/content/posts/2019-03-25-CSS-Colors-Fonts-and-Design-Principles.mdx similarity index 65% rename from content/2019-03-25-CSS-Colors-Fonts-and-Design-Principles/index.md rename to src/content/posts/2019-03-25-CSS-Colors-Fonts-and-Design-Principles.mdx index f5868f8..8e01694 100644 --- a/content/2019-03-25-CSS-Colors-Fonts-and-Design-Principles/index.md +++ b/src/content/posts/2019-03-25-CSS-Colors-Fonts-and-Design-Principles.mdx @@ -1,20 +1,30 @@ --- title: "Design in CSS: Colors, Fonts, and Themes" category: "Design" -date: 2019-03-25 +date: "03/25/2019" published: "true" slug: "design-in-css-colors-fonts-and-theme" tags: - - CSS - - design - - colors - - fonts - - css variables - - web development - - beginner - - high school lesson + - CSS + - design + - colors + - fonts + - css variables + - web development + - beginner + - high school lesson +# isHighSchoolLesson: true --- +import Image from "@/components/MDX/MdxImage.astro" +import google_fonts_gif from "../../assets/posts/google-fonts.gif" +import mac_color_picker_icon from "../../assets/posts/mac_color_picker_icon.png" +import mac_color_picker from "../../assets/posts/mac_color_picker.png" +import behance_css_colors_fonts from "../../assets/posts/behance_css_colors_fonts.jpg" +import ExternalLink from "@/components/Common/ExternalLink.astro" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + Before I pretend like I know what I'm talking about, I am going to admit that I am relatively design deficient. Don't get me wrong. If you show me something that's nicely designed, I'll notice it. I have quality taste. What I lack is the ability to envision how something nice should look before it exists. @@ -36,7 +46,7 @@ We'll only look at a few core web design principles. Each concept has a signific ## Colors -Early on, it's possible to get by only using color names. Colors like `tomato`, `teal`, and `wheat` can actually take you pretty far. Eventually though, you're going to want to have more fine-grained control of your color choices. +Early on, it's possible to get by only using color names. Colors like `tomato`, `teal`, and `wheat` can actually take you pretty far. Eventually though, you're going to want to have more fine-grained control of your color choices. When that day does come, you'll have a few options to choose from. For most of my students, I recommend they start with `rgb` values. The best way to think about rgb (stands for red, green, blue) is to see how the computer uses them to represent pixels on a screen. @@ -44,9 +54,7 @@ If you're using a mac, click on the magnifying glass in the top right hand of yo
            -
            - -
            +mac color picker icon
            @@ -54,15 +62,13 @@ When you open up the application, you'll see a floating window that indicates th
            -
            - -
            +mac color picker icon
            To use rgb values in your CSS, simply assign them to your `background`, `color`, or `border` properties as necessary. -```css +```css .black-bg { background: rgb(0, 0, 0); } @@ -77,21 +83,27 @@ To use rgb values in your CSS, simply assign them to your `background`, `color`, } ``` -Again, the first number in the rgb value represents how red the color should be, the second how green it should be, and the third how blue. Numbers range from 0 - 255 and can be combined to create essentially any color. Note that it's also possible to add an alpha attribute to rgb. Using `rgba()`, we are able to specify a fourth value that determines the **opacity** of the color. Think of opacity as how *see through* it is. +Again, the first number in the rgb value represents how red the color should be, the second how green it should be, and the third how blue. Numbers range from 0 - 255 and can be combined to create essentially any color. Note that it's also possible to add an alpha attribute to rgb. Using `rgba()`, we are able to specify a fourth value that determines the **opacity** of the color. Think of opacity as how _see through_ it is. Play around with this codepen taken from the Mozilla Developer Network to get a handle on how we select custom colors. - You'll also notice that rgb values aren't the only way to represent colors. We can also use Hex codes and HSL values, but we'll save that for another post. If you want to dive further into color theory, start by reading this [page](https://developer.mozilla.org/en-US/docs/Web/HTML/Applying_color) in MDN. - - So how do we go about selecting the right colors for our site? ### Choosing a Color Palette @@ -109,14 +121,15 @@ Head to that link and try and create a color palette that matches your liking. D When you find something you like, save the colors to a CSS file. Normally, it makes sense to select colors for the following: -+ base color -+ light base color -+ dark base color -+ secondary color -+ accent color -+ main text -+ secondary text -+ icons + +- base color +- light base color +- dark base color +- secondary color +- accent color +- main text +- secondary text +- icons Don't worry if you don't find the perfect combo of colors, we'll play around with themes later in this post. @@ -132,44 +145,45 @@ The Google Fonts team offers sage advice in their [choosing web fonts beginner's Generally, it makes sense to pick a font for header text and a font for paragraph text, and then call it quits. More than 2 or 3 different fonts looks sloppy. Instead of choosing many fonts, go for fonts that pair nicely with each other. Some good font pairing resources include things like: -+ [typ.io](https://typ.io) -+ [fontpair.co](https://fontpair.co) -+ [Google Fonts](https://fonts.google.com) -+ [typekits](https://fonts.adobe.com/) +- [typ.io](https://typ.io) +- [fontpair.co](https://fontpair.co) +- [Google Fonts](https://fonts.google.com) +- [typekits](https://fonts.adobe.com/) When pairing fonts, visualizing the end result can often be tricky. It might help to see examples of the fonts in use, and then translate that to your use case. Use examples like the ones below as a guide. -
            - -
            - Taken from this Behance post that has many more examples like the one above. +behance fonts example +
            + Taken from this Behance post that has many more examples like the one above.
            -
            + Also, make your life easy. Use Google Fonts when you can. You're more than welcome to download your own custom fonts and handle that business yourself. But until you're sure you need something that Google Fonts doesn't have, stick to what works. Using Google Fonts is simple. First, go to Google Fonts and browse around until you find 1 or 2 fonts that you like. Click on the + icon in the top right of the font card to add it to your "font cart". -
            - -
            -
            +google fonts selection If you're up for it, customize your font to include a range of sizes and strengths. Get a thin font, a bold font, an italics font, and whatever else you might need. Then, open up your selected fonts, and click on the "@import" tab, and then copy the line of code that starts with `@import`. It should look something like this: ```css -@import url('https://fonts.googleapis.com/css?family=Pangolin'); +@import url("https://fonts.googleapis.com/css?family=Pangolin"); ``` + Paste this line in at the top of your CSS stylesheet. You can now use the font in your styling, using the `font-family` property: ```css - h1 { - font-family: 'Pangolin'; - } +h1 { + font-family: "Pangolin"; +} ``` That's all there is to it! @@ -189,57 +203,71 @@ For colors, we could choose between baby blue, light pink, or any other color th With the main color decisions made, it's time to get something on the page. -```html{numberLines: true} - -
            -
            -

            Baby Name Picker

            -

            Find your baby the perfect name by using our baby name picking algorithm. If you have a unique name to add to our baby name database, feel free to submit them below.

            -
            - - - - -
            -
            - +```html + + + + Baby Name Picker + + + + + +
            +
            +

            Baby Name Picker

            +

            + Find your baby the perfect name by using our baby name picking algorithm. If you have a unique name to add to + our baby name database, feel free to submit them below. +

            +
            + + + + +
            +
            + + ``` -```css{numberLines: true} -*, *::after, *::before { +```css +*, +*::after, +*::before { box-sizing: border-box; } body { margin: 0; - font-family: sans-serif; + font-family: sans-serif; font-size: 20px; - background: rgb(242,242,242); + background: rgb(242, 242, 242); } -.topbg { +.bg { position: absolute; height: 200px; background: rgb(83, 38, 163); - border-bottom: solid 6px rgb(255,105,180); + border-bottom: solid 6px rgb(255, 105, 180); width: 100vw; z-index: -1000; } -.main { +main { width: 80vw; background: white; border: solid 1px rgb(202, 202, 202); min-height: 60vh; box-shadow: 0px 1px 2px 0px rgb(202, 202, 202); margin-left: 10vw; - transform: translateY(20vh); - padding: 1em; + transform: translateY(140px); + padding: 16px; } .interaction { display: flex; justify-content: flex-start; } button { - padding: 0.5em 0.8em; - font-size: 1.2em; + padding: 8px 13px; + font-size: 1.2rem; color: white; background: rgb(83, 38, 163); border-radius: 4px; @@ -248,9 +276,9 @@ button { } input { margin-left: auto; - padding: 0.5em 0.8em; - font-size: 1.2em; - background: rgb(242,242,242); + padding: 8px 13px; + font-size: 1.2rem; + background: rgb(242, 242, 242); border-radius: 4px; border: solid 1px rgb(202, 202, 202); } @@ -270,17 +298,16 @@ input:focus { } .interaction input { width: 100%; - } + } } ``` -The code above produces the results seen in the following codepen. - +The code above produces the results seen here: - + We see our color theme applied nicely to our page, and we also see that our page is in dire need of better font choices. Let's fix that. @@ -289,15 +316,15 @@ Head to google fonts and select the Comfortaa and Lato fonts. We'll use these tw The code to import those two fonts is as follows: ```css -@import url('https://fonts.googleapis.com/css?family=Comfortaa|Lato'); +@import url("https://fonts.googleapis.com/css?family=Comfortaa|Lato"); ``` Add that to the top of the CSS file and we can then use those fonts in our css. Tell the h1, button, and input to have a `font-family: Comfortaa`, while giving the body `font-family: Lato`. - + What a difference some nice fonts make. And with those additions, we've completed our basic theme. While this is nice and could by all means be done, we can do better. @@ -309,13 +336,13 @@ To declare a variable, create a `:root` pseudo class selector and define variabl ```css :root { - --color-bg: rgb(83, 38, 163); - --color-accent: rgb(255,105,180); - --color-button: rgb(83, 38, 163); + --color-bg: rgb(83, 38, 163); + --color-accent: rgb(255, 105, 180); + --color-button: rgb(83, 38, 163); --color-button-text: rgb(255, 255, 255); - --color-body-bg: rgb(255, 255, 255); - --font-header: Comfortaa; - --font-main: Lato; + --color-body-bg: rgb(255, 255, 255); + --font-header: Comfortaa; + --font-main: Lato; } ``` @@ -347,11 +374,11 @@ Feel free to give the data-theme attribute whatever value you like - it's arbitr ```css [data-theme="theme-1"] { - --color-bg: rgb(83, 38, 163); - --color-accent: rgb(255, 64, 129); - --color-button-bg: rgb(83, 38, 163); - --color-button-text: rgb(255, 255, 255); - --color-body-bg: rgb(242,242,242); + --color-bg: rgb(83, 38, 163); + --color-accent: rgb(255, 64, 129); + --color-button-bg: rgb(83, 38, 163); + --color-button-text: rgb(255, 255, 255); + --color-body-bg: rgb(242, 242, 242); } ``` @@ -361,56 +388,56 @@ And if we can have one theme, why not two? I've gone ahead and picked a few new ```css [data-theme="theme-2"] { - --color-bg: rgb(137,207,240); - --color-accent: rgb(82, 12, 232); - --button-bg: rgb(137,207,240); - --button-color: rgb(10, 10, 10); - --color-body-bg: rgb(255,255,255); + --color-bg: rgb(137, 207, 240); + --color-accent: rgb(82, 12, 232); + --button-bg: rgb(137, 207, 240); + --button-color: rgb(10, 10, 10); + --color-body-bg: rgb(255, 255, 255); } ``` We'll leave the font variables in the root class, as they'll apply to both themes. Our site will now look like the codepen below: - - + With proper color selections handled, we can add a tiny bit of JavaScript to switch between each theme easily. ```javascript -document.querySelector('#theme1').addEventListener('click', e => { - document.body.setAttribute('data-theme', 'theme-1') +document.querySelector("#theme1").addEventListener("click", (e) => { + document.body.setAttribute("data-theme", "theme-1") }) -document.querySelector('#theme2').addEventListener('click', e => { - document.body.setAttribute('data-theme', 'theme-2') +document.querySelector("#theme2").addEventListener("click", (e) => { + document.body.setAttribute("data-theme", "theme-2") }) - ``` These two click handlers allow us to easily convert between themes on our site, showcasing the power of CSS Variables. And ta-da! Click the theme 1 and theme 2 buttons to see it in action. Play around with this codepen and add your own themes if you'd like. - + + +
            ## Wrapping Up and Resources -+ Improve Your Design with the Principles of Similarity and Proximity - https://www.smashingmagazine.com/2016/05/improve-your-designs-with-principles-similarity-proximity-part-1/ -+ Color Theory for Designers - Lovely infographic on color and how to think about them. https://uxdesign.cc/color-theory-for-designers-a-crash-course-with-infographic-41d8b4c45619 -+ NYU CS: Color Theory - Basic introduction to color theory in a short snippet. https://cs.nyu.edu/courses/fall02/V22.0380-001/color_theory.htm -+ Smashing Magazine: Visual Perception and Gestalt Principles - https://www.smashingmagazine.com/2014/03/design-principles-visual-perception-and-the-principles-of-gestalt/ -+ Vanseo Design: Gestalt Principles - http://vanseodesign.com/web-design/gestalt-principles-of-perception/ -+ VITSOE - The Power of Good Design. https://www.vitsoe.com/us/about/good-design -+ MDN: Applying Color - https://developer.mozilla.org/en-US/docs/Web/HTML/Applying_color -+ Web Design is 95% Typography - Author lays out a compelling argument for why understanding typography is central to understanding web design. Also offers some fantastic books for those looking to get more into design and fonts. https://ia.net/topics/the-web-is-all-about-typography-period -+ Google Design: Choosing Web Fonts - Google Font's step-by-step guide to choosing the right font(s) for your site. https://design.google/library/choosing-web-fonts-beginners-guide/ -+ Creative Bloq: 100 Free Fonts for Designer - https://www.creativebloq.com/graphic-design-tips/best-free-fonts-for-designers-1233380 -+ Invision: Big List of Typography Resources - https://www.invisionapp.com/inside-design/free-typography-resources/ -+ Canva: Typography Terminology - https://www.canva.com/learn/typography-terms/ -+ Butterick's Practical Typography - https://practicaltypography.com/ +- Improve Your Design with the Principles of Similarity and Proximity - https://www.smashingmagazine.com/2016/05/improve-your-designs-with-principles-similarity-proximity-part-1/ +- Color Theory for Designers - Lovely infographic on color and how to think about them. https://uxdesign.cc/color-theory-for-designers-a-crash-course-with-infographic-41d8b4c45619 +- NYU CS: Color Theory - Basic introduction to color theory in a short snippet. https://cs.nyu.edu/courses/fall02/V22.0380-001/color_theory.htm +- Smashing Magazine: Visual Perception and Gestalt Principles - https://www.smashingmagazine.com/2014/03/design-principles-visual-perception-and-the-principles-of-gestalt/ +- Vanseo Design: Gestalt Principles - http://vanseodesign.com/web-design/gestalt-principles-of-perception/ +- VITSOE - The Power of Good Design. https://www.vitsoe.com/us/about/good-design +- MDN: Applying Color - https://developer.mozilla.org/en-US/docs/Web/HTML/Applying_color +- Web Design is 95% Typography - Author lays out a compelling argument for why understanding typography is central to understanding web design. Also offers some fantastic books for those looking to get more into design and fonts. https://ia.net/topics/the-web-is-all-about-typography-period +- Google Design: Choosing Web Fonts - Google Font's step-by-step guide to choosing the right font(s) for your site. https://design.google/library/choosing-web-fonts-beginners-guide/ +- Creative Bloq: 100 Free Fonts for Designer - https://www.creativebloq.com/graphic-design-tips/best-free-fonts-for-designers-1233380 +- Invision: Big List of Typography Resources - https://www.invisionapp.com/inside-design/free-typography-resources/ +- Canva: Typography Terminology - https://www.canva.com/learn/typography-terms/ +- Butterick's Practical Typography - https://practicaltypography.com/ diff --git a/content/2019-03-26-Make-Websites-Interactive/index.md b/src/content/posts/2019-03-26-Make-Websites-Interactive.mdx similarity index 70% rename from content/2019-03-26-Make-Websites-Interactive/index.md rename to src/content/posts/2019-03-26-Make-Websites-Interactive.mdx index 51b8c0c..5cbb4ba 100644 --- a/content/2019-03-26-Make-Websites-Interactive/index.md +++ b/src/content/posts/2019-03-26-Make-Websites-Interactive.mdx @@ -1,20 +1,26 @@ --- title: "How to Make Websites Interactive with JavaScript" category: "Web Development" -date: 2019-03-26 +date: "03/26/2019" published: "true" slug: "make-websites-interactive-with-javascript-dom" tags: - - html - - selectors - - javascript - - console - - dom - - beginner - - high school lesson + - html + - selectors + - javascript + - console + - dom + - beginner + - high school lesson +# isHighSchoolLesson: true --- -Remember that every page on the internet is an HTML **document**. We can add HTML tags to the page, and use CSS to style our HTML. However, almost every modern website allows you to *interact* with it via clicking and typing. +import Image from "@/components/MDX/MdxImage.astro" +import chromeDevTools from "../../assets/posts/chrome_devtools.png" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + +Remember that every page on the internet is an HTML **document**. We can add HTML tags to the page, and use CSS to style our HTML. However, almost every modern website allows you to _interact_ with it via clicking and typing. In this post, we'll walk through the essentials of website interaction by discussing the DOM, or Document Object Model. First, we'll examine how to access the DOM using JavaScript query selectors. Afterwards, the focus will shift to manipulating the DOM through event listeners, callbacks, and class list methods. @@ -22,10 +28,9 @@ In this post, we'll walk through the essentials of website interaction by discus By the end of this post, you should know/be able to: -* Access HTML elements on a website using **querySelector()** -* Change the class of a selected element -* Change the inner content of a selected element - +- Access HTML elements on a website using **querySelector()** +- Change the class of a selected element +- Change the inner content of a selected element ## Sequence @@ -35,12 +40,11 @@ By the end of this post, you should know/be able to: 4. Class List Methods 5. Content Properties - ## Prerequisites -* You have mastered how to use id, class, and element selectors in your CSS. -* You understand that you can associate multiple classes with a single element by separating the class names with a space. -* You understand what purpose variables serve in JavaScript, and can write custom functions +- You have mastered how to use id, class, and element selectors in your CSS. +- You understand that you can associate multiple classes with a single element by separating the class names with a space. +- You understand what purpose variables serve in JavaScript, and can write custom functions ## The Console @@ -54,11 +58,11 @@ touch index.html style.css Add the following code to your index.html file and preview it in your browser. Make sure to open the preview up in a new tab. -```html{numberLines: true} - +```html + - + @@ -74,17 +78,25 @@ Add the following code to your index.html file and preview it in your browser. M - ``` Then paste this code in your style.css file. -```css{numberLines: true} +```css /* The first several elements that are styled are really just for the purpose of having */ /* Border-box sizing, Default to a sans-serif font throughout */ -*, *:before, *:after {-webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-family: sans-serif; } +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-family: sans-serif; +} /* Black background color */ body { @@ -127,12 +139,7 @@ body { Preview the HTML file and open up the JavaScript console in your browser. To do that in chrome, click on the 3 dots in the top right corner of the browser, navigate to 'More Tools', and then click on 'Developer Tools'. -
            - chrome_dev_tools -
            - Taken from the chrome devtools documentation. -
            -
            +chrome dev tools In the new browser window that opens up, select the console tab and you should see our message, "JavaScript is working!" printed to the screen. @@ -146,18 +153,18 @@ Whenever a web browser, like Chrome, renders an HTML file, it also creates JavaS ## Querying the DOM -Let's get an element from the page and store it in a variable. Then we'll console log that variable to make sure we got the element we were looking for. Edit the script in your HTML file to have the following code. +Let's get an element from the page and store it in a variable. Then we'll console log that variable to make sure we got the element we were looking for. Edit the script in your HTML file to have the following code. ```html ``` -When you refresh the page, you'll notice that the `firstPhone` variable is storing a div. We grabbed that div using `document.querySelector('#phone1')`. The `querySelector` method allows us to *query* the document, or *ask a question*, about an element having the selector `#phone1`. +When you refresh the page, you'll notice that the `firstPhone` variable is storing a div. We grabbed that div using `document.querySelector('#phone1')`. The `querySelector` method allows us to _query_ the document, or _ask a question_, about an element having the selector `#phone1`. Using this method, we were able to capture the element with the attribute `id="phone1"` and print it to the console. Capture the remaining two phone divs in variables and console log them to confirm that you got them both. @@ -166,7 +173,7 @@ Now, update the script with the following code. Before you refresh your page, wh ```js console.log("JavaScript is working!") -let firstPhone = document.querySelector('#phone1') +let firstPhone = document.querySelector("#phone1") console.log(firstPhone) console.log(firstPhone.id) @@ -184,9 +191,9 @@ What's an event listener? Event listeners can be attached to an element on the DOM like so: ```javascript -let firstPhone = document.querySelector('#phone1') +let firstPhone = document.querySelector("#phone1") -firstPhone.addEventListener('click', (e) => { +firstPhone.addEventListener("click", (e) => { console.log("first phone clicked!") }) ``` @@ -198,7 +205,7 @@ So what happened here? First, we used querySelector to grab the phone div from t 1. Take the JavaScript that you've written so far. 2. Add a `
          • +
          @@ -118,14 +132,12 @@ To see our results in action, add add some HTML and icons to the navbar.
          - + - -
          + +
          ``` @@ -134,7 +146,7 @@ We're making use of the font-awesome library to create icons in our HTML by usin ```html Twitter Clone - + ``` @@ -147,10 +159,10 @@ Notice how each div has the class name equal to what we've specified in the `gri grid-area: bird; } .navbar .search { - grid-area: search; + grid-area: search; } .navbar .actions { - grid-area: actions + grid-area: actions; } ``` @@ -172,13 +184,13 @@ We'll start by working on our navbar links. Add the following css to your file. ```css .navbar .links li { flex: 1; - height: 49px; + height: 49px; cursor: pointer; padding: 0px 10px; - display: flex; + display: flex; align-items: center; justify-content: center; - color: var(--color-font-secondary); + color: var(--color-font-secondary); border-bottom: solid 2px transparent; transition: all 0.2s ease; } @@ -190,21 +202,31 @@ We'll start by working on our navbar links. Add the following css to your file. Let's take a sneak peek to see how it looks (telling the bird, search, and actions sections not to display). - Wow. That made a big difference. We told each `
        • ` inside our `.links` list to have a flex of 1. This tells the list items to take up all the available space inside the list, and share it equally. The minimum size of each `
        • ` is set to 1/4 the size of the `
        `. -We make the list items 49px tall and set their `border-bottom` to `solid 2px transparent`. This will take up all 50px of the navbar and 1 extra pixel. It will also provide a nice transition effect when we add color to the border in a second. We finish it off with `transition: all 0.2s ease` so that all animations will have a 200ms delay (200ms is 2/10 of 1 second) and look smooth. +We make the list items 49px tall and set their `border-bottom` to `solid 2px transparent`. This will take up all 50px of the navbar and 1 extra pixel. It will also provide a nice transition effect when we add color to the border in a second. We finish it off with `transition: all 0.2s ease` so that all animations will have a 200ms delay (200ms is 2/10 of 1 second) and look smooth. Add the following to your css to see that colored effect happen. ```css .navbar .links li.active, .navbar .links li:hover { - color: var(--color-twitter-blue); + color: var(--color-twitter-blue); border-bottom: solid 2px var(--color-twitter-blue); } ``` @@ -215,14 +237,14 @@ If you look carefully at Twitter's home page, you'll see there's a tiny dot abov ```css .navbar .links li.active span::before { - content: ''; + content: ""; width: 5px; - height: 5px; + height: 5px; border-radius: 50%; position: absolute; - top: 10px; + top: 10px; margin-left: -3vw; - background: var(--color-twitter-blue); + background: var(--color-twitter-blue); } ``` @@ -237,7 +259,7 @@ Mozilla outlines the `::before` pseudo-selector - See the Pen Twitter Clone Part Ia by Jeff Astor - (@jastor11-the-decoder) on CodePen. + Let's finish up the rest of this navbar real quick. @@ -262,7 +294,7 @@ Throw the rest of this css into your page to complete the navbar. *****************/ .navbar .bird { grid-area: bird; - font-size: 20px; + font-size: 20px; margin-left: 30px; } .navbar .bird i { @@ -273,20 +305,20 @@ Throw the rest of this css into your page to complete the navbar. NAVBAR SEARCH *****************/ .navbar .search { - grid-area: search; + grid-area: search; } .navbar .search input { width: 100%; padding: 8px 12px; border-radius: 20px; border: solid 1px var(--color-twitter-mid-gray); - background: var(--color-twitter-light-gray); + background: var(--color-twitter-light-gray); transition: all 0.2s ease; } .navbar .search input:focus { outline: none; background: white; - border: solid 2px var(--color-twitter-blue); + border: solid 2px var(--color-twitter-blue); } .navbar .search i { transform: translateX(-25px); @@ -297,11 +329,10 @@ Throw the rest of this css into your page to complete the navbar. NAVBAR ACTIONS *******************/ .navbar .actions { - grid-area: actions + grid-area: actions; } .navbar .actions .avatar { display: flex; - } .navbar .actions i.fa-circle { color: var(--color-twitter-mid-gray); @@ -315,7 +346,7 @@ Throw the rest of this css into your page to complete the navbar. .navbar .actions i.fa-user { color: var(--color-twitter-dark-gray); font-size: 24px; - transform: translateY(6px) + transform: translateY(6px); } .navbar .actions button { color: white; @@ -337,24 +368,21 @@ To polish off the navbar, add the following media queries so that everything loo @media screen and (max-width: 1150px) { .navbar { padding: 0 20px; - grid-template-areas: - " links links links . . bird . search search search actions actions" - } + grid-template-areas: " links links links . . bird . search search search actions actions"; + } } @media screen and (max-width: 900px) { .navbar { - grid-template-areas: - "links links links links . . bird search search search actions actions" - } + grid-template-areas: "links links links links . . bird search search search actions actions"; + } .navbar .links li.active span::before { - margin-left: -25px + margin-left: -25px; } } @media screen and (max-width: 740px) { .navbar { - grid-template-areas: - "links links links links . . . . . . . bird" - } + grid-template-areas: "links links links links . . . . . . . bird"; + } .search, .actions { display: none; @@ -366,9 +394,19 @@ Showcasing the power of CSS grid, these rules guarantee that our navbar looks de Let's check out our page one more time. - ## The Main Tweet Feed Section @@ -379,50 +417,45 @@ Update your `.main` div to look like this: ```html
        - -
        -
        +
        + +
        + ``` and then add the following code to the bottom of your CSS section. ```css - /****************** MAIN SECTION *******************/ @@ -431,16 +464,16 @@ and then add the following code to the bottom of your CSS section. display: grid; grid-template-columns: repeat(12, 1fr); grid-column-gap: 10px; - grid-template-areas: - ". left-col left-col left-col feed feed feed feed feed right-col right-col ." + grid-template-areas: ". left-col left-col left-col feed feed feed feed feed right-col right-col ."; +} +.col { } -.col {} /****************** LEFT COLUMN *******************/ .left-col { - grid-area: left-col + grid-area: left-col; } .left-col .user-info { background: white; @@ -470,12 +503,12 @@ and then add the following code to the bottom of your CSS section. .left-col .card-mid .fa-plus { color: var(--color-twitter-blue); font-size: 8px; - transform: translateY(11px) + transform: translateY(11px); } .left-col .card-mid .twitter-handle { margin-top: 10px; - margin-left: 10px; - letter-spacing: .2px + margin-left: 10px; + letter-spacing: 0.2px; } .left-col .card-mid .twitter-handle h3 { font-size: 18px; @@ -496,13 +529,13 @@ and then add the following code to the bottom of your CSS section. } .left-col .card-bottom p { font-size: 14px; - color: var(--color-twitter-dark-gray) + color: var(--color-twitter-dark-gray); } .left-col .card-bottom h3 { font-size: 20px; font-weight: bold; color: var(--color-twitter-blue); - margin-top: -10px + margin-top: -10px; } /****************** @@ -550,48 +583,52 @@ This is all standard HTML and CSS with an added pinch of CSS flexbox and CSS gri @media screen and (max-width: 1150px) { .navbar { padding: 0 20px; - grid-template-areas: - " links links links . . bird . search search search actions actions" - } + grid-template-areas: " links links links . . bird . search search search actions actions"; + } .main { padding: 60px 20px; - grid-template-areas: - "left-col left-col left-col feed feed feed feed feed feed right-col right-col right-col" + grid-template-areas: "left-col left-col left-col feed feed feed feed feed feed right-col right-col right-col"; } } @media screen and (max-width: 900px) { .navbar { - grid-template-areas: - "links links links links . . bird search search search actions actions" - } + grid-template-areas: "links links links links . . bird search search search actions actions"; + } .main { padding: 60px 20px; - grid-template-areas: - "left-col left-col left-col feed feed feed feed feed feed right-col right-col right-col" - } + grid-template-areas: "left-col left-col left-col feed feed feed feed feed feed right-col right-col right-col"; + } } @media screen and (max-width: 740px) { .navbar { - grid-template-areas: - "links links links links . . . . . . . bird" - } + grid-template-areas: "links links links links . . . . . . . bird"; + } .search, .actions { display: none; } .main { padding: 60px 20px; - grid-template-areas: - "left-col left-col left-col left-col left-col left-col right-col right-col right-col right-col right-col" - } + grid-template-areas: "left-col left-col left-col left-col left-col left-col right-col right-col right-col right-col right-col"; + } } ``` With all that code, we finally get to something like this codepen. - Not looking half bad. We'll fix those media queries when we add the feed in to make things look nicer. @@ -602,8 +639,6 @@ That's more than enough code for one day. Let's save the tweet box and feed unti Our halftime result isn't anything to scoff at. It took more code than I originally thought it would, and there's definitely room for improvement. Still, we're almost at a working prototype and we're ready to make our site interactive. -Find part II [here](https://www.jeffastor.com/create-an-interactive-twitter-feed-part-ii). - -+ Twitter's Mobile Technology Stack - An article discussing how Twitter relies on Node.js, Express.js, and React to create their mobile app. https://www.infoq.com/news/2017/02/twitter-react-mobile-stack -+ Twitter Engineering Blog - The Infrastructure Behind Twitter Scale is a cool article showing how Twitter deals with the enormous amount of data they handle each day. https://blog.twitter.com/engineering/en_us/topics/infrastructure/2017/the-infrastructure-behind-twitter-scale.html -+ Quora - Twitter's tech stack. https://www.quora.com/What-is-the-technology-stack-behind-Twitter +- Twitter's Mobile Technology Stack - An article discussing how Twitter relies on Node.js, Express.js, and React to create their mobile app. https://www.infoq.com/news/2017/02/twitter-react-mobile-stack +- Twitter Engineering Blog - The Infrastructure Behind Twitter Scale is a cool article showing how Twitter deals with the enormous amount of data they handle each day. https://blog.twitter.com/engineering/en_us/topics/infrastructure/2017/the-infrastructure-behind-twitter-scale.html +- Quora - Twitter's tech stack. https://www.quora.com/What-is-the-technology-stack-behind-Twitter diff --git a/content/2019-04-03-Clone-Twitters-Front-End-Part-II/index.md b/src/content/posts/2019-04-03-Clone-Twitters-Front-End-Part-II.mdx similarity index 73% rename from content/2019-04-03-Clone-Twitters-Front-End-Part-II/index.md rename to src/content/posts/2019-04-03-Clone-Twitters-Front-End-Part-II.mdx index 67b1bf3..32ef60d 100644 --- a/content/2019-04-03-Clone-Twitters-Front-End-Part-II/index.md +++ b/src/content/posts/2019-04-03-Clone-Twitters-Front-End-Part-II.mdx @@ -1,34 +1,48 @@ --- title: "Clone Twitter's Front End Part II" category: "Web Development" -date: 2019-04-03 +date: "04/03/2019" published: "true" slug: "create-an-interactive-twitter-feed-part-ii" tags: - - web development - - javascript - - dom - - beginner - - high school lesson + - web development + - javascript + - dom + - beginner + - high school lesson +# isHighSchoolLesson: true --- -Last post, we attempted to rebuild the front-end of Twitter's feed page. It was all HTML and CSS before. This time, we'll sprinkle in a dash of JavaScript and throw some animations in the mix. +import Image from "@/components/MDX/MdxImage.astro" +import twitter from "../../assets/posts/twitter-feed-ii.png" + +> NOTE: This post was originally created as a lesson for my high school students. The target audience is beginners to web development. + +Last post, we attempted to rebuild the front-end of Twitter's feed page. It was all HTML and CSS before. This time, we'll sprinkle in a dash of JavaScript and throw some animations in the mix. This post won't be as long as the last one, but it'll be more logically challenging. Again, this screenshot will serve as our guide during the process. -
        - twitter landing page -
        +twitter landing page Let's start by building the tweet box and creating an animation for an expanding and collapsing effect. If you don't have the code from the previous post, here's the codepen we left off with last time. - Now we can get to business. @@ -37,12 +51,12 @@ Now we can get to business. Update your feed column to have the following HTML code. -```html{4, 6} +```html {4, 6}
        - + @@ -68,7 +82,7 @@ Update your feed column to have the following HTML code.

        See 13 New Tweets

        - +
        ``` @@ -87,7 +101,7 @@ and just a smidgeon of CSS. .fa-stack i.fa-user { color: var(--color-twitter-dark-gray); font-size: 24px; - transform: translateY(6px) + transform: translateY(6px); } /*************** @@ -112,14 +126,14 @@ Add the next chunk of CSS to your codepen. ```css .main .feed .tweet-container #tweet-box-top { - padding-top: 10px + padding-top: 10px; } .main .feed .tweet-avatar { margin-left: 20px; } .main .feed textarea { flex: 1; - height: 40px ; + height: 40px; margin-left: 10px; margin-right: -10px; border-radius: 8px; @@ -175,7 +189,7 @@ Now for the second row. padding: 4px; border: solid 1px var(--color-twitter-blue); border-radius: 4px; - transform: translateY(0px) + transform: translateY(0px); } .main .feed .tweet-container #tweet-actions .submit { flex: 1; @@ -216,12 +230,21 @@ Add the `.see-new-tweets` section CSS as well. Now we have something like this. - - Oooh, pretty. ## The Twitter Feed and Event Listeners @@ -232,40 +255,40 @@ To do that, we'll need to setup a few event listeners. My personal preference is to grab all the DOM elements we'll need to manipulate at the beginning of my script. -```js{numberLines: true} +```js // Header: ./index.js -let tweetBox = document.querySelector('#tweet-box') -let tweetContainer = document.querySelector('.tweet-container') -let tweetBoxTop = document.querySelector('#tweet-box-top') -let tweetActions = document.querySelector('#tweet-actions') -let tweetBoxImage = document.querySelector('#tweet-box-img') -let tweetSubmitBtn = document.querySelector('#tweet-submit') +let tweetBox = document.querySelector("#tweet-box") +let tweetContainer = document.querySelector(".tweet-container") +let tweetBoxTop = document.querySelector("#tweet-box-top") +let tweetActions = document.querySelector("#tweet-actions") +let tweetBoxImage = document.querySelector("#tweet-box-img") +let tweetSubmitBtn = document.querySelector("#tweet-submit") ``` All we're doing here is storing different HTML elements in variables. We do this by using the `document.querySelector( )` method and the element's id. I've found that my code is generally more bug-free when I select elements by their id. There is even a method called `document.getElementById( )` that has the same effect. However, `document.querySelector( )` aligns with CSS better, so I pretty much always use it. Next, we'll make two functions that will either expand or reset the tweet box. -```js{numberLines: true} -let tweetBox = document.querySelector('#tweet-box') -let tweetContainer = document.querySelector('.tweet-container') -let tweetBoxTop = document.querySelector('#tweet-box-top') -let tweetActions = document.querySelector('#tweet-actions') -let tweetBoxImage = document.querySelector('#tweet-box-img') -let tweetSubmitBtn = document.querySelector('#tweet-submit') +```js +let tweetBox = document.querySelector("#tweet-box") +let tweetContainer = document.querySelector(".tweet-container") +let tweetBoxTop = document.querySelector("#tweet-box-top") +let tweetActions = document.querySelector("#tweet-actions") +let tweetBoxImage = document.querySelector("#tweet-box-img") +let tweetSubmitBtn = document.querySelector("#tweet-submit") const resetTweetBox = () => { - tweetActions.style.display = 'none' - tweetBox.style.height = '40px' - tweetContainer.style.height = '70px' - tweetBoxImage.setAttribute('class', 'fas fa-image') + tweetActions.style.display = "none" + tweetBox.style.height = "40px" + tweetContainer.style.height = "70px" + tweetBoxImage.setAttribute("class", "fas fa-image") } const expandTweetBox = () => { - tweetBox.style.height = '150px' - tweetActions.style.display = 'flex' - tweetContainer.style.height = '220px' - tweetBoxImage.setAttribute('class', 'fas fa-smile') + tweetBox.style.height = "150px" + tweetActions.style.display = "flex" + tweetContainer.style.height = "220px" + tweetBoxImage.setAttribute("class", "fas fa-smile") } ``` @@ -273,18 +296,18 @@ This part is more interesting. On line 8, we're creating a variable called `rese The difference between the `let` and `const` variable declarations is that `const` variables can not be re-assigned. -So, this is *legal*: +So, this is _legal_: ```js let age = 17 age = 19 ``` -And this is *illegal*: +And this is _illegal_: ```js -const name = 'Will Smith' -name = 'William Smith' // ERROR - can't reassign const variable +const name = "Will Smith" +name = "William Smith" // ERROR - can't reassign const variable ``` When we say `const resetTweetBox = () => {}`, we are assigning an anonymous function to the variable `resetTweetBox`. The anonymous function is new ES6 style JavaScript and is created by writing open and closed parenthesis `( )`, followed by a fat arrow `=>`, followed by the body of the function. The body usually goes inside the squiggly brackets `{}`. If we need to add arguments, we place them inside the parenthesis just like normal functions. @@ -305,7 +328,7 @@ Great. Now let's set up the event listener. Add this to the bottom of your JS sc // ...other javascript resetTweetBox() -tweetBox.addEventListener('focus', (e) => { +tweetBox.addEventListener("focus", (e) => { expandTweetBox() }) ``` @@ -314,9 +337,19 @@ We start by running the `resetTweetBox( )` function as soon as the file loads. T Click inside the textarea to see it in action. - Check that out! The section expands nicely and the image changes. @@ -331,9 +364,9 @@ If the user didn't click inside the tweet container and didn't click on the TWEE ```js // ...other JavaScript -document.addEventListener('click', (event) => { - if (event.target.closest('.tweet-container')) { - if (event.target.id === 'tweet-submit') { +document.addEventListener("click", (event) => { + if (event.target.closest(".tweet-container")) { + if (event.target.id === "tweet-submit") { // submit the tweet submitTweet() } @@ -345,9 +378,19 @@ document.addEventListener('click', (event) => { And now we should see something like the following: - Ta-da! Hope you feel as good about that as I do. @@ -392,7 +435,6 @@ Update the HTML for your feed column to look like this: -
        @@ -441,7 +483,7 @@ Then add the following CSS to your file: grid-template-columns: 70px 1fr; } #twitter-posts .tweet-left { - padding: 5px + padding: 5px; } #twitter-posts .tweet-right { padding: 15px; @@ -452,16 +494,16 @@ Then add the following CSS to your file: justify-content: space-between; } #twitter-posts .tweet-right .tweet-top-meta span { - color: var(--color-twitter-dark-gray) + color: var(--color-twitter-dark-gray); } #twitter-posts .tweet-right .tweet-top-meta i { color: var(--color-twitter-blue); - transform: translateY(-7px) + transform: translateY(-7px); } #twitter-posts .tweet-right .tweet-top-meta i.fa-check { color: white; font-size: 8px; - transform: translateY(0px) + transform: translateY(0px); } #twitter-posts .tweet-right .tweet-top-meta .tweet-top-name-and-handle { display: flex; @@ -482,14 +524,14 @@ Then add the following CSS to your file: color: var(--color-twitter-dark-gray); } #twitter-posts .tweet-right .tweet-bottom-meta span { - cursor: pointer + cursor: pointer; } #twitter-posts .tweet-right .tweet-bottom-meta span:hover i { color: var(--color-twitter-blue); } #twitter-posts .tweet-right .tweet-bottom-meta span { margin-right: 40px; - color: var(--color-twitter-dark-gray) ; + color: var(--color-twitter-dark-gray); display: flex; } ``` @@ -503,37 +545,32 @@ And finally, replace your media queries section with this code: @media screen and (max-width: 1150px) { .navbar { padding: 0 15px; - grid-template-areas: - " links links links . . bird . search search search actions actions" - } + grid-template-areas: " links links links . . bird . search search search actions actions"; + } .main { padding: 60px 20px; - grid-template-areas: - "left-col left-col left-col feed feed feed feed feed feed right-col right-col right-col" + grid-template-areas: "left-col left-col left-col feed feed feed feed feed feed right-col right-col right-col"; } } @media screen and (max-width: 900px) { .navbar { - grid-template-areas: - "links links links links . . bird search search search actions actions"; - } + grid-template-areas: "links links links links . . bird search search search actions actions"; + } .main { padding: 60px 20px; - grid-template-areas: - "left-col left-col left-col left-col feed feed feed feed feed feed feed feed"; + grid-template-areas: "left-col left-col left-col left-col feed feed feed feed feed feed feed feed"; } .navbar .links li.active span::before { - margin-left: -25px - } + margin-left: -25px; + } .right-col { display: none; - } + } } @media screen and (max-width: 740px) { .navbar { - grid-template-areas: - "links links links links . . . . . . . bird" - } + grid-template-areas: "links links links links . . . . . . . bird"; + } .search, .actions { display: none; @@ -544,15 +581,14 @@ And finally, replace your media queries section with this code: } .main { padding: 60px 20px; - grid-template-areas: - "feed feed feed feed feed feed feed feed feed feed feed feed" - } + grid-template-areas: "feed feed feed feed feed feed feed feed feed feed feed feed"; + } .tweet { min-width: 300px; } .tweet .tweet-text { width: 80%; - } + } } ``` @@ -573,10 +609,9 @@ This function first creates a variable called `tweet` and then grabs the value o That `addNewTweet()` function looks like this: ```js - const addNewTweet = (tweet) => { - let tweetHTML = document.createElement('div') - tweetHTML.setAttribute('class', 'tweet') + let tweetHTML = document.createElement("div") + tweetHTML.setAttribute("class", "tweet") tweetHTML.innerHTML = `
        @@ -609,7 +644,7 @@ const addNewTweet = (tweet) => { ` tweetFeed.prepend(tweetHTML) - tweetBox.value = '' + tweetBox.value = "" } resetTweetBox() @@ -627,9 +662,19 @@ Oh my that's been a lot of typing. I think we're finally ready to call it quits. Heres what it looks like up to now. - ## Wrapping Up and Resources @@ -638,6 +683,6 @@ Well that was fun. Remaking famous web apps is a good way to practice various fr Feel free to send me any and all improvements or suggestions. -+ Event Bubbling in Vanilla JavaScript - https://gomakethings.com/checking-event-target-selectors-with-event-bubbling-in-vanilla-javascript/#closest -+ MDN prepend - https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend -+ MDN closest - https://developer.mozilla.org/en-US/docs/Web/API/Element/closest +- Event Bubbling in Vanilla JavaScript - https://gomakethings.com/checking-event-target-selectors-with-event-bubbling-in-vanilla-javascript/#closest +- MDN prepend - https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend +- MDN closest - https://developer.mozilla.org/en-US/docs/Web/API/Element/closest diff --git a/content/2019-04-29-Advanced-NLP-with-SpaCy/index.md b/src/content/posts/2019-04-29-Advanced-NLP-with-SpaCy.mdx similarity index 96% rename from content/2019-04-29-Advanced-NLP-with-SpaCy/index.md rename to src/content/posts/2019-04-29-Advanced-NLP-with-SpaCy.mdx index f1845eb..e53697d 100644 --- a/content/2019-04-29-Advanced-NLP-with-SpaCy/index.md +++ b/src/content/posts/2019-04-29-Advanced-NLP-with-SpaCy.mdx @@ -1,7 +1,7 @@ --- title: "Advanced NLP with spaCy" category: "Course Review" -date: 2019-04-29 +date: "04/29/2019" slug: "advanced-nlp-with-spacy-review" author: "Ines Montani" link: "https://course.spacy.io" @@ -16,8 +16,6 @@ tags: NLTK has been the king of NLP for most of recent history. I've used it extensively and found its API to be fine, though somewhat clumsy. In my humble opinion, spaCy is definitely more user-friendly. This course capitalizes on that fact and takes advantages of some of the best qualities of python. - - ## Pros Not to mention it's created with Gatsby and the Juniper plugin - which lets users run Jupyter Notebook backed python in the browser. The creator, Ines Montani, did an excellent job at sequencing content and coding exercises. Hats off to her. The code snippets have a fill-in-the-blank, Datacamp-esque feel (the course was originally created for Datacamp). diff --git a/src/content/posts/2019-06-01-Volume-Learning.mdx b/src/content/posts/2019-06-01-Volume-Learning.mdx new file mode 100644 index 0000000..ab38204 --- /dev/null +++ b/src/content/posts/2019-06-01-Volume-Learning.mdx @@ -0,0 +1,99 @@ +--- +title: "Volume Learning" +category: "Learning" +date: "06/01/2019" +published: "false" +slug: "why-we-learn-best-through-sheer-volume" +tags: + - learning + - practice + - the brain +--- + +Ask any good programmer how to get better, and they'll say the same thing. + +Write a *lot* of code. + +They never back it up with any reasoning though. We just all know it's true, and so we assume other people do as well. What I'd like to put forth in this post is my intuition for how volume learning improves our ability to understand something. + +## Why it works + +Early on we improve rapidly because our brain makes connections between things we didn't have a grasp of before, but after a while our improvement slows. + +The reason why sheer volume helps us learn things well is because we often don't have the context necessary to digest new content. In his great lecture series on the learning brain, Professor _____XXXXX_______ shows a simple, yet powerful example of how context is essential to storing, remembering, and accessing something you hear for the first time. + +He then backs it up with scientific evidence, because guess what? It's all based on a study. Here's how it goes. + +____XXXX____ read the passage, explain what it was, look at it again and discuss the results of the published study. + +Without context, we don't retain anything we learn. Each time we write more code, view another tutorial, we've established a stronger context for the content we're learning. This makes it easier to consume and process new information - allowing us to relate ideas to our previous knowledge more effectively. + +## How I do volume learning + +Often when I'm learning a new framework or technology, I read the documentation and understand very little. So I go straight to the examples and try to learn the content through building things. When I come back to the documentation months later, the explanations are crystal clear. +Building a product with the tool in question teaches you just enough to understand written explanations of it. + +This works. When I attempted to learn probability, I took a course from MIT and understood about 60% of it. Afterwards, I took the Harvard statistics course and understood a lot more. It's boring to take the same class twice. It's less boring to learn the same thing from two different interesting instructors. Also, every professor has a unique set of lessons that they consider to be the best vehicle for content delivery. + +I'm in the middle of the UCSD probability and statistics course using python and I feel so much more confident about my comprehension of the course materials this time around than I did in the previous two. Take classes, build things, assess yourself, repeat. Do this until you feel like you've truly mastered the content. You'll know because the coursework comes easy for you. As soon as you're ready, move on. + +## What have I mastered with the volume learning approach? + +I've always found this to be the most effective system for learning anything. When I started my data science journey, I poured over every Jupyter notebook and tutorial I could find. I understood some things, but most concepts zipped in one ear and out the other. + +Honestly, that's fine. + +Most of my time was spent on Kaggle and doing neural network courses (including Fast.ai and Francois Chollet's Deep Learning with Python notebooks which I highly recommend). I made cool stuff, but I never fully grasped how things worked. For me to feel like I know something, I need to have internalized a formal process that I can use as soon as I come across a problem. If I don't have that, I know I'm not ready. + +This is why I've found teaching something to be such an effective way to learn things. You have to codify your existing knowledge into digestible chunks so that students can process it themselves. + +--- + +After a few weeks of this, I dove into a more structured environment. Starting with the simplest course is always best - the least amount of context is required. + +For me, that was Berkeley's EdX Intro to Data Science Course. They abstract all the numpy, pandas, and matplotlib into their own library. This makes it easy to focus on exactly what it is we need to understand to actually do the things data scientists do. Then I took their Inferential Thinking by Resampling course and the statistics was over my head. Still good though, and definitely worth it. + +Afterwards I tried out the DataCamp Python for Data Science series and soared through most of it. I'm pretty meh about DataCamp. Too structured, not enough fun. +The visualization courses were very helpful though. I also side-tracked and did Mode Analytics' intro to pandas and SQL courses. They're great and I highly recommend them. + +Next I jumped back over to Kaggle and saw how experts had implemented the things I'd learned in my previous courses. With slightly more bearings than before, I went to a definitive guide - Jake VanderPlas' Data Science for Python Handbook. Hands down the best resource on the subject. Accessible, yet rigorous. He doesn't shy away from complicated code or computer science. At the same time, he also ensures that he covers a wide swath of content. It's a handbook after all, ya know? The final chapters lost me on a lot of the math, but that's fine. + +Then I went back to Harvard's CS 109 course on data science and I was loving it. I finally had enough knowledge to actually internalize the content. After completing that course I worked through the notebooks in a General Assembly data science course and completed the exercises as best I could. + +Once more, I returned to Kaggle to try out a few competitions and still sucked. That felt ok. It's still fun to watch the experts do it and try to replicate their processes. + +Back to structure. This time, I wanted to hone my skills in a number of areas and so I sought out a few resources. Scikit learn pops up everywhere, so I read the original paper and did all the Andreas Mueller courses and workshops I could find. Loved the whole thing. + +My conceptual framework was lacking, so I took Caltech's Learning from Data Course and think it's absolutely amazing as well. Take it! Highly recommend it. I also read through an Introduction to Statistical Learning and got rocked on a lot of it. My R skills were underwhelming to say the least. I didn't get through the whole thing, but I'll come back to it eventually. + +At the same time I started reading Hal Daume III's Intro to ML book, which is free, written in pseudocode, and overall pretty great. My approach was to read chapters of the book, attempt to turn the pseudocode into python algorithms, and then work through curriculum of a course that closely mirrored the content. I ended up choosing the Kaggle kernels available at https://mlcourse.ai/. This has to be one of the best, most in-depth course into how machine learning algorithms work and is my current favorite for guided practice of writing your own ML algorithms. The combo of these two was great. + +All this was going swell until I got my hands on the curriculum for two different data science bootcamps and went through them systematically. It took me about 5 months to complete them both (one was in R and one was in Python). Afterwards I had clearly identified my weakness - statistics and probability. Never took any of those courses in high school or undergrad, so it was back to school for me. + +## A more formal education + +At this point I felt pretty good. I could complete most exercises on my own and had even completed a few cool projects that would fit my portfolio well. I wasn't content though. My math was eh, and my statistics was nah. + +Realizing I needed some guidance, I enrolled in MIT's MicroMasters in Statistics and Data Science program. This was the first time I actually shelled out cash to learn, but it ended up being great. I probably didn't even have to do it, but all the MIT MOOCs I'd taken previously had been top notch, so I decided to support them. + +Boy did I need it too. The probability and statistics courses pushed me to the edge of my mental abilities and I actually ended up dropping one class before taking it again later. + +I had mostly done machine learning stuff, so I didn't have any rigorous experience completing statistics problem sets. Good to know I still got it. I'll finish up my last machine learning course in the program this fall, and then I'll complete the capstone project. + +After that, I'll be done. Yay to me. I feel way more learned than when I started, and I owe it all to sheer volume. + +## Who this system is for + +Honestly, this isn't going to work for everyone. Some people aren't content not understand each and every detail of something they're learning for the first time. + +If you're not ok with just moving on even when you don't know something, you'll hate this. + +However, if you can swallow your pride and just move through things, you'll be fine. I promise. Just put your head down and get to work. + + +## Some other courses I took along the way and loved + ++ Kadenze - Creative Applications of Deep Learning with TensorFlow ++ Google's Machine Learning Crash Course ++ EdX - MIT Probability and Statistics ++ MIT - Computer Science with Python diff --git a/content/2019-09-24-Django-ORM-Queries-in-the-Shell/index.md b/src/content/posts/2019-09-24-Django-ORM-Queries-in-the-Shell.mdx similarity index 99% rename from content/2019-09-24-Django-ORM-Queries-in-the-Shell/index.md rename to src/content/posts/2019-09-24-Django-ORM-Queries-in-the-Shell.mdx index bcc1374..56fecd5 100644 --- a/content/2019-09-24-Django-ORM-Queries-in-the-Shell/index.md +++ b/src/content/posts/2019-09-24-Django-ORM-Queries-in-the-Shell.mdx @@ -1,7 +1,7 @@ --- title: "Django ORM Queries in the Shell" category: "Databases" -date: 2019-09-24 +date: "09/24/2019" published: "true" slug: "django-orm-queries-in-the-shell" tags: diff --git a/content/2019-11-01-Doing-Data-Science-in-the-Browser/index.md b/src/content/posts/2019-11-01-Doing-Data-Science-in-the-Browser.mdx similarity index 87% rename from content/2019-11-01-Doing-Data-Science-in-the-Browser/index.md rename to src/content/posts/2019-11-01-Doing-Data-Science-in-the-Browser.mdx index c8d9ada..ed1ee0b 100644 --- a/content/2019-11-01-Doing-Data-Science-in-the-Browser/index.md +++ b/src/content/posts/2019-11-01-Doing-Data-Science-in-the-Browser.mdx @@ -1,19 +1,23 @@ --- title: "Doing Data Science in the Browser" category: "Data Science" -date: 2019-11-01 +date: "11/01/2019" published: "true" slug: "doing-data-science-in-the-browser" tags: - - python - - data science - - colab - - visualizations + - python + - data science + - colab + - visualizations --- +import Image from "@/components/MDX/MdxImage.astro" +import colabChangeSettings from "../../assets/posts/google_colab_change_settings.gif" +import colabGetFileId from "../../assets/posts/google_colab_get_file_id.gif" + Four years ago when I read my first article on building neural networks from scratch, the author had me installing anaconda, tensorflow, and getting a Jupyter notebook environment running on my laptop. A couple hours later, I was up and running and had a neural network training on my cpu. Besides using every resource my compaq had in it, my homemade multi-layer perceptron took about 12 hours to train. Who knew GPUs would be so useful? -For a high school computer science teacher, the cost of putting together a legit deep learning box was prohibitively high. Lucky for me, in the last half decade the cloud has evolved and adopted data science in its entirety. Now I can spin up a deep learning instance for pennies on the dollar in the Google Cloud or on AWS. +For a high school computer science teacher, the cost of putting together a legit deep learning box was prohibitively high. Lucky for me, in the last half decade the cloud has evolved and adopted data science in its entirety. Now I can spin up a deep learning instance for pennies on the dollar in the Google Cloud or on AWS. For simple exploration, exploratory data analysis, and data munging I prefer to do my data science work in the browser. Kaggle kernels come preloaded with hundreds of data sets, most modern data science libraries, and 12 hours of GPU time for free. Google Colab offers the same and syncs up with Google Drive or external APIs for seamless importing of data. @@ -37,23 +41,20 @@ To change the settings, click on Tools > Settings like the following GIF: */}
        -
        - -
        - +colab change settings ## Importing Data There are a number of ways to get data into Google Colab, but the one I like the best works as follows. First, upload the data to Google drive. Then, we'll install PyDrive, mount the PyDrive client, and download the file into the Google Colab VM. You can install PyDrive like so: ```bash -!pip install -U -q PyDrive +!pip install -U -q PyDrive ``` Now that you've installed PyDrive, you can mount the client like so: ```python -# Get the necessary imports +# Get the necessary imports from pydrive.auth import GoogleAuth from pydrive.drive import GoogleDrive from google.colab import auth @@ -66,11 +67,11 @@ gauth.credentials = GoogleCredentials.get_application_default() drive = GoogleDrive(gauth) ``` -You'll be asked to follow up with some Google OAuth, which you'll have to do *every* time you run this notebook. It's annoying, but I find it more seamless than uploading the data each time I run the notebook again. +You'll be asked to follow up with some Google OAuth, which you'll have to do _every_ time you run this notebook. It's annoying, but I find it more seamless than uploading the data each time I run the notebook again. Now finally, we can download the file into the Colab VM. Enter the following code into another cell: -```python +```python file_id = '' # Download file from gDrive and store its contents @@ -88,10 +89,7 @@ Open up the Google Drive folder that holds the csv file. Preview the file and op */} - -
        - -
        +colab get file id Store that id in the variable `file_id` and run the cell. @@ -112,10 +110,10 @@ At this point, we're ready to go. We'll start by importing pandas, numpy and mat Create another cell that has the following code: ```python -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -%matplotlib inline +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +%matplotlib inline plt.style.use('ggplot') ``` @@ -140,10 +138,7 @@ Give it a shot and I'll have the answers for you in the next post. That's all you'll need to get started, but your journey into data science has just begun. If you're looking for reference materials, check out the resources below: -+ https://pandas.pydata.org/ -+ https://chrisalbon.com/ -+ https://colab.research.google.com/notebooks/welcome.ipynb -+ https://research.google.com/colaboratory/faq.html - - - +- https://pandas.pydata.org/ +- https://chrisalbon.com/ +- https://colab.research.google.com/notebooks/welcome.ipynb +- https://research.google.com/colaboratory/faq.html diff --git a/content/2019-11-19-Synthesized-Statistics-Permutations-and-Combinations/index.md b/src/content/posts/2019-11-19-Synthesized-Statistics-Permutations-and-Combinations.mdx similarity index 81% rename from content/2019-11-19-Synthesized-Statistics-Permutations-and-Combinations/index.md rename to src/content/posts/2019-11-19-Synthesized-Statistics-Permutations-and-Combinations.mdx index 0ceb1f6..5ffe1c4 100644 --- a/content/2019-11-19-Synthesized-Statistics-Permutations-and-Combinations/index.md +++ b/src/content/posts/2019-11-19-Synthesized-Statistics-Permutations-and-Combinations.mdx @@ -1,13 +1,13 @@ --- title: "Synthesized Statistics - Permutations and Combinations" category: "Statistics" -date: 2019-11-19 +date: "11/19/2019" published: "true" slug: "synthesized-statistics-permutations-and-combinations" tags: - - python - - statistics - - probability + - python + - statistics + - probability --- Statistics is tough for me. I'm only ok at math and I never intuitively understand something the first time I hear it. So when I sucked at MIT's intro to probability course, I took another one from Harvard and synthesized my knowledge of the two together into this series of blog posts. Most of the time, I try and convert the mathematics into code, and that gives me slightly better insight into how things work. Follow along and hopefully you'll learn something too. @@ -26,17 +26,17 @@ $$ n! $$ translates pretty evenly into this code block: def factorial(n): total = 1 for i in range(n, 1, -1): - total *= i + total *= i return total factorial(3) # 6 because 3 * 2 * 1 is 6 factorial(4) # 24 -factorial(5) # 120 +factorial(5) # 120 ``` Recognize the pattern? -We are essentially counting down from n and multiplying each value by the previous total. Python's `range()` function takes three arguments, the starting value, the ending value, and the step. By ending at 1 and using a step of -1, we first multiply total by 3, and then by 2, and then by 1. +We are essentially counting down from n and multiplying each value by the previous total. Python's `range()` function takes three arguments, the starting value, the ending value, and the step. By ending at 1 and using a step of -1, we first multiply total by 3, and then by 2, and then by 1. If you want to get really terse, you could shorten this to something like this: @@ -60,11 +60,11 @@ def factorial(n): total = 1 for i in range(n, 1, -1): - total *= i - return total + total *= i + return total ``` -That's not quite as pretty, but it's functional. +That's not quite as pretty, but it's functional. Fortunately, we can check our work by importing the math module and using the built in factorial function. @@ -80,36 +80,36 @@ print(math.factorial(0)) # 1 print(math.factorial(-1)) # error ``` -You'll notice that the error printed is the same as the one used in our custom function. And you're right, I stole it. +You'll notice that the error printed is the same as the one used in our custom function. And you're right, I stole it. So why do we need to know all this mathy goodness? ## Getting Choosy -Unit 1 in Harvard's Intro to Probability course covers the math of counting. I enjoyed this section and found examples to be the best way to conceptualize the math. Let me preface this by saying that some forms of counting are easier than others. We'll start small and build up. +Unit 1 in Harvard's Intro to Probability course covers the math of counting. I enjoyed this section and found examples to be the best way to conceptualize the math. Let me preface this by saying that some forms of counting are easier than others. We'll start small and build up. -The easiest kind of counting is done when we are looking to sample from a collection of items *with replacement*. A good example would be choosing a four digit passcode for your smart phone. You can pick 4 numbers, and you're welcome to use any of those numbers more than once. That's what *with replacement* refers to - being able to choose an item from a collection, replace it, and then choose it again. +The easiest kind of counting is done when we are looking to sample from a collection of items _with replacement_. A good example would be choosing a four digit passcode for your smart phone. You can pick 4 numbers, and you're welcome to use any of those numbers more than once. That's what _with replacement_ refers to - being able to choose an item from a collection, replace it, and then choose it again. -If you were to count the total number of unique passcodes available, you would get $ 10^{4} $, or 10,000 possible choices. That's easy math. As a rule of thumb, when choosing $ k $ items from a collection of size $ n $ with replacement, the formula will always be $ n^{k} $. +If you were to count the total number of unique passcodes available, you would get $ 10^{4} $, or 10,000 possible choices. That's easy math. As a rule of thumb, when choosing $ k $ items from a collection of size $ n $ with replacement, the formula will always be $ n^{k} $. -The next type of counting we'll learn is how to calculate the number of ways to choose groups of size $ k $ from $ n $ items in a particular order. +The next type of counting we'll learn is how to calculate the number of ways to choose groups of size $ k $ from $ n $ items in a particular order. -This is essentially discussing the number of **permutations**. -Here's a concrete example for when permutations come into play. We have a deck of 52 cards. How many possible ways can we deal 5 cards? +This is essentially discussing the number of **permutations**. +Here's a concrete example for when permutations come into play. We have a deck of 52 cards. How many possible ways can we deal 5 cards? -One example would be the dealer placing down an Ace of spades and a 2 of diamonds on the flop. Then up comes a Queen of hearts, followed by a 7 of clubs, and finally a 10 of diamonds. That's just one possible outcome, and things start to get out of hand if we try to count every possible permutation by hand. +One example would be the dealer placing down an Ace of spades and a 2 of diamonds on the flop. Then up comes a Queen of hearts, followed by a 7 of clubs, and finally a 10 of diamonds. That's just one possible outcome, and things start to get out of hand if we try to count every possible permutation by hand. The answer can instead be found by using the following formula: -$$ P_{k}^{n} = \dfrac{n!}{(n-k)!} $$ +$$ P\_{k}^{n} = \dfrac{n!}{(n-k)!} $$ -So now we know why we need factorials. +So now we know why we need factorials. Plug in 52 (the number of cards) for $ n $, and 5 (the group size) for $ k $. What value do you get? $$ \dfrac{52!}{(52-5)!} $$ -Using our previously defined function, we can calculate the answer as **311,875,200**. Memorize that number and you'll be *super* fun at cocktail parties. +Using our previously defined function, we can calculate the answer as **311,875,200**. Memorize that number and you'll be _super_ fun at cocktail parties. If you think about it, what we're essentially doing is saying that at first we can choose any one of the 52 cards. The next time we deal a card, we can choose any one of the remaining 51 cards, then 50 cards, then 49, and finally 48. @@ -121,15 +121,15 @@ Ok, so what if order doesn't matter? Well in that case, we're no longer dealing In general, combinations provide an answer to the question: "How many ways can we create a subset $ k $ out of $ n $ items?". -We write that out mathematically as $ (_{k}^{n}) $. +We write that out mathematically as $ (\_{k}^{n}) $. -That looks kinda weird, right? We say this out loud like $ n $ choose $ k $. +That looks kinda weird, right? We say this out loud like $ n $ choose $ k $. -Calculating $ n $ choose $ k $ takes advantage of our previous knowledge about permutations, coupled with the fact that we don't need to know the order, so we divide that value by $ k! $. +Calculating $ n $ choose $ k $ takes advantage of our previous knowledge about permutations, coupled with the fact that we don't need to know the order, so we divide that value by $ k! $. Doing so gives us this formula: -$$ \displaystyle\binom{n}{k} = \dfrac{P_{k}^{n}}{k!} = \dfrac{ \dfrac{n!}{(n-k)!}}{k!} = \dfrac{n!}{(n-k)!k!} $$ +$$ \displaystyle\binom{n}{k} = \dfrac{P\_{k}^{n}}{k!} = \dfrac{ \dfrac{n!}{(n-k)!}}{k!} = \dfrac{n!}{(n-k)!k!} $$ Ok, that makes sense. Now what if we wanted to figure out how many distinct 5-card hands we could deal to a given player? Well in this case, the order doesn't matter, so we're working with combinations. @@ -145,20 +145,24 @@ With those two formulas, we're well equipped to take on most counting problems. Here's some of the resources I use to think through problems in this category: -+ Harvard Intro to Probability Course - https://courses.edx.org/courses/course-v1:HarvardX+STAT110x+3T2019/course/ -+ MITs Probability - The Science of Uncertainty and Data - https://www.edx.org/course/probability-the-science-of-uncertainty-and-data -+ Jeremy Kun - Probability Theory: A Primer. https://jeremykun.com/2013/01/04/probability-theory-a-primer/ -+ Khan Academy - Probability. https://www.khanacademy.org/math/probability/probability-geometry +- Harvard Intro to Probability Course - https://courses.edx.org/courses/course-v1:HarvardX+STAT110x+3T2019/course/ +- MITs Probability - The Science of Uncertainty and Data - https://www.edx.org/course/probability-the-science-of-uncertainty-and-data +- Jeremy Kun - Probability Theory: A Primer. https://jeremykun.com/2013/01/04/probability-theory-a-primer/ +- Khan Academy - Probability. https://www.khanacademy.org/math/probability/probability-geometry - +As you hopefully recall, the formula is $ P\_{k}^{n} = \dfrac{n!}{(n-k)!} $. Since we're operating in groups of 4, and there are 10 possible numbers to choose from, this means +`} + diff --git a/content/2019-11-28-Building-Your-Own-Monty-Hall-Simulation/index.mdx b/src/content/posts/2019-11-28-Building-Your-Own-Monty-Hall-Simulation.mdx similarity index 70% rename from content/2019-11-28-Building-Your-Own-Monty-Hall-Simulation/index.mdx rename to src/content/posts/2019-11-28-Building-Your-Own-Monty-Hall-Simulation.mdx index 29d32c5..b884803 100644 --- a/content/2019-11-28-Building-Your-Own-Monty-Hall-Simulation/index.mdx +++ b/src/content/posts/2019-11-28-Building-Your-Own-Monty-Hall-Simulation.mdx @@ -1,25 +1,27 @@ --- title: "Conditional Probability and the Monty Hall Game" category: "Simulations and Visualizations" -date: 2019-11-28 +date: "11/28/2019" published: "true" slug: "the-monty-hall-problem-visualized" tags: - - python - - statistics - - probability + - python + - statistics + - probability --- -import { MontyHall } from "src/components" +import { Image } from "astro:assets" +import MontyHall from "@/components/MDX/viz/MontyHall" -In the poker-playing, black-jack stats movie 21, there's a scene where the protagonist is in a lecture hall with the professor. They discuss a game-show where a contestant chooses one of three closed doors, two of which have a goat behind them and one of which has a car. +import montyOpenDoor from "../../assets/posts/440px-Monty_open_door.png" +import montyTreeDoor from "../../assets/posts/Monty_tree_door1.png" -Monty, who knows where the car is, then opens one of the two remaining doors. The door he opens always has a goat behind it (he never reveals the car!). If he has a choice, then he picks a door at random with equal probabilities. +In the poker-playing, black-jack stats movie 21, there's a scene where the protagonist is in a lecture hall with the professor. They discuss a game-show where a contestant chooses one of three closed doors, two of which have a goat behind them and one of which has a car. -
        - -
        +Monty, who knows where the car is, then opens one of the two remaining doors. The door he opens always has a goat behind it (he never reveals the car!). If he has a choice, then he picks a door at random with equal probabilities. +monty hall door + Monty then offers the contestant the option of switching to the other unopened door. If the contestant's goal is to get the car, should she switch doors? The protagonist in 21 nails the numbers behind the right answer, and throws in some pseudo-math jargon along the way. In a grand monologue, he disregards his intuition in favor of math and statistics. @@ -28,18 +30,18 @@ Neither really helped me understand the correct answer. I had to see it to belie The simulation from the Harvard Intro to Stats course did the job really well, and I went ahead and built my own in React. - + ## The Results Play the game as many times as you want. As you play, look at the results table as it tracks your score. Take note. Which strategy seems to be working better for you? Switch or stay? -Now simulate the game with **switch** as many times as you want. I did 10,000. Do the same for **stay**. +Now simulate the game with **switch** as many times as you want. I did 10,000. Do the same for **stay**. -+ What do your results indicate? -+ What win % does each of the strategies converge on? -+ Can you reason why? +- What do your results indicate? +- What win % does each of the strategies converge on? +- Can you reason why? ## Giving Away the Answer @@ -47,7 +49,6 @@ Wikipedia has a graphic that explains the results better than anything I've seen Either way - here it is: -![monty hall answer](https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/Monty_tree_door1.svg/1110px-Monty_tree_door1.svg.png) - +monty hall answer -Does it make more sense now? \ No newline at end of file +Does it make more sense now? diff --git a/content/2019-12-02-Advent-of-Code-Day-2/index.md b/src/content/posts/2019-12-02-Advent-of-Code-Day-2.mdx similarity index 78% rename from content/2019-12-02-Advent-of-Code-Day-2/index.md rename to src/content/posts/2019-12-02-Advent-of-Code-Day-2.mdx index 284c2b9..0c4ac11 100644 --- a/content/2019-12-02-Advent-of-Code-Day-2/index.md +++ b/src/content/posts/2019-12-02-Advent-of-Code-Day-2.mdx @@ -1,13 +1,13 @@ --- title: "Advent of Code - Day 2" category: "Solutions" -date: 2019-12-02 +date: "12/02/2019" published: "true" slug: "advent-of-code-day-2" tags: - - python - - statistics - - probability + - python + - statistics + - probability --- Advent of Code day 2 was a fun one. The explanation for part two took me a whie to wrap my head around,but once I did, the rest was pretty straightforward. @@ -25,12 +25,12 @@ Either way, here's the code in it's entirety. ## My Solution ```python -unique_intcode = "1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,1,10,19,1,19,5,23,2,23,9,27,1,5,27,31,1,9,31,35,1,35,10,39,2,13,39,43,1,43,9,47,1,47,9,51,1,6,51,55,1,13,55,59,1,59,13,63,1,13,63,67,1,6,67,71,1,71,13,75,2,10,75,79,1,13,79,83,1,83,10,87,2,9,87,91,1,6,91,95,1,9,95,99,2,99,10,103,1,103,5,107,2,6,107,111,1,111,6,115,1,9,115,119,1,9,119,123,2,10,123,127,1,127,5,131,2,6,131,135,1,135,5,139,1,9,139,143,2,143,13,147,1,9,147,151,1,151,2,155,1,9,155,0,99,2,0,14,0" +unique_intcode = '1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,1,10,19,1,19,5,23,2,23,9,27,1,5,27,31,1,9,31,35,1,35,10,39,2,13,39,43,1,43,9,47,1,47,9,51,1,6,51,55,1,13,55,59,1,59,13,63,1,13,63,67,1,6,67,71,1,71,13,75,2,10,75,79,1,13,79,83,1,83,10,87,2,9,87,91,1,6,91,95,1,9,95,99,2,99,10,103,1,103,5,107,2,6,107,111,1,111,6,115,1,9,115,119,1,9,119,123,2,10,123,127,1,127,5,131,2,6,131,135,1,135,5,139,1,9,139,143,2,143,13,147,1,9,147,151,1,151,2,155,1,9,155,0,99,2,0,14,0' class Intcode: def __init__(self, raw_intcode, noun=12, verb=2): self.active = True - self.__raw_intcode = raw_intcode + self.__raw_intcode = raw_intcode self.__parsed_intcode = self._pre_process_intcode( self._parse_raw_intcode(raw_intcode), noun=noun, @@ -53,8 +53,8 @@ class Intcode: memory = self._process_intcode(memory, silent=True) if memory[0] == value: - print("noun is {}".format(noun)) - print("verb is {}".format(verb)) + print('noun is {}'.format(noun)) + print('verb is {}'.format(verb)) @@ -63,47 +63,47 @@ class Intcode: new_intcode = intcode.copy() new_intcode[1] = noun new_intcode[2] = verb - return new_intcode + return new_intcode def _parse_raw_intcode(self, raw_intcode): - return list(map(int, raw_intcode.split(','))) + return list(map(int, raw_intcode.split(','))) def _process_intcode(self, memory, silent=False): self.active = True - instruction_length = 4 + instruction_length = 4 ip = 0 while self.active: - valid_opcode = Opcode(memory[ip]) + valid_opcode = Opcode(memory[ip]) if valid_opcode.value == 99: self._terminate(silent=silent) elif valid_opcode.value == 1: # get instruction - opcode, *parameters = memory[ip:ip+instruction_length] - # execute instruction - addr_a, addr_b, output_addr = parameters + opcode, *parameters = memory[ip:ip+instruction_length] + # execute instruction + addr_a, addr_b, output_addr = parameters memory[output_addr] = memory[addr_a] + memory[addr_b] # increment instruction pointer ip += instruction_length elif valid_opcode.value == 2: # get instruction - opcode, *parameters = memory[ip:ip+instruction_length] + opcode, *parameters = memory[ip:ip+instruction_length] # mult the two address values into the output address - addr_a, addr_b, output_addr = parameters + addr_a, addr_b, output_addr = parameters memory[output_addr] = memory[addr_a] * memory[addr_b] # increment instruction pointer ip += instruction_length else: self.active = False - raise ValueError("Something went wrong.") + raise ValueError('Something went wrong.') + + return memory - return memory - def _terminate(self, silent=False): if self.active: if not silent: - print("---- completed intcode processing ----") + print('---- completed intcode processing ----') self.active = False # PROPERTIES @@ -111,11 +111,11 @@ class Intcode: def result(self): return ','.join([str(i) for i in self.__data]) - @property + @property def raw_intcode(self): - return self.__raw_intcode - - @property + return self.__raw_intcode + + @property def parsed_intcode(self): return self.__parsed_intcode @@ -125,9 +125,9 @@ class Opcode: self.__opcode = opcode valid_opcodes = [1, 2, 99] if opcode not in valid_opcodes: - raise ValueError("Incorrect opcode: {}. Must be one of {}".format(opcode, valid_opcodes)) + raise ValueError('Incorrect opcode: {}. Must be one of {}'.format(opcode, valid_opcodes)) @property def value(self): return self.__opcode -``` \ No newline at end of file +``` diff --git a/content/2020-03-17-Plaid-Inspired-Inputs-With-React-Hooks/index.mdx b/src/content/posts/2020-03-17-Plaid-Inspired-Inputs-With-React-Hooks.mdx similarity index 80% rename from content/2020-03-17-Plaid-Inspired-Inputs-With-React-Hooks/index.mdx rename to src/content/posts/2020-03-17-Plaid-Inspired-Inputs-With-React-Hooks.mdx index 3c85db0..5b6b78f 100644 --- a/content/2020-03-17-Plaid-Inspired-Inputs-With-React-Hooks/index.mdx +++ b/src/content/posts/2020-03-17-Plaid-Inspired-Inputs-With-React-Hooks.mdx @@ -1,27 +1,28 @@ --- title: "Plaid Inspired Inputs with React Hooks and Styled Components" category: "React" -date: 2020-03-17 +date: "03/17/2020" slug: "plaid-inspired-inputs-with-react-hooks" published: "true" tags: - - javascript - - react + - javascript + - react --- -import Input1 from '../../src/components/BlogDisplayElements/Input/Input1' -import Input2 from '../../src/components/BlogDisplayElements/Input/Input2' -import Input3 from '../../src/components/BlogDisplayElements/Input/Input3' -import Input4 from '../../src/components/BlogDisplayElements/Input/Input4' -import InputForm from '../../src/components/BlogDisplayElements/Input/InputForm' -Having been a React.js user since the days of `React.createClass`, I wasn't initially motivated to dive into React Hooks when they were announced. Since then, I've bit the bullet and relearned the React state paradigm. Though I still occasionally dip into classes, for the most part I'm a hooks convert. +import { Input1 } from "@/components/MDX/custom/PlaidInputs/Input1" +import { Input2 } from "@/components/MDX/custom/PlaidInputs/Input2" +import { Input3 } from "@/components/MDX/custom/PlaidInputs/Input3" +import { Input4 } from "@/components/MDX/custom/PlaidInputs/Input4" +import { InputForm } from "@/components/MDX/custom/PlaidInputs/InputForm" + +Having been a React.js user since the days of `React.createClass`, I wasn't initially motivated to dive into React Hooks when they were announced. Since then, I've bit the bullet and relearned the React state paradigm. Though I still occasionally dip into classes, for the most part I'm a hooks convert. I'd like to briefly show their utility here and demonstrate a use-case where hooks have a clear advantage. In light of Visa recently acquiring fintech startup Plaid, we're going to hop on the bandwagon and recreate an element of their UI that I enjoy. Here's what we're going to build:
        - +
        A quick look at the Plaid login page
        @@ -60,8 +61,8 @@ Now it's time to write some code. First things first, let's build out our Input component. Here's the skeleton we'll start out with. ```javascript -import React from 'react' -import styled from 'styled-components' +import React from "react" +import styled from "styled-components" const InputContainer = styled.div` display: flex; @@ -91,7 +92,7 @@ const InputContainer = styled.div` /** * A Plaid-inspired custom input component - * + * * @param {string} value - the value of the controlled input * @param {string} type - the type of input we'll deal with * @param {string} label - the label used to designate info on how to fill out the input @@ -100,28 +101,19 @@ const InputContainer = styled.div` * @param {function} onBlur - function called when the input loses focus * @param {function} setRef - function used to add this input as a ref for a parent component */ -const Input = ({ - value, - type, - label, - onChange, - onFocus, - onBlur, - setRef, - ...props -}) => { - const renderLabel = () => label && +const Input = ({ value, type, label, onChange, onFocus, onBlur, setRef, ...props }) => { + const renderLabel = () => label && return ( - { renderLabel() } - onChange(e.target.value)} + onChange={(e) => onChange(e.target.value)} onFocus={onFocus} onBlur={onBlur} - ref={ref => setRef(ref)} + ref={(ref) => setRef(ref)} {...props} /> @@ -131,7 +123,9 @@ const Input = ({ Input.defaultProps = { type: "text", label: "", - onChange: (text) => { console.error(`Missing onChange prop: ${text}`) }, + onChange: (text) => { + console.error(`Missing onChange prop: ${text}`) + }, onFocus: () => {}, onBlur: () => {}, setRef: () => {}, @@ -144,7 +138,7 @@ A few things going on here. Let's break it down in pieces. ### Styling -We're encompassing the component in a single styled `
        `. This `InputContainer` element will control the presentation of both the label and the input. We've specified the parent div to have `position: relative` and the label to have `position: absolute`. Doing so makes it easy to manipulate the location of label depending on how the user interacts with the input. +We're encompassing the component in a single styled `
        `. This `InputContainer` element will control the presentation of both the label and the input. We've specified the parent div to have `position: relative` and the label to have `position: absolute`. Doing so makes it easy to manipulate the location of label depending on how the user interacts with the input. We're also not including a placeholder. The label will serve as the placeholder until the user focuses on the input. This won't quite mimic the Plaid inputs, but that's ok. We'll get to that part shortly, anyway. @@ -162,7 +156,7 @@ Because we want our parent component to be able to hook into the `Input` element And we're left with an operational, albeit bland and not quite functional, custom input. - + ## Improving Our UX @@ -170,42 +164,33 @@ Try typing into our input. Not very pretty, is it? The label overlays the input ```javascript // ... other code -const Input = ({ - value, - type, - label, - onChange, - onFocus, - onBlur, - setRef, - ...props -}) => { +const Input = ({ value, type, label, onChange, onFocus, onBlur, setRef, ...props }) => { const [focused, setFocused] = React.useState(false) const handleOnFocus = () => { setFocused(true) onFocus() - } + } const handleOnBlur = () => { setFocused(false) onBlur() } - const renderLabel = () => label && + const renderLabel = () => label && const isFocused = focused || String(value).length || type === "date" return ( - { renderLabel() } - onChange(e.target.value)} + onChange={(e) => onChange(e.target.value)} onFocus={handleOnFocus} onBlur={handleOnBlur} - ref={ref => setRef(ref)} + ref={(ref) => setRef(ref)} {...props} /> @@ -219,15 +204,15 @@ Finally! React hooks! If you're underwhelmed, that's ok. That's really all there The general pattern is `const [state, setState] = React.useState(false)`. Easy enough, right? -If you want to learn more about hooks, check out the docs - they're fantastic. +If you want to learn more about hooks, check out the docs - they're fantastic. In our case, we're using a focused flag to determine whether or not the user has clicked on the input. Besides that, the main refactor we've done here is to wrap our `onFocus` and `onBlur` handlers so we can update the `