# pretendard's readme pins all 24 CDN URLs to a 2023 release, and its system stack puts it after the native fonts

> A Korean system-ui alternative typeface, OFL-1.1 licensed in the manifest and empty in the repository metadata, built from three named upstream fonts and shipped in nine weights plus a variable cut. Two details are worth knowing before you copy anything. The newest release is dated 2023-11-05 while commits continued into 2026, and every documented stylesheet URL pins that 2023 version. And the recommended stack for matching your system deliberately lists the native fonts before Pretendard, so on Apple hardware you get the system face instead.

**orioncactus/pretendard** — 어느 플랫폼에서든 사용할 수 있는 system-ui 대체 글꼴 | A system-ui alternative font for all cross-platform

- Repository: https://github.com/orioncactus/pretendard
- Website: https://cactus.tistory.com/306
- Stars: 3,594 · Forks: 208
- Language: Python
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/orioncactus-pretendard

## Every documented CDN URL pins the 2023 release

Read the release history and the documentation together and a gap appears.

The three recorded releases are dated 2023-11-05, 2023-06-26 and 2023-06-11. The last commit to the default branch is dated 2026-07-23. So the newest tag is close to three years old and there have been commits for roughly two and a half years after it.

That matters far more for a font than it does for most projects, because of how this font gets used. The readme does not tell you to download an archive and install it. It tells you to link a stylesheet from a content delivery network, and every one of those examples pins a version tag, and every tag in the examples is the 2023 release.

So the documented integration path is frozen. Even if work has landed in the repository since, a developer following this file links the 2023 cut, and a developer who notices the newer commits has to construct new URLs themselves because there is no documentation telling them what changed or whether the newer state is one they should ship.

There is a second-order problem. Because the examples are pinned by version rather than by a floating alias, every URL in the file becomes something that has to be edited when the version moves. And as the next section shows, there are a great many of them.

The honest summary is that this repository's documentation describes a release from 2023 accurately and thoroughly, and has not been revisited since.

## The licence is empty in metadata and correct in the manifest

For a typeface, the licence is the single most important line in the repository, and this one gets it right where it counts.

The recorded licence for the repository is empty rather than a named licence. The package manifest declares a specific one, and it is the right one: the SIL Open Font License, version 1.1. A licence file sits at the top level beside it.

So this is the same shape as a number of the projects where the metadata field is simply not populated while the manifest carries the real answer. For a font in particular, declaring the open font licence rather than a software licence is a considered choice rather than a default, because that licence exists to permit embedding and redistribution in documents and applications, which is what fonts get used for.

The distinction also has a practical consequence that a generic permissive software licence would not: the open font licence operates through a reserved font name mechanism. That is worth knowing about if you ever modify the typeface, and it is the kind of thing the manifest's precise identifier tells you and the empty metadata field does not.

The rest of the manifest is short and tells you the repository's role. The root package is named for the repository rather than for the font, it is marked private so it can never be published by accident, it declares a workspace covering the subpackages directory, and it pins its package manager exactly. So the distributable artefacts live in subpackages and this root is purely a build container.

## The system-matching stack lists Pretendard after the native fonts

The project describes itself as a system-ui alternative, and then recommends two different font stacks, with Pretendard in a different position in each.

The first stack is recommended for matching your system as closely as possible. It begins with the Apple system font, then a Blink-specific system font, then a named Korean system face for Apple platforms, and only after those come Pretendard Variable, then Pretendard, then Roboto and two more named Korean system faces, and finally the colour emoji and symbol faces.

The second stack is recommended for having the identical environment everywhere. That one inverts the order: Pretendard Variable and Pretendard come first, and the platform system fonts follow them as fallbacks.

So the difference is not subtle and it is not an oversight. On any Apple device, the first stack resolves to the native system face at the first name in the list, and Pretendard is never reached unless that font is missing. On other platforms the ordering behaves differently because the leading names do not exist there.

That is exactly what you would expect from a stack whose stated purpose is to match the system, and it is why the second stack exists. But it does mean the headline description and the primary recommendation point in different directions: one says this is an alternative to the platform default, and the other says use the platform default and keep this as a fallback.

