@dataimago/css 0.1.0-alpha.21 → 0.1.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +241 -0
  2. package/dist/bin/dataimago-figures.mjs +703 -0
  3. package/dist/bin/dataimago-sync.mjs +299 -19
  4. package/dist/dataimago.css +9 -3
  5. package/dist/dataimago.min.css +1 -1
  6. package/dist/dataviz/dataimago-dataviz.css +1 -1
  7. package/dist/dataviz/dataimago-dataviz.json +2 -2
  8. package/dist/dataviz/dataimago-dataviz.tex +1 -1
  9. package/dist/dataviz/dataimago_dataviz.R +1 -1
  10. package/dist/figures/NotoSans-Bold.otf +0 -0
  11. package/dist/figures/NotoSans-BoldItalic.otf +0 -0
  12. package/dist/figures/NotoSans-Italic.otf +0 -0
  13. package/dist/figures/NotoSans-Regular.otf +0 -0
  14. package/dist/figures/NotoSansMath-Regular.ttf +0 -0
  15. package/dist/figures/OFL-NotoSans.txt +93 -0
  16. package/dist/figures/OFL-NotoSansMath.txt +93 -0
  17. package/dist/figures/dataimago-figure.sty +105 -0
  18. package/dist/figures/dataimago-tokens.sty +134 -0
  19. package/dist/fonts/cyrillic/OFL-NotoSans.txt +93 -0
  20. package/dist/fonts/cyrillic/OFL-NotoSansMono.txt +93 -0
  21. package/dist/fonts/cyrillic/fonts.css +27 -0
  22. package/dist/fonts/cyrillic/noto-sans-italic.woff2 +0 -0
  23. package/dist/fonts/cyrillic/noto-sans-mono.woff2 +0 -0
  24. package/dist/fonts/cyrillic/noto-sans.woff2 +0 -0
  25. package/dist/fonts/cyrillic-ext/OFL-NotoSans.txt +93 -0
  26. package/dist/fonts/cyrillic-ext/OFL-NotoSansMono.txt +93 -0
  27. package/dist/fonts/cyrillic-ext/fonts.css +27 -0
  28. package/dist/fonts/cyrillic-ext/noto-sans-italic.woff2 +0 -0
  29. package/dist/fonts/cyrillic-ext/noto-sans-mono.woff2 +0 -0
  30. package/dist/fonts/cyrillic-ext/noto-sans.woff2 +0 -0
  31. package/dist/fonts/greek/OFL-NotoSans.txt +93 -0
  32. package/dist/fonts/greek/OFL-NotoSansMono.txt +93 -0
  33. package/dist/fonts/greek/fonts.css +27 -0
  34. package/dist/fonts/greek/noto-sans-italic.woff2 +0 -0
  35. package/dist/fonts/greek/noto-sans-mono.woff2 +0 -0
  36. package/dist/fonts/greek/noto-sans.woff2 +0 -0
  37. package/dist/fonts/greek-ext/OFL-NotoSans.txt +93 -0
  38. package/dist/fonts/greek-ext/OFL-NotoSansMono.txt +93 -0
  39. package/dist/fonts/greek-ext/fonts.css +27 -0
  40. package/dist/fonts/greek-ext/noto-sans-italic.woff2 +0 -0
  41. package/dist/fonts/greek-ext/noto-sans-mono.woff2 +0 -0
  42. package/dist/fonts/greek-ext/noto-sans.woff2 +0 -0
  43. package/dist/fonts/josefin/OFL-JosefinSans.txt +93 -0
  44. package/dist/fonts/josefin/fonts.css +8 -0
  45. package/dist/fonts/josefin/josefin-sans.woff2 +0 -0
  46. package/dist/fonts/lang.css +181 -0
  47. package/dist/fonts/latin/OFL-NotoSans.txt +93 -0
  48. package/dist/fonts/latin/OFL-NotoSansMono.txt +93 -0
  49. package/dist/fonts/latin/fonts.css +27 -0
  50. package/dist/fonts/latin/noto-sans-italic.woff2 +0 -0
  51. package/dist/fonts/latin/noto-sans-mono.woff2 +0 -0
  52. package/dist/fonts/latin/noto-sans.woff2 +0 -0
  53. package/dist/fonts/latin-ext/OFL-NotoSans.txt +93 -0
  54. package/dist/fonts/latin-ext/OFL-NotoSansMono.txt +93 -0
  55. package/dist/fonts/latin-ext/fonts.css +27 -0
  56. package/dist/fonts/latin-ext/noto-sans-italic.woff2 +0 -0
  57. package/dist/fonts/latin-ext/noto-sans-mono.woff2 +0 -0
  58. package/dist/fonts/latin-ext/noto-sans.woff2 +0 -0
  59. package/dist/fonts/math/OFL-NotoSansMath.txt +93 -0
  60. package/dist/fonts/math/fonts.css +8 -0
  61. package/dist/fonts/math/noto-sans-math.woff2 +0 -0
  62. package/dist/fonts/pkg/GFL.txt +29 -0
  63. package/dist/fonts/pkg/README-lm.txt +27 -0
  64. package/dist/fonts/pkg/fonts.css +8 -0
  65. package/dist/fonts/pkg/lmsans10-regular.woff2 +0 -0
  66. package/dist/fonts/vietnamese/OFL-NotoSans.txt +93 -0
  67. package/dist/fonts/vietnamese/OFL-NotoSansMono.txt +93 -0
  68. package/dist/fonts/vietnamese/fonts.css +27 -0
  69. package/dist/fonts/vietnamese/noto-sans-italic.woff2 +0 -0
  70. package/dist/fonts/vietnamese/noto-sans-mono.woff2 +0 -0
  71. package/dist/fonts/vietnamese/noto-sans.woff2 +0 -0
  72. package/dist/latex/dataimago-tokens.sty +14 -2
  73. package/dist/latex/dataimago.sty +6 -1
  74. package/dist/outputs.json +114 -21
  75. package/dist/quarto/_extensions/dataimago/monograph/_extension.yml +35 -0
  76. package/dist/quarto/_extensions/dataimago/monograph/dataimago-base.sty +275 -0
  77. package/dist/quarto/_extensions/dataimago/monograph/dataimago-monograph.sty +270 -0
  78. package/dist/quarto/_extensions/dataimago/monograph/dataimago-tokens.sty +134 -0
  79. package/dist/quarto/_extensions/dataimago/monograph/filters/figure-plate.lua +52 -0
  80. package/dist/quarto/_extensions/dataimago/monograph/filters/matter.lua +170 -0
  81. package/dist/quarto/_extensions/dataimago/monograph/filters/pkg-name.lua +17 -0
  82. package/dist/quarto/_extensions/dataimago/monograph/fonts/GFL.txt +29 -0
  83. package/dist/quarto/_extensions/dataimago/monograph/fonts/JosefinSans-VF.ttf +0 -0
  84. package/dist/quarto/_extensions/dataimago/monograph/fonts/NotoSans-Italic-VF.ttf +0 -0
  85. package/dist/quarto/_extensions/dataimago/monograph/fonts/NotoSans-VF.ttf +0 -0
  86. package/dist/quarto/_extensions/dataimago/monograph/fonts/NotoSansMath-Regular.ttf +0 -0
  87. package/dist/quarto/_extensions/dataimago/monograph/fonts/NotoSansMono-VF.ttf +0 -0
  88. package/dist/quarto/_extensions/dataimago/monograph/fonts/OFL-JosefinSans.txt +93 -0
  89. package/dist/quarto/_extensions/dataimago/monograph/fonts/OFL-NotoSans.txt +93 -0
  90. package/dist/quarto/_extensions/dataimago/monograph/fonts/OFL-NotoSansMath.txt +93 -0
  91. package/dist/quarto/_extensions/dataimago/monograph/fonts/OFL-NotoSansMono.txt +93 -0
  92. package/dist/quarto/_extensions/dataimago/monograph/fonts/README-lm.txt +27 -0
  93. package/dist/quarto/_extensions/dataimago/monograph/fonts/lmsans10-regular.otf +0 -0
  94. package/dist/quarto/_extensions/dataimago/monograph/partials/before-body.tex +16 -0
  95. package/dist/quarto/_extensions/dataimago/monograph/partials/title.tex +27 -0
  96. package/dist/quarto/_extensions/dataimago/pkgsite/_extension.yml +28 -0
  97. package/dist/quarto/_extensions/dataimago/pkgsite/components.scss +243 -0
  98. package/dist/quarto/_extensions/dataimago/pkgsite/dataimago-dark.scss +344 -0
  99. package/dist/quarto/_extensions/dataimago/pkgsite/dataimago-light.scss +338 -0
  100. package/dist/quarto/_extensions/dataimago/pkgsite/filters/landing.lua +233 -0
  101. package/dist/quarto/_extensions/dataimago/pkgsite/filters/provenance.lua +212 -0
  102. package/dist/quarto/_extensions/dataimago/pkgsite/hero-fallback.svg +53 -0
  103. package/dist/quarto/_extensions/dataimago/pkgsite/pkgsite-hero.js +99 -0
  104. package/dist/quarto/_extensions/dataimago/pkgsite/pkgsite.scss +281 -0
  105. package/dist/quarto/components.scss +14 -1
  106. package/dist/quarto/dataimago-dark.scss +22 -6
  107. package/dist/quarto/dataimago-light.scss +16 -6
  108. package/dist/tailwind-preset.js +4 -4
  109. package/dist/templates/KNOWLEDGE-explorer.md +1 -1
  110. package/dist/templates/KNOWLEDGE-framework.md +1 -1
  111. package/dist/templates/KNOWLEDGE-research.md +1 -1
  112. package/dist/templates/quarto_website/README.md +4 -2
  113. package/dist/templates/quarto_website/_quarto.yml.template +4 -2
  114. package/dist/templates/quarto_website/about.qmd.template +1 -1
  115. package/dist/templates/quarto_website/assets/css/lang.css +181 -0
  116. package/dist/templates/quarto_website/assets/js/custom-anchors.js +1 -1
  117. package/dist/templates/quarto_website/assets/js/logo-switch.js +1 -1
  118. package/dist/templates/quarto_website/assets/js/scroll.js +1 -1
  119. package/dist/templates/quarto_website/ethics.qmd.template +1 -1
  120. package/dist/templates/quarto_website/index.qmd.template +1 -1
  121. package/dist/tokens.css +3 -3
  122. package/dist/tokens.scss +3 -3
  123. package/package.json +6 -3
