A week ago, one of my friends, Noah Kleij, reached out to me about building a website where he could share his knowledge of chemistry and other subjects for free. Noah holds a PhD in General and Organic Chemistry from the University of Manchester and wanted to serve as a positive example to the world. He decided to create and publish educational content on a website without any ads.
This was a fantastic opportunity for me, as I had always wanted to create an educational site but lacked the expertise to make it worthwhile. I loved his idea and immediately started working on it for free because I wanted to contribute meaningfully to the noble cause he had undertaken.
Initially, I considered using React as a framework but soon realized it might not be necessary since he only wanted a basic website. I started with a simple concept and a name, which I got from ChatGPT—Neuron IQ, a fitting choice.
For the framework, I decided to stick with basic HTML, CSS, and JavaScript. I built tools using these technologies, like a professional developer. While I could have used React, working with plain HTML, CSS, and JavaScript allowed me to understand how routers and other features are crafted from the ground up. Interestingly, I didn’t use Node.js either, keeping the project lightweight and straightforward.
What I appreciated the most was that he gave me a lot of creative freedom in designing the site's layout and style. I truly valued this opportunity. While I’m not a professional UI/UX designer, I’ve developed enough websites to understand the basics of design and typography, and yeah, I choose, Dark theme, because, I didn't want to give myself seizure.
That's a lot of backstory, let's get straight to content;
Hour 1:
Like any good project, we started with the basics. I fired up VS Code and, using the !
shortcut, quickly generated my basic HTML boilerplate:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document Title</title>
<link rel="stylesheet" href="style.css">
<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=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet">
<script src="script.js" defer></script>
</head>
<body>
</body>
</html>
I have a custom-modified version of this boilerplate with style.css
and script.js
already included, saving me the effort of adding them manually.
I started by setting the title, which is crucial for SEO and how the site appears in browser tabs. The title I settled on, with help from ChatGPT, was: "Neuron IQ - High-Quality Educational Notes and Answers."
Next, I focused on the user experience, starting with the header. A clear and user-friendly navigation is key to a good website—or at least, that’s what I’ve been taught.
<header>
<nav>
<div class="logo">Neuron IQ</div>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="subjects.html">Subjects</a></li>
<li><a href="about.html">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
</header>
I used a custom boilerplate for the navigation, which automatically includes these elements. I quickly updated the addresses, except for the contact page, as we were still undecided about its content and structure. For now, I added links to the homepage, subjects page, and about page. Most of these are placeholders, except for the homepage, which is currently my main focus.
I wanted to create a striking visual section to immediately engage visitors, so I designed a hero section with a clear and impactful message:
<section class="hero">
<div class="hero-content">
<h1>Unlock Your Potential with Neuron IQ</h1>
<p>Your Source for High-Quality Educational Notes and Answers</p>
<a href="#" class="btn">Explore Our Resources</a>
</div>
</section>
The <h1>
tag presents a strong and immediate value proposition. A <p>
tag provides a concise explanation of what Neuron IQ offers. I included a button labeled "Explore Our Resources" to encourage user interaction and guide them to the next step.
And, for the Hero, I specifically wanted to address the user concerns, specifically, why they should choose NeuronIQ and not something like study.com
Hour 2
Time flies quickly, but I’ve made some progress on the site. Next, I focused on creating a visually appealing section for subjects. I initially stumbled a bit but eventually returned to basics, opting for a simple and functional layout that works:
<section class="categories">
<h2>Explore Our Subjects</h2>
<div class="categories-grid">
<a href="#">
<img src="icons/maths.png" alt="Mathematics Icon">
<h3>Mathematics</h3>
</a>
<a href="#">
<img src="icons/physics.png" alt="Physics Icon">
<h3>Physics</h3>
</a>
<a href="/Chemistry/index.html">
<img src="icons/chemistry.png" alt="Chemistry Icon">
<h3>Chemistry</h3>
</a>
<a href="#">
<img src="icons/biology.png" alt="Biology Icon">
<h3>Biology</h3>
</a>
<a href="#">
<img src="icons/programming.png" alt="Programming Icon">
<h3>Computer Science</h3>
</a>
<a href="#">
<img src="icons/history.png" alt="History Icon">
<h3>History</h3>
</a>
<a href="#">
<img src="icons/english.png" alt="English Icon">
<h3>English Literature</h3>
</a>
</div>
</section>
I chose a grid layout instead of a traditional list because it’s clean, standard, and allows users to quickly find the subjects they’re interested in. Plus, I was inspired by how platforms like Udemy organize their categories—it’s intuitive and visually engaging.
Initially, I considered a more playful design:
However, I realized the content Noah would be adding is advanced, and that design felt too simplistic—more suited for kids than for a serious educational platform. This led me to pivot to a more professional and mature look.
And, spent about 45 minutes in this.
Then, I wanted to add a social proof, like a testimonial section. I quickly added it, took about 20 minutes more.
<section class="testimonials">
<h2>What Students Are Saying</h2>
<div class="testimonial-slider">
<div class="testimonial-slide">
<p>"Neuron IQ helped me ace my exams! The notes are so clear and easy to understand."</p>
<p class="author">- Sarah J.</p>
</div>
<div class="testimonial-slide">
<p>"I love how comprehensive and helpful the resources are. I can always find what I need!"</p>
<p class="author">- Alex M.</p>
</div>
<div class="testimonial-slide">
<p>"I have been using this since last year and have gotten better at studies!"</p>
<p class="author">- Mike</p>
</div>
</div>
<div class="slider-buttons">
<button class="prev-btn">←</button>
<button class="next-btn">→</button>
</div>
</section>
Hour 3
Now, with basics done, I want a thing to present over there, a section showing the most recent resources:
<section class="latest">
<h2>Latest Resources</h2>
<div class="latest-grid">
<div class="latest-card">
<h3>Introduction to Calculus</h3>
<p>A brief introduction to the fundamental concepts of Calculus.</p>
<a href="#">Read More</a>
</div>
<div class="latest-card">
<h3>Quantum Mechanics</h3>
<p>Introductory quantum mechanics notes for those interested in the subject.</p>
<a href="#">Read More</a>
</div>
<div class="latest-card">
<h3>History of World War II</h3>
<p>An exploration of events that led to World War II and its consequences.</p>
<a href="#">Read More</a>
</div>
</div>
</section>
Yeah, this section is intentionally basic—I didn’t want anything more from it. The content should speak for itself, not the design. Of course, I want the website to look modern and not like something from the '90s, but for now, I prefer a clean, simple design rather than going all-in on a flashy Web 3.0 aesthetic. That’s just my style at the moment.
After 2 hours and 40 minutes of work, we had almost everything done. I then added a call-to-action section and wrapped it up with a simple footer:
<footer>
<p>© 2025 Neuron IQ. All rights reserved.</p>
<ul>
<li><a href="about.html">About Us</a></li>
<li><a href="#">Contact Us</a></li>
<li><a href="#">Privacy Policy</a></li>
<li><a href="#">Terms of Use</a></li>
</ul>
</footer>
The footer includes a few important links that I felt were essential for the site.
Three hours into the project, we completed the basic HTML structure of the page. The next steps are to style the page, add the script file, and populate it with content.
Hour 4: Setting Up the Basics
I started by setting up the fundamental styling for the page. I always begin with the body
to establish the overall look and feel.
body {
font-family: 'Poppins', sans-serif;
margin: 0;
padding: 0;
line-height: 1.6;
color: #e0e0e0;
background-color: #1a1a1a;
}
Here, I set the font to ‘Poppins,’ removed default margins and padding, adjusted the line-height for readability, set the text color to a light grey, and used a dark background. I chose the dark theme to make sure no one gets seizure and also because it looks cooler, especially on a website dedicated to learning and STEM.
Next, I moved on to styling the header. A good header can make or break the first impression.
header {
background: #252525;
padding: 1rem 0;
box-shadow: 0 2px 4px rgba(255,255,255,0.1);
position: sticky;
top: 0;
z-index: 100;
}
I added a dark grey background, padding, a subtle shadow, and set its position to sticky, to ensure that the navbar is always visible at the top as the user scrolls. This provides a continuous navigation experience, like the navbar is following you.
Now, I styled the navigation bar itself:
nav {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
This uses flexbox to align the logo to the left and the navigation links to the right. I also added max-width
for responsiveness and centering, ensuring it doesn't stretch too much on large screens.
For the logo, I used a unique font color for brand recognition:
.logo {
font-size: 1.8rem;
font-weight: 600;
color: #64b5f6;
}
And the navigation links:
nav ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
nav ul li {
margin-left: 20px;
}
nav ul li a {
text-decoration: none;
color: #e0e0e0;
transition: color 0.3s;
}
nav ul li a:hover {
color: #64b5f6;
}
The style is standard and clean, but I always aim for simplicity rather than using overly complex designs. I removed default list styling, used flexbox to display the links horizontally, added spacing between each link and a nice hover effect.
For consistent spacing throughout the site, I styled the section
:
section {
max-width: 1200px;
margin: 40px auto;
padding: 20px;
text-align: center;
}
This gives all sections the same max-width, margin, and text alignment.
Hour 5: Styling the Hero Section
Moving on to the hero section, I wanted a striking visual element to capture attention:
.hero {
background: linear-gradient(135deg, #2c3e50, #1a237e);
padding: 80px 20px;
color: #e0e0e0;
}
I used a linear gradient for the background, which provides depth and a modern feel, and added padding for better spacing.
.hero-content {
max-width: 700px;
margin: 0 auto;
opacity: 0; /* Start hidden */
transform: translateY(20px); /* Slight move down */
transition: opacity 0.8s ease, transform 0.8s ease; /* Added transition */
}
.hero.loaded .hero-content {
opacity: 1;
transform: translateY(0); /* Move to original postition */
}
.hero-content h1, .hero-content p{
opacity:0;
transform: translateY(-20px);
transition: opacity 0.8s ease, transform 0.8s ease; /* Added transition */
}
.hero.loaded .hero-content h1, .hero.loaded .hero-content p{
opacity: 1;
transform: translateY(0); /* Move to original postition */
}
.hero-content .btn{
opacity:0;
transform: translateY(20px);
transition: opacity 0.8s ease, transform 0.8s ease; /* Added transition */
}
.hero.loaded .hero-content .btn{
opacity: 1;
transform: translateY(0); /* Move to original postition */
}
This styles the hero’s text content, ensuring it doesn't spread too much, centers it horizontally and gives a smooth transition effect when hero-section is loaded.
The heading and paragraph styles are simple and standard:
.hero h1 {
font-size: 2.5rem;
margin-bottom: 15px;
}
.hero p {
font-size: 1.2rem;
margin-bottom: 25px;
}
Finally, the button:
.btn {
display: inline-block;
background: #64b5f6;
color: #252525;
padding: 12px 25px;
text-decoration: none;
border-radius: 5px;
transition: background 0.3s;
}
.btn:hover {
background: #42a5f5;
}
This makes the button visually appealing and interactive, with the background changing on hover.
Hour 6: Styling the Introduction, Categories and Testimonials
Now, I needed to style the intro section.
.intro-grid{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
text-align: left;
}
.intro-grid div{
padding: 20px;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.intro-grid div.fade-in {
opacity: 1;
transform: translateY(0);
}
.intro-grid div img{
width: 30px;
height: 30px;
margin-right: 10px;
float:left;
}
.intro-grid div h3{
margin-bottom: 0;
padding-bottom: 0px;
}
This creates a responsive grid for the intro section using a grid to align them side-by-side, and provides subtle fade-in animation for each intro-card.
For the categories section:
.categories-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
padding-top: 20px;
}
.categories-grid a{
display: flex;
flex-direction: column;
align-items: center;
text-decoration: none;
color: #e0e0e0;
background: #252525;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(255,255,255,0.1);
transition: box-shadow 0.3s;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.categories-grid a:hover{
box-shadow: 0 4px 8px rgba(255,255,255,0.2);
}
.categories-grid a.fade-in {
opacity: 1;
transform: translateY(0);
}
.categories-grid a img {
width: 40px;
height: 40px;
margin-bottom: 10px;
}
This creates another responsive grid, ensuring subjects are displayed in an organized way with a subtle fade-in animation. I used flexbox to align the images and text, added padding and spacing and hover effects.
And now for the testimonial section, a carousel.
.testimonials{
background-color: #252525;
padding: 40px 20px;
}
.testimonial-slider{
display: flex;
overflow: hidden;
width: 80%;
margin: 20px auto;
}
.testimonial-slide{
min-width: 100%;
text-align: left;
padding: 20px;
box-sizing: border-box;
transition: transform 0.5s ease-in-out, opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
opacity: 0.7;
transform: scale(0.95);
}
.testimonial-slide.active{
opacity: 1;
transform: scale(1);
}
.testimonial-slide p:last-child{
margin-top: 10px;
font-weight: 500;
text-align: right;
font-style: italic;
}
.slider-buttons{
text-align: center;
margin-top: 10px;
}
.slider-buttons button{
background: none;
border: none;
font-size: 2rem;
cursor: pointer;
margin: 0 10px;
color: #ddd;
}
This is a standard css for the testimonial carousel, with transition and scale effects for visual appeal.
Hour 7: Styling Latest Resources and CTA
I now style the latest resources section.
.latest-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
text-align: left;
}
.latest-card {
background: #252525;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(255,255,255,0.1);
transition: box-shadow 0.3s;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.latest-card:hover {
box-shadow: 0 4px 8px rgba(255,255,255,0.2);
}
.latest-card.fade-in {
opacity: 1;
transform: translateY(0);
}
.latest-card h3 {
margin-bottom: 10px;
}
.latest-card a {
display: inline-block;
margin-top: 10px;
text-decoration: none;
color: #64b5f6;
font-weight: 500;
transition: color 0.3s;
}
.latest-card a:hover {
color: #42a5f5;
}
Here, I implemented another responsive grid, styled the cards, added hover effects, and included a fade-in animation to bring these cards to life.
Now, for the call-to-action section, CTA:
.cta {
background: #252525;
color: #fff;
padding: 60px 20px;
margin-top: 40px;
}
.cta h2 {
font-size: 2rem;
margin-bottom: 20px;
}
This is a simple, distinct section for users to get more engaged with the site.
Hour 8: Finishing Touches
To complete the styling, I focused on the footer:
footer {
background: #1a1a1a;
color: #fff;
padding: 20px;
text-align: center;
}
footer ul {
list-style: none;
padding: 0;
margin: 10px 0;
display: flex;
justify-content: center;
}
footer ul li {
margin: 0 10px;
}
footer ul li a {
color: #fff;
text-decoration: none;
transition: color 0.3s;
}
footer ul li a:hover {
color: #ccc;
}
This styles the footer, includes a list of links, and sets hover effects.
Next, I added responsive design using media queries:
@media (max-width: 768px){
nav{
flex-direction: column;
text-align: center;
}
nav ul {
flex-direction: column;
margin-top: 10px;
}
nav ul li{
margin: 5px 0;
}
.intro-grid{
text-align: center;
}
.intro-grid div{
padding: 20px 0;
}
.intro-grid div img{
float: none;
margin-right: 0px;
}
}
This ensures the site looks good on smaller devices, adjusting navigation and grid layouts.
Hour 9: Subject and About Page Styling
Finally, I add the styling for subject and about pages, keeping them as minimal as possible, since the objective of this website is to promote the knowledge of Noah. So, the design is kept as minimal as possible.
.subject-hero{
background: linear-gradient(135deg, #2c3e50, #1a237e);
padding: 80px 20px;
color: #e0e0e0;
}
.subject-hero-content{
max-width: 700px;
margin: 0 auto;
}
.subject-list{
margin: 40px auto;
}
.subject-grid{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.subject-grid a{
display: flex;
flex-direction: column;
align-items: center;
text-decoration: none;
color: #e0e0e0;
background: #252525;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(255,255,255,0.1);
transition: box-shadow 0.3s;
}
.subject-grid a img{
width: 40px;
height: 40px;
margin-bottom: 10px;
}
.subject-grid a:hover{
box-shadow: 0 4px 8px rgba(255,255,255,0.2);
}
/* About page */
.about-hero{
background: linear-gradient(135deg, #2c3e50, #1a237e);
padding: 80px 20px;
color: #e0e0e0;
}
.about-hero-content{
max-width: 700px;
margin: 0 auto;
}
.about-container{
margin: 40px auto;
max-width: 800px;
text-align: left;
}
.about-container h2{
margin-bottom: 10px;
}
.about-team{
margin: 40px auto;
}
.team-grid{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.team-member{
background: #252525;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(255,255,255,0.1);
transition: box-shadow 0.3s;
text-align: center;
}
.team-member:hover{
box-shadow: 0 4px 8px rgba(255,255,255,0.2);
}
.team-member img{
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
margin-bottom: 10px;
}
These styles follow the same theme, creating a consistent look across all pages, with minor changes to reflect specific page elements.
And that completes the styling for Neuron IQ, taking about six hours to get everything where I wanted it. Now, it’s time to dive into the final piece of the puzzle: the JavaScript.
Before moving to the JS let's see our website looks right now:
Or, if you want to see the live-preview, its over here: NeuronIQ
This part, I gotta be honest, tested my patience a bit. I mean, I knew it wouldn't be a cakewalk, but still…
Hour 10: Starting with Animations and Scroll Effects
I started off with the goal to make the website feel alive, not just a static page. I began by setting up the hero section animation:
document.addEventListener('DOMContentLoaded', function() {
// Hero Section animation
const hero = document.querySelector('.hero');
hero.classList.add('loaded');
This is straightforward—once the DOM is fully loaded, I add a class that triggers the animation for the hero section and it animates smoothly, just as planned.
Next, I aimed to add animations when sections scroll into the viewport. For this, I used the IntersectionObserver
API. This part was a little more challenging.
// Intro section animation on scroll
function handleIntersection(entries, observer){
entries.forEach(entry => {
if(entry.isIntersecting){
entry.target.classList.add('fade-in');
observer.unobserve(entry.target);
}
});
}
const observer = new IntersectionObserver(handleIntersection, {
threshold: 0.2
});
const introDivs = document.querySelectorAll('.intro-grid div');
introDivs.forEach(div=> {
observer.observe(div)
})
This code sets up an observer for the .intro-grid div
elements. When they come into view, it adds the fade-in
class, triggering the animation, and then removes observer since its not needed again.
I did the same thing for categories section:
// Category Section animation on scroll
function handleIntersectionCategories(entries, observer){
entries.forEach(entry => {
if(entry.isIntersecting){
entry.target.classList.add('fade-in');
observer.unobserve(entry.target);
}
});
}
const observerCategories = new IntersectionObserver(handleIntersectionCategories, {
threshold: 0.2
});
const categoryLinks = document.querySelectorAll('.categories-grid a');
categoryLinks.forEach((link, index)=> {
link.style.transitionDelay = `${index * 0.1}s`;
observerCategories.observe(link)
})
I used the same structure for the categories section and added a delay, with a very basic delay. It still needed a lot of refinement.
Hour 11: The Testimonial Slider
I then moved on to implement a testimonial carousel:
// Testimonials slider
const slider = document.querySelector('.testimonial-slider');
const slides = document.querySelectorAll('.testimonial-slide');
const prevBtn = document.querySelector('.prev-btn');
const nextBtn = document.querySelector('.next-btn');
let currentIndex = 0;
function updateSlider() {
slides.forEach((slide, index)=>{
slide.classList.remove('active')
});
slides[currentIndex].classList.add('active');
slider.style.transform = `translateX(-${currentIndex * 100}%)`;
}
let autoSlideInterval;
function startAutoSlide(){
autoSlideInterval = setInterval(()=>{
currentIndex = (currentIndex + 1) % slides.length;
updateSlider();
}, 5000)
}
function stopAutoSlide(){
clearInterval(autoSlideInterval)
}
prevBtn.addEventListener('click', () => {
stopAutoSlide();
currentIndex = (currentIndex - 1 + slides.length) % slides.length;
updateSlider();
startAutoSlide();
});
nextBtn.addEventListener('click', () => {
stopAutoSlide();
currentIndex = (currentIndex + 1) % slides.length;
updateSlider();
startAutoSlide();
});
startAutoSlide();
This is a fairly standard setup for a slider. I added functions to update the slider, control movement, and set up automatic sliding with a 5-second delay between slides.
Lastly, I implemented the scroll effect for the latest resources section.
// Latest resource animation on scroll
function handleIntersectionLatest(entries, observer){
entries.forEach(entry => {
if(entry.isIntersecting){
entry.target.classList.add('fade-in');
observer.unobserve(entry.target);
}
});
}
const observerLatest = new IntersectionObserver(handleIntersectionLatest, {
threshold: 0.2
});
const latestCards = document.querySelectorAll('.latest-card');
latestCards.forEach(card=> {
observerLatest.observe(card)
})
});
I followed a similar structure as before, observing and adding fade-in
class to these cards, and unobserving it.
Hour 12 & 13: Frustration and Debugging
Here's where things got hairy. After testing, I realized that the scroll animations were not working consistently. Sometimes, the animations would trigger way too early, sometimes, they wouldn't trigger at all, and sometimes, they would just flicker on the page. It was a mess.
I spent the next two hours diving into the code, trying different values for the threshold
, and even trying different methods to trigger the animations. I was getting frustrated because no matter what I did, the sections kept animating randomly.
Initially, I thought the threshold wasn't set correctly, since it was too low, and the elements would be seen by the observer even when the user hasnt scrolled to it. So, I thought, 0.2 threshold seems appropriate for all elements. Well, apparently not; some elements needed some other number.
I tried logging every step, inspecting the browser's developer console, and even Googled every possible error message I encountered (and Stack Overflow became my best friend during this time). It turns out, I was setting up each observer on multiple elements with only one threshold, and for some elements, that wasn't working. That's why it would flicker, animate randomly, or not animate at all.
My approach was clearly wrong, and I had to adjust it. I ended up spending about 2 hours debugging this mess. Eventually, I re-wrote my observer to get the specific target threshold. After the changes, finally, the animations were working as expected.
I had to modify each handleIntersection
function to receive the proper threshold, and that fixed it.
So after 3 hours of JavaScript coding, and 2 hours of debugging, I'm finally done with my coding for this day!
Top comments (2)
Why not React? Atleast you should have tried it out.
I actually did try React first, but, I ran into some problems. And, didn't want to waste my time debugging React code.
So, the next day, I just decided to make it with plain-simple HTML, CSS and JS. Back to the basics. A lot of design choice were influenced by the React try.
If you want to see the code or add any improvements, here's the Github link:
Github — Neuron IQ 2
And the netlify link is this:
Neuron IQ 2 —Netlify