In the previous article, “A Practical Framework from The Minto Pyramid Principle for Solving Blog Issues,” we listed our UI problems and formed the hypothesis that typography was a key culprit. Here we test that hypothesis. For the fix, I referred to the book Web & Graphic Design Master Course: Everything You Need in One Volume (link above). There is already a wealth of detailed information on the Web, so this post sticks to the essentials.
Current State of the Blog
- I never paid attention to text color or typeface.
- The gray background feels hard to read.
- I’m not sure the size and line-spacing of the headings are appropriate.
Let’s see what typography has to say.
What Is Typography?
Put simply, it’s the craft of making text look good—covering placement, color, shapes, and more. Below are the basics.
Typeface
Classification by appearance:
- Japanese body copy → Mincho / serif faces work best; avoid heavy weights that hurt legibility.
- Slides or at-a-glance materials → Gothic / sans-serif faces are clearer.
Font Size
Industry guidelines for digital text:
Usage | Digital (px) |
---|---|
Body (standard) | 14–20 px |
Body (children, seniors, etc.) | 16–20 px |
Minimum readable size | ≥ 10 px |
“Jump Rate”
The ratio of a heading’s size to the body text beneath it.
- A 1.5 – 2× ratio to the body copy is generally ideal.
Line Spacing
Normally 0.5–1 em. About 40 characters per line is common; if you exceed 60, widen the line-height a bit.
Applying Typographic Principles
Now to the actual fixes.
Typeface
Body: Noto Serif JP Headings: Noto Sans JP
The change is subtle so far.
Next.js setup
// src/app/(i18n)/lang/[lang]/layout.tsx
const notoSerifJp = Noto_Serif_JP({
subsets: ['latin'],
weight: ['400'],
style: ['normal'],
display: 'swap',
variable: '--font-noto-serif-jp',
});
const notoSansJp = Noto_Sans_JP({
subsets: ['latin'],
weight: ['400'],
style: ['normal'],
display: 'swap',
variable: '--font-noto-sans-jp',
});
export default async function RootLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: Promise<{ lang: 'en' | 'ja' }>;
}>) {
return (
<html lang={(await params).lang} className={`${notoSansJp.variable} ${notoSerifJp.variable}`}>
<body>
<OriginalI18nProvider>{children}</OriginalI18nProvider>
<FirebaseAnalytics />
</body>
</html>
);
}
CSS
.blog {
margin-top: 50px;
font-family: var(--font-noto-serif-jp), serif;
background-color: #f9f9f9;
color: #333;
line-height: 1.6;
padding: 1rem;
max-width: 84vw;
}
Font Size & Jump Rate
- Body text: 20 px (defined as
1rem
). - Jump rate: h2 = 1.5×, h1 = 2× the body size.
This already feels easier to read.
CSS variables
html {
font-size: 125%;
}
html,
body {
margin: 0;
padding: 0;
min-height: 100vh;
}
:root {
--h1FontSize: 2rem;
--h1FontWeight: 400;
--h2FontSize: 1.5rem;
--h2FontWeight: 400;
--pFontSize: 1rem;
--pLineHeight: 1.8;
--timeFontSize: 1rem;
}
Line Spacing
line-height
is 1.5 by default, bumped to 1.8 on viewports wider than 768 px—much more comfortable.
Summary
By tackling typography—the root cause of our poor UI—we made solid progress:
- Switched to Noto Serif JP for body text and Noto Sans JP for headings.
- Unified body text at 20 px / 1 rem; heading jump rate set to 1.5–2×.
- Adjusted
line-height
to 1.5 (or 1.8 on larger screens).
There’s still room to improve, but this is an important first step.