Skip to main content

A Day of Growing the Blog — Grinding Through CSS to Polish the Site

· 5 min read
Rintaro Nakahodo
NLP Researcher · Engineer · Creator

From morning to night, I kept fixing small things across the blog. It wasn't about adding features — it was a day of hunting down every "something feels off." Unglamorous work, but it's days like this that make a site grow.

Building the profile sidebar​

I decided to put an author profile on the blog's top page — because "a blog where you can't tell who's writing it feels lonely."

The implementation itself is simple: set position: sticky; top: 5rem on a React component to make it a sidebar. On desktop it stays fixed on the right side and follows you as you scroll.


Icon image not showing​

I set the profile card icon image with src="/img/icon.png", but it didn't show up.

The culprit was the baseUrl trap. This blog lives at nakahodo.com/blog/, so Docusaurus is configured with baseUrl: '/blog/'. Writing an absolute path like /img/icon.png tells the browser to look at nakahodo.com/img/icon.png. The correct path is /blog/img/icon.png.

I'd already fallen into this exact trap with the author icon config before, and I fell into it again.


Overhauling the mobile menu​

The existing mobile menu was a simple list that expanded below the navbar — a bit cramped on a phone. I decided to rebuild it as a fullscreen overlay.

Portfolio side​

The portfolio's index.html is free-form JavaScript, so it's not hard. Add an overlay DOM element and toggle CSS animations with class toggling.

function openMenu() {
hamburger.classList.add('is-open');
overlay.classList.add('is-open');
document.body.style.overflow = 'hidden'; // stop background scrolling
}

Blog side (Docusaurus)​

The tricky part was the Docusaurus side. Since Docusaurus generates its own HTML that can't be edited directly, I had to make the mobile sidebar look fullscreen using CSS alone.

.navbar-sidebar {
width: 100% !important;
background: rgba(6, 6, 12, 0.97) !important;
transform: translateX(100%);
transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1) !important;
}

.navbar-sidebar--show .navbar-sidebar {
transform: translateX(0);
}

Using a lot of !important felt a bit ugly, but it was unavoidable for overriding framework styles. The cubic-bezier(0.23, 1, 0.32, 1) is close to easeOutQuint — it springs open and snaps to a stop.


The z-index trap: can't close with the × button​

After the menu opened, tapping the × button in the top-right did nothing.

Root cause​

Looking at the stacking order:

Elementz-index
nav (hamburger button's parent)500
Overlay999

When the overlay opens, it sits in front of the nav at a higher z-index. The navbar — and the hamburger button inside it — gets covered by the overlay, so click events never reach it.

Fix​

One line. Change z-index: 500 to z-index: 1000.

nav {
z-index: 1000; /* 1000 > overlay's 999 */
}

Once you know the cause it's a one-second fix, but identifying the cause while testing on a phone is quietly exhausting.


Tag pages leaking into the GA4 ranking​

Tag listing pages like /posts/tags/engineering/ were showing up in the access ranking. Same article appearing twice was also happening.

Tag page leakage​

The API filter was pulling paths starting with /blog/posts/, which caught tag pages too. Added an exclusion filter:

{
notExpression: {
filter: {
fieldName: 'pagePath',
stringFilter: { matchType: 'BEGINS_WITH', value: '/blog/posts/tags/' },
},
},
}

Duplicate entries​

GA4 sometimes counts trailing-slash and no-trailing-slash URLs as separate entries. It also creates separate rows for the same URL when the page title changes.

Fixed by normalizing paths and aggregating with a Map:

const path = rawPath.endsWith('/') ? rawPath : rawPath + '/';
if (merged.has(path)) {
merged.get(path).views += views;
} else {
merged.set(path, { path, title, views });
}

GA4 also appends | Your Site Name | Blog to titles, so strip that with a regex:

const cleanTitle = title.replace(/\s*\|\s*Your Site Name.*$/, '').trim();

Light/dark mode only changing the header​

When I clicked the theme toggle, the navbar color changed but the page body didn't.

Root cause​

I had hard-coded color values directly in CSS:

/* ❌ doesn't respond to theme changes */
.page {
background: #0a0a0f;
color: #e4e0d8;
}

Docusaurus theme switching just toggles <html data-theme="dark"> / <html data-theme="light"> — hard-coded values can't detect that change.

Fix​

Replace everything with CSS custom properties. Define light and dark values in custom.css, and have index.module.css only reference variables:

/* custom.css */
:root {
--page-profile-bg: #f8f5ef;
--page-border: #e0dbd0;
--page-text-muted: #9a96a0;
}
[data-theme='dark'] {
--page-profile-bg: #0d0d14;
--page-border: #1a1a24;
--page-text-muted: #3a3a52;
}

/* index.module.css */
.profileCard {
background: var(--page-profile-bg);
}

Colors are now managed in one place, and adding more themes in the future will be much easier.


Thoughts on the day​

Nothing big was added. But bugs were squashed, rendering was corrected, and the feel of things got better. Days like this are what slowly push a site toward completion.

The ratio of time spent tracking down that z-index bug to the satisfaction of fixing it seems off — but maybe that's just frontend development.

Live with a Smile!