The second stack also reveals why there are two Pretendard names in either list. Both the static and the variable cut appear in the same stack, variable first, so a stylesheet declaring either name works against either build.

## Two family names exist so the static and variable cuts can coexist

The most confusing thing about this font in a stylesheet is that it has two names, and the reason is mechanical rather than mysterious.

The static build is declared under the family name Pretendard. The variable build is declared under the family name Pretendard Variable, quoted in the documentation because it contains a space.

Two names is the standard solution to a real constraint. If both cuts declared the same family name, a page that loaded both would get a collision, with the browser picking one and ignoring the other according to font matching rules rather than order. Declaring them separately means both can be present, and the documented stacks list both in a deliberate order, variable first.

The variable cut exists because the typeface ships in nine static weights as well, and a page that needs two of those weights would otherwise have to load two complete files. A variable font carries the whole weight axis in one file, so nine weights become one download, at the cost of a slightly larger file than any single static weight.

That trade is then improved on a second axis. The documentation offers four stylesheet variants in total: the full static webfont, a dynamically subsetted static webfont, a variable webfont, and a variable webfont with dynamic subsetting. So the two axes, weight and character coverage, are each independently available in both forms.

For most projects the fourth variant is the right answer, and it is the one whose documentation says it is substantially smaller than the third.

## Four stylesheet variants exist because unsplit Korean webfonts are impractical

The dynamic subsetting section explains the problem the variants solve, and the explanation is specific to Korean.

The technique is described as the same one Google Fonts uses for its Korean fonts: only the characters actually present on the page are downloaded, rather than the whole character set being fetched up front.

That matters here more than it does for a Latin typeface. Hangul is a large character set, and a full webfont containing it is measured in tens of megabytes rather than hundreds of kilobytes. No amount of compression makes that an acceptable page weight, so subsetting is not an optimisation here, it is the difference between a usable font and an unusable one.

The variable dynamic subset is described as dramatically smaller than the plain dynamic subset, which follows from the two axes interacting: subsetting cuts by character coverage, and the variable axis cuts by weight. Combining both means one file covering one weight axis and only the glyphs the page needs.

The documented pattern for all four variants is identical in shape: a stylesheet link or an import, a version pin, and a font-family name. Only the filename changes. That consistency is a kindness, and it is also why the count of things you can get wrong is higher than it looks.

## Three CDNs expose three different directory shapes, so a version bump means editing twenty-four URLs

The readme documents three content delivery networks, and they are not interchangeable mirrors of each other. The paths are structured differently, and the documentation hardcodes all three.

The recommended network serves from a repository path with a version tag in it, and the file lives under a distribution directory and then a web subdirectory and then a static subdirectory. The second network serves from a versioned library path with the tag reduced to a bare version number, no letter prefix, and the file sits directly in a static directory. The third serves from the package name with the version tag, and the file sits under the distribution directory.

For the variable cuts the divergence widens. The first and third networks put those files under a web directory and then a variable subdirectory, while the second puts them under a variable directory at the top of its own layout. So the path is not derivable from one CDN to the next by substituting the hostname.

Now multiply that by the variants and the syntaxes. There are four stylesheet variants, two ways of including them in a page, and three networks. That is twenty-four documented URLs, and every single one of them contains the version pin.

So a release means editing twenty-four strings across this file, each in a slightly different shape, with no tooling shown for generating them. That is the concrete maintenance cost of the current documentation style, and it is a plausible explanation for why the file still points at a 2023 release.

## The build toolchain is a font library plus two compressors

There is no build script in the readme, but the Python requirements file says what the build actually does, and it is seven packages.

The file's first line identifies itself as being maintained by a tool that checks packages for new versions, and links to that tool. So the pins are kept current by automation rather than by hand, which is the right way to do it.

Two of the seven packages do the real work. One is the font manipulation library, which is what any tooling that opens, subsets and re-encodes a typeface depends on. The other two are compression libraries, one for a modern high-ratio compression format and one for a stronger variant of the older one.