package/README.md CHANGED
@@ -13,6 +13,8 @@ Depends on [`@dataimago/tokens`](../tokens) for the canonical house identity.
13
13
  - `dist/latex/dataimago-tokens.sty` + `dataimago.sty` — the generated `di-*` colour layer and the LaTeX macro layer.
14
14
  - `dist/dataviz/` — the Okabe-Ito data-viz palette as CSS, TeX (PSTricks/TikZ), JSON and R.
15
15
  - `dist/templates/` — the project templates (`KNOWLEDGE-*.md`, the Quarto website scaffold) as stamped copies, synced into a project with `dataimago-sync` like the other outputs (house only).
16
+ - `dist/fonts/` — the token fonts as WOFF2 subsets with `fonts.css` per script, and `lang.css` (see Fonts).
17
+ - `dist/quarto/_extensions/dataimago/monograph/`, `dist/figures/`, `dist/fonts/pkg/` — the PDF format, the figure toolkit and the package-name face, with the `dataimago-figures` bin (see PDF).
16
18
  - `dist/outputs.json` and the `dataimago-sync` bin — pinned sync for non-JS consumers (below).
17
19
 
18
20
  Use [`@dataimago/css-neutral`](../css-neutral) instead when an externally-owned Level-3 app or future third-party hub needs the neutral identity.
