| 130 |
130 |
|
std::fs::write(path, css).expect("write geometry css");
|
| 131 |
131 |
|
}
|
| 132 |
132 |
|
|
| 133 |
|
- |
/// All three generated files at the layout every Tauri consumer already uses:
|
| 134 |
|
- |
/// `themes/` beside the manifest, and `frontend/css/{geometry,layout}.css`
|
| 135 |
|
- |
/// under it.
|
|
133 |
+ |
/// Write the house typography layer to `path`: the two `@font-face` rules and
|
|
134 |
+ |
/// the two tokens they back.
|
| 136 |
135 |
|
///
|
| 137 |
|
- |
/// Pass `env!("CARGO_MANIFEST_DIR")`. Consumers that want different paths call
|
| 138 |
|
- |
/// [`themes`] and [`layout_css`] directly.
|
|
136 |
+ |
/// `font_url` is the directory the consumer serves its fonts from, without a
|
|
137 |
+ |
/// trailing slash — `/static/fonts` on the MNW server, `fonts` for a Tauri
|
|
138 |
+ |
/// frontend loading relative to its index.
|
|
139 |
+ |
///
|
|
140 |
+ |
/// Generated rather than hand-written for the same reason the spacing layer is:
|
|
141 |
+ |
/// the facts are the crates' and stating them per app is how three apps came to
|
|
142 |
+ |
/// hold three different answers to `--font-mono`. It is a separate file from
|
|
143 |
+ |
/// the layout stylesheet because `@font-face` rules take no part in the
|
|
144 |
+ |
/// cascade and a consumer may need to load them ahead of a layer order it
|
|
145 |
+ |
/// declares elsewhere.
|
|
146 |
+ |
///
|
|
147 |
+ |
/// # The consumer still has to put the faces there
|
|
148 |
+ |
///
|
|
149 |
+ |
/// This writes the CSS that fetches `QuasiMono.woff2` and `QuasiBody.woff2`; it
|
|
150 |
+ |
/// does not write the fonts. It cannot: they are cut by `quasi-type`, which is
|
|
151 |
+ |
/// `publish = false`, and this crate is on crates.io. A consumer takes
|
|
152 |
+ |
/// quasi-type as a git dependency in its own `build.rs` and calls
|
|
153 |
+ |
/// `quasi_type::cut`, the way `shop-font` does, writing each slot's woff2 under
|
|
154 |
+ |
/// [`makeover::WEBFONT_MONO_FILE`] and [`makeover::WEBFONT_SANS_FILE`].
|
| 139 |
155 |
|
///
|
| 140 |
156 |
|
/// # Panics
|
| 141 |
157 |
|
///
|
| 142 |
|
- |
/// If either file cannot be written.
|
|
158 |
+ |
/// If the file cannot be written.
|
|
159 |
+ |
pub fn typography_css(path: impl AsRef<Path>, font_url: &str) {
|
|
160 |
+ |
let mut css = String::from(
|
|
161 |
+ |
"/* Generated by makeover-build from makeover. Do not edit.\n \
|
|
162 |
+ |
Two needs, two names, then a system generic. The faces are cut by\n \
|
|
163 |
+ |
quasi-type from Atkinson Hyperlegible plus the house glyph set, and\n \
|
|
164 |
+ |
both are variable over wght 200-800 in one file — which is why the\n \
|
|
165 |
+ |
@font-face rules name the range. The mono face opens at ExtraLight. */\n\n",
|
|
166 |
+ |
);
|
|
167 |
+ |
css.push_str(&makeover::font_face_css(font_url));
|
|
168 |
+ |
css.push_str(&makeover::typography_css_vars());
|
|
169 |
+ |
std::fs::write(path, css).expect("write typography css");
|
|
170 |
+ |
}
|
|
171 |
+ |
|
|
172 |
+ |
/// All the generated files at the layout every Tauri consumer already uses:
|
|
173 |
+ |
/// `themes/` beside the manifest, and
|
|
174 |
+ |
/// `frontend/css/{geometry,layout,typography}.css` under it.
|
|
175 |
+ |
///
|
|
176 |
+ |
/// Pass `env!("CARGO_MANIFEST_DIR")`. Consumers that want different paths call
|
|
177 |
+ |
/// [`themes`], [`layout_css`] and [`typography_css`] directly.
|
|
178 |
+ |
///
|
|
179 |
+ |
/// The font URL is `fonts`, relative to the frontend's index — the one layout
|
|
180 |
+ |
/// a Tauri app has, since its frontend is served from its own directory.
|
|
181 |
+ |
///
|
|
182 |
+ |
/// # Panics
|
|
183 |
+ |
///
|
|
184 |
+ |
/// If any file cannot be written.
|
| 143 |
185 |
|
pub fn tauri_frontend(
|
| 144 |
186 |
|
manifest_dir: impl AsRef<Path>,
|
| 145 |
187 |
|
opts: &makeover_webview::Emit,
|
| 150 |
192 |
|
themes(root.join("themes"));
|
| 151 |
193 |
|
geometry_css(css.join("geometry.css"), explicit_touch);
|
| 152 |
194 |
|
layout_css(css.join("layout.css"), opts);
|
|
195 |
+ |
typography_css(css.join("typography.css"), "../fonts");
|
| 153 |
196 |
|
}
|
| 154 |
197 |
|
|
| 155 |
198 |
|
#[cfg(test)]
|
| 208 |
251 |
|
);
|
| 209 |
252 |
|
}
|
| 210 |
253 |
|
|
|
254 |
+ |
#[test]
|
|
255 |
+ |
fn the_typography_file_declares_the_faces_before_the_tokens_that_name_them() {
|
|
256 |
+ |
// The vocabulary itself is tested in makeover. What is this crate's
|
|
257 |
+ |
// job is that both halves reach one file, in an order that works: a
|
|
258 |
+ |
// `@font-face` may follow its use in the cascade, but reading the file
|
|
259 |
+ |
// is how anyone finds out a face is fetched at all.
|
|
260 |
+ |
let dir = scratch("typography");
|
|
261 |
+ |
let path = dir.join("typography.css");
|
|
262 |
+ |
typography_css(&path, "/static/fonts");
|
|
263 |
+ |
let css = std::fs::read_to_string(&path).unwrap();
|
|
264 |
+ |
|
|
265 |
+ |
assert!(css.starts_with("/* Generated by makeover-build"));
|
|
266 |
+ |
assert!(
|
|
267 |
+ |
css.find("@font-face").unwrap() < css.find(":root").unwrap(),
|
|
268 |
+ |
"the tokens come first, so the file reads as a stack with no ground"
|
|
269 |
+ |
);
|
|
270 |
+ |
assert!(css.contains("url(\"/static/fonts/QuasiMono.woff2\")"));
|
|
271 |
+ |
assert!(css.contains("--font-sans: \"Quasi Body\", sans-serif;"));
|
|
272 |
+ |
|
|
273 |
+ |
// Not a cascade layer. `@font-face` takes no part in the cascade and a
|
|
274 |
+ |
// consumer may need these rules ahead of a layer order it declares
|
|
275 |
+ |
// elsewhere, so wrapping this file in one would be a silent trap.
|
|
276 |
+ |
assert!(!css.contains("@layer"));
|
|
277 |
+ |
}
|
|
278 |
+ |
|
| 211 |
279 |
|
#[test]
|
| 212 |
280 |
|
fn the_geometry_file_carries_the_crates_policy_and_a_banner() {
|
| 213 |
281 |
|
// The policy itself is tested in makeover-geometry. What is this
|