Noto Sans Webfont Guide
The Problem
AI agents loading Noto Sans (especially Noto Sans JP for Japanese content) commonly make a few mistakes: loading all nine weights when only two or three are needed, omitting font-display: swap, or not using subsetting — resulting in megabytes of font data blocking page render. For Japanese, the default Noto Sans JP file is several MB; without subsetting it can be the single largest asset on the page.
A subtler problem: AI agents default to using font-weight: bold for headings and font-weight: normal for body text, missing the more refined hierarchy that Noto Sans's wide weight range enables — such as light (300) body text with regular (400) headings, or regular body with medium (500) headings. This approach creates visual differentiation through weight contrast rather than just bold vs. normal.
The Solution
Use Noto Sans with deliberate weight selection, proper subsetting, and font-display: swap. Define your font stack as a CSS custom property so it can be reused consistently, and choose weights that create the hierarchy you want — not just bold vs. normal.
Code Examples
Loading via Google Fonts CDN
The simplest approach. Google Fonts automatically handles subsetting and returns only the glyphs needed for the language detected in the request.
<!-- Efficient: request only the weights you need -->
<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=Noto+Sans+JP:wght@300;400;500&display=swap"
rel="stylesheet"
/>
:root {
--font-noto: 'Noto Sans JP', 'Noto Sans', ui-sans-serif, system-ui,
-apple-system, 'Hiragino Sans', sans-serif;
}
body {
font-family: var(--font-noto);
font-weight: 300; /* Light — creates breathing room in body text */
}
h1,
h2,
h3 {
font-family: var(--font-noto);
font-weight: 400; /* Regular — stands out against light body without going bold */
}
Loading via @fontsource (Self-hosted)
@fontsource packages allow self-hosting with subsetting. Install only the weights you need:
# Install Noto Sans JP — only weights 300 and 400
npm install @fontsource/noto-sans-jp
/* Import only the weights you actually use */
@import '@fontsource/noto-sans-jp/300.css';
@import '@fontsource/noto-sans-jp/400.css';
@import '@fontsource/noto-sans-jp/500.css';
Loading via Next.js next/font/google
Next.js next/font/google automatically optimizes the font: self-hosts it, eliminates the external request, and inlines critical CSS.
import { Noto_Sans_JP } from 'next/font/google';
const notoSans = Noto_Sans_JP({
weight: ['300', '400', '500'], // Only load what you use
subsets: ['latin'],
variable: '--font-noto',
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="ja">
<body className={notoSans.variable}>{children}</body>
</html>
);
}
body {
font-family: var(--font-noto), 'Hiragino Sans', sans-serif;
}
Loading in Docusaurus
In Docusaurus, add the font link to your docusaurus.config.ts and apply it via custom.css:
// docusaurus.config.ts
const config = {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'preconnect',
href: 'https://fonts.googleapis.com',
},
},
{
tagName: 'link',
attributes: {
rel: 'preconnect',
href: 'https://fonts.gstatic.com',
crossorigin: 'anonymous',
},
},
{
tagName: 'link',
attributes: {
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500&display=swap',
},
},
],
};
/* src/css/custom.css */
:root {
--ifm-font-family-base: 'Noto Sans JP', 'Noto Sans', ui-sans-serif,
system-ui, -apple-system, 'Hiragino Sans', sans-serif;
--ifm-font-weight-base: 300;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 400;
}
Font Weight Hierarchy Patterns
Weight Comparison across the Full Range
CSS Variable Approach for Easy Switching
Performance: Subsetting Japanese Fonts
Noto Sans JP covers all Japanese characters — this makes the full font file large (several MB). Google Fonts handles subsetting automatically based on the text on the page. For self-hosted fonts, use unicode-range to split by script:
/* Latin characters — small subset */
@font-face {
font-family: 'Noto Sans JP';
src: url('/fonts/noto-sans-jp-latin.woff2') format('woff2');
font-weight: 300;
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
}
/* Japanese characters — larger file, loaded only when Japanese text is present */
@font-face {
font-family: 'Noto Sans JP';
src: url('/fonts/noto-sans-jp-japanese.woff2') format('woff2');
font-weight: 300;
font-display: swap;
unicode-range: U+3000-9FFF, U+F900-FAFF, U+FF00-FFEF;
}
For Google Fonts with display=swap already appended, the URL approach handles this automatically — you don't need to manage unicode-range manually.
Common AI Mistakes
- Loading all 9 weights (
wght@100;200;300;400;500;600;700;800;900) when the design only uses 2–3 — this wastes bandwidth and slows page load significantly for Japanese fonts - Omitting
display=swapin Google Fonts URLs, which causes FOIT (Flash of Invisible Text) on slow connections - Using
font-weight: bold(700) for headings when the body isfont-weight: normal(400) — this creates an abrupt jump; more refined designs use smaller weight increments like 300/400 or 400/500 - Not including the
preconnecthints before the Google Fonts<link>, adding unnecessary latency to font requests - Referencing Noto Sans without the JP variant (
'Noto Sans'instead of'Noto Sans JP') when the page contains Japanese text — the base Noto Sans does not include Japanese glyphs - Using a flat font family string without a CSS variable, making it hard to change the font stack consistently across the codebase
- Self-hosting Noto Sans JP without subsetting — the full file can be 4–8 MB per weight, making self-hosting impractical without a build tool that handles subsetting
When to Use
Light body (300) + Regular headings (400)
- Long-form content, editorial sites, documentation
- Designs aiming for an elegant, refined appearance
- Dark-on-light color schemes where lighter text reduces visual fatigue
Regular body (400) + Medium headings (500)
- UI-heavy applications, dashboards, admin interfaces
- Designs where information density is high and clear hierarchy matters
- Pages where both body and heading text need to be readable at small sizes
Using Noto Sans JP specifically
- Any page containing Japanese, Korean, or CJK characters
- Multilingual sites that need consistent rendering across scripts
- Projects where a neutral, highly legible typeface is preferred over a distinctive design voice