Skip to main content
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

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

Light Body (300) + Regular Headings (400)
Regular Body (400) + Medium Headings (500)

Weight Comparison across the Full Range

Noto Sans JP — All Available Weights

CSS Variable Approach for Easy Switching

CSS Variable Font Stack — Easy Theming

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=swap in Google Fonts URLs, which causes FOIT (Flash of Invisible Text) on slow connections
  • Using font-weight: bold (700) for headings when the body is font-weight: normal (400) — this creates an abrupt jump; more refined designs use smaller weight increments like 300/400 or 400/500
  • Not including the preconnect hints 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

References