Real artists ship so that’s what I vow to do from here on in.
📸 Photo by Anas Alshanti on Unsplash
This is monumental for me; I actually shipped something I said I was going to ship, and in less than a month. The thing I shipped is my new portfolio, built with Gatsby and based on the work of Jacob Martinez and heavily inspired by Brittany Chiang’s portfolio which I gush over in this post.
It took me two weeks working on it every couple days for a couple hours a day. I took the gatsby-simple-portfolio theme, a simple starter, and made it my own.
The easy part
I wanted the color theme to look like Brittany’s because, well, I really like it. I used a tool for Mac called Sip to grab the colors from her site and went to work.
The hard parts
There were several hard parts:
- I wanted to have a downloadable resume in the resume button instead of a link to my resume site
- I wanted to have navigation on the site instead of relying on scroll-reveal and scrolling all the way down the page
- I wanted each nav item to scroll to its id on the page
- I wanted a hamburger menu to show only on mobile
We’ll tackle these in the next few sections.
1. Downloadable resume
For this I wanted a pdf file to download when users clicked the resume
button. I didn’t know or understand how people could download files, whether in Gatsby, or HTML in general.
I spent a lot of time in the Gatsby documentation learning about the filesystem and how to store files. The example showed a GraphQL query but I didn’t need GraphQL; this was a static site with no moving parts. The theme did not include GraphQL because it was basic. I could have added all of that but why? I just needed to serve up some images, links, and a pdf; there was no content and no pages.
I went back a couple pages in the docs and found what I needed.
I created a download
directory in the src
directory and placed the pdf in there. I then needed a way to download the pdf from that directory.
First, I added the directory to gatsby-config.js
using the gatsby-source-filesystem
plugin:
{
resolve: `gatsby-source-filesystem`,
options: {
name: `downloads`,
path: `${__dirname}/src/downloads/`,
},
}
Then I imported the pdf into the About.jsx
component like so:
// some other imports
import resumeDownload from "../../downloads/resume.pdf";
Next, I needed to figure out a way to download a file from a React component.
Initially, I thought I’d need to come up with some convoluted system to do this but it was as simple as using an anchor tag and a couple built-in React props:
<span className="d-flex mt-3">
<a
target="_blank"
rel="noopener noreferrer"
className="cta-btn cta-btn--resume"
download
href={resumeDownload}
>
Resume
</a>
</span>
Now, when the user clicks the Resume
button, a copy of my resume will download with some sort of UUID at the end1.
2. Navigation
Brittany Chiang’s portfolio has an interesting navigation bar that I wanted to have. I picked a similar font to the one on her portfolio, and went about building the nav.
In order to get the numbers to be a separate color from the text, I wrapped the numbers in a <span>
and added a class so that I could target just those spans:
<span className="menu-numbers">01.</span> Intro
which results in this nice navbar at the top:
It took a little bit of CSS-fu to get it aligned properly, trying to not rely on the react-bootstrap
CSS that came with the theme2.
3. Anchor tag scrolling
I forgot how this worked. I did some searching and found that I could add an id
to the top node in the component tree and target each component that way:
<section id="projects">
and in the Nav
<a href="#projects" className="menu-text">
<span className="menu-numbers">03.</span> Projects
</a>
Now, tapping any of those links in the nav take you to the section immediately instead of needing to scroll down the page3.
4. Creating a responsive hamburger nav
This was the hardest part about this project. I didn’t use react-bootstrap for the navigation, which would have made this whole endeavor easier. Bootstrap has some utility classes that make adding a hambuger nav easy.
But since I wanted a custom nav, using Bootstrap wasn’t going to work. So I built a hamburger nav from scratch. Only, it was hard.
I wasn’t sure how to do it in a React app. I did some searching and come across a tutorial on CSS-Tricks that gave a step by step guide on how to build a hambuger nav4. The issue was I wanted the hamburger nav to show up only when the viewport reached 769px
.
I searched some more and found that I could make the hamburger icon its own component, called Burger.jsx
and a mobile nav called MobileNav.jsx
. I used styled-components to create both of these, with code from a video that also used the same code in that CSS-Tricks article5.
Mobile nav
For the mobile nav we have a simple unordered list of menu items that link to the sections ids of the sections of the portfolio:
const StyledList = styled.ul`
list-style: none;
display: flex;
flex-flow: row nowrap;
li {
padding: 18px 10px;
}
// More styled component CSS
}
`
const MobileNav = ({ open }) => {
return (
<StyledList open={open}>
<li>
<a href="#hero" className="menu-text">
<span className="menu-numbers">01.</span> Intro
</a>
</li>
<li>
{" "}
<a href="#about" className="menu-text">
<span className="menu-numbers">02.</span> About
</a>
</li>
<li>
{" "}
<a href="#projects" className="menu-text">
<span className="menu-numbers">03.</span> Projects
</a>
</li>
<li>
<a href="#contact" className="menu-text">
<span className="menu-numbers">04.</span> Contact
</a>
</li>
</StyledList>
);
};
Here we use the StyledList
to create the MobileNav
passing in an open
prop to the styled list.
Then, in Burger.jsx
we set open
’s state using the useState
hook and setting the intial state to false
:
const Burger = () => {
const [open, setOpen] = useState(false);
return (
<>
<StyledBurger open={open} onClick={() => setOpen(!open)}>
<div />
<div />
<div />
<MobileNav open={open}/>
</StyledBurger>
</>
);
};
When someone taps or clicks on the burger part of the mobile nav, the new state returns true, and the mobile nav opens. Having MobileNav
outisde StyledBurger
tripped me up; I kept seeing two menus and no burger when testing on mobile. I moved the MobileNav
inside the StyledBurger
which worked and gave me the hamburger menu I wanted. But it was always there.
Media queries, breakpoints, and defeat
I tried to use media queries to get the hamburger menu to only show when the viewport reached 769px
>. Nothing I tried worked.
I kept searching and, in a moment of defeat, because I spent a couple days trying to figure this out, I used a little library called react-socks, a React library to render components only on specific viewports. I was able to easily set the breakpoints I needed with an import and some nesting in App.jsx
:
<BreakpointProvider>
<Breakpoint large up>
<Nav />
</Breakpoint>
<Breakpoint medium down>
<Burger />
</Breakpoint>
</BreakpointProvider>
Couldn’t get any easier than that.
Fixes
The navigation jumps a bit when loaded on mobile. I think it may have to do with the library or how I am implementing the mobile nav. In either case, I need to fix it.
There are some content fixes that need to happen as well so I will work on them this weekend.
Conclusion
I shipped a thing I said I was going to ship and I am proud of that. I am not done shipping new projects. I am starting another this weekend and when that one is finished I want to build a full stack app with Next.js.
I am a better dev and know much more now. Real artists ship. Now onto the next.
-
I’m assuming it is a UUID; a weird combination of letters and numbers follow after the filename. ↩
-
Which has its own
Navbar
component but I needed a custom one, and all Bootstrap navs are unappealing. I also just wanted it quick and without the need to override stubborn Bootstrap utility classes with!important
littered everywhere. ↩ -
I am going to wrap each anchor tag scroll-reveal’s
<Fade>
component for a smoother scroll experience. ↩ -
And with Hooks! ↩
-
The video creator didn’t mention the article. It was a blatant copy of the CSS-Tricks article. Not sure how I feel about that, but will make sure to credit the author of the article and link to the repo. ↩