I recently updated my website and changed the layout around because I felt like my old design was too corporate and “souless” in a way. This blog post shows how I did that, as well as showing some snippets of code if you want to create some of these effects for yourself!
I added this star background which is really nice and got inspiration from shar to add a wave/ocean effect on the bottom. I also overall just polished up the website a bit more, adding hover effects and fixing bugs up everywhere. The website should look good on all display sizes now! (unless you use like a 300px display but shhhhh)
Front Page Menu
For the front page, I added some buttons! The icons are from Material Symbols because you can animate the fill using CSS animations.
The CSS for their hover effects was pretty simple! The way I implemented it was probably not the best, but it works fine and I couldn’t find an easier solution.
I have two @keyframes
animations, fillicon
and unfillicon
.
@keyframes fillicon {
from {
font-variation-settings: 'FILL' 0.0;
}
to {
font-variation-settings: 'FILL' 1.0;
}
}
@keyframes unfillicon {
from {
font-variation-settings: 'FILL' 1.0;
}
to {
font-variation-settings: 'FILL' 0.0;
}
}
When the page loads, I make it play the unfillicon
animation and set the fill to 0.0. When it gets hovered, it plays the fillicon
animation and sets the fill to 1.0. Since I set the unfillicon
animation to play by default, when it is unhovered, it will play the unfillicon
animation again.
a {
.material-symbols-rounded {
animation-direction: normal;
animation-name: unfillicon;
animation-duration: 300ms;
animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
font-variation-settings: 'FILL' 0.0;
}
&:hover {
scale: 1.2;
.material-symbols-rounded {
animation-name: fillicon;
animation-duration: 300ms;
animation-direction: normal;
font-variation-settings: 'FILL' 1.0;
}
}
}
I tried using the animation-direction
property but it didn’t work :(
Sky Background
I used SVGs in the HTML to create the stars. Each star is made out of 2 circles: a solid white circle and a circle with a radial gradient as its fill.
<!--A circle-->
<g>
<circle ...></circle>
<circle class="glow" ...></circle>
</g>
I spawn and animate them using vanilla JavaScript. I do this by making an array that holds all the stars, along with their positions, radii, and respective elements.
type Star = Point & {
r: number,
o: number,
circleEl: SVGCircleElement,
glowEl: SVGCircleElement,
el: SVGGElement
}
let stars: Star[] = []
Then, I get a random point on the screen, as well as a random radius for the star. I also create the elements here to be added later.
for (let i = 0; i < starCount; i++) {
const circleEl = document.createElementNS(svgns, 'circle')
const gEl = document.createElementNS(svgns, 'g')
const glowEl = document.createElementNS(svgns, 'circle')
const star: Star = {
x: Math.random() * starsWidth,
y: Math.random() * starsHeight,
r: Math.pow(Math.random(), 2) + 0.25,
o: Math.random() * 2 * Math.PI,
circleEl: circleEl,
glowEl: glowEl,
el: gEl
}
// Set attributes
gEl.setAttribute('transform', `translate(${star.x},${star.y})`)
circleEl.setAttribute('r', star.r.toString())
glowEl.classList.add('glow')
glowEl.setAttribute('r', (star.r * 5).toString())
gEl.appendChild(circleEl)
gEl.appendChild(glowEl)
stars.push(star)
}
I use the .replaceChildren
function instead of appending the children one by one in case the stars need to be regenerated, such as when the screen gets resized.
starsSvgEl.querySelector('g#starsgroup')!
.replaceChildren(...stars.map(star => star.el))
To make the stars twinkle, I have a tick function that changes the size of the stars every frame. I used Math.cos
because it is repeating and smooth, which makes the twinkling effect look natural and repetitive, similar to how actual stars twinkle. So, we can just plug in how much time has passed, multiply it by a speed factor, add a random offset that is different for each star to avoid repetition, and get an adjusted radius that we can set to our circle elements.
for (const star of stars) {
const newRadius = star.r + (Math.cos(star.o + timeElapsed * starTwinkleSpeed) + 2)
star.circleEl.setAttribute('r', newRadius.toString())
star.glowEl.setAttribute('r', ((newRadius ** 2) * 1.5).toString())
}
I also square the radius of the glow so that the glow will be larger and brighter for larger stars, but be smaller and dimmer for smaller ones.
Shooting Star
For the shooting star, I created a simple shooting star SVG using Inkscape.
I then made code to move the star to a random spot on screen, then move down and left. Because this animation was a bit more complex, I decided to use AnimeJS, a simple JavaScript animation library that I already used for some existing effects.
const shootingStarDistance = 250
const shootingStarSpeed = 600
function shootStar() {
const startingX = Math.random() * (starsWidth) + shootingStarDistance * 1.5
const startingY = Math.random() * (starsHeight - shootingStarDistance * 3)
- shootingStarDistance
animate('#shootingstar', {
keyframes: [
{
translateX: startingX,
translateY: startingY,
opacity: 0
},
{
translateX: startingX - shootingStarDistance,
translateY: startingY + shootingStarDistance,
opacity: 1
},
{
translateX: startingX - shootingStarDistance * 2,
translateY: startingY + shootingStarDistance * 2,
opacity: 1
},
{
translateX: startingX - shootingStarDistance * 3,
translateY: startingY + shootingStarDistance * 3,
opacity: 0
},
],
duration: shootingStarSpeed
})
}
I made sure that all spawn positions were able to be seen on screen. I also made the shooting star fade in and out when travelling.
I then call this function randomly in the same tick function as the star tick function. I check to make sure that it has been enough time since the last shooting star to play the animation again. Each tick after 2.5x the animation duration (1.5 seconds), the shooting star has a 1% to spawn.
timeSinceLastShootingStar += deltaTime
// ...
if (timeSinceLastShootingStar > shootingStarSpeed * 2.5) {
if (Math.random() * 100 <= 1) {
shootStar()
timeSinceLastShootingStar = 0
}
}
Wave
Like I said earlier, I got this idea from shar’s website, who also made a great video explaining how she redesigned her own website.
The majority of the wave code was made by MikoĊaj Stolarski in this Codepen before I adapted to fit my website. This is the same codepen that react-wavify is based off of.
The code is almost identical to the Codepen, the main difference being that I instead create two waves instead of one. I have one “main” wave, but I also created another wave that is moved down with transparent fill. The stroke size changes to create a foamy water effect that I think makes the water look alot nicer and more natural!
The Link Hover Animation
Ok im probably overreacting BUT I LOVE THE LINK HOVER ANIMATION SO MUCH ITS SO SATISFYING LIKE HERE TRY IT OUT AFIDUHUFA
This is one of the CSS snippets that I stole from my old website, and the CSS code is pretty simple!
First, set the position to relative
to make sure that when we create the ::after
pseudo-element, it will snap right below our text instead of snapping to the bottom of the page or whatever parent element we have it in. white-space
is set to pre
to make sure that all our text is in one line.
a {
position: relative;
color: $primary;
white-space: pre;
text-decoration: none;
transition: color 300ms;
}
Then, change the color when the link is hovered.
a:hover {
color: $accent;
}
Ok, so the pseudo-element. Set the content to ''
to create the ::after
pseudo-element, then set its position to absolute
and move it to right below the text. Then, set the width to 0%
and the height to 2px
. Also set the color of the underline here (via the background-color
property). Then, create a transition property for the width
and background-color
properties so that they can be changed later.
a::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
width: 0%;
height: 2px;
background-color: $primary;
transition:
width 300ms,
background-color 300ms;
border-radius: 999px;
}
For a minor finishing touch, I set the border-radius
property to 999px
so that it will be as round as possible in the corners. This is something I added later but I think it adds alot to the effect when zoomed in.
Finally, just set the width
and background-color
to a new color when it’s hovered!
a:hover::after {
background-color: $accent;
width: 100%;
}
Conclusion
Aside from some minor tweaks to the UI and some layout changes, that’s all I changed this time! I just wanted to write this blog to document the process and hopefully help some other people designing their website! I also noticed a few very glaringly obvious bugs while making this blog post which I have fixed both in the post and on the website.
I think after this I’ll try and spruce up the blog post UI, as it looks very empty and boring right now to be honest. Especially the boring post header and the extremely lonely singular “Blog” button on the website header.
I also don’t have a good 404 page, so I might make that soon too!
uhh how do you end a blog post im not good at this
oranges