Some social media apps, in particular Instagram, do not allow you to include links to other pages in posts. The only place you can include a URL is in your bio, and you can only include one. This soon led to a trend of people promoting something with a post, but including “Link in bio!” in the caption. They would then update the solitary link in their bio to the latest thing they wanted to promote.
Soon, a bunch of services cropped up that allow people to manage multiple links in a single, mobile-friendly page; and include a link to that page in their bio. The most popular of these is Linktree, but there are many others.
Linktree has many users, from media companies to celebrities to, most likely, some of your friends — and maybe even you? Here are some examples:
HBO’s Instagram bio:
And HBO’s list of links:
Katy Perry’s Instagram bio:
And Katy Perry’s list of links:
In this project, we’re going to build our own mobile-friendly list of links that we can include in our social media profiles or anywhere else that we like. Here’s mine:
There are several benefits to writing our own rather than using a service like Linktree:
linktree.com/raghubetina
, notice that my URL is simply rag.hu
.We have all the power of CSS at our fingerprints to customize the design. Linktree, like all “no code” tools, limits you to a set of pre-defined constraints that you have to work within. Not so if you’re writing the code yourself.
For example, if you click through to mine, you’ll notice a lot of touches that aren’t available options even on the paid Linktree plans: an animated background, a “frosted glass” effect on the links, etc.
Let’s get started.
So, for example, my repo name is “raghubetina.github.io”.
Next, we’ll tweak some settings of our repository to allow for superfast, free hosting using GitHub Pages.
In the Settings of the repository, find Pages in the left sidebar and switch the folder to docs/
. Then Save:
gitpod.io/#https://github.com/your-username/your-username.github.io
bin/server
at a terminal tab as usual to start up your web server.index.html
in the docs/
folder. Add “hello, world” and make sure that it shows up when you refresh your app preview.To start with, we need content. You’ll probably want to gather:
I downloaded the image and then uploaded it to my Gitpod workspace by drag-and-dropping it into the docs/
folder.
If you want to, you can use fake placeholder images and links for everything, but I encourage you to gather real content. It’ll be more fun, you’ll end up with a page that you can actually use on your social media profiles, and you’ll have a good piece for your portfolio.
Let’s add our images, copy, and links. I wrapped each block of content in a <div>
for now, to create some separation. I also gave each <div>
a class=""
attribute; since we haven’t written any CSS yet, they don’t do anything, but they help me remember what each <div>
represents. Something like this:
<div class="banner">
<img src="profile-pic.jpg" alt="Raghu Betina headshot">
</div>
<div class="name">
Raghu Betina
</div>
<div class="social-icons">
<a href="https://github.com/raghubetina" target="_blank">
GitHub
</a>
<a href="https://www.linkedin.com/in/raghubetina/" target="_blank">
LinkedIn
</a>
<a href="https://www.instagram.com/raghubetina/" target="_blank">
Instagram
</a>
<a href="https://twitter.com/raghubetina" target="_blank">
Twitter
</a>
</div>
<div class="link">
<img
src="https://www.ycombinator.com/assets/ycdc/yc-og-image-0cfa80cac837d64d9b4f0705950000b66906ac032791376bd721f246fafcc7b4.png">
<a target="_blank" href="http://paulgraham.com/startupideas.html">How to Get Startup Ideas
— Paul Graham</a>
</div>
<div class="link">
<img src="/thumbnails/typography.jpg">
<a target="_blank"
href="https://practicaltypography.com/typography-in-ten-minutes.html">Typography in ten minutes — Matthew
Butterick</a>
</div>
<div class="link">
<img
src="https://media.newyorker.com/photos/59096d451c7a8e33fb38e4ca/16:9/w_1280,c_limit/071210_r16884_p646.jpg">
<a target="_blank" href="https://www.newyorker.com/magazine/2007/12/10/the-checklist/">A
Life-Saving Checklist — The New Yorker</a>
</div>
<!-- Etc, for as many links as you want -->
Of course, I also have the standard HTML boilerplate (<html>
, <head>
, <body
, etc) in order to make it a valid document.
Two things to notice:
target="_blank"
attribute on any links that I want to open in a new tab.docs/
folder:
thumbnails
to help keep things organized, and moved the images there.When referencing the URL of the image in a src=""
attribute, I start with a leading slash — e.g.:
<img src="/thumbnails/typography.jpg">
I do not include /docs
in the URL.
At this stage, your page technically serves its purpose — a collection of links people can click on — but it probably looks terrible, like this.
If at any point your HTML breaks, or you suspect you e.g. forgot a closing tag but can’t find it, the W3C HTML Validator might come in handy. Paste in your entire HTML document and click “Check”, and it will give you a list of feedback. Some of them are yellow warnings and can be safely ignored for now, but any critical mistakes (like missing closing tags or quotation marks) will show up as red errors.
For example, if you run your HTML through the validator right now (try it), it will display a bunch of errors because we don’t have alt=""
attributes on our <img>
s. These are important to include for accessibility reasons, and just in case the image breaks for some reason (i.e. the URL changes). We should add descriptive alternative text for each image.
You should try to type out your page yourself — avoid copy-pasting. Making mistakes, fixing them, and developing your own muscle memory is important.
That said, after you’ve attempted it yourself, you can View Source (Windows: Ctrl+U or Mac: Command+Option+U) on any of my example intermediate pages to see my code.
Now, let’s start to make the page look better. Let’s add a <style>
element inside the <head>
of the document so that we can begin applying CSS. We won’t use an external stylesheet for now, since we’re only planning to apply this CSS to a single page. Maybe we can start by giving the body
some breathing room with top and bottom padding:
<style>
body {
padding-top: 40px;
padding-bottom: 40px;
}
</style>
Let’s start by sizing the profile picture. We need to give it a class so that we can apply CSS rules to it — let’s call it banner-image
:
<div class="banner">
<img src="profile-pic.jpg" alt="Raghu Betina headshot" class="banner-image">
</div>
Then, let’s make the banner image 128 pixels by 128 pixels:
<style>
.banner-image {
width: 128px;
height: 128px;
}
</style>
Unless your original image was exactly square, this probably will look squished when you check out the results. To solve this problem, we can use the object-fit
property:
.banner-image {
width: 128px;
height: 128px;
object-fit: cover;
}
It’s quite common to see circular profile pictures. If you want to, set a large border-radius
to achieve that:
.banner-image {
width: 128px;
height: 128px;
object-fit: cover;
border-radius: 128px;
}
I didn’t use one on my profile picture, but you should feel free to get creative with borders, as well.
Similarly, let’s size all of the thumbnail images as 48px by 48px:
.thumbnail {
width: 48px;
height: 48px;
border-radius: 48px;
object-fit: cover;
}
Don’t forget to add the class to the relevant <img>
elements. Now, your page should look something like this — much better.
Let’s add some color to make it easier to see how much space each element is occupying. We’ll, at a minimum, need a color for the background of the entire page and for each link.
You can come up with your own palette, or you can use Happy Hues, a nice set of curated color palettes.
I used this palette. I used the background color, headline color, button color, and button text color like this:
body {
background-color: #004643;
}
.name {
color: #fffffe;
}
.social-icons a {
color: #fffffe;
text-decoration: none;
}
.link {
background-color: #f9bc60;
}
.link a {
color: #001e1d;
text-decoration: none;
}
As you can see, I also removed the underlines from links with text-decoration: none
.
Notice that I used the descendant combinator:
.link a {
/* ... */
}
This selector targets only <a>
elements that are descendants of elements with class link
. This is a handy alternative to adding a new class to all of the elements I’m interested in targeting.1
My page now looks like this.
Now, for the interesting part — laying out the elements where we want them on the page.
Since this is a relatively simple, single-column layout, we could stay in the normal flow mode and achieve most of the positioning that we want with margin
and padding
.
But let’s use display: flex
instead. It will make it much easier to do things like vertically center the text within each link’s box.
First, I’ll wrap all of our content — the div.banner
, the div.name
, the div.social-icons
, and all of the div.links
— within a new parent <div>
with a class called items
. This will make it easier for me to position and size everything uniformly. Then I will switch the layout mode of the new div.items
from the default normal flow to flex
:
.items {
display: flex;
}
If you try that and refresh your page, you’ll see that all of the elements are now horizontally side-by-side. This is because the default flex-direction
is row
. Let’s switch the flex-direction
to column
to lay them out vertically again:
.items {
display: flex;
flex-direction: column;
}
If you refresh, you’ll see the layout is vertical again. When we’re using display: flex
mode, we can use the gap
property to provide some breathing room between each child element, rather than having to add margin
:
.items {
display: flex;
flex-direction: column;
gap: 30px;
}
On laptop screens, the buttons are running all the way to the edge of the screen, which isn’t very attractive. Let’s set a max-width
of around 640px (you choose a value that looks good to you):
.items {
display: flex;
flex-direction: column;
gap: 30px;
max-width: 640px;
}
Within each link, it would be nice if:
display: flex
to the rescue!
We can vertically center child elements with the align-items
property:
.link {
background-color: #f9bc60;
display: flex;
align-items: center;
}
If you refresh, the thumbnails should be lined up nicely with the link text.
Next, let’s center the text of each link. I’m going to expand the existing rule that we have for .link a
. Can we simply add text-align: center
and call it a day?
.link a {
color: #001e1d;
text-decoration: none;
text-align: center;
}
If you refresh, you’ll see that didn’t work. Why?
To make it easier to see things while I am working on layouts, I often use the following hack:
* {
border: thin red solid;
}
This puts a thin red border around every element. Your page should now look something like this:
We can see that the <a>
elements are only just wide enough to fit their content, so centering within them isn’t doing anything. Instead, we want the <a>
element to occupy all of the available space to the right of the thumbnail.
To achieve, this we can use the flex-grow
property:
.link a {
color: #001e1d;
text-decoration: none;
text-align: center;
flex-grow: 1;
}
Now the <a>
element grows to fill any available space, while the <img>
element stays the same size (it still has the default value for flex-grow
, which is 0
). And our text should be nicely centered since we already added the text-align
.
Now let’s take care of centering the profile picture and name, as well as putting some breathing room between the social links.
Flexbox has a wonderful property called justify-content
that will help with all of these things:
.name {
color: #fffffe;
display: flex;
justify-content: center;
}
.banner {
display: flex;
justify-content: center;
}
.social-icons {
display: flex;
justify-content: space-around;
}
You could also use the same technique to center the div.items
within the <body>
:
body {
display: flex;
justify-content: center;
}
But then we again need to tell the child elements to grow to take up all available space with flex-grow
:
.items {
display: flex;
flex-direction: column;
gap: 30px;
max-width: 640px;
flex-grow: 1;
}
Our page layout should now look solid!
Let’s add a little bit of padding inside our div.link
s:
.link {
background-color: #f9bc60;
display: flex;
align-items: center;
padding: 5px;
}
It should now be safe to remove the * { border: thin red solid; }
hack, unless you want to continue playing with layout, padding, etc.
Our page now looks something like this.
We’ve made a lot of progress, and our list of links is functional and looking pretty solid! We can and will spend more time fine-tuning, but this seems like a great time to deploy our app to industrial-grade hosting so that we can actually link to it in our bios!
Sadly, after over a decade, Heroku’s free tier was eliminated as of November 28th, 2022. There are some silver linings — they have launched a new discount through GitHub Student, and they created a couple of new, less expensive plans (Eco, Basic, Mini).
For full-stack, dynamic, interactive, database-backed apps, I plan to still use Heroku. However, Heroku is no longer a good choice for hosting static HTML websites. There are several other free and fast options for that (e.g. Netlify and Vercel).
Today, I want to show you GitHub’s offering for hosting static websites: GitHub Pages. We will deploy our Link In Bio via GitHub Pages. This will have several benefits:
.github.io
. This carries some developer cred.Here’s what to do to get your first GitHub Pages site going:
/git
.https://your-username.github.io
!index.html
— a list of projects that you’ve built, for example.yourname.com
rather than something like your-username.github.io
, then you’ll first have to purchase a domain.
You can create as many GitHub Pages sites as you want — one per repo. Read more about GitHub Pages here.
This is the end of the required portion of this assignment. In Canvas, submit the URL of your deployed app (something.github.io, not your GitPod preview). If you are willing to share your creation, please also include “Add me to the showcase” in the submission text area.
Now that we have the basics up and running, it’s time for you to get creative and make it your own! You can continue to make changes, commit, and push them to deploy. We aren’t grading for anything particular, so just have fun with it.
Here are a bunch of resources for you to explore:
The following are a collection of neat tools that will help you create interesting backgrounds, generate gradients and shadows, find font pairings, etc.
Glance at each one and pick one or two to integrate into your design. If you want to bounce ideas on things you can integrate, chat with an instructor.
Once you’ve picked something to add, try to figure out how to integrate it on your own; ask lots of questions when you get stuck.
To use Font Awesome icons (for example, for your LinkedIn/GitHub/Twitter/etc links), first include this in the <head>
of your document:
<!-- Connect Font Awesome -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/js/all.min.js"></script>
Then peruse the icon list and copy-paste the code examples into your HTML.
Be sure to make lots of git commits along the way as you work! Have fun ☺️
If you want to become a pro at writing CSS selectors, I recommend an interactive tutorial/game called CSS Diner. If you make it through all 32 levels, you’ll be better than most front-end developers at writing advanced CSS selectors. In addition to making it easier to style your own pages, knowing how to write advanced selectors will pay dividends if you’re interested in doing any web scraping. ↩