Correct the CSS font docs: TrueType only, and no fonts/ subdirectory requirement - #5502
Correct the CSS font docs: TrueType only, and no fonts/ subdirectory requirement#5502shai-almog wants to merge 3 commits into
Conversation
…requirement A customer bundled Nexa as .otf files, put them in common/src/css, and got no font change and no error. Two of the three reasons were things the docs told them. OTF is not supported. It compiles without complaint because the CSS compiler loads fonts through java.awt.Font.createFont(TRUETYPE_FONT, ...), which also parses OpenType/CFF, but Font.createTrueTypeFont rejects any file name that doesn't end in .ttf, and IPhoneBuilder registers only .ttf files in UIAppFonts. The developer guide claimed "TTF/OTF fonts" and the initializr CSS skill reference said ".ttf (or .otf)". Both now say TrueType only and explain the failure mode. The fonts/ subdirectory was never a requirement either. A relative src URL is resolved against the directory holding the CSS file, and merge mode syncs that whole directory, so a font sitting directly beside theme.css works exactly as well as one under fonts/. The guide didn't document the resolution rule at all and three skill references presented common/src/main/css/fonts/ as the location. While in the section, document the parts that make a font silently do nothing: a font-family name with an unquoted space parses as separate identifiers and only the first is read back, so every weight collides under one family; and the @font-face font-weight/font-style descriptors are parsed but never consulted when a family is matched, so each weight needs its own family name and a whole-theme swap goes through the Default selector. Also corrects two stale claims: fonts land next to the compiled theme.res rather than in the project src directory, and the remote-font download cache lives in the build directory. Tests pin both halves of the location contract, since it is now documented: CSSFontFaceLocationTest compiles a theme with the font in the CSS root, in a subdirectory, and with two quoted multi-word families, asserting which file each family resolves to and that it is deployed flat next to theme.res. CN1CSSCLILogicTest covers the merge-mode url() rewrite that Maven actually takes, including that remote and absolute URLs pass through untouched.
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 151 screenshots: 151 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
Cloudflare Preview
|
|
Compared 181 screenshots: 181 matched. |
|
Compared 143 screenshots: 143 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
|
Compared 217 screenshots: 217 matched. |
|
Compared 144 screenshots: 144 matched. |
The guide has a gate I missed: validate-guide-snippets.py requires every [source] block to be include-backed from docs/demos, so the two inline CSS examples I added failed the docs build. Moved both into guide-snippets-theme.css as tags css-css-044 and css-css-045. That means the demo build now compiles them, which is the point of the fixture rule -- so the examples had to use real fonts rather than a made-up MyFont. Added GuideRootFont.ttf (the 5.8KB icon font the CSSFontFaceTest sample already carries) at the CSS root, alongside the existing res/GuideDemoFont-Bold.ttf, so the snippet demonstrates the root and subdirectory forms with files that exist. Pointed Default at "GuideRootFont" rather than the regular demo font on purpose. A @font-face is only copied to the build output when some style actually references it, so without a reference the root-level font would compile silently and prove nothing. With it, mvn -f docs/demos/pom.xml process-classes deploys GuideRootFont.ttf next to guide-snippets-theme.res, which is exactly the behaviour the new section documents.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb7301ce3c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| A `font-family` name that contains spaces must be quoted, both in the `@font-face` rule and wherever you reference it. An unquoted name is parsed as a list of separate identifiers, so `font-family: GuideDemoFont Bold` registers the family as `GuideDemoFont` and collides with your regular weight. | ||
|
|
||
| `font-weight` and `font-style` select between the built-in `native:` fonts, but they have no effect once `font-family` resolves to a `@font-face` rule. Declare one `@font-face` per weight and style you need, each with its own family name, as in the example above, then reference the right family from each UIID. |
There was a problem hiding this comment.
Correct custom-font weight and style behavior
This categorical statement conflates unused font-weight/font-style descriptors inside @font-face with those properties on a UIID. For a UIID using a custom face, CSSTheme.getThemeFont() preserves the requested style, and Resources.createTrueTypeFont() passes it to derive(); Android and JavaSE consequently synthesize bold/italic variants, although iOS currently ignores those flags. Please describe this as platform-dependent and recommend separate files for consistent cross-platform rendering rather than saying the properties have no effect; the same inaccurate claim also appears in scripts/initializr/common/src/main/resources/skill/references/css.md.
Useful? React with 👍 / 👎.
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Touching the file pulled it into the copyright gate's scope, and it never had a header. It is first-party CN1 content, so it gets the header rather than an entry in copyright-header-exclusions.txt, which is reserved for third-party sources.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f71e01789f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| `font-weight` and `font-style` select between the built-in `native:` fonts, but they have no effect once `font-family` resolves to a `@font-face` rule. Declare one `@font-face` per weight and style you need, each with its own family name, as in the example above, then reference the right family from each UIID. | ||
|
|
||
| To change the base font of an entire theme, set `font-family` on the special `Default` selector, then override the UIIDs that need a bold or italic face: |
There was a problem hiding this comment.
Override explicit regular fonts in theme-wide swaps
When includeNativeBool is enabled, as it is in this guide fixture, setting Default.font only changes the fallback style; the bundled native themes explicitly set fonts on regular UIIDs such as Label and Button, so those components remain on native:MainRegular/native:MainLight. This recipe therefore does not change the base font of the entire theme, and users must also override every font-bearing regular UIID (not merely bold or italic ones); the same misleading whole-theme-swap advice appears in scripts/initializr/common/src/main/resources/skill/references/css.md.
Useful? React with 👍 / 👎.
|
Compared 148 screenshots: 148 matched. Benchmark Results
Detailed Performance Metrics
|
|
Compared 149 screenshots: 149 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
|
Folded into #5508 - one PR for the whole change. |
Why
A customer bundled Nexa as
.otffiles, put them incommon/src/css, and got no font change and no error. Two of the three reasons were things our own docs told them.OTF is not supported. It compiles without complaint because the CSS compiler loads fonts through
java.awt.Font.createFont(TRUETYPE_FONT, ...), which also parses OpenType/CFF — butFont.createTrueTypeFontrejects any file name that doesn't end in.ttf(Font.java:342), andIPhoneBuilderregisters only.ttffiles inUIAppFonts(IPhoneBuilder.java:4989). The developer guide claimed "TTF/OTF fonts" and the initializr CSS skill reference said ".ttf(or.otf)".The
fonts/subdirectory was never a requirement. A relativesrcURL is resolved against the directory holding the CSS file (CSSTheme.java:1173), and merge mode syncs that whole directory, so a font sitting directly besidetheme.cssworks exactly as well as one underfonts/. The guide didn't document the resolution rule at all, and three skill references presentedcommon/src/main/css/fonts/as the location.What changed
Docs only — no behavior change. There was no code requirement to remove; root-level fonts already worked.
docs/developer-guide/css.asciidoc— TTF only, with anIMPORTANTexplaining the runtime rejection. New subsections covering where the font file may live, family-name quoting, and how weights/styles actually resolve.css.md,android-to-cn1.md,react-to-cn1.md) — same two corrections, plus a troubleshooting row for "custom font is ignored entirely, no error."While in the section, documented the other two things that make a font silently do nothing:
font-familyname with an unquoted space parses as separate identifiers and only the first is read back (CSSTheme.java:5306), so every weight collides under one family.@font-facefont-weight/font-styledescriptors are parsed but never consulted when a family is matched (CSSTheme.java:5522-5526), so each weight needs its own family name and a whole-theme swap goes through theDefaultselector.Also corrected two stale claims: fonts land next to the compiled
theme.resrather than in the projectsrcdirectory, and the remote-font download cache lives in the build directory (somvn cleandiscards it).Tests
The location contract is now documented, so it is pinned:
CSSFontFaceLocationTest(new) — compiles a theme with the font in the CSS root, in a subdirectory, and with two quoted multi-word families, asserting which file each family resolves to and that it is deployed flat next totheme.res.CN1CSSCLILogicTest— covers the merge-modeurl()rewrite that Maven actually takes, including that remote and absolute URLs pass through untouched.The root-placement test was checked for vacuity by renaming the fixture; it fails with
FileNotFoundExceptionon the expected root path.Verification
mvn -pl css-compiler,css-cli test— 15 tests, green--failure-level WARNclean; LanguageTool 0 matches, status=ok🤖 Generated with Claude Code