Have you ever felt overwhelmed by the extensive use of frameworks and advanced technologies to build a simple portfolio website? Same here, which is why I chose to keep things simple by developing it using only HTML, CSS, and JavaScript. Here is how I did it:
Thought:
After finishing my resume, I felt something was missing: it was a place to showcase my skills, certifications, projects, and contact details in a way that was interactive and easy to explore.
Prototype:
I only used HTML tags to display various sections and their information to mirror the resume document on the webpage.
Then I started with using basic CSS code to change the font color and size so that the content becomes more readable.
Tip: Replicating your resume into a simple webpage is a good step for beginners because it makes them familiar with the development environment.
Template Code:
<!DOCTYPE html>
<html>
<head>
<title>Portfolio</title>
</head>
<body>
<h1>[Your Name]</h1>
<h2>[Your Title]</h2>
<h2>[Your Profession]</h2>
<h3>About</h3>
<p>[Brief description about yourself]</p>
<h3>Education</h3>
<h4>[Degree]</h4>
<p>[Field of Study]</p>
<p>[Institution Name]</p>
<p>[Start Year] - [End Year]</p>
<p>[Current CGPA or Percentage]</p>
<h3>Skills</h3>
<h4>Programming Languages</h4>
<ul>
<li>[Language 1]</li>
<li>[Language 2]</li>
<li>[Language 3]</li>
</ul>
<h4>Productivity Tools</h4>
<ul>
<li>[Tool 1]</li>
<li>[Tool 2]</li>
<li>[Tool 3]</li>
</ul>
<h4>Soft Skills</h4>
<ul>
<li>[Skill 1]</li>
<li>[Skill 2]</li>
<li>[Skill 3]</li>
</ul>
<h3>Projects</h3>
<h4>[Project Name]</h4>
<p>[Date]</p>
<ul>
<li>[Project Detail 1]</li>
<li>[Project Detail 2]</li>
<li>[Project Detail 3]</li>
</ul>
<h3>Certifications</h3>
<ul>
<li>[Certification 1]</li>
<li>[Certification 2]</li>
<li>[Certification 3]</li>
</ul>
<h3>Contact</h3>
<p>Email: [Your Email]</p>
<p>GitHub: [Your GitHub]</p>
<p>LinkedIn: [Your LinkedIn]</p>
<footer>
<p>© [Year], [Your Name]. All rights reserved.</p>
</footer>
</body>
</html>
Design:
I experimented with typography, spacing and layout while making changes to the prototype code.
As I first started with only coding in HTML/CSS, there were no errors to worry about, and I was confidently playing around, modifying different elements and their properties to see what it would look like.
Keeping JavaScript for later helped to focus on hierarchy and flow before interactions.
After making some minor tweaks, I finalised on a very basic HTML website with minimal CSS. Now, the website has a unique look and feel.
Template Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="description" content="Portfolio template: projects, skills, certifications, and contact details.">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="logo.png" type="image/x-icon">
<title>Portfolio</title>
<style>
body {
padding: 30px;
font-family: Arial, Helvetica, sans-serif;
background-color: #f5f8fd;
color: #0b1a3a;
margin: 0;
}
#nav-div {
padding: 12px;
display: flex;
justify-content: space-between;
position: fixed;
width: 95%;
top: 20px;
left: 50%;
transform: translateX(-50%);
border-radius: 12px;
background-color: rgba(197,213,232,.9);
box-shadow: 0 2px 6px rgba(0,0,0,.08);
z-index: 99;
}
#nav-items {
display: flex;
gap: 12px;
align-items: center;
margin: 0;
padding: 0;
list-style: none;
}
#nav-items a {
text-decoration: none;
color: inherit;
font-weight: 700;
font-size: 18px;
padding: 6px 8px;
border-bottom: 3px solid transparent;
}
#nav-logo img {
height: 44px;
width: 44px;
display: block;
}
#intro {
margin-top: 120px;
text-align: center;
padding: 40px 20px;
}
#intro h1 {
margin: 0 0 6px 0;
font-size: 2rem;
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
#intro h2 {
margin: 0;
font-size: 1.1rem;
color: #525e70;
}
#home, #about, #education, #skills, #projects, #certifications, #contact {
margin: 30px 0;
padding: 16px;
border-radius: 10px;
background-color: rgba(255,255,255,0.9);
}
h3.sub-heading {
margin: 0 0 12px 0;
font-size: 1.25rem;
color: #08145a;
}
p, li {
font-size: 1rem;
line-height: 1.5;
color: #111827;
}
.education-details, .project, .certification, .contact-item {
background-color: #f0f6ff;
padding: 12px;
margin: 12px 0;
border-radius: 10px;
}
.education-header, .project-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.project-img, .certificate-img, .badge-img {
max-width: 100%;
display: block;
margin: 12px 0;
border-radius: 6px;
}
.project-links {
display: inline-block;
text-decoration: none;
padding: 8px 10px;
margin-right: 8px;
border-radius: 6px;
border: 1px solid rgba(0,0,0,.08);
color: #07204a;
background-color: #ffffff;
font-weight: 700;
}
#contact-list {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
}
.contact-item {
display: flex;
gap: 10px;
align-items: center;
border: 1px solid rgba(0,0,0,.06);
}
.contact-logo-img {
height: 40px;
width: 40px;
object-fit: contain;
margin: 6px;
}
#download-resume {
position: fixed;
bottom: 20px;
right: 20px;
background-color: #e4e9ff;
padding: 10px 14px;
border-radius: 10px;
text-decoration: none;
color: #05123a;
font-weight: 700;
border: 1px solid rgba(0,0,0,.06);
}
#footer {
margin: 50px 0 20px 0;
text-align: center;
color: rgba(0,0,0,.6);
}
/* Simple responsive adjustments */
@media (max-width: 900px) {
#nav-div {
width: 95%;
top: 12px;
padding: 8px;
}
#intro {
margin-top: 100px;
padding: 20px 10px;
}
#nav-items {
gap: 8px;
flex-wrap: wrap;
}
}
@media (max-width: 580px) {
body {
padding: 12px;
}
#intro h1 {
font-size: 1.25rem;
}
#nav-div {
position: static;
transform: none;
margin-bottom: 12px;
}
}
</style>
</head>
<body>
<div id="intro">
<h1>[Your Name]</h1>
<h2>[Short tagline or title]</h2>
</div>
<div id="about">
<h3 class="sub-heading">About</h3>
<p>
[Brief description about yourself. Keep this concise and professional.]
</p>
</div>
<div id="education">
<h3 class="sub-heading">Education</h3>
<div id="educationList">
<div class="education-details">
<div class="education-header">
<h4 class="education-type">[Degree]</h4>
<h5 class="education-duration">[Start Year] - [End Year]</h5>
</div>
<p class="education-specialization">[Field of Study]</p>
<p class="education-institute">[Institution Name]</p>
<p class="education-grade">[Current CGPA or Percentage]</p>
</div>
</div>
</div>
<div id="skills">
<h3 class="sub-heading">Skills</h3>
<div class="technical-skill">
<h4>Programming Languages</h4>
<ul>
<li>[Language 1]</li>
<li>[Language 2]</li>
<li>[Language 3]</li>
</ul>
<h4>Productivity Tools</h4>
<ul>
<li>[Tool 1]</li>
<li>[Tool 2]</li>
<li>[Tool 3]</li>
</ul>
</div>
<div class="non-technical-skill">
<h4>Soft Skills</h4>
<ul>
<li>[Skill 1]</li>
<li>[Skill 2]</li>
<li>[Skill 3]</li>
</ul>
</div>
</div>
<div id="projects">
<h3 class="sub-heading">Projects</h3>
<div id="projectsList">
<div class="project">
<div class="project-header">
<h4 class="project-title">Project Name</h4>
<h5 class="project-duration">Date</h5>
</div>
<ul class="project-description">
<li>Brief project description and accomplishments.</li>
</ul>
<img alt="project-1" class="project-img" src="project-1.png">
<div>
<a class="project-links" href="#" target="_blank">Source Code</a>
<a class="project-links" href="#" target="_blank">Website</a>
</div>
</div>
</div>
</div>
<div id="certifications">
<h3 class="sub-heading">Certifications</h3>
<div id="course-completion">
<div class="certification">
<h5 class="course-title">Certification Name</h5>
<p class="course-duration">Date</p>
<img alt="certificate" src="certificate1.png" class="certificate-img">
</div>
</div>
</div>
<div id="contact">
<h3 class="sub-heading">Contact</h3>
<div id="contact-list">
<a class="contact-item" href="mailto:your.email@example.com">
<img alt="gmail" src="gmail-icon.png" class="contact-logo-img">
<div>Email<br> your.email@example.com</div>
</a>
<a class="contact-item" href="https://github.com/" target="_blank">
<img alt="github" src="github-icon.png" class="contact-logo-img">
<div>GitHub<br> your-github</div>
</a>
<a class="contact-item" href="https://www.linkedin.com/" target="_blank">
<img alt="linkedin" src="linkedin-icon.png" class="contact-logo-img">
<div>LinkedIn<br> your-linkedin</div>
</a>
</div>
</div>
<a id="download-resume" href="Resume.pdf" download>Download Resume</a>
<div id="footer">
<h3>© 2025, [Your Name]. All rights reserved.</h3>
</div>
</body>
</html>
Development:
After the design was finalised, the website looked more like a document rather than a web application, so it was time to add some web application features to the website.
Navigation Bar
Since my resume-based website didn't have a navigation bar, I added a navigation bar to make it easy to jump between sections: Home, About, Education, Skills, Projects, Certifications, and Contact.
The href of each link points to a section, such as About, having href="#about"
when clicked scrolls to the element id="about"
; in our case, it would be About Section.
<style>
#nav-div{
padding: 12px;
display: flex;
justify-content: space-between;
position: fixed;
width: 95%;
top: 20px;
left: 50%;
transform: translateX(-50%);
border-radius: 12px;
background-color: rgba(197,213,232,.9);
box-shadow: 0 2px 6px rgba(0,0,0,.08);
z-index: 99;
}
#nav-items{
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
</style>
<div id="nav-div">
<ul id="nav-items">
<li class="nav-item"> <a href="#home">Home</a></li>
<li class="nav-item"> <a href="#about">About</a></li>
<li class="nav-item"> <a href="#education">Education</a></li>
<li class="nav-item"> <a href="#skills">Skills</a></li>
<li class="nav-item"> <a href="#projects">Projects</a></li>
<li class="nav-item"> <a href="#certifications">Certifications</a></li>
<li class="nav-item"> <a href="#contact">Contact</a></li>
</ul>
</div>
But I noticed that when I clicked on a link in the navigation bar, the website scrolled to the section, but the navigation bar covered the heading of the section and some of its content.
To counter this, I used JavaScript to create a custom scrolling function scrollToDivNav()
, which scrolled to the relevant section with some offset.
While the anchors are accessible and work without JavaScript, custom scrolling gives better offset control.
function scrollToDivNav(sectionID, offset= -120){
var sectionDiv = document.getElementById(sectionID);
var topSectionDivLength = Math.round(sectionDiv.getBoundingClientRect().top,0)+offset;
window.scrollBy({top: topSectionDivLength, behavior: "smooth"});
}
And now, using the onclick
attribute in the tags, I called the function scrollToDivNav()
from each navigation item. Removed <a>
tags as we were using my own custom JavaScript code now for the navigation. Also added a placeholder element for my logo image on the navigation bar.
<div id="nav-div">
<div id="nav-logo"><img alt="logo image" src="logo.png" height="50px" width="50px"></div>
<ul id="nav-items">
<li class="nav-item" onclick="scrollToDivNav('intro')">Home</li>
<li class="nav-item" onclick="scrollToDivNav('about-subheading')">About</li>
<li class="nav-item" onclick="scrollToDivNav('education-subheading')">Education</li>
<li class="nav-item" onclick="scrollToDivNav('skills-subheading')">Skills</li>
<li class="nav-item" onclick="scrollToDivNav('projects-subheading')">Projects</li>
<li class="nav-item" onclick="scrollToDivNav('certifications-subheading')">Certifications</li>
<li class="nav-item" onclick="scrollToDivNav('contact-subheading',100)">Contact</li>
</ul>
</div>
Additional CSS
Now that the navigation bar was ready, it was time to properly structure the whole website to make it look more like a web application rather than a document. So, I started implementing more CSS for various sections in a sequential manner.
Note: Make sure to use @media queries
in CSS to make the website suitable for different screens.
I made my portfolio website to be responsive and suitable for 3 different screens, which are: Mobile, Tablet, and Desktop.
I created a separate navigation menu for Mobile devices, which was positioned at the bottom. Since it was now at the bottom, I used another custom scroll function called scrollToDivMenu()
for mobile devices.
<script>
function scrollToDivMenu(sectionID){
var sectionDiv = document.getElementById(sectionID);
sectionDiv.scrollIntoView({behavior: "smooth"});
}
</script>
<div id="menu-div">
<div id="menu-logo"><img alt="logo image" src="logo.png" height="50px" width="50px"></div>
<ul id="menu-items">
<li class="menu-item" onclick="scrollToDivMenu('intro')"><img alt="Go to home section" class="menu-icon-img" src="home-icon.png"><p class="menu-item-name">Home</p></li>
<li class="menu-item" onclick="scrollToDivMenu('about')"><img alt="Go to about section" class="menu-icon-img" src="about-icon.png"><p class="menu-item-name">About</p></li>
<li class="menu-item" onclick="scrollToDivMenu('education')"><img alt="Go to education section" class="menu-icon-img" src="education.png"><p class="menu-item-name">Education</p></li>
<li class="menu-item" onclick="scrollToDivMenu('skills')"><img alt="Go to skills section" class="menu-icon-img" src="skills-icon.png"><p class="menu-item-name">Skills</p></li>
<li class="menu-item" onclick="scrollToDivMenu('projects')"><img alt="Go to projects section" class="menu-icon-img" src="projects-icon.png"><p class="menu-item-name">Projects</p></li>
<li class="menu-item" onclick="scrollToDivMenu('certifications')"><img alt="Go to certifications section" class="menu-icon-img" src="certifications-icon.png"><p class="menu-item-name">Certifications</p></li>
<li class="menu-item" onclick="scrollToDivMenu('contact')"><img alt="Go to contact section" class="menu-icon-img" src="contact.png"><p class="menu-item-name">Contact</p></li>
</ul>
</div>
Animations
Then, to give the website a dynamic look and feel, I started adding animations to the elements on the website. Such as for the nav-items on the nav-bar, I gave a little underline animation using @keyframes
in CSS.
<style>
.nav-item:hover{
cursor: pointer;
border-bottom: 3px solid rgba(0, 0, 0, 0.5);
filter: drop-shadow(0px 0px 15px rgba(154, 160, 255, 0.981));
animation: underline-black 0.3s;
}
@keyframes underline-black{
0%{
border-bottom: 0px solid rgba(0, 0, 0, 0.5);
}
100%{
border-bottom: 3px solid rgba(0, 0, 0, 0.5);
}
}
</style>
I also used animation-timeline: view()
in CSS to play the animation when the element is being viewed on the screen. This gave the animated elements a more immersive and responsive appearance.
<style>
#about, #education, #skills, #projects, #contact{
animation: fade-in;
animation-timeline: view();
}
@keyframes fade-in {
0%{
opacity: 0.8;
scale: 0.3;
}
30%{
opacity: 1;
scale: 0.99;
}
100%{
opacity: 1;
scale: 1;
}
}
</style>
Tip: Animations are optional, content remains fully readable without them, and if implemented, then they should maintain reduced motion, so that the content on the animated element is readable.
JavaScript
Until now, we have already developed a static portfolio website using HTML/CSS. But to give it a more dynamic feel and to add some features such as an animated background, toggle themes, etc, we can use JavaScript.
Animated Background using JavaScript
Firstly, I stored the reference to the div with the id "body-bg" in a JavaScript variable called bodyBg using the var keyword.
Then also store the screen width and height so that we can use these values to display shapes of relative sizes in the background.
The shapes are then populated and arranged in a grid format by incrementing the left and bottom positions of the shapes using a for loop.
Then, using another for loop, I modified the animation of random elements using the random()
function, which occurred periodically using setInterval()
.
Using window.addEventListener("resize",()=>{})
, the whole process of setting up the shapes restarts by calling the bgBoxes()
function when the size of the window is changed.
Animated Background Code:
<style>
#body-bg{
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
scale: 1.5;
z-index: -1;
opacity: 1;
filter: blur(3px);
}
.body-bg-box-odd{
background-color: rgba(225, 232, 255, 0.3);
position: fixed;
height: 30px;
width: 30px;
z-index: -1;
opacity: 0;
}
.body-bg-box-even{
background-color: rgba(223, 234, 249, 0.5);
position: fixed;
height: 30px;
width: 30px;
z-index: -1;
opacity: 0;
}
@keyframes bg-shapes {
0%{
opacity: 1;
scale: 1;
}
50%{
opacity: 0.5;
scale: 1.5;
background-color: rgba(179, 183, 224, 0.5);
border-radius: 15px;
transform: rotate(720deg);
}
100%{
opacity: 1;
scale: 1;
}
}
@keyframes bg-shapes-color {
0%{
opacity: 1;
scale: 1;
}
50%{
opacity: 0.5;
scale: 1;
background-color: rgba(122, 156, 242, 0.5);
}
100%{
opacity: 1;
scale: 1;
}
}
</style>
<div id="body-bg"></div>
<script>
function bgBoxes(){
var bodyBg = document.getElementById("body-bg");
var screenwidth = window.innerWidth;
var screenheight = window.innerHeight;
var shapeWidth = 50;
var shapeHeight = 50;
nBgShapesWidth = (screenwidth/shapeWidth) % 2 == 0 ? (screenwidth/shapeWidth) / 2 : ((screenwidth + (screenwidth % shapeWidth))/shapeWidth);
nBgShapesHeight = (screenheight/shapeHeight) % 2 == 0 ? (screenheight/shapeHeight) / 2 : ((screenheight + (screenheight % shapeHeight))/shapeHeight);
var bodyBgHTML = "";
for(i=1; i<=nBgShapesWidth; i++){
for(var j=1; j<=nBgShapesHeight; j++){
if((i+j) % 2 == 0){
bodyBgHTML = bodyBgHTML + "<div class=\"body-bg-box-even\" style=\"left: "+ i*shapeWidth +"px; top: "+ j*shapeHeight +"px;\"> </div>";
}
else{
bodyBgHTML = bodyBgHTML + "<div class=\"body-bg-box-odd\" style=\"left: "+ i*shapeWidth +"px; top: "+ j*shapeHeight +"px;\"> </div>";
}
}
}
bodyBg.innerHTML = bodyBgHTML;
var bgBoxesDivs = document.querySelectorAll(".body-bg-box-even, .body-bg-box-odd");
setInterval(()=>{
for(i=0; i<5; i++){
bgBoxesDivs[Math.round(Math.random()*1000,0)%bgBoxesDivs.length].style.animation = "bg-shapes 3s";
bgBoxesDivs[Math.round(Math.random()*1000,0)%bgBoxesDivs.length].style.animation = "bg-shapes-color 1s";
}
},500);
}
bgBoxes();
window.addEventListener("resize",()=>{
bgBoxes();
});
</script>
Toggle Theme using JavaScript
I also added a Dark mode theme for the website using JavaScript DOM manipulations.
I initialised a variable named theme to 0, then defined a function named toggleTheme()
, which will be called when the toggle theme button is clicked.
<style>
#toggle-theme{
position: fixed;
bottom: 35px;
right: 35px;
background-color: rgba(25, 25, 85, 0.5);
padding: 5px;
border-radius: 50%;
height: 50px;
width: 50px;
z-index: 99;
}
#toggle-theme:hover{
cursor: pointer;
}
</style>
<div id="toggle-theme" onclick="toggleTheme()"><img alt="toggle-theme" id="toggle-icon-img" src="dark-mode-icon.png" height="50px"></div>
In the function, I initialised a variable for each element to apply the toggle theme on, then if the theme value was equal to 0, then applied light theme properties to the elements and updated the theme value to 1; else, applied dark theme properties to the elements and updated the theme value to 0.
<script>
var theme = 0;
function toggleTheme(){
const bodyTag = document.getElementsByTagName("body")[0];
var bodyBg = document.getElementById("body-bg");
const navDiv = document.getElementById("nav-div");
// ... similarly for many more elements
if(theme == 1){
bodyTag.style.backgroundColor = "rgb(245, 248, 253)";
bodyTag.style.color = "rgb(245, 248, 253)";
navDiv.style.color = "rgb(5, 12, 61)";
navDiv.style.backgroundColor = "rgba(197, 213, 232, 0.8)";
// ... similarly for many more elements
theme = 0;
}
else{
bodyTag.style.backgroundColor = "rgb(8, 17, 32)";
navDiv.style.backgroundColor = "rgba(55, 78, 106, 0.8)";
navDiv.style.color = "rgb(225, 227, 244)";
navDiv.style.boxShadow = "3px 3px 30px rgba(214, 208, 240, 0.1)";
// ... similarly for many more elements
theme = 1;
}
}
</script>
Then, I added the relevant images of certificates, projects, logos, etc, into the website wherever suitable. After applying some final tweaks and touches, the portfolio website was ready!
Deployment:
The portfolio website was hosted locally till now; it was time to deploy and host it online, so it becomes accessible to everyone.
While there are multiple options to deploy and host a website online, I chose GitHub Pages.
I created a repository in GitHub, uploaded the files, and hosted it using GitHub Pages. Now my website is Live!
GitHub Repository
Website Live Preview
Testing
Now it was time to test my website. I used Google's PageSpeed Insights Website to check my portfolio website's performance, accessibility, best practices and SEO (Search Engine Optimisation). Initial PageSpeed tests showed image size and optimisation errors.
After converting the images to WebP, matching rendered sizes, adding alt text, meta tags, improving meta tags, and some similar optimisations. PageSpeed Insights reported a score of 100/100 in performance, accessibility, best practices, and SEO on 18-10-2025.
Later, I also tested the carbon footprint of my portfolio website and got a website score of 97% in the report generated by thecarbonfootprint.ai on 18-10-2025
I also tested the responsiveness of my portfolio website on different screens to see how my portfolio website performed on different screens and it performed well.
Well, that was how I made my portfolio website using HTML, CSS and JavaScript. Ready to build yours? Feel free to comment your thoughts on this!
Thank you for taking your time to read this article
Top comments (1)
So nice, and so blue - is it blue? I think so…
But what really matters are the metrics, the conversion rate, and whether you like it. Everything else is irrelevant.