Building Masite: A Portfolio Website Builder — Part 3: User Interface Design and Customization
This 8 part series focuses on building Masite: A Portfolio Website Builder. Link to the website: https://masite-portfolio-website-builder.vercel.app/
Hi there, I am Mrinal Prakash. I am a Software Developer with a passion for building efficient, scalable, and user-friendly web applications.
In the previous parts of this series, we set up the Masite project using React and organized its core components.
Now, it’s time to dive into one of the most crucial aspects of any portfolio builder: the User Interface (UI) Design and Customization.
This part of the series focuses on designing a responsive, modern, and customizable UI that can adapt to various user needs and screen sizes.
1. Header Section
Starting with the JSX code
The header is one of the most important elements of any website. It usually contains navigation links that allow users to explore different sections. The header design we’re working on is simple, clean, and responsive.
Here’s how we can structure the Header component using React using two functions Header and Headerlinks:
import React from 'react';
export default function Header({ links }) {
return (
<header className="header">
<nav className="topnav">
<div className="hamburger">
<div></div>
<div></div>
<div></div>
</div>
<HeaderLinks links={links} />
</nav>
</header>
);
}
function HeaderLinks({ links }) {
return (
<ul>
{links.map((link, index) => (
<li key={index}>
<a href={`#${link.toLowerCase()}`}>{link}</a>
</li>
))}
</ul>
);
}
In this code:
- The
Header
component takes alinks
prop and renders a navigation bar (nav
). - Inside the
nav
, we use ahamburger
menu (initially hidden) for small screens and aHeaderLinks
component that maps over the provided links.
Let’s add the CSS that will style our header and make it responsive. We will name it header.css
.header {
background-color: transparent;
}
.topnav {
width: 500px;
margin: 75px auto;
padding: 10px;
height: 30px;
background-color: white;
line-height: 1.8;
border-radius: 25px;
box-shadow: 0px 4px 12px -8px #888;
}
.topnav > ul {
display: flex;
align-items: center;
justify-content: space-around;
gap: 30px;
}
.topnav > ul > li > a {
padding: 10px;
color: var(--primary-font-color);
font-weight: bold;
}
.hamburger {
position: relative;
display: none;
}
.hamburger > div {
height: 4px;
width: 23px;
background-color: #888;
margin: 5px auto;
}
@media screen and (max-width: 425px) {
.topnav {
width: 30px;
position: fixed;
right: 70px;
top: 20px;
margin: 0;
z-index: 1;
}
.topnav > ul > li > a {
display: none;
}
.hamburger {
display: block;
}
}
Key Points in the Styles:
top sever styling
: Thetopnav
class styles the navigation bar with a fixed width of 500px, a white background, and subtle shadowing. We use flexbox (display: flex
) to evenly space out the navigation links.- Hamburger Menu: The hamburger icon is initially hidden, but we vmake it visible on smaller screens using media queries.
To ensure the header works seamlessly across different screen sizes, we add a media query that kicks in for devices with a screen width of 425px or less.
The top navigation menu shrinks into a hamburger icon, and the links disappear until triggered by the hamburger click event (event handling to be added later).
Creating the Common CSS
It is important to create a common CSS in order to keep the styling same for some elements across all the sections which we will create in this portfolio website:
body {
margin: 0;
background-color: #eee;
position: relative;
}
ul{
list-style-type: none;
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
section{
--primary-font-color: #666;
--secondary-font-color: #888;
--button-background: #fff;
}
Key Points in the CSS:
- Top Navigation Styling: The
.topnav
function sets the width at 500px, centers it, and adds styling such as a white background, padding, and box-shadow. - Flexbox for Layout: The
ul
inside.topnav
uses flexbox to align and space out the navigation links, making the layout easy to manage. - Hamburger Menu: Initially hidden on larger screens, the hamburger icon is displayed when the screen size falls below 425px, ensuring a responsive mobile-friendly design.
Running the Code
Now after writing all the codes and designing the header section, it’s now time to run the code, we will use the command
npm run start
After we run the code, we will get to see the following on our http://localhost:3000
This means that our code is working properly.
2. Home Section
This section typically includes the user’s name, profile image, an introduction, and links to social media profiles, all presented in a responsive, visually appealing way.
Writing the JSX Code
Here is the implementation of the Home component using React:
import React from "react";
import './about.css';
export default function About({name, img, title, description, socialLinks})
{
return (
<section className="home">
<img src={img} alt={name} />
<h1>{name}</h1>
<p className="sm-intro">{title}</p>
<p className="intro-part">{description}</p>
<SocialLinks socialLinks={socialLinks} />
</section>
);
}
function SocialLinks({socialLinks})
{
return (
<ul className="social-links">
{socialLinks.map((link, index) => (
<li key={index}>
<a href={link.href}>
<img src={link.imgSrc} alt={link.alt} />
</a>
</li>
))}
</ul>
)
}
In this code:
- About component: This component displays the user’s name, profile image, introduction, and a short description.
- SocialLinks component: This renders a set of social media icons, with links that open the user’s social media profiles.
Styling the Page using CSS
Below is the CSS that styles the Home section, ensuring that it is both visually engaging and responsive across various screen sizes:
.home
{
text-align:center;
font-size:24px;
color:#666;
width:750px;
margin:auto;
}
.home > img
{
margin-top:90px;
border:10px solid white;
border-radius:100px;
}
.home > h1
{
margin-top:30px;
font-weight:bold;
}
p.sm-intro
{
margin-top:20px;
font-size:20px;
color:#666;
}
p.intro-part
{
margin-top:20px;
font-size:20px;
color:#888
}
ul.social-links
{
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin: auto;
max-width: 400px;
justify-content: center;
}
ul.social-links > li {
text-align: center;
}
ul.social-links > li > a {
display: inline-block;
height: 60px;
width: 60px;
}
ul.social-links > li > a > img {
height: 60px;
width: 60px;
}
@media screen and (max-width:425px)
{
.home > img
{
margin-top:90px;
margin:0 auto;
height: auto;
width: auto;
}
.home
{
width:385px;
margin:100px auto;
height:500px;
}
p.sm-intro
{
font-size: 10px;
padding: 0 20px;
}
p.intro-part
{
font-size: 15px;
padding: 0 20px;
}
.home > h1
{
font-size: 20px;
}
ul.social-links > li > a
{
height: 20px;
width: 20px;
}
ul.social-links > li > a > img
{
height: 20px;
width: 20px;
}
ul.social-links
{
gap: 10px;
padding: 0 50px;
}
}
Key Points in the Design:
- Centered Layout: The content is centered on the page for a clean, balanced look, making it easy for users to focus on the main details.
- Grid for Social Links: The social media icons are displayed in a grid using
grid-template-columns
, ensuring a neat and structured appearance. - Responsive Design: On smaller screens, the layout shrinks, and the profile image is centered to ensure the design adapts smoothly to mobile devices.
Modifying App.js
To integrate the About section into the application, we’ll update the App.js
file. This will render the About component using data from a configuration file (app.config
).
import logo from './logo.svg';
import './App.css';
import appConfig from './app.config';
import Header from './components/Header/header';
import './components/Header/header.css';
import './components/Header/common.css';
import About from './components/About/about.jsx';
function App() {
return (
<div className="App">
<Header links={appConfig.links} />
<About name={appConfig.about.name} img={appConfig.about.img} description={appConfig.about.description} title={appConfig.about.title} socialLinks={appConfig.about.socialLinks} />
</div>
);
}
export default App;
The App
component now imports the About component and passes the user’s data (name, profile image, description, and social links) from the app.config
file.
Running the Code
Having written the frontend and designing of the page, we will now run the code using npm run start to get the following screen:
3. Articles Section
Now we will move ahead to the articles section. For the time-being, I am using static articles which I will be defining in app.config.js file, later we will dynamically fetch articles from medium, hashnode and other sites.
We will first define articles in app.config.js like this:
articles: [
{
date: "September 28, 2024",
title: "Building Masite",
description: "Masite: A Portfolio Website Builder helps you to build your customized website",
link: "#"
},
],
Writing the JSX Code
This Articles
component provides a clean and simple way to dynamically render a list of articles, each with a date, title, description, and link to read more. By using props, it can be reused with different data sets, making it flexible and modular.
import React from 'react';
import './articles.css';
const Articles = ({ articles }) => {
return (
<section>
<article className="tools">
<h2>Articles</h2>
{articles.map((article, index) => (
<article key={index}>
<p>{article.date}</p>
<h3>{article.title}</h3>
<p>{article.description}</p>
<a href={article.link}>Read More</a>
</article>
))}
</article>
</section>
);
};
export default Articles;
Writing CSS
article.tools
{
width:1000px;
margin:auto;
margin-top:150px;
}
article.tools>h2
{
color:#666;
text-align: left;
}
article.tools>article
{
background-color:white;
border-radius:8px;
margin-top:30px;
padding:30px;
}
article.tools>article>p
{
font-size:14px;
color:#666;
text-align: left;
margin-left: 15px;
}
article.tools>article>p:first-child
{
border-left:4px solid #eee;
padding-left: 8px;
}
article.tools>article>h3
{
font-size:16px;
font-weight:bold;
color:#888;
text-align: left;
margin-left: 15px;
}
article.tools>article>a
{
text-decoration:none;
color:#888;
padding:10px 8px 10px 8px;
margin-left:10px;
display:flex;
align-items:center;
width:90px;;
background-image:url(../../img/right-arrow-dark.png);
background-repeat: no-repeat;
background-position: right;
}
article.tools>article>a:hover
{
background-color:black;
color:white;
background-image:url(../../img/right-arrow-light.png);
background-repeat: no-repeat;
}
@media screen and (max-width:425px)
{
article.tools
{
width:calc(100vw - 50px);
margin:0px auto;
padding-left: 50px;
}
article.tools>article
{
margin-top:30px;
padding:10px 10px 10px 10px;
}
article.tools>article>p
{
font-size:12px;
color:#666;
}
.tools > h2
{
font-size: 15px;
}
}
Running the Code
When we run the above codes, we get to see the following screen:
4. Projects Section
The Projects section showcases the user’s work, giving a visual representation of the projects they’ve worked on, and making it easy for visitors to browse through them.
Modifying app.config.js file
import Github from './img/GitHub.png';
import Youtube from './img/Youtube.png';
import LinkedIn from './img/Linkedin.png';
import Instagram from './img/Instagram.png';
import VegKing from './img/vegking.png';
import CarStats from './img/carstats.png';
import BlogUIKit from './img/bloguikit.png';
import WebMonitor from './img/webmonitor.png';
const appConfig = {
// Previous Codes
projects: [
{
number: "01",
imgSrc: VegKing,
name: "VegKing"
},
{
number: "02",
imgSrc: CarStats,
name: "CarStats"
},
{
number: "01",
imgSrc: BlogUIKit,
name: "Blog UI Kit"
},
{
number: "01",
imgSrc: WebMonitor,
name: "Web Monitor"
},
],
};
export default appConfig;
Writing the JSX Code
Here’s the implementation of the Projects
component using React:
import React from "react";
import './projects.css';
export default function Projects({projects})
{
return (
<section className="projects">
<h2>Projects</h2>
<div>
{projects.map((project, index) => (
<ProjectItem project={project} index={index} key={index} />
))}
</div>
</section>
);
}
function ProjectItem({project, index})
{
return (
<div>
<span className={`no-${index+1}`}>{project.number}</span>
<img src={project.imgSrc} alt={project.name} />
<p className={`project-${index+1}`}>{project.name}</p>
</div>
);
}
In this code:
- Projects Component: This component receives a list of
projects
as props and maps through them, rendering aProjectItem
for each one. - ProjectItem Component: Displays an individual project with a number, image, and name.
Styling the Projects Section
The following CSS will style the Projects
section to make it visually appealing and responsive:
.projects > div
{
display: grid;
width: 1000px;
margin: 0 auto;
grid-template-columns: repeat(2, 1fr);
}
.projects > div > div
{
height: 300px;
width: 500px;
position: relative;
}
.projects > h2
{
width: 1000px;
margin: 20px auto;
color:var(--primary-font-color);
text-align: left;
}
.projects span
{
position: absolute;
top: 20px;
padding: 10px 20px;
color: white;
background-color: black;
font-size: 20px;
font-weight: bold;
border-left: 8px solid rgb(0 161 255 / 56%) ;
}
.projects span.no-1
{
border-left: 8px solid rgb(255 40 0 / 64%) ;
}
.projects span.no-4
{
border-left: 8px solid rgb(127 127 127 / 64%) ;
}
.projects p
{
padding: 15px 0 15px 20px;
bottom: 0;
height: 20px;
background-color:rgb(0 161 255 / 56%) ;
color: white;
font-weight: bold;
font-size: 20px;
position: absolute;
width: 480px;
margin: 0;
text-align: left;
}
.projects p.project-1
{
background-color:rgb(255 40 0 / 64%);
}
.projects p.project-4
{
background-color:rgb(127 127 127 / 64%);
}
@media screen and (max-width: 425px)
{
.projects > div > div
{
height: 210px;
position: relative;
margin:0px auto;
width:350px;
}
.projects h2
{
width: 385px;
font-size: 15px;
}
.projects
{
width:calc(100vw - 50px);
padding: 30px;
}
.projects > div
{
display: flex;
width: 385px;
gap: 30px;
flex-direction: column;
}
.projects img
{
height: 200px;
width:calc(100vw - 130px);
}
.projects p
{
width:calc(100vw - 130px);
font-size: 15px;
left: 25px;
bottom: 10px;
}
}
Modifying App.js
import logo from './logo.svg';
import './App.css';
import appConfig from './app.config';
import Header from './components/Header/header';
import './components/Header/header.css';
import './components/Header/common.css';
import About from './components/About/about';
import Articles from './components/Articles/articles';
import Projects from './components/Projects/projects';
function App() {
return (
<div className="App">
<Header links={appConfig.links} />
<About name={appConfig.about.name} img={appConfig.about.img} description={appConfig.about.description} title={appConfig.about.title} socialLinks={appConfig.about.socialLinks} />
<Articles articles={appConfig.articles} />
<Projects projects={appConfig.projects} />
</div>
);
}
export default App;
Running the code
We get to see the following output:
5. Presentation Section
The Presentations section allows users to feature their public talks or presentations. We’ll use images and titles to link to the presentation content.
Modifying app.config.js file
import Presentation01 from './img/presentation-01.png';
import Presentation02 from './img/presentation-02.png';
import PlayButton from './img/play-button.png';
const appConfig = {
presentations: [
{
title: "A Talk on Optimizing CSS for performance",
date: "November 13, 2023",
imgSrc: Presentation01,
playBtnImgSrc: PlayButton
},
{
title: "Make responsive website for ecommerce",
date: "December 13, 2023",
imgSrc: Presentation02,
playBtnImgSrc: PlayButton
}
],
};
export default appConfig;
JSX Code for Presentations
Here’s how to create the Presentation
component in React:
import React from 'react';
import './presentations.css';
export default function Presentation({ presentations }) {
return (
<section className="presentations">
<h2>Presentations</h2>
<div>
{presentations.map((presentation, index) => (
<PresentationItem presentation={presentation} key={index} />
))}
</div>
</section>
);
}
function PresentationItem({ presentation }) {
return (
<div>
<div className="presentation-video">
<img src={presentation.imgSrc} alt={presentation.title} />
<img src={presentation.playBtnImgSrc} alt="Play" />
</div>
<div>
<h4>{presentation.title}</h4>
<p>{presentation.date}</p>
</div>
</div>
);
}
In this code:
- Presentation Component: This component receives
presentations
data and maps through it to renderPresentationItem
. - PresentationItem Component: Displays an individual presentation with a play button.
Styling the Presentations Section
Below is the CSS to style the presentations section:
.presentations > h2 {
width: 1000px;
margin: 20px auto;
color: var(--primary-font-color);
text-align: left;
}
.presentations > div {
display: flex;
gap: 40px;
width: 1000px;
margin: 0 auto;
}
.presentations {
margin: 50px 0;
}
.presentations h4 {
font-size: 20px;
margin: 10px 10px 10px 0;
text-align: left;
}
div.presentation-video {
height: 300px;
width: 480px;
position: relative;
}
div.presentation-video > img:last-child {
position: absolute;
top: 120px;
right: 210px;
}
.presentations > div > div > div:last-child {
padding: 20px 20px 30px 20px;
background-color: white;
height: 60px;
text-align: left;
}
p {
color: var(--secondary-font-color);
}
@media screen and (max-width: 425px) {
.presentations > h2 {
width: 385px;
margin: 20px 40px;
}
.presentations > div {
width: 350px;
display: flex;
flex-direction: column;
}
div.presentation-video {
height: 210px;
width: 350px;
position: relative;
}
div.presentation-video > img:first-child {
height: 210px;
width: 350px;
}
div.presentation-video > img:last-child {
top: 76px;
right: 145px;
}
.presentations h4 {
font-size: 16px;
}
}
Key Points:
- Presentation Layout: The presentations are laid out in a flexible row that can wrap to fit smaller screens.
- Play Button Overlay: The play button is placed over the presentation thumbnail using absolute positioning.
Modifying App.js
import logo from './logo.svg';
import './App.css';
import appConfig from './app.config';
import Header from './components/Header/header';
import './components/Header/header.css';
import './components/Header/common.css';
import About from './components/About/about';
import Articles from './components/Articles/articles';
import Projects from './components/Projects/projects';
import Presentation from './components/Presentations/presentations';
function App() {
return (
<div className="App">
<Header links={appConfig.links} />
<About name={appConfig.about.name} img={appConfig.about.img} description={appConfig.about.description} title={appConfig.about.title} socialLinks={appConfig.about.socialLinks} />
<Articles articles={appConfig.articles} />
<Projects projects={appConfig.projects} />
<Presentation presentations={appConfig.presentations} />
</div>
);
}
export default App;
Running the Code
Now we will get to see the following screen when we run the code:
6. Footer Section
The Footer section is the last part of the website and typically contains contact information, links to important pages, or copyright information.
Writing the JSX Code
Here’s how to implement the Footer
component:
import React from "react";
import './footer.css';
export default function Footer({links, name})
{
return (
<div>
<hr />
<FooterContent links = {links} name = {name} />
</div>
);
}
function FooterContent({links, name})
{
return (
<footer>
<ul className="footer-links">
{links.map((link, index) => (
<li key={index}>
<a href={`#${link.toLowerCase()}`}>{link}</a>
</li>
))}
</ul>
<a>© 2024 {name}. All rights reserved</a>
</footer>
)
}
Styling the Footer
Here’s the CSS to style the footer:
footer
{
display: flex;
justify-content: space-around;
padding: 20px;
}
.footer-links
{
display: flex;
gap: 20px;
}
footer a
{
color: #666;
}
@media screen and (max-width: 425px)
{
footer
{
margin: 20px 0;
display: block;
}
footer > div > ul
{
flex-direction: column;
}
}
Key Points:
- Simple and Clean Design: The footer is centered and styled with a dark background and white text.
- Footer Links: Inline links to important pages like privacy policy and terms of service are included, and they change color on hover.
Modifying App.js
import logo from './logo.svg';
import './App.css';
import appConfig from './app.config';
import Header from './components/Header/header';
import './components/Header/header.css';
import './components/Header/common.css';
import About from './components/About/about';
import Articles from './components/Articles/articles';
import Projects from './components/Projects/projects';
import Presentation from './components/Presentations/presentations';
import Footer from './components/Footer/footer';
function App() {
return (
<div className="App">
<Header links={appConfig.links} />
<About name={appConfig.about.name} img={appConfig.about.img} description={appConfig.about.description} title={appConfig.about.title} socialLinks={appConfig.about.socialLinks} />
<Articles articles={appConfig.articles} />
<Projects projects={appConfig.projects} />
<Presentation presentations={appConfig.presentations} />
<Footer links={appConfig.links} name={appConfig.about.name} />
</div>
);
}
export default App;
Responsive Website Demo
The website that we have created is also responsive:
Conclusion
In this part of the Masite project, we designed and implemented key UI components for the header, home, articles, projects, presentations and footer sections.
These sections are responsive and offer a clean user interface that works across devices. In the next part, we’ll delve into adding dynamic content and enhancing interactivity.
In the next part, we will further upgrade our project and switch to NextJS.
If you like my work, connect with me on LinkedIn, follow me on X (formerly twitter) as well as on Medium