This post is the second in a series and was originally published on Dev.to.
Quick recap. My personal website sucks. I’m updating it using Gatsby and the Novela theme by narative, because it’s popular, and I’m basic.
FYI, I’m using a Mac, so there may be some Mac-specific commands.
Alright, part 2, customizing your site. Part 1 was very entry-level, very basic. Part 2 is much more involved. Part 2 is where you make your site your own. Let's not waste any more time. Let's get started.
Update your site metadata
- In Terminal, cd to your website directory
git checkout -b initial_customizations
Your terminal output should be something like the following.
1Switched to a new branch 'initial_customizations'
- Open
gatsby-config.js
in your code editor (I use Visual Studio Code)- Update your site metadata and save. Here’s mine.
1module.exports = {2 siteMetadata: {3 title: `thtmnisamnstr`,4 name: `thtmnisamnstr`,5 siteUrl: `https://thtmnisamnstr.com/`,6 description: `Gavin Johnson's personal site. Resume, blog posts, tech product marketing stuff, maybe some tech-y stuff too.`,7 hero: {8 heading: `Hi. I'm Gavin. My handle is usually thtmnisamnstr. This is my site.`,9 maxWidth: 652,10 },11 social: [12 {13 name: `twitter`,14 url: `https://twitter.com/gavinjtech`,15 },16 {17 name: `devto`,18 url: `https://dev.to/thtmnisamnstr`,19 },20 {21 name: `github`,22 url: `https://github.com/thtmnisamnstr`,23 },24 {25 name: `linkedin`,26 url: `https://www.linkedin.com/in/gavin-johnson/`,27 },28 {29 name: `instagram`,30 url: `https://www.instagram.com/thtmnisamnstr`,31 },32 ],33 },34 plugins: [35 {36 resolve: "@narative/gatsby-theme-novela",37 options: {38 contentPosts: "content/posts",39 contentAuthors: "content/authors",40 basePath: "/",41 authorsPage: true,42 sources: {43 local: true,44 },45 },46 },47 {48 resolve: `gatsby-plugin-manifest`,49 options: {50 name: `Novela by Narative`,51 short_name: `Novela`,52 start_url: `/`,53 background_color: `#fff`,54 theme_color: `#fff`,55 display: `standalone`,56 icon: `src/assets/favicon.png`,57 },58 },59 {60 resolve: `gatsby-plugin-netlify-cms`,61 options: {62 },63 },64 ],65};
I also changed the favicon,././src/assets/favicon.png
Add your author info
- Add your author info for your author page
- Create a new file under
./content/authors/
- Name it
authors.yml
and add yourname
,bio
,avatar
,featured
,social
parameters, and save. Here’s mine.
- Name it
- Create a new file under
1- name: Gavin Johnson2 bio: |3 Gavin is a programmer-turned-marketer with experience in IT, consulting, and marketing.4 He has been at New Relic since 2018, focusing his time and efforts on helping New Relic5 be more open and telling New Relic's open source story. Gavin started his career as a6 programmer and sys admin prior to attending business school at the University of Southern7 California. Following b-school, he worked for Deloitte Digital as a Senior Consultant and8 Manager, designing and delivering custom applications for clients. He then moved to AT&T,9 working as a Lead Product Marketing Manager for their digital video products. Gavin is an10 Oregon native but has made Los Angeles his home since 2009. He is a happily married,11 Brazilian Jiu Jitsu purple belt, film photographer, homebrewer, and former marathoner.12 shortBio: |13 Open Source marketing person at New Relic. Ex-AT&T marketer. Ex-Deloitte consultant. Ex-sys14 admin. (Sometimes)Ex-developer.15 avatar: ./avatars/gavin-johnson.jpg16 social:17 - url: https://twitter.com/gavinjtech18 - url: https://dev.to/thtmnisamnstr19 - url: https://github.com/thtmnisamnstr20 - url: https://www.linkedin.com/in/gavin-johnson/21 - url: https://www.instagram.com/thtmnisamnstr22 featured: true
- Create a new folder under
./content/authors/
. Name itavatars
. - Add your avatar image to
./content/authors/avatars/
. Name it[your-name].jpg
. Here’s mine.
- Update the existing example blog post to use your author info
- Open
./content/posts/2020-01-01-my-first-post-using-novela-by-narative/index.md
- Change the
author
parameter to [your name] and save. Here’s the first few lines of mine.
- Change the
- Open
1---2title: My first post using Novela by Narative3author: Gavin Johnson4date: 2019-04-305hero: ./images/hero.jpg6excerpt: With the growing community interest in Gatsby, we hope to create more resources that make it easier for anyone to grasp the power of this incredible tool.7---
- Delete the example author files
- Delete
./content/authors/authors/
and all files under it
- Delete
Change the site logo
- I’m changing the site logo to simple text by shadowing the Logo component.
- In your root directory,
mkdir -p './src/@narative/gatsby-theme-novela/components/Logo/'
cp './node_modules/@narative/gatsby-theme-novela/src/components/Logo/Logo.tsx' './src/@narative/gatsby-theme-novela/components/Logo/Logo.tsx'
- Open
./src/@narative/gatsby-theme-novela/components/Logo/Logo.tsx
- Update your SVG info and save. Here’s mine.
- In your root directory,
1import React from "react";2import styled from "@emotion/styled";34import mediaqueries from "@styles/media";56import { Icon } from '@types';78const Logo: Icon = ({ fill = "white" }) => {9 return (10 <LogoContainer>11 <svg12 width="192"13 height="23"14 viewBox="0 0 192 23"15 fill="none"16 xmlns="http://www.w3.org/2000/svg"17 className="Logo__Desktop"18 >19 {/* Gavin 20200719: Changed SVG logo to text */}20 <text x="0" y="11" dx fontFamily="Helvetica Neue" fontWeight="bold" textAnchor="start" fill={fill}>21 thtmnisamnstr22 </text>23 <defs>24 <clipPath id="clip0">25 <rect width="191.156" height="23" fill="white" />26 </clipPath>27 </defs>28 </svg>2930 <svg31 width="18"32 height="23"33 viewBox="0 0 18 23"34 fill="none"35 xmlns="http://www.w3.org/2000/svg"36 className="Logo__Mobile"37 >38 <text x="0" y="11" dx fontFamily="Helvetica Neue" fontWeight="bold" textAnchor="start" fill={fill}>39 thtmnisamnstr40 </text>41 </svg>42 </LogoContainer>43 );44};4546export default Logo;4748const LogoContainer = styled.div`49 .Logo__Mobile {50 display: none;51 }5253 ${mediaqueries.tablet`54 .Logo__Desktop {55 display: none;56 }5758 .Logo__Mobile{59 display: block;60 }61 `}62`;
- Run
gatsby develop
to see what your site looks like. Here’s mine.
I don’t love how big the bio text is. I want a short bio. So I’mma add a new key to the Authors YAML, shortBio
, and put that in the Featured Author section.
- Add shortBio to the authors YAML
- Open
/content/authors/authors.yml
- Add a short bio. Here’s my authors file.
- Open
1- name: Gavin Johnson2 bio: |3 Gavin is a programmer-turned-marketer with experience in IT, consulting, and marketing.4 He has been at New Relic since 2018, focusing his time and efforts on helping New Relic5 be more open and telling New Relic's open source story. Gavin started his career as a6 programmer and sys admin prior to attending business school at the University of Southern7 California. Following b-school, he worked for Deloitte Digital as a Senior Consultant and8 Manager, designing and delivering custom applications for clients. He then moved to AT&T,9 working as a Lead Product Marketing Manager for their digital video products. Gavin is an10 Oregon native but has made Los Angeles his home since 2009. He is a happily married,11 Brazilian Jiu Jitsu purple belt, film photographer, homebrewer, and former marathoner.12 shortBio: |13 Open Source marketing person at New Relic. Ex-AT&T marketer. Ex-Deloitte consultant. Ex-sys14 admin. (Sometimes)Ex-developer.15 avatar: ./avatars/gavin-johnson.jpg16 social:17 - url: https://twitter.com/gavinjtech18 - url: https://dev.to/thtmnisamnstr19 - url: https://github.com/thtmnisamnstr20 - url: https://www.linkedin.com/in/gavin-johnson/21 - url: https://www.instagram.com/thtmnisamnstr22 featured: true
Add a short bio to the Author markdown for use in the featured author component
- Shadow the Bio component,
Bio.tsx
- In your root directory,
mkdir -p './src/@narative/gatsby-theme-novela/components/Bio/'
cp './node_modules/@narative/gatsby-theme-novela/src/components/Bio/Bio.tsx' './src/@narative/gatsby-theme-novela/components/Bio/Bio.tsx'
- Open
./src/@narative/gatsby-theme-novela/components/Bio/Bio.tsx
- Change
author.bio
toauthor.shortBio
and save. Here’s mine.
- In your root directory,
1import React from 'react';2import { Link } from 'gatsby';3import styled from '@emotion/styled';45import Image from '@components/Image';6import { IAuthor } from '@types';78const Bio: React.FC<IAuthor> = ({ author }) => {9 return (10 <BioContainer>11 <BioAvatar12 as={author.authorsPage ? Link : 'div'}13 to={author.slug}14 data-a11y="false"15 aria-label="Author's bio"16 >17 <BioAvatarInner>18 <RoundedImage src={author.avatar.medium} />19 </BioAvatarInner>20 </BioAvatar>21 {/* Gavin, 20200719: Changed to shortBio */}22 <BioText dangerouslySetInnerHTML={{ __html: author.shortBio }} />23 </BioContainer>24 );25};2627export default Bio;2829const BioContainer = styled.div`30 display: flex;31 align-items: center;32 position: relative;33 left: -10px;34`;3536const BioAvatar = styled.div`37 display: block;38 position: relative;39 height: 40px;40 width: 40px;41 border-radius: 50%;42 background: rgba(0, 0, 0, 0.25);43 margin-right: 16px;44 margin: 10px 26px 10px 10px;4546 &::after {47 content: '';48 position: absolute;49 left: -5px;50 top: -5px;51 width: 50px;52 height: 50px;53 border-radius: 50%;54 border: 1px solid rgba(0, 0, 0, 0.25);55 }5657 &[data-a11y='true']:focus::after {58 content: '';59 position: absolute;60 left: -5px;61 top: -5px;62 width: 50px;63 height: 50px;64 border: 2px solid ${p => p.theme.colors.accent};65 }66`;6768const RoundedImage = styled(Image)`69 border-radius: 50%;70`;7172const BioAvatarInner = styled.div`73 height: 40px;74 width: 40px;75 border-radius: 50%;76 background: rgba(0, 0, 0, 0.25);77 margin-right: 16px;78 overflow: hidden;79`;8081const BioText = styled.p`82 max-width: 430px;83 font-size: 14px;84 line-height: 1.45;85 color: ${p => p.theme.colors.grey};8687 a {88 color: ${p => p.theme.colors.grey};89 text-decoration: underline;90 }91`;
- Shadow the
IAuthor
interface inindex.d.ts
- In your root directory,
mkdir -p './src/@narative/gatsby-theme-novela/types/'
cp './node_modules/@narative/gatsby-theme-novela/src/types/index.d.ts' './src/@narative/gatsby-theme-novela/types/index.d.ts'
- Open
./src/@narative/gatsby-theme-novela/types/index.d.ts
- Add
shortBio
to yourIAuthor
interface and save. Here’s mine.
- In your root directory,
1import React from "react";23export interface IPaginator {4 pageCount: number;5 index: number;6 pathPrefix: string;7}89interface IGatsbyImage {10 src: string;11 base64?: string;12 srcWebp?: string;13 srcSet?: string;14 srcSetWebp?: string;15 tracedSVG?: string;16}1718interface IGatsbyImageFluid extends IGatsbyImage {19 maxHeight: number;20 maxWidth: number;21}2223interface IGatsbyImageFixed extends IGatsbyImage {24 height: number;25 width: number;26}2728export interface IAuthor {29 authorsPage?: boolean;30 featured?: boolean;31 name: string;32 slug: string;33 bio: string;34 // Gavin, 20200719: Added shortBio35 shortBio: string;36 avatar: {37 image: IGatsbyImageFluid;38 full: IGatsbyImageFluid;39 };40}4142export interface IArticle {43 slug: string;44 authors: IAuthor[];45 excerpt: string;46 body: string;47 id: string;48 hero: {49 full: IGatsbyImageFluid;50 preview: IGatsbyImageFluid;51 regular: IGatsbyImageFluid;52 seo: string;53 };54 timeToRead: number;55 date: string;56 secret: boolean;57}5859interface IArticleQuery {60 edges: {61 node: IArticle;62 }[];63}6465export interface IProgress {66 height: number;67 offset: number;68 title: string;69 mode: string;70 onClose?: () => void;71}7273export type Icon = React.FC<{74 fill: string75}>7677export type Template = React.FC<{78 pageContext: {79 article: IArticle;80 authors: IAuthor[];81 mailchimp: boolean;82 next: IArticle[];83 };84 location: Location;85}>
- The correct thing to do next would be to shadow
data.query.js
and add theshortBio
field, but that isn’t working for some reason. So let’s do it directly in the Novela theme module.- Open
./node_modules/@narative/gatsby-theme-novela/src/gatsby/data/data.query.js
- Add
shortBio
to your author queries and save. Here’s mine.
- Open
1/* eslint-disable */23// https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-transformer-sharp/src/fragments.js45const GatsbyFluid_withWebp = `6 base647 aspectRatio8 src9 srcSet10 srcWebp11 srcSetWebp12 sizes13`;1415module.exports.local = {16 articles: `{17 articles: allArticle(18 sort: { fields: [date, title], order: DESC }19 limit: 100020 ) {21 edges {22 node {23 id24 slug25 secret26 title27 author28 date(formatString: "MMMM Do, YYYY")29 dateForSEO: date30 timeToRead31 excerpt32 canonical_url33 subscription34 body35 hero {36 full: childImageSharp {37 fluid(maxWidth: 944, quality: 100) {38 ${GatsbyFluid_withWebp}39 }40 }41 regular: childImageSharp {42 fluid(maxWidth: 653, quality: 100) {43 ${GatsbyFluid_withWebp}44 }45 }46 narrow: childImageSharp {47 fluid(maxWidth: 457, quality: 100) {48 ${GatsbyFluid_withWebp}49 }50 }51 seo: childImageSharp {52 fixed(width: 1200, quality: 100) {53 src54 }55 }56 }57 }58 }59 }60 }`,61 authors: `{62 authors: allAuthor {63 edges {64 node {65 authorsPage66 bio67 shortBio68 id69 name70 featured71 social {72 url73 }74 slug75 avatar {76 small: childImageSharp {77 fluid(maxWidth: 50, quality: 100) {78 ${GatsbyFluid_withWebp}79 }80 }81 medium: childImageSharp {82 fluid(maxWidth: 100, quality: 100) {83 ${GatsbyFluid_withWebp}84 }85 }86 large: childImageSharp {87 fluid(maxWidth: 328, quality: 100) {88 ${GatsbyFluid_withWebp}89 }90 }91 }92 }93 }94 }95 }`,96};9798module.exports.contentful = {99 articles: `{100 articles: allContentfulArticle(sort: {fields: [date, title], order: DESC}, limit: 1000) {101 edges {102 node {103 body {104 childMdx {105 body106 timeToRead107 }108 }109 excerpt110 title111 slug112 secret113 date(formatString: "MMMM Do, YYYY")114 dateForSEO: date115 hero {116 full: fluid(maxWidth: 944, quality: 100) {117 ${GatsbyFluid_withWebp}118 }119 regular: fluid(maxWidth: 653, quality: 100) {120 ${GatsbyFluid_withWebp}121 }122 narrow: fluid(maxWidth: 457, quality: 100) {123 ${GatsbyFluid_withWebp}124 }125 seo: fixed(width: 1200, quality: 100) {126 src127 }128 }129 id130 author {131 name132 }133 }134 }135 }136 }137 `,138 authors: `{139 authors: allContentfulAuthor {140 edges {141 node {142 avatar {143 small: fluid(maxWidth: 50, quality: 100) {144 ${GatsbyFluid_withWebp}145 }146 medium: fluid(maxWidth: 100, quality: 100) {147 ${GatsbyFluid_withWebp}148 }149 large: fluid(maxWidth: 328, quality: 100) {150 ${GatsbyFluid_withWebp}151 }152 }153 fields {154 authorsPage155 slug156 }157 bio158 shortBio159 id160 name161 social162 featured163 }164 }165 }166 }`,167};
- Because
data.query.js
couldn’t be shadowed, add the Novela theme undernode_modules
to your git repo- Open
./.gitignore
- Update to include the Novela theme folder in your repo and save. Here’s mine.
- Open
1# Logs2logs3*.log4npm-debug.log*5yarn-debug.log*6yarn-error.log*78# Runtime data9pids10*.pid11*.seed12*.pid.lock1314# Directory for instrumented libs generated by jscoverage/JSCover15lib-cov1617# Coverage directory used by tools like istanbul18coverage1920# nyc test coverage21.nyc_output2223# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)24.grunt2526# Bower dependency directory (https://bower.io/)27bower_components2829# node-waf configuration30.lock-wscript3132# Compiled binary addons (http://nodejs.org/api/addons.html)33build/Release3435# Dependency directories36jspm_packages/37# Gavin, 20200719: Including the Novela theme directory under node_modules38node_modules/*39!node_modules/@narative/40node_modules/@narative/*41!node_modules/@narative/gatsby-theme-novela/4243# Typescript v1 declaration files44typings/4546# Optional npm cache directory47.npm4849# Optional eslint cache50.eslintcache5152# Optional REPL history53.node_repl_history5455# Output of 'npm pack'56*.tgz5758# dotenv environment variables file59.env6061# gatsby files62.cache/63public6465# Mac files66.DS_Store6768# Yarn69yarn-error.log70.pnp/71.pnp.js72# Yarn Integrity file73.yarn-integrity7475.netlify/
Run
gatsby develop
to see what your site looks like. Here’s mine.Click on the author avatar to go to the Author page. Note that it still uses the standard
bio
field. This is what I was aiming for. Here’s mine.
Add a resume page and navigation
I also want a resume page as well. I could go and create a new content type, update the query to include it, add it to createPages
, …, or I could create a static page, but I just want a resume I can write in markdown and direct-link to. Plus, I’m trying to learn Gatsby anyway, so I’m going to make a resume page by making a secret post that includes a slug.
- Create a secret post for the resume page
mkdir -p './content/posts/SECRET-20200719-Resume/images/'
touch './content/posts/SECRET-20200719-Resume/index.md'
- Open
./content/posts/SECRET-20200719-Resume/index.md
- Write your resume and save make sure you set
secret: true
andslug: [first]-[last]-resume
. Here’s mine.
1---2title: Resume3author: Gavin Johnson4date: 2020-07-195hero:6secret: true7slug: gavin-johnson-resume8---910*Experienced marketer and strategist with a record of approaching business questions through an analytical lens and driving action through that analysis.*11* *Data analysis and data driven decision making*12* *Marketing strategy and content development*13* *Executive presentation and public speaking*14* *Technical background, both as a dev and dev lead*1516## Experience1718### New Relic *-- Los Angeles, CA (Remote)*19#### Principal Product Marketing Manager, Open Source *-- May 2020-Present*20Led go-to-market strategy and execution for [New Relic Open Source](https://opensource.newrelic.com) and for [New Relic's open source instrumentation](https://blog.newrelic.com/product-news/introducing-open-source-agents-and-projects/).21#### Principal Solutions Marketing Manager, Digital Customer Experience (now Digital Experience Management) *-- November 2018-May 2020*22Built the strategy for New Relic’s Digital Customer (DCX) solution and executed on it. Led creation of DCX Marketing and Sales content. Integrated DCX into New Relic’s campaigns and tactics. Represented New Relic as an external speaker.23* Built New Relic’s DCX solution strategy. Analyzed internal and external content developed for and tactics previously used by other New Relic solutions. Performed competitive analysis. Developed a DCX content and tactics roadmap and calendar.24* Wrote New Relic’s DCX solution positioning and messaging. Described how we wanted the DCX solution perceived by the market, who our targeted personas were, and wrote taglines, copy blocks, and email templates for use by Marketing and Sales.25* Created DCX solution content for Sales. Collaborated with the solution consulting group from Sales to build industry specific DCX content (e.g. pitch decks, solution guide decks, industry primers, discovery questions, objection handling, etc.). Targeted four industries: Media, Retail, FinServ, and Travel.26* Integrated DCX solution into Marketing campaigns and tactics. Worked cross-functionally with demand generation, content and creative, digital marketing, and field marketing. Led creation of and co-wrote content (e.g. articles, solution sheets, white papers, etc.) and landing pages. Developed an event response campaign and cadence.27* Represented New Relic as an external speaker. Presented on the importance of DCX for modern, digital businesses and connecting technical application performance to business outcomes. Presented at New Relic’s FutureStack Los Angeles 2019, WP Engine Summit 2019, and Gartner IT Symposium Xpo 2019.2829### AT&T Mobility & Entertainment *-- El Segundo, CA*30#### Lead Product Marketing Manager, Video Lifecycle Strategy *-- June 2017-November 2018*31Built analytical models and business cases for strategic project analysis. Managed enablement projects for product go-to-market, ensuring ancillary systems and processes worked correctly for customers.32* Built the analytical model and business case that got Netflix on AT&T TV’s upcoming streaming box. Built analytical scenario model comparing multiple levels of integration with Netflix. Collaborated across teams in Marketing and Corporate Strategy to align on assumptions, key takeaways, and to build the executive presentation deck.33* Developed product sunsetting strategy for AT&T’s IPTV service. Built a yearly profitability model for the IPTV service. Used existing subscriber and revenue as well as churn and churn acceleration projections to project revenue 5 years forward. Collaborated with engineering to build a 5-year IPTV cost projection model.34* Managed product enablement projects. Insured customer-impacting issues did not occur because of internal, cross-team coordination issues. (e.g. Got a customer authentication feature for AT&T TV’s upcoming streaming box in the product backlog, engaged a 3rd party to provide at-home delivery for AT&T TV’s upcoming streaming box, etc.).35* Built profitability models, business cases, and recommendations for VP and SVP-initiated innovation proposals (e.g. TV service for Cricket Mobile customers, a hyperlocal video service, etc.).3637### Deloitte Digital / Deloitte Consulting (*Los Angeles, CA*)38#### Manager, Consulting *-- August 2016-June 2017*39Trusted partner to clients, consulting at Fox for ~2 years. Led teams to create, expand, and maintain custom-built business applications for a diverse set of companies, from startup (Human Longevity Inc.) to Fortune 10 (AT&T).40* Led expansion and maintenance of Fox Theatrical’s global release planning application. Managed two different teams of onshore and offshore consultants and developers through two major expansion projects. Led a cross-functional teams of marketing users, IT leadership, and our consultants through the entire SDLC for each project.41* Served as a trusted client partner. Traveled with my primary client at Fox to Europe and Asia to introduce Global Marketing to major updates of the global release planning application and promote application use in non-Domestic regions. Also used these trips to gather first-hand feedback on the application that informed future features and fixes.42* Recognized with “Extraordinary Moments” award from company leadership for being a company-wide top performer at my level multiple years consecutively.43#### Senior Consultant *-- July 2014-August 2016*44* Managed tech consulting projects for a variety of companies. Led teams of business analysts, consultants, and developers through project design and delivery (e.g. Led a team through development of a digital marketing transformation roadmap for a major grocery wholesaler, led a team through the deployment of a tailored Salesforce CRM application for a biotech startup, etc.).45#### Summer Associate (*June 2013-August 2013*)46* Defined program management processes and quality management plan for a multi-project, customer service enhancement program at T-Mobile. Established PMO reporting templates and cadence.4748### National Technical Systems *-- Calabasas, CA*49#### Microsoft Dynamics ERP Administrator *-- June 2009-July 2012*50Led a cross-company ERP implementation serving 600+ business users at 20+ facilities. Managed 2 internal developers and 7 consultants through design, development, and delivery of system customizations.51* Led cross-functional team of business users from finance, sales, marketing, operations, and IT; developers; and consultants to consolidate fragmented CRM, sales, and financial systems into a single ERP system.52* Owned yearly project CapEx budget for the ERP system. Worked with CIO to build yearly CapEx requests. Managed hardware purchases, software licensing, 3rd party add-on licensing, and consulting costs.5354### Timber Products Company *-- Springfield, OR*55#### Computer Programmer / Analyst *-- June 2007-June 2009*56Collaborated with business users from inventory management, sales, logistics, accounting, and finance to design, development, and delivery of enhancements to the company’s Microsoft Dynamics ERP system.5758### ViewPlus Technologies *-- Corvallis, OR*59#### Product Development Assistant *-- November 2004-June 2007*60Developed testing protocols for and tested Braille printer drivers and firmware for release. Developed a text-to-Braille application for release.6162## Education6364### University of Southern California65#### MBA, Masters of Business Administration *-- Graduated May 2014*66* Beta Gamma Sigma, Business Honor Society.67* Program Lead, Marshall Case Competition Program.68 * Trained fellow MBA candidates on how to compete in case competitions, and coached case competition teams. Program achieved 89% participation rate for the class of 2015.69* Asia-Pacific Economic Cooperation (APEC), Business Advisory Council Research Project. Foreign Direct Investment across APEC: Impediments and Opportunities for Improvement.70 * Large, 12-person, MBA research project sponsored by and presented to APEC.71 * Led data analysis team. Sourced and sanitized data, built SQL database, developed data analysis methodology, and led team of 3 MBA candidates though creation of country data analysis slides.72* Director of Technology Consulting, Marshall Consulting & Strategy Club.73* Business of Entertainment Graduate Certificate from the USC School of Cinematic Arts.7475### Oregon State University76#### BS, Computer Science *-- Graduated June 2007*77* Cum Laude.78* Upsilon Pi Epsilon, Computer Science Honor Society.79Other
I also want to change the default behavior of posts to not have a hero image if one isn’t defined, like in the resume post.
- Shadow the
Article.Hero.tsx
section component- In your root directory,
mkdir -p './src/@narative/gatsby-theme-novela/sections/article/'
cp -R './node_modules/@narative/gatsby-theme-novela/src/sections/article/' './src/@narative/gatsby-theme-novela/sections/article/'
- In your root directory,
Yes, you have to shadow all the damn files in the folder even though you really only want to change one.”
- Open ‘./src/@narative/gatsby-theme-novela/sections/article/Article.Hero.tsx’
- Update to not render hero image if none is specified and save. Here’s mine.
1import React from 'react';2import styled from '@emotion/styled';34import Headings from '@components/Headings';5import Image, { ImagePlaceholder } from '@components/Image';67import mediaqueries from '@styles/media';8import { IArticle, IAuthor } from '@types';910import ArticleAuthors from './Article.Authors';1112interface ArticleHeroProps {13 article: IArticle;14 authors: IAuthor[];15}1617const ArticleHero: React.FC<ArticleHeroProps> = ({ article, authors }) => {18 const hasCoAUthors = authors.length > 1;19 const hasHeroImage =20 article.hero &&21 Object.keys(article.hero.full).length !== 0 &&22 article.hero.full.constructor === Object;2324 return (25 <Hero>26 <Header>27 <HeroHeading>{article.title}</HeroHeading>28 <HeroSubtitle hasCoAUthors={hasCoAUthors}>29 <ArticleAuthors authors={authors} />30 <ArticleMeta hasCoAUthors={hasCoAUthors}>31 {article.date} · {article.timeToRead} min read32 </ArticleMeta>33 </HeroSubtitle>34 </Header>35 <HeroImage id="ArticleImage__Hero">36 {hasHeroImage && (37 <Image src={article.hero.full} />38 )}39 </HeroImage>40 </Hero>41 );42};4344export default ArticleHero;4546const Hero = styled.div`47 ${p => mediaqueries.phablet`48 &::before {49 content: "";50 width: 100%;51 height: 20px;52 background: ${p.theme.colors.primary};53 position: absolute;54 left: 0;55 top: 0;56 transition: ${p.theme.colorModeTransition};57 }5859 &::after {60 content: "";61 width: 100%;62 height: 10px;63 background: ${p.theme.colors.background};64 position: absolute;65 left: 0;66 top: 10px;67 border-top-left-radius: 25px;68 border-top-right-radius: 25px;69 transition: ${p.theme.colorModeTransition};70 }71 `}72`;7374const ArticleMeta = styled.div<{ hasCoAUthors: boolean }>`75 margin-left: ${p => (p.hasCoAUthors ? '10px' : '0')};7677 ${mediaqueries.phablet`78 margin-left: 0;79 `}80`;8182/* Gavin 20200722: Reduced bottom margin on the Header because it looked too83 big w/ no hero */84const Header = styled.header`85 position: relative;86 z-index: 10;87 margin:50px auto 120px;88 padding-left: 68px;89 max-width: 749px;9091 ${mediaqueries.desktop`92 padding-left: 53px;93 max-width: calc(507px + 53px);94 margin: 50px auto 70px;95 `}9697 ${mediaqueries.tablet`98 padding-left: 0;99 margin: 50px auto 70px;100 max-width: 480px;101 `}102103 ${mediaqueries.phablet`104 margin: 50px auto 180px;105 padding: 0 40px;106 `}107108 @media screen and (max-height: 700px) {109 margin: 50px auto;110 }111`;112113const HeroHeading = styled(Headings.h1)`114 font-size: 48px;115 font-family: ${p => p.theme.fonts.serif};116 margin-bottom: 25px;117 font-weight: bold;118 line-height: 1.32;119120 ${mediaqueries.tablet`121 margin-bottom: 20px;122 font-size: 36px;123 `}124125 ${mediaqueries.phablet`126 font-size: 32px;127 `}128`;129130const HeroSubtitle = styled.div<{ hasCoAUthors: boolean }>`131 position: relative;132 display: flex;133 font-size: 18px;134 color: ${p => p.theme.colors.grey};135136 ${p => mediaqueries.phablet`137 font-size: 14px;138 flex-direction: column;139140 ${p.hasCoAUthors &&141 `142 &::before {143 content: '';144 position: absolute;145 left: -20px;146 right: -20px;147 top: -10px;148 bottom: -10px;149 border: 1px solid ${p.theme.colors.horizontalRule};150 opacity: 0.5;151 border-radius: 5px;152 }153 `}154155156 strong {157 display: block;158 font-weight: 500;159 margin-bottom: 5px;160 }161 `}162`;163164/* Gavin 20200724: Changed from 'height: 220px' to 'max-width: 100%' for phablet */165const HeroImage = styled.div`166 position: relative;167 z-index: 1;168 width: 100%;169 max-width: 944px;170 overflow: hidden;171 margin: 0 auto;172 box-shadow: 0 30px 60px -10px rgba(0, 0, 0, 0.2),173 0 18px 36px -18px rgba(0, 0, 0, 0.22);174175 ${mediaqueries.tablet`176 max-width: 100%;177 `}178179 ${mediaqueries.phablet`180 margin: 0 auto;181 max-width: 100%;182183 & > div {184 max-width: 100%;185 }186`}187`;
Now I need to create a link to the resume in the navigation. I’m throwing in a Home L1 as well, because it looks weird without one.
- Shadow
Navigation.Header.tsx
component- In your root directory,
mkdir -p './src/@narative/gatsby-theme-novela/components/Navigation/'
cp './node_modules/@narative/gatsby-theme-novela/src/components/Navigation/Navigation.Header.tsx' './src/@narative/gatsby-theme-novela/components/Navigation/Navigation.Header.tsx'
- Open
./src/@narative/gatsby-theme-novela/components/Navigation/Navigation.Header.tsx
- Hardcode and style (I know, I’m lazy) the navigation links and save. Here’s mine.
- In your root directory,
1import React, { useState, useEffect } from "react";2import styled from "@emotion/styled";3import { Link, navigate, graphql, useStaticQuery } from "gatsby";4import { useColorMode } from "theme-ui";56import Section from "@components/Section";7import Logo from "@components/Logo";89import Icons from "@icons";10import mediaqueries from "@styles/media";11import {12 copyToClipboard,13 getWindowDimensions,14 getBreakpointFromTheme,15} from "@utils";1617const siteQuery = graphql`18 {19 sitePlugin(name: { eq: "@narative/gatsby-theme-novela" }) {20 pluginOptions {21 rootPath22 basePath23 }24 }25 }26`;2728const DarkModeToggle: React.FC<{}> = () => {29 const [colorMode, setColorMode] = useColorMode();30 const isDark = colorMode === `dark`;3132 function toggleColorMode(event) {33 event.preventDefault();34 setColorMode(isDark ? `light` : `dark`);35 }3637 return (38 <IconWrapper39 isDark={isDark}40 onClick={toggleColorMode}41 data-a11y="false"42 aria-label={isDark ? "Activate light mode" : "Activate dark mode"}43 title={isDark ? "Activate light mode" : "Activate dark mode"}44 >45 <MoonOrSun isDark={isDark} />46 <MoonMask isDark={isDark} />47 </IconWrapper>48 );49};5051const SharePageButton: React.FC<{}> = () => {52 const [hasCopied, setHasCopied] = useState<boolean>(false);53 const [colorMode] = useColorMode();54 const isDark = colorMode === `dark`;55 const fill = isDark ? "#fff" : "#000";5657 function copyToClipboardOnClick() {58 if (hasCopied) return;5960 copyToClipboard(window.location.href);61 setHasCopied(true);6263 setTimeout(() => {64 setHasCopied(false);65 }, 1000);66 }6768 return (69 <IconWrapper70 isDark={isDark}71 onClick={copyToClipboardOnClick}72 data-a11y="false"73 aria-label="Copy URL to clipboard"74 title="Copy URL to clipboard"75 >76 <Icons.Link fill={fill} />77 <ToolTip isDark={isDark} hasCopied={hasCopied}>78 Copied79 </ToolTip>80 </IconWrapper>81 );82};8384const NavigationHeader: React.FC<{}> = () => {85 const [showBackArrow, setShowBackArrow] = useState<boolean>(false);86 const [previousPath, setPreviousPath] = useState<string>("/");87 const { sitePlugin } = useStaticQuery(siteQuery);8889 const [colorMode] = useColorMode();90 const fill = colorMode === "dark" ? "#fff" : "#000";91 const { rootPath, basePath } = sitePlugin.pluginOptions;9293 useEffect(() => {94 const { width } = getWindowDimensions();95 const phablet = getBreakpointFromTheme("phablet");9697 const prev = localStorage.getItem("previousPath");98 const previousPathWasHomepage =99 prev === (rootPath || basePath) || (prev && prev.includes("/page/"));100 const currentPathIsHomepage =101 location.pathname === (rootPath || basePath) || location.pathname.includes("/page/");102103 setShowBackArrow(104 previousPathWasHomepage && !currentPathIsHomepage && width <= phablet,105 );106 setPreviousPath(prev);107 }, []);108109 return (110 <Section>111 <NavContainer>112 <LogoLink113 to={rootPath || basePath}114 data-a11y="false"115 title="Navigate back to the homepage"116 aria-label="Navigate back to the homepage"117 back={showBackArrow ? "true" : "false"}118 >119 {showBackArrow && (120 <BackArrowIconContainer>121 <Icons.ChevronLeft fill={fill} />122 </BackArrowIconContainer>123 )}124 <Logo fill={fill} />125 <Hidden>Navigate back to the homepage</Hidden>126 {/* Gavin 20200719: Lazy ass navigation links */}127 <div style={{marginLeft: '25px', marginRight: '25px'}}><Link to="/" style={{fontWeight: 'bolder', color: fill}}>Home</Link></div>128 <div style={{marginLeft: '25px', marginRight: '25px'}}><Link to="/gavin-johnson-resume" style={{fontWeight: 'bolder', color: fill}}>Resume</Link></div>129 </LogoLink>130 <NavControls>131 {showBackArrow ? (132 <button133 onClick={() => navigate(previousPath)}134 title="Navigate back to the homepage"135 aria-label="Navigate back to the homepage"136 >137 <Icons.Ex fill={fill} />138 </button>139 ) : (140 <>141 <SharePageButton />142 <DarkModeToggle />143 </>144 )}145 </NavControls>146 </NavContainer>147 </Section>148 );149};150151export default NavigationHeader;152153const BackArrowIconContainer = styled.div`154 transition: 0.2s transform var(--ease-out-quad);155 opacity: 0;156 padding-right: 30px;157 animation: fadein 0.3s linear forwards;158159 @keyframes fadein {160 to {161 opacity: 1;162 }163 }164165 ${mediaqueries.desktop_medium`166 display: none;167 `}168`;169170const NavContainer = styled.div`171 position: relative;172 z-index: 100;173 padding-top: 100px;174 display: flex;175 justify-content: space-between;176177 ${mediaqueries.desktop_medium`178 padding-top: 50px;179 `};180181 @media screen and (max-height: 800px) {182 padding-top: 50px;183 }184`;185186const LogoLink = styled(Link)<{ back: string }>`187 position: relative;188 display: flex;189 align-items: center;190 left: ${p => (p.back === "true" ? "-54px" : 0)};191192 ${mediaqueries.desktop_medium`193 left: 0194 `}195196 &[data-a11y="true"]:focus::after {197 content: "";198 position: absolute;199 left: -10%;200 top: -30%;201 width: 120%;202 height: 160%;203 border: 2px solid ${p => p.theme.colors.accent};204 background: rgba(255, 255, 255, 0.01);205 border-radius: 5px;206 }207208 &:hover {209 ${BackArrowIconContainer} {210 transform: translateX(-3px);211 }212 }213`;214215const NavControls = styled.div`216 position: relative;217 display: flex;218 align-items: center;219220 ${mediaqueries.phablet`221 right: -5px;222 `}223`;224225const ToolTip = styled.div<{ isDark: boolean; hasCopied: boolean }>`226 position: absolute;227 padding: 4px 13px;228 background: ${p => (p.isDark ? "#000" : "rgba(0,0,0,0.1)")};229 color: ${p => (p.isDark ? "#fff" : "#000")};230 border-radius: 5px;231 font-size: 14px;232 top: -35px;233 opacity: ${p => (p.hasCopied ? 1 : 0)};234 transform: ${p => (p.hasCopied ? "translateY(-3px)" : "none")};235 transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;236237 &::after {238 content: "";239 position: absolute;240 left: 0;241 right: 0;242 bottom: -6px;243 margin: 0 auto;244 width: 0;245 height: 0;246 border-left: 6px solid transparent;247 border-right: 6px solid transparent;248 border-top: 6px solid ${p => (p.isDark ? "#000" : "rgba(0,0,0,0.1)")};249 }250`;251252const IconWrapper = styled.button<{ isDark: boolean }>`253 opacity: 0.5;254 position: relative;255 border-radius: 5px;256 width: 40px;257 height: 25px;258 display: flex;259 align-items: center;260 justify-content: center;261 transition: opacity 0.3s ease;262 margin-left: 30px;263264 &:hover {265 opacity: 1;266 }267268 &[data-a11y="true"]:focus::after {269 content: "";270 position: absolute;271 left: 0;272 top: -30%;273 width: 100%;274 height: 160%;275 border: 2px solid ${p => p.theme.colors.accent};276 background: rgba(255, 255, 255, 0.01);277 border-radius: 5px;278 }279280 ${mediaqueries.tablet`281 display: inline-flex;282 transform: scale(0.708);283 margin-left: 10px;284285286 &:hover {287 opacity: 0.5;288 }289 `}290`;291292// This is based off a codepen! Much appreciated to: https://codepen.io/aaroniker/pen/KGpXZo293const MoonOrSun = styled.div<{ isDark: boolean }>`294 position: relative;295 width: 24px;296 height: 24px;297 border-radius: 50%;298 border: ${p => (p.isDark ? "4px" : "2px")} solid299 ${p => p.theme.colors.primary};300 background: ${p => p.theme.colors.primary};301 transform: scale(${p => (p.isDark ? 0.55 : 1)});302 transition: all 0.45s ease;303 overflow: ${p => (p.isDark ? "visible" : "hidden")};304305 &::before {306 content: "";307 position: absolute;308 right: -9px;309 top: -9px;310 height: 24px;311 width: 24px;312 border: 2px solid ${p => p.theme.colors.primary};313 border-radius: 50%;314 transform: translate(${p => (p.isDark ? "14px, -14px" : "0, 0")});315 opacity: ${p => (p.isDark ? 0 : 1)};316 transition: transform 0.45s ease;317 }318319 &::after {320 content: "";321 width: 8px;322 height: 8px;323 border-radius: 50%;324 margin: -4px 0 0 -4px;325 position: absolute;326 top: 50%;327 left: 50%;328 box-shadow: 0 -23px 0 ${p => p.theme.colors.primary},329 0 23px 0 ${p => p.theme.colors.primary},330 23px 0 0 ${p => p.theme.colors.primary},331 -23px 0 0 ${p => p.theme.colors.primary},332 15px 15px 0 ${p => p.theme.colors.primary},333 -15px 15px 0 ${p => p.theme.colors.primary},334 15px -15px 0 ${p => p.theme.colors.primary},335 -15px -15px 0 ${p => p.theme.colors.primary};336 transform: scale(${p => (p.isDark ? 1 : 0)});337 transition: all 0.35s ease;338339 ${p => mediaqueries.tablet`340 transform: scale(${p.isDark ? 0.92 : 0});341 `}342 }343`;344345const MoonMask = styled.div<{ isDark: boolean }>`346 position: absolute;347 right: -1px;348 top: -8px;349 height: 24px;350 width: 24px;351 border-radius: 50%;352 border: 0;353 background: ${p => p.theme.colors.background};354 transform: translate(${p => (p.isDark ? "14px, -14px" : "0, 0")});355 opacity: ${p => (p.isDark ? 0 : 1)};356 transition: ${p => p.theme.colorModeTransition}, transform 0.45s ease;357`;358359const Hidden = styled.span`360 position: absolute;361 display: inline-block;362 opacity: 0;363 width: 0px;364 height: 0px;365 visibility: hidden;366 overflow: hidden;367`;
Run
gatsby develop
to see what your site looks like. Here’s mine.Click on the Resume L1 menu item to go to the Resume page. Here’s mine.
Now that we’ve gotten rid of the hero image, there is a gap between the author info and the resume body that I don’t like. So let’s fix that.
- Shadow the
article.template.tsx
template- In your root directory,
mkdir -p './src/@narative/gatsby-theme-novela/templates/'
cp './node_modules/@narative/gatsby-theme-novela/src/templates/article.template.tsx' './src/@narative/gatsby-theme-novela/templates/article.template.tsx'
- Open ‘./src/@narative/gatsby-theme-novela/templates/article.template.tsx’
- Update the top padding on
ArticleBody
and save. Here’s mine.
- In your root directory,
1import React, { useRef, useState, useEffect } from "react";2import styled from "@emotion/styled";3import throttle from "lodash/throttle";4import { graphql, useStaticQuery } from "gatsby";56import Layout from "@components/Layout";7import MDXRenderer from "@components/MDX";8import Progress from "@components/Progress";9import Section from "@components/Section";10import Subscription from "@components/Subscription";1112import mediaqueries from "@styles/media";13import { debounce } from "@utils";1415import ArticleAside from "../sections/article/Article.Aside";16import ArticleHero from "../sections/article/Article.Hero";17import ArticleControls from "../sections/article/Article.Controls";18import ArticlesNext from "../sections/article/Article.Next";19import ArticleSEO from "../sections/article/Article.SEO";20import ArticleShare from "../sections/article/Article.Share";2122import { Template } from "@types";2324const siteQuery = graphql`25 {26 allSite {27 edges {28 node {29 siteMetadata {30 name31 }32 }33 }34 }35 }36`;3738const Article: Template = ({ pageContext, location }) => {39 const contentSectionRef = useRef<HTMLElement>(null);4041 const [hasCalculated, setHasCalculated] = useState<boolean>(false);42 const [contentHeight, setContentHeight] = useState<number>(0);4344 const results = useStaticQuery(siteQuery);45 const name = results.allSite.edges[0].node.siteMetadata.name;4647 const { article, authors, mailchimp, next } = pageContext;4849 useEffect(() => {50 const calculateBodySize = throttle(() => {51 const contentSection = contentSectionRef.current;5253 if (!contentSection) return;5455 /**56 * If we haven't checked the content's height before,57 * we want to add listeners to the content area's58 * imagery to recheck when it's loaded59 */60 if (!hasCalculated) {61 const debouncedCalculation = debounce(calculateBodySize);62 const $imgs = contentSection.querySelectorAll("img");6364 $imgs.forEach($img => {65 // If the image hasn't finished loading then add a listener66 if (!$img.complete) $img.onload = debouncedCalculation;67 });6869 // Prevent rerun of the listener attachment70 setHasCalculated(true);71 }7273 // Set the height and offset of the content area74 setContentHeight(contentSection.getBoundingClientRect().height);75 }, 20);7677 calculateBodySize();78 window.addEventListener("resize", calculateBodySize);7980 return () => window.removeEventListener("resize", calculateBodySize);81 }, []);8283 return (84 <Layout>85 <ArticleSEO article={article} authors={authors} location={location} />86 <ArticleHero article={article} authors={authors} />87 <ArticleAside contentHeight={contentHeight}>88 <Progress contentHeight={contentHeight} />89 </ArticleAside>90 <MobileControls>91 <ArticleControls />92 </MobileControls>93 <ArticleBody ref={contentSectionRef}>94 <MDXRenderer content={article.body}>95 <ArticleShare />96 </MDXRenderer>97 </ArticleBody>98 {mailchimp && article.subscription && <Subscription />}99 {next.length > 0 && (100 <NextArticle narrow>101 <FooterNext>More articles from {name}</FooterNext>102 <ArticlesNext articles={next} />103 <FooterSpacer />104 </NextArticle>105 )}106 </Layout>107 );108};109110export default Article;111112const MobileControls = styled.div`113 position: relative;114 padding-top: 60px;115 transition: background 0.2s linear;116 text-align: center;117118 ${mediaqueries.tablet_up`119 display: none;120 `}121`;122123/* Gavin 20200722: Reduced top padding on the ArticleBody because it looked too124 big w/ no hero */125const ArticleBody = styled.article`126 position: relative;127 padding: 50px 0 35px;128 padding-left: 68px;129 transition: background 0.2s linear;130131 ${mediaqueries.desktop`132 padding-left: 53px;133 `}134135 ${mediaqueries.tablet`136 padding: 25px 0 80px;137 `}138139 ${mediaqueries.phablet`140 padding: 20px 0;141 `}142`;143144const NextArticle = styled(Section)`145 display: block;146`;147148const FooterNext = styled.h3`149 position: relative;150 opacity: 0.25;151 margin-bottom: 100px;152 font-weight: 400;153 color: ${p => p.theme.colors.primary};154155 ${mediaqueries.tablet`156 margin-bottom: 60px;157 `}158159 &::after {160 content: '';161 position: absolute;162 background: ${p => p.theme.colors.grey};163 width: ${(910 / 1140) * 100}%;164 height: 1px;165 right: 0;166 top: 11px;167168 ${mediaqueries.tablet`169 width: ${(600 / 1140) * 100}%;170 `}171172 ${mediaqueries.phablet`173 width: ${(400 / 1140) * 100}%;174 `}175176 ${mediaqueries.phone`177 width: 90px178 `}179 }180`;181182const FooterSpacer = styled.div`183 margin-bottom: 65px;184`;
- Open ‘./src/@narative/gatsby-theme-novela/templates/article.template.tsx’
- Reduce the bottom margin on
Header
and save. Here’s mine.
- Reduce the bottom margin on
1import React from 'react';2import styled from '@emotion/styled';34import Headings from '@components/Headings';5import Image, { ImagePlaceholder } from '@components/Image';67import mediaqueries from '@styles/media';8import { IArticle, IAuthor } from '@types';910import ArticleAuthors from './Article.Authors';1112interface ArticleHeroProps {13 article: IArticle;14 authors: IAuthor[];15}1617const ArticleHero: React.FC<ArticleHeroProps> = ({ article, authors }) => {18 const hasCoAUthors = authors.length > 1;19 const hasHeroImage =20 article.hero &&21 Object.keys(article.hero.full).length !== 0 &&22 article.hero.full.constructor === Object;2324 return (25 <Hero>26 <Header>27 <HeroHeading>{article.title}</HeroHeading>28 <HeroSubtitle hasCoAUthors={hasCoAUthors}>29 <ArticleAuthors authors={authors} />30 <ArticleMeta hasCoAUthors={hasCoAUthors}>31 {article.date} · {article.timeToRead} min read32 </ArticleMeta>33 </HeroSubtitle>34 </Header>35 <HeroImage id="ArticleImage__Hero">36 {hasHeroImage && (37 <Image src={article.hero.full} />38 )}39 </HeroImage>40 </Hero>41 );42};4344export default ArticleHero;4546const Hero = styled.div`47 ${p => mediaqueries.phablet`48 &::before {49 content: "";50 width: 100%;51 height: 20px;52 background: ${p.theme.colors.primary};53 position: absolute;54 left: 0;55 top: 0;56 transition: ${p.theme.colorModeTransition};57 }5859 &::after {60 content: "";61 width: 100%;62 height: 10px;63 background: ${p.theme.colors.background};64 position: absolute;65 left: 0;66 top: 10px;67 border-top-left-radius: 25px;68 border-top-right-radius: 25px;69 transition: ${p.theme.colorModeTransition};70 }71 `}72`;7374const ArticleMeta = styled.div<{ hasCoAUthors: boolean }>`75 margin-left: ${p => (p.hasCoAUthors ? '10px' : '0')};7677 ${mediaqueries.phablet`78 margin-left: 0;79 `}80`;8182/* Gavin 20200722: Reduced bottom margin on the Header because it looked too83 big w/ no hero */84const Header = styled.header`85 position: relative;86 z-index: 10;87 margin:50px auto 120px;88 padding-left: 68px;89 max-width: 749px;9091 ${mediaqueries.desktop`92 padding-left: 53px;93 max-width: calc(507px + 53px);94 margin: 50px auto 70px;95 `}9697 ${mediaqueries.tablet`98 padding-left: 0;99 margin: 50px auto 70px;100 max-width: 480px;101 `}102103 ${mediaqueries.phablet`104 margin: 50px auto 180px;105 padding: 0 40px;106 `}107108 @media screen and (max-height: 700px) {109 margin: 50px auto;110 }111`;112113const HeroHeading = styled(Headings.h1)`114 font-size: 48px;115 font-family: ${p => p.theme.fonts.serif};116 margin-bottom: 25px;117 font-weight: bold;118 line-height: 1.32;119120 ${mediaqueries.tablet`121 margin-bottom: 20px;122 font-size: 36px;123 `}124125 ${mediaqueries.phablet`126 font-size: 32px;127 `}128`;129130const HeroSubtitle = styled.div<{ hasCoAUthors: boolean }>`131 position: relative;132 display: flex;133 font-size: 18px;134 color: ${p => p.theme.colors.grey};135136 ${p => mediaqueries.phablet`137 font-size: 14px;138 flex-direction: column;139140 ${p.hasCoAUthors &&141 `142 &::before {143 content: '';144 position: absolute;145 left: -20px;146 right: -20px;147 top: -10px;148 bottom: -10px;149 border: 1px solid ${p.theme.colors.horizontalRule};150 opacity: 0.5;151 border-radius: 5px;152 }153 `}154155156 strong {157 display: block;158 font-weight: 500;159 margin-bottom: 5px;160 }161 `}162`;163164const HeroImage = styled.div`165 position: relative;166 z-index: 1;167 width: 100%;168 max-width: 944px;169 overflow: hidden;170 margin: 0 auto;171 box-shadow: 0 30px 60px -10px rgba(0, 0, 0, 0.2),172 0 18px 36px -18px rgba(0, 0, 0, 0.22);173174 ${mediaqueries.tablet`175 max-width: 100%;176 `}177178 ${mediaqueries.phablet`179 margin: 0 auto;180 width: calc(100vw - 40px);181 height: 220px;182183 & > div {184 height: 220px;185 }186`}187`;
- Run
gatsby develop
and click on the Resume L1 menu item to go to the Resume page. Here’s mine.
Much better. Now let’s commit your changes to your repo.
Merge your changes into your repo
- Add all new files
git add --all
- Commit your changes
git commit -a -m "[your commit message]"
- Push your branch to your repo on GitHub
git push -u origin initial_customizations
Boom! Your branch and changes have been saved to your repo. Now create a pull request on GitHub and merge your changes into the main branch.
If you got an “Authentication failed…” error even though you entered your GitHub username and password correctly, you may have to create and use a personal access token. Read this Medium blog for details on how to do that.
- In your repo on GitHub, click on the “Pull Requests” tab
- Click on the “New pull request” button
- Click the “Compare” dropdown and select the
initial_customizations
branch - Compare your changes
- When you are good with all of the change made, click the “Create pull request” button
- Write your PR notes and click the “Create pull request” button
- Click the “Merge pull request” button
- Click the “Confirm merge” button
- In Terminal,
git checkout main
Boom!
Changes have been merged, and you are back on the main
branch, ready to move forward.
That’s it for today. I’ll post tutorial next week on how to deploy your site to Firebase and maybe how to automate deploys with GitHub Actions. We’ll see what I have time to get around to.