@@ -63,6 +65,245 @@ in CI: it fails on any drift. The shipped outputs are listed in `dist/outputs.js
63
65
  (e.g. `https://unpkg.com/@dataimago/css@<version>/dist/outputs.json`). See
64
66
  [`wiki/patterns/new-consumer-checklist.md`](../../wiki/patterns/new-consumer-checklist.md).
65
67
 
68
+ ## PDF
69
+
70
+ `@dataimago/css` (house only; `css-neutral` ships none of this) carries the PDF format: a Quarto extension, a figure toolkit and the package-name face. See `wiki/decisions/pdf-format.md` and `src/latex/README.md`.
71
+
72
+ - `dist/quarto/_extensions/dataimago/monograph/`: the `dataimago/monograph-pdf` leaf (`_extension.yml`, filters, partials, `dataimago-base.sty`, `dataimago-monograph.sty`, `dataimago-tokens.sty`, `fonts/`).
73
+ - `dist/figures/`: `dataimago-figure.sty`, the tokens, the static Noto fonts and licences, for the `dataimago-figures` bin.
74
+ - `dist/fonts/pkg/`: Latin Modern Sans (WOFF2, `fonts.css`, `GFL.txt`, `README-lm.txt`) for the `.pkg` span in HTML.
75
+
76
+ **design.json mappings** (directory keys end in `/`):
77
+
78
+ ```json
79
+ { "package": "@dataimago/css", "version": "<exact version>",
80
+ "outputs": { "quarto/_extensions/dataimago/monograph/": "_extensions/dataimago/monograph/",
81
+ "figures/": "figures/",
82
+ "fonts/pkg/": "assets/fonts/pkg/",
83
+ "quarto/dataimago-light.scss": "styles/", "quarto/dataimago-dark.scss": "styles/",
84
+ "quarto/components.scss": "styles/" } }
85
+ ```
86
+
87
+ **The paired `_quarto.yml`.** The extension is PDF only; the work keeps its own HTML format with the synced theme pair:
88
+
89
+ ```yaml
90
+ format:
91
+ html:
92
+ theme:
93
+ light: [cosmo, styles/dataimago-light.scss, styles/components.scss]
94
+ dark: [cosmo, styles/dataimago-dark.scss, styles/components.scss]
95
+ html-math-method: mathml
96
+ css: [assets/fonts/pkg/fonts.css]
97
+ dataimago/monograph-pdf: default
98
+ ```
99
+
100
+ **Metadata.** `title`, `subtitle`, `author` (with `affiliations`), `version`, `date`, `license` (`text`, `url`), `epigraph` (`text`, `attribution`), `toc`, `abstract` (set as a front-matter chapter), `dataimago-logo` (reserved, default `false`; Quarto owns `logo`) and `license-badge` (reserved, default `false`; `true` warns and the licence prints as text).
101
+
102
+ **Matter classes.** Put `{.frontmatter}`, `{.backmatter}` or `{.appendix}` on a level-1 heading. On a deeper heading the render stops and names it. In HTML these are ordinary sections. Write no raw LaTeX in the `.qmd`.
103
+
104
+ **Figures.** `{.box}` images get the figure plate and frame (`#f7f7f6` plate, light frame, in both modes). Build PSTricks sources with `npx -p @dataimago/css@<version> dataimago-figures build <dir>`: each source compiles once into `.pdf`, `.svg` (fonts as paths) and RGBA `.png`. Run it from a checkout of the work with `figures/` synced.
105
+
106
+ **TeX requirements.** LuaLaTeX (TeX Live 2026 or later) for documents, with memoir, fontspec, unicode-math, lua-ul and luacolor; XeLaTeX, PSTricks, `dvisvgm` and poppler's `pdftocairo` for figures. The fonts come with the extension, so nothing is installed system-wide. Quarto copies the leaf's `fonts/` (about 6 MB) next to each rendered `.tex`, so add a `.gitignore` rule for it. The repository checks the format with `pnpm run validate:pdf` and the `PDF` CI job.
107
+
108
+ **Copies Quarto leaves behind.** Quarto copies the leaf's `fonts/` (about 6 MB) and the three `dataimago-*.sty` files beside the source directory of every document it renders (the project root, and each subdirectory that holds a rendered `.qmd`), and leaves them there. They are build output, so ignore them in the work's own repository, scoped to the directories that hold rendered documents:
109
+
110
+ ```gitignore
111
+ /fonts/
112
+ /chapters/fonts/
113
+ dataimago-*.sty
114
+ ```
115
+
116
+ Use `**/fonts/` only if no directory of the work has a `fonts/` of its own: a work that keeps its own fonts under `fonts/` must list the copies by directory as above, or ignore `dataimago-*.sty` and the leaf's font file names instead.
117
+
118
+ ## Package site
119
+
120
+ `@dataimago/css` (house only; `css-neutral` ships none of this) carries the package-site leaf: the Quarto format `dataimago/pkgsite` (key `pkgsite-html`) that dresses an R package's GitHub Pages site with a landing stage, cards, reference pages and the AI-composed label. **The normal way to use it is `dataimago::create_package_site()`**, which writes everything below, syncs it and renders the pages; read on to do it by hand or to see what it writes. See `wiki/decisions/package-site-factory.md`.
121
+
122
+ - `dist/quarto/_extensions/dataimago/pkgsite/`: `_extension.yml`, `pkgsite.scss`, `filters/landing.lua`, `filters/provenance.lua`, `pkgsite-hero.js` (JS heroes only) and `hero-fallback.svg`. The leaf ships no fonts.
123
+
124
+ **design.json mapping** (directory keys end in `/`; the generator merges into an existing file by adding keys):
125
+
126
+ ```json
127
+ { "package": "@dataimago/css", "version": "<exact version>",
128
+ "outputs": {
129
+ "quarto/_extensions/dataimago/pkgsite/": "_extensions/dataimago/pkgsite/",
130
+ "fonts/latin/": "assets/fonts/latin/", "fonts/latin-ext/": "assets/fonts/latin-ext/",
131
+ "fonts/josefin/": "assets/fonts/josefin/", "fonts/math/": "assets/fonts/math/",
132
+ "fonts/lang.css": "assets/fonts/" } }
133
+ ```
134
+
135
+ **The paired `_quarto.yml`.** List each mapped `fonts.css`, and `lang.css`, under the format. A JS hero is imported at run time, so Quarto does not publish it: `project.resources` always lists `assets/fonts/**` (the generator writes it unconditionally and its CI guard fails without it) and adds `assets/hero/**` only for a JS hero (an `svg` or `fallback` hero needs no entry).
136
+
137
+ ```yaml
138
+ project:
139
+ type: website
140
+ resources:
141
+ - assets/fonts/** # always listed
142
+ - assets/hero/** # only for hero.kind: js
143
+ format:
144
+ pkgsite-html:
145
+ css:
146
+ - assets/fonts/latin/fonts.css
147
+ - assets/fonts/latin-ext/fonts.css
148
+ - assets/fonts/josefin/fonts.css
149
+ - assets/fonts/math/fonts.css
150
+ - assets/fonts/lang.css
151
+ ```
152
+
153
+ **The landing.** `index.qmd` uses `page-layout: custom` and loads `_landing.yml`; the `.di-landing` div is replaced by the stage (a page without it is left as written):
154
+
155
+ ```markdown
156
+ ---
157
+ pagetitle: "<pkg>"
158
+ page-layout: custom
159
+ toc: false
160
+ metadata-files: [_landing.yml]
161
+ ---
162
+ ::: {.di-landing}
163
+ ::: {.di-landing-overview}
164
+ <README body, without badges or the first H1>
165
+ :::
166
+ :::
167
+ ```
168
+
169
+ A README's own `##` and `###` headings, lists and code blocks are fine in the overview: the stage is built from Div nodes, so Quarto's section wrapping cannot swallow the hero or the cards. A page with a `.di-landing` stage never shows a table of contents, whatever the project sets (the generator writes `toc: false`; the leaf also sets it from the filter, with a CSS backstop). `hero.src` and `hero.poster` must be relative paths with no `..` segment, and an SVG hero is re-checked by the leaf before it is inlined; an unsafe one fails the render and names the file.
170
+
171
+ ```yaml
172
+ pkgsite:
173
+ name: toOrdinal
174
+ title: "Cardinal to Ordinal Number & Date Conversion"
175
+ version: "1.5-0.0"
176
+ hero: { kind: svg, src: assets/hero/hero.svg, poster: assets/hero/poster.svg, alt: "One sentence." } # svg | js | fallback
177
+ links: [ { text: GitHub, href: "…" } ]
178
+ cards: # keys in order: overview, documentation, api, news, source; a card with no target is left out
179
+ - { key: overview, title: Overview, href: overview/index.qmd, desc: "…" }
180
+ ```
181
+
182
+ **Hero inputs.** `ui/hero/hero.svg`: CSS animation only, inside `@media (prefers-reduced-motion: no-preference)`, `animation-iteration-count: 3`; no `<script>`, SMIL, `<?xml` declaration or external reference; styles scoped under the root class; a `viewBox`. Or `ui/hero/hero.js` with `ui/hero/poster.{svg,png}`: a default export `mount(el, {signal}) -> {pause(), play(), destroy()}` that stops after a finite run, served from the same origin and scheme as the site. Optional `ui/hero/hero.yml` with `alt:`. With neither, the brand fallback shows. A visible pause control is always present, and under reduced motion the poster shows and no module loads.
183
+
184
+ **AI-composed pages.** A page whose front matter has a `knowledge:` block (`status: draft | approved`, `composer`, `source_sha`, `source_url`, and for approved pages `approved_by`, `approved_on`, `edited_by_author`) gets the "AI-composed" label. A malformed or inconsistent block fails the render and names the page.
185
+
186
+ **Navbar.** Overview · Documentation · API · What's new, then a GitHub link. A pinned navbar is offset in CSS on landing pages at 1024 px and up; keep the navbar to one line (4rem), or it overlaps the pinned hero. The repository checks the leaf with `pnpm run validate:pkgsite` and `tools/motion-lint.mjs`.
187
+
188
+ ## Fonts
189
+
190
+ The package ships the token fonts as WOFF2 files, so a site or document does not depend on Google's CDN. Everything is under `dist/fonts/`, one directory per script, each with a `fonts.css` of `@font-face` rules and the licence text:
191
+
192
+ - Noto Sans, upright and italic, variable in weight (100 to 900), width pinned at 100.
193
+ - Noto Sans Mono, upright only, variable in weight (100 to 900).
194
+ - Noto Sans Math, one static weight (400), for equations.
195
+ - Josefin Sans, upright only, variable in weight (100 to 700), for house headings. It is the upstream file wrapped as WOFF2, never subset (its licence reserves the font name).
196
+
197
+ Directories: latin, latin-ext, greek, greek-ext, cyrillic, cyrillic-ext, vietnamese, josefin, math. Each `@font-face` uses `font-display: swap`, a weight range read from the font file, and a `unicode-range` on the subset files, so a browser downloads only the scripts a page renders. There is no `local()` source and no preload. Subsets keep every layout feature the kept text uses; `lnum` and `tnum` work in Noto Sans. Noto Sans Mono's digits are monospaced and lining by design, and it has no figure features. Josefin Sans has no `lnum` or `tnum` feature; its digits are lining and proportional. `dist/fonts/lang.css` holds the language rules (below). The licence is OFL-1.1; `dist/fonts/` carries each licence file.
198
+
199
+ Combining marks: the `latin` and `latin-ext` files also carry the whole combining-diacritics block (U+0300-036F), a documented addition to Google's ranges, so decomposed accents (for example e + U+0301, or o + U+031B) draw from the same file as their base letter.
200
+
201
+ ### Using them
202
+
203
+ `dist/fonts/**` is listed in `dist/outputs.json`, so `dataimago-sync` copies it. A key ending in `/` maps a whole directory to a destination ending in `/`.
204
+
205
+ **A web page.** Map every script you ship and `fonts/lang.css`, then link the CSS files. The browser fetches only what the page renders.
206
+
207
+ ```json
208
+ { "package": "@dataimago/css", "version": "<exact version>",
209
+ "outputs": { "fonts/latin/": "assets/fonts/latin/", "fonts/latin-ext/": "assets/fonts/latin-ext/",
210
+ "fonts/greek/": "assets/fonts/greek/", "fonts/greek-ext/": "assets/fonts/greek-ext/",
211
+ "fonts/cyrillic/": "assets/fonts/cyrillic/", "fonts/cyrillic-ext/": "assets/fonts/cyrillic-ext/",
212
+ "fonts/vietnamese/": "assets/fonts/vietnamese/",
213
+ "fonts/josefin/": "assets/fonts/josefin/",
214
+ "fonts/math/": "assets/fonts/math/", "fonts/lang.css": "assets/fonts/" } }
215
+ ```
216
+
217
+ **A self-contained Quarto output** (`embed-resources: true`). Map only the scripts the work's language needs, for example `fonts/latin/` and `fonts/latin-ext/` (add `fonts/vietnamese/` for Vietnamese text), plus `fonts/math/` if the work has equations and `fonts/lang.css`. Add `fonts/josefin/` for the house headings. List each CSS file under `css:` in `_quarto.yml` and set `html-math-method: mathml`. Quarto then embeds just the files those rules reach.
218
+
219
+ ```yaml
220
+ format:
221
+ html:
222
+ embed-resources: true
223
+ html-math-method: mathml
224
+ css:
225
+ - assets/fonts/latin/fonts.css
226
+ - assets/fonts/latin-ext/fonts.css
227
+ - assets/fonts/vietnamese/fonts.css
228
+ - assets/fonts/josefin/fonts.css
229
+ - assets/fonts/math/fonts.css
230
+ - assets/fonts/lang.css
231
+ ```
232
+
233
+ **A translated work in a script the package does not ship** (Arabic, Hebrew, Devanagari, and so on). `lang.css` already names the family. Load it from Google Fonts on the web, or make the files yourself (see "Making a script the package does not ship") and keep them in the work's own repository.
234
+
235
+ ### Numeric figures
236
+
237
+ Josefin Sans has no figure features: its digits are lining and proportional, so they do not line up in columns.
238
+
239
+ - `.di-numeric-display` keeps Josefin and the light weight, for hero numbers (a statistic callout, a year).
240
+ - `.di-numeric-tabular` uses Noto Sans with tabular lining figures, for tables, ledgers and anything stacked that must align.
241
+
242
+ ```html
243
+ <span class="di-numeric-display">2026</span>
244
+ <td class="di-numeric-tabular">1,234.50</td>
245
+ ```
246
+
247
+ ### Other languages
248
+
249
+ Text in another language must carry a `lang` attribute, or the browser applies its own fallback. In Quarto write `[text]{lang=ar}` for a span, a fenced div `::: {lang=ar}` for a block, or `lang: ar` in the front matter for a whole work. `lang.css` maps each language to a family by `:lang()` and rebinds the display variables on headings and navigation only; the brand mark, code and maths are left alone. The tags `zh-CN`, `zh-SG` and bare `zh` map to Noto Sans SC (bare `zh` is treated as Simplified); `zh-TW`, `zh-HK` and `zh-MO` map to Noto Sans TC. Tag Traditional text `zh-Hant` or a Traditional region.
250
+
251
+ ### Maths
252
+
253
+ Equations use native MathML in `Noto Sans Math` (`font.family.math` is `Noto Sans Math, math`, so a reader without the font gets the browser's maths face). Set `html-math-method: mathml`. MathJax and KaTeX bring their own fonts, and with `embed-resources` Quarto fetches them from the network unless `self-contained-math` is set. A property that chooses one of them is outside this contract.
254
+
255
+ ### Synthesised oblique
256
+
257
+ Noto Sans Mono and Josefin Sans ship upright only. Italic text in them is the browser's synthesised oblique. Noto Sans has a true italic.
258
+
259
+ ### Preloading
260
+
261
+ Preloading is yours to opt into; the package does not. For the Latin body file, in the page head:
262
+
263
+ ```html
264
+ <link rel="preload" as="font" type="font/woff2" href="/assets/fonts/latin/noto-sans.woff2" crossorigin>
265
+ ```
266
+
267
+ `crossorigin` is required even for a same-origin font, or the browser fetches it twice.
268
+
269
+ ### Caching
270
+
271
+ File names are stable and change only when a family changes, so serve them with revalidation. That is the default on the hosts these properties use (confirmed 2026-10-31): GitHub Pages sends `cache-control: max-age=600` with an ETag, and Vercel serves static files with `public, max-age=0, must-revalidate`. Do not set a long immutable lifetime.
272
+
273
+ ### The Google alternative
274
+
275
+ A project that does not sync files can link Google's CDN instead. This is the query matching what the package ships:
276
+
277
+ ```
278
+ https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@100..700&family=Noto+Sans:ital,wdth,wght@0,100,100..900;1,100,100..900&family=Noto+Sans+Math&family=Noto+Sans+Mono:wght@100..900&display=swap
279
+ ```
280
+
281
+ Add `lang.css` as well. This loads full variable files from Google rather than the subsets, so a self-contained Quarto output becomes much larger. The Google route serves Google's own ranges, so decomposed accents using U+0302, U+0306 or U+031B fall back there (and on sites built from the Quarto website template, which uses that route); the shipped files carry the combining marks in the Latin files.
282
+
283
+ ### Making a script the package does not ship
284
+
285
+ `subset.mjs` in the dataimago-design repository has an out mode that fetches a pinned upstream file, checks its digest, subsets it and writes `<script>/<family>.woff2`, `<script>/fonts.css` and the licence into a directory outside `packages/`. It refuses a family whose licence reserves the font name.
286
+
287
+ ```bash
288
+ node tools/fonts/subset.mjs --out ../my-work/fonts \
289
+ --upstream <url pinned to a tag or commit> --sha256 <hex> --script arabic \
290
+ [--unicode-range "U+0600-06FF, U+200C-200E"] [--style normal|italic] \
291
+ --licence-url <url> --licence-sha256 <hex> \
292
+ [--member <path in archive> --member-sha256 <hex>] \
293
+ [--licence-member <path> --licence-member-sha256 <hex>]
294
+ ```
295
+
296
+ Without the tool, fontTools (the repository pins Python 3.13 and `tools/fonts/requirements.txt`) does the same in two steps. Pin the width axis if the font has one, then subset keeping every layout feature:
297
+
298
+ ```bash
299
+ fonttools varLib.instancer NotoSansArabic[wdth,wght].ttf wdth=100 --no-recalc-timestamp -o instanced.ttf
300
+ pyftsubset instanced.ttf --unicodes="U+0600-06FF,U+200C-200E" --layout-features='*' \
301
+ --name-IDs='*' --name-languages='*' --notdef-outline --no-recalc-timestamp \
302
+ --flavor=woff2 --output-file=noto-sans-arabic.woff2
303
+ ```
304
+
305
+ To wrap a file as WOFF2 without subsetting: `fonttools ttLib.woff2 compress -o out.woff2 in.ttf`. (The repository's tool also turns off table transforms so the tables stay byte-identical; the command line does not, so the bytes can differ while the font is the same.) Write the `@font-face` rule with `font-display: swap`, the font's own weight range and `unicode-range`, and no `local()`.
306
+
66
307
  ## Building
67
308
 
68
309
  ```bash