Creating a resume using HTML and CSS and saving it as a PDF
I recently wanted to have a resume that followed a specific modern format, like the one you see in the image below. In this article, I will share how I solved some technical challenges and the step-by-step process of adding details.
My personal goal was to have a simple template, with a few variables so that if I wanted to have the feeling of a big change, I could easily do that by adjusting the font family or the font size.
I must admit that I am not a great designer, and nor did I write clean and extendible HTML and CSS code, so I expect that there is a lot to improve in my project. But for my needs, this was good enough, and hopefully, it can help other people as well.
I might suggest that before going ahead and choosing the font and your CV layout, it’s useful to read some articles about design. I found particularly useful this article from 99design.com.
Code here on Github!
Up until now, I preferred simpler formats, and most of my resumes that I sent to companies have been written in LaTeX. But now I decided that it’s a good opportunity to change the style.
Step by step code
Now I will share with you how I started. The first thing was to configure some basic things that allowed us to write multi-page resumés.
Layout
The main thing that made it possible to avoid most, but not all, rendering problems was to use everywhere centimeters as a CSS unit. It looks good enough for me in the browser, and the conversion to PDF doesn’t suffer much.
And here is the result. Not that impressive, but at least when you right-click and print, you get the same format in PDF.
Next, I wanted to create some divs and add some CSS to them.
From here on I will add snippets of code with CSS and HTML. Each new snippet of HTML has a bit of context from the previous one like a <div> or other elements. Hopefully, this will not confuse you too much.
index.css
.container {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
}.leftPanel {
width: 27%;
background-color: #484444;
padding: 0.7cm;
display: flex;
flex-direction: column;
align-items: center;
}.rightPanel {
width: 73%;
padding: 0.7cm;
}
index.html
<page size="A4">
<div class="container">
<div class="leftPanel">
</div>
<div class="rightPanel">
</div>
</div>
</page>
And it’s starting to catch some shape:
Left panel
Here, along with the HTML and CSS, I also added a bit of link that included Google fonts, just to give it a bit of a nice touch.
index.css
.item {
padding-bottom: 0.7cm;
padding-top: 0.7cm;
}.item h2{
margin-top: 0;
}.bottomLineSeparator {
border-bottom: 0.05cm solid white;
}h2 {
font-family: 'Archivo Narrow', sans-serif;
}.leftPanel h2 {
color: white;
}img {
width: 4cm;
height: 4cm;
margin-bottom: 0.7cm;
border-radius: 50%;
border: 0.15cm solid white;
object-fit: cover;
object-position: 50% 50%;
}.details {
width: 100%;
display: flex;
flex-direction: column;
}
index.html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin
<link href="https://fonts.googleapis.com/css2?family=Archivo+Narrow&family=Julius+Sans+One&family=Open+Sans&family=Source+Sans+Pro&display=swap" rel="stylesheet"><div class="leftPanel">
<img src="avatar.png"/>
<div class="details">
<div class="item bottomLineSeparator">
<h2>
CONTACT
</h2>
</div>
<div class="item bottomLineSeparator">
<h2>
SKILLS
</h2>
</div>
<div class="item">
<h2>
EDUCATION
</h2>
</div>
</div>
</div>
Using Font Awesome
Next, I used Font Awesome for adding some pretty and easily configurable icons.
index.css
.leftPanel .smallText,
.leftPanel .smallText,
.leftPanel .smallText span,
.leftPanel .smallText p,
.smallText a {
font-size: 0.45cm;
}.smallText,
.smallText span,
.smallText p,
.smallText a {
font-family: 'Source Sans Pro', sans-serif;
text-align: justify;
}.contactIcon {
width: 0.5cm;
text-align: center;
}.leftPanel,
.leftPanel a {
color: #bebebe;
text-decoration: none;
}
index.html
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<div class="item bottomLineSeparator">
<h2>
CONTACT
</h2>
<div class="smallText">
<p>
<i class="fa fa-phone contactIcon" aria-hidden="true"></i>
(+33) 777 777 77
</p>
<p>
<i class="fa fa-envelope contactIcon" aria-hidden="true"></i>
<a href="lorem@ipsum.com@gmail.com">
lorem@ipsum.com
</a>
</p>
<p>
<i class="fa fa-map-marker contactIcon" aria-hidden="true"></i>
New York, USA
</p>
<p>
<i class="fa fa-linkedin-square contactIcon" aria-hidden="true"></i>
<a href="#">
in/loremipsum
</a>
</p>
<p class="lastParagrafNoMarginBottom">
<i class="fa fa-skype contactIcon" aria-hidden="true"></i>
<a href="#">
loremipsum
</a>
</p>
</div>
</div>
Aligning numbers and words
The next fine detail I wanted to implement was to align the number of years of experience and to align the singular and plural of the word “year”
index.css
.skill {
display: flex;
flex-direction: row;
justify-content: space-between;
}.yearsOfExperience {
width: 1.6cm;
display: flex;
flex-direction: row;
justify-content: center;
}.alignleft {
text-align: left !important;
width: 1cm;
}.alignright {
text-align: right !important;
width: 0.6cm;
margin-right: 0.1cm
}
index.html
<div class="item bottomLineSeparator">
<h2>
SKILLS
</h2>
<div class="smallText">
<div class="skill">
<div>
<span>Accounting</span>
</div>
<div class="yearsOfExperience">
<span class="alignright">14</span>
<span class="alignleft">years</span>
</div>
</div>
<div class="skill">
<div>
<span>Word</span>
</div>
<div class="yearsOfExperience">
<span class="alignright">3</span>
<span class="alignleft">years</span>
</div>
</div>
<div class="skill">
<div>
<span>Powerpoint</span>
</div>
<div class="yearsOfExperience">
<span class="alignright">1</span>
<span class="alignleft">year</span>
</div>
</div>
</div>
</div>
Filler section
The following section doesn’t have any technical challenge, but it’s a very important part of a resumé.
index.css
.bolded {
font-weight: bold;
}.white {
color: white;
}
index.html
<div class="item">
<h2>
EDUCATION
</h2>
<div class="smallText">
<p class="bolded white">
Bachelor of Economics
</p>
<p>
The University of Sydney
</p>
<p>
2010 - 2014
</p>
</div>
</div>
Right panel
For the next part, I noticed that in other resume models that I have analyzed, the name of the person is slightly elongated, so I attempted to do the same by adding a transform to the h1 CSS. I am not sure if it’s the best idea, or if it could have been achieved more easily, but it did the job for me.
index.css
h1 {
font-family: 'Julius Sans One', sans-serif;
}h1 {
font-weight: 300;
font-size: 1.2cm;
transform:scale(1,1.15);
margin-bottom: 0.2cm;
margin-top: 0.2cm;
text-transform: uppercase;
}h3 {
font-family: 'Open Sans', sans-serif;
}
index.html
<div class="rightPanel">
<div>
<h1>
Jhon Doe
</h1>
<div class="smallText">
<h3>
Accountant
</h3>
</div>
</div>
</div>
index.html
<div>
<h2>
About me
</h2>
<div class="smallText">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris venenatis, justo sed feugiat pulvinar., quam ipsum tincidunt enim, ac gravida est metus sit amet neque. Curabitur ut arcu ut nunc finibus accumsan id id elit.
</p>
<p>
Vivamus non magna quis neque viverra finibus quis a tortor.
</p>
</div>
</div>
Connecting bullet points
The last challenge below was to add a pretty line connecting the bullet points of the items in the work experience section. To implement this, I read this nice article.
index.css
.workExperience>ul>li ul {
padding-left: 0.5cm;
list-style-type: disc;
}.workExperience>ul {
list-style-type: none;
padding-left: 0;
}.workExperience>ul>li {
position: relative;
margin: 0;
padding-bottom: 0.5cm;
padding-left: 0.5cm;
}.workExperience>ul>li:before {
background-color: #b8abab;
width: 0.05cm;
content: '';
position: absolute;
top: 0.1cm;
bottom: -0.2cm; /* change this after border removal */
left: 0.05cm;
}.workExperience>ul>li::after {
content: '';
position: absolute;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' viewBox='0 0 32 32' focusable='false'%3E%3Ccircle stroke='none' fill='%23484444' cx='16' cy='16' r='10'%3E%3C/circle%3E%3C/svg%3E");
background-repeat: no-repeat;
background-size: contain;
left: -0.09cm;
top: 0;
width: 0.35cm;
height: 0.35cm;
}.jobPosition {
display: flex;
flex-direction: row;
justify-content: space-between;
}.jobPosition span,
.projectName span {
font-family: 'Source Sans Pro', sans-serif;
}
index.html
<div class="workExperience">
<h2>
Work experience
</h2>
<ul>
<li>
<div class="jobPosition">
<span class="bolded">
Accountant
</span>
<span>
Jun 2014 - Sept 2015
</span>
</div>
<div class="projectName bolded">
<span>
Accounting project name | Company name
</span>
</div>
</li>
<li>
<div class="jobPosition">
<span class="bolded">
Digital Marketing Expert
</span>
<span>
Nov 2020 - Sept 2021
</span>
</div>
<div class="projectName bolded">
<span>
Project name | Company name
</span>
</div>
</li>
<li>
<div class="jobPosition">
<span class="bolded">
Accountant
</span>
<span>
Jun 2017 - May 2020
</span>
</div>
<div class="projectName bolded">
<span>
Project name | Company name
</span>
</div>
</li>
</ul>
</div>
I skipped the details in the work experience from this article since they don’t add much value, and the hard work of the project was done in the previous steps.
Quickly changing style
For the last part, I refactored the CSS section to order it by functionality and I created subsections for font family, colors, and sizes. Now, with just a few modifications I can give the feeling of a completely new style.
In the image below, I used monospace fonts. It also took me around 2 minutes to also adjust the font size. This isn’t the best use of the font family, since it’s not the most readable font, so I wouldn’t send it to any recruiter.
Conclusion
Writing documents destined for printing is definitely possible using HTML and CSS, but attention needs to be paid to every change in order to not end up with bad surprises.
Also, this was definitely a fun exercise since it definitely improved my design skills, and I am happy I also made it to an article. Writing a resumé in HTML and CSS was on my to-do list for a long time, and this challenge was the perfect opportunity to cross another item from the list.