Two compressors is the interesting tell. A single font pipeline usually needs exactly one, whichever format it targets. Two compressors means the build produces more than one compressed output, most plausibly the modern format for web use and the stronger variant for contexts where maximum compression matters more than decode speed. That is consistent with a project shipping several distribution formats per variant.

The remaining four are ordinary runtime helpers: two small compatibility shims, a filesystem library, and a timezone library. The filesystem library is the only one with a version range rather than a compatible-release pin, which is the single deviation in an otherwise consistent pinning style.

The repository also commits its built output rather than generating it on demand, so the distributable files are inspectable in the tree alongside their sources.

## Three sibling packages cover three environments, and the deep dive lives on a blog

The readme is Korean, with English and Japanese versions linked from the top. Both of those live under the main package's documentation directory rather than beside the readme, so the language switcher points into a subpackage.

Three sibling packages are offered for specific environments, and each has its own directory and its own readme. One is suited to the Japanese environment, with an additional feature so it can also be used for Korean hanja. One is suited to Latin environments. One is suited to South Korean public service environments.

That last one is the most interesting, because a government-specific font build implies a procurement or accessibility requirement rather than a design preference. Korean public sector bodies have their own typography guidance, and shipping a dedicated cut is the standard way to satisfy one without shipping the general-purpose face.

The Japanese variant is the mirror image: a face built for Japanese text, extended so that Chinese characters appearing in Japanese text still render correctly. That is a real and underappreciated problem, since a Japanese-designed typeface handles kanji differently from a Korean one.

The remaining pointer is the background section, which sends you off-repository for the detailed account of the design, the features and the OpenType functionality. The destination is a personal blog on a Korean blogging platform, and the readme's own homepage field points at that same post.

So for a typeface, the authoritative long-form explanation of why it looks the way it does is not in the repository, in any of the three languages the repository documents itself in.

## Conclusion

Pretendard suits a Korean-language product that wants a consistent, tuned typeface across platforms without shipping the platform's own defaults, and the variable cut with dynamic subsetting is the version to start with if the payload matters. Four things to check first. Which version you are actually loading, since every documented URL pins the 2023 release while the repository has moved on and the newest tag is nearly three years old. Which family name you declare, because the static and variable cuts use different names and the stack you copy decides which one applies. Whether you want the system-matching stack at all, since that one puts native fonts first and will not show Pretendard on Apple devices. And which sibling package fits, because there are separate builds for Japanese, Latin-only and Korean public-sector use. The licence is the permissive open font licence, and the metadata field is empty while the manifest declares it properly.

## FAQ

### Is Pretendard free?

Yes, under the SIL Open Font License. The package manifest declares that licence by name and version, and a licence file sits at the top of the repository. The recorded licence field for the repository itself is empty, so the manifest is the place to read the answer from.

### pretendard vs inter

They are not competitors in this repository's account, they are lineage. The readme names Inter as one of three sources Pretendard was refined from, alongside Source Han Sans Korean for the Korean forms and M PLUS 1p. It presents Pretendard as derived and tuned from those three rather than as an alternative to any of them.

### pretendard vs noto sans kr

The readme does not mention Noto at all, so there is no comparison to be had from this repository. The three named sources are Inter, Source Han Sans Korean from Adobe, and M PLUS 1p, and the Korean basis is given as the Adobe one.

### pretendard alternative

The readme names no alternative typeface. What it offers instead is three sibling packages aimed at specific environments: one for the Japanese environment with an added feature for Korean hanja, one for Latin environments, and one for South Korean public service environments. The three upstream source fonts are named instead.

### vscode pretendard

Nothing in this repository addresses editor integration. The documentation covers webfont delivery through three content delivery networks, four stylesheet variants, two font-family names and two recommended font stacks, plus package manager distribution. The two bundled examples are for a cross-platform mobile framework and a mobile widget framework.

## Sources

- [Issues](https://github.com/orioncactus/pretendard/issues)
- [orioncactus/pretendard on GitHub](https://github.com/orioncactus/pretendard)
- [Project website](https://cactus.tistory.com/306)
- [README](https://github.com/orioncactus/pretendard/blob/main/README.md)
- [Releases](https://github.com/orioncactus/pretendard/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/orioncactus-pretendard
