- Built with
- Project Setup
- Designing the Mobile Navigation Menu
- Disabling Animations on Page Load
- Planned Revisions
- Semantic HTML5 markup
- Flexbox
- CSS Grid
- Sass
- Javascript
In order to stay on top of compiling SCSS code, I used the VS Code extension Live Sass Compiler. This extension compiles your SCSS into CSS automatically on save.
I began my project by planning out the organization of my scss files. Here's a look at my folder structure:
/scss
/components
_footer.scss
_hero.scss
_index.scss // forwards
_mobile.scss
_nav.scss
_reset.scss // CSS reset by Josh Comeau
_sections.scss
/variables
_colors.scss
_fonts.scss
_index.scss // forwards
_math.scss
index.scss // main file
In the components directory, I separated my files into different parts of the site (ie. nav, hero, footer). The variables directory contained colors, fonts, and basic sizing information. My main index.scss file was relatively empty, just containing references to the components and variables:
@use './variables/' as *;
@use './components/' as *;
I found this structure fairly easy to work with, and it was nice being able to easily find the styles I was looking for.
To get the mobile navbar to work, I used the CSS clip-path property to create the shape of the menu:
ul {
clip-path: polygon(0 10%, 94% 10%, 100% 0%, 100% 100%, 0 100%);
}
I used javascript to add a "show" class to the ul when the menu was open, and a "hide" class when the menu was closed. As a bonus, I wanted to make sure the menu would close not just when the menu icon was clicked but also if a user clicked outside the menu, or if the user clicks an item in the navigation menu. The if and forEach statements below accomplish this.
hamburger.addEventListener('click', () => {
ul.classList.toggle('show');
ul.classList.toggle('hide');
const isVisible = ul.classList.contains('show');
if (isVisible) {
document.addEventListener('click', e => {
if (!document.querySelector('.nav').contains(e.target) && e.target !== hamburger) {
ul.classList.remove('show');
ul.classList.add('hide');
}
});
lis.forEach(li => {
li.addEventListener('click', () => {
ul.classList.remove('show');
ul.classList.add('hide');
});
});
}
});
Now, when the menu is opened, it is given the "show class" and the CSS animation shown below is triggered. The same happens when the menu is closed.
.show {
display: flex;
animation: slide-down 1000ms forwards;
}
.hide {
display: flex;
animation: slide-up 1000ms forwards;
}
@keyframes slide-down {
0% {
transform: scale(0);
transform-origin: top right;
}
100% {
transform: scale(1);
transform-origin: top right;
}
}
@keyframes slide-up {
0% {
transform: scale(1);
transform-origin: top right;
}
100% {
transform: scale(0);
transform-origin: top right;
}
}
By default, the "hide" animation will run every time the page loads or refreshes. I solved this issue by adding a preload class to remove animations. A setTimeout function removes this class after the page loads and a short delay, and then allows for animations to continue as normal.
setTimeout(function() {
ul.classList.remove('preload');
}, 1000);
I also decided to make the navbar sticky, and in doing so ran into contrast issues. I decided that adding a slightly transparent background color which only appears when the use scrolls outside the hero section would make the text more legible. This was not specified in the project's design, but it follows the color scheme and allows for the text to be read.
window.addEventListener('scroll', () => {
let scrolled = window.scrollY;
if (scrolled <= heroHeight) {
navbar.classList.remove('scrolled');
} else {
navbar.classList.add('scrolled');
}
});
Finally, to make sure things continued working as expected even if a user resized their browser window, I added an event listener to check for resizes and adjust as needed. In case the hero section's height changes, the window will notice and update the variable. The window will also add the preload class to the ul again to make sure a window resize doesn't trigger an unwanted animation.
window.addEventListener('resize', () => {
heroHeight = hero.clientHeight - 5;
ul.classList.add('preload');
setTimeout(function() {
ul.classList.remove('preload');
}, 1000);
});
- Replace keyframe animations with transitions instead
- Separate mobile scss files