pub struct Vocabulary<'a> { /* private fields */ }Expand description
Fail the build if a hand-written stylesheet takes a property the generated one already sets, on the same class or on an element that carries it.
The generated sheet sits in @layer makeover. App CSS beats it whatever the
specificity, either by being unlayered or by sitting in a layer the app’s
order statement puts after makeover, so an app declaration for a property
makeover already sets does not merge with it: it wins, silently, and the
design system’s version of that component stops applying, and nothing looks.
§Two passes, because a rule can carry no class
The class pass compares an app .button against the generated .button.
It reads rules by the classes in their selectors, so a rule with no class in
it is invisible to it, and button { color: var(--content) } is exactly
that: it sets the property the generated .button sets on every described
act, .button[data-tone="danger"]’s tone included, which draws a
destructive act like an ordinary one.
The element pass reads that case. crate::vocabulary::ELEMENT_CLASSES
says which generated classes an element can carry (CSS cannot say it, and
the renderer can), and a bare element rule taking a property the design
system sets on one of those classes is the same defect as the class case.
Two things are not reported, both deliberately:
- A scoped rule (
.page button). It reaches the elements inside one region rather than every one of them, so whether it lands on a described act depends on where that act renders. The certain case is the one this reads. - A property the app names on the class itself, from a rule that outranks
the element rule. Both are the app’s and both sit in the same layer, so
that one contest is settled by specificity, and what reaches the design
system is the class rule, which the class pass has already reported
or reviewed. The rank matters:
.fielddoes not beatinput[type="text"], and a handoff written as the weaker of the two is a remedy that looks written and is not.
§Why properties and not class names
A shared class name is not by itself a divergence. An app .badge that sets
shape while the generated .badge sets colour is a correct split, and a
check on names would ask for it to be deleted. On properties, the split is
what the check enforces.
§The exception list
Allow::class names a (class, property) pair this app has reviewed and
kept.
Deciding which are legitimate here would need a selector matcher, and a check
that guesses wrong about specificity fails correct builds, so the app
declares it instead, the same shape as quasi-webview’s RENDERER_OWN.
A reviewed pairing is a claim about who owns a property, and it expires when the design system takes the property back.
Allow::element is the same thing one pass down: an element rule reaching
a generated class that has been read and kept.
The remedy is usually neither. A later layer can hand the property back
with revert-layer, which says “whatever the design system set here, keep
it” on the arms makeover actually paints, and that is a statement in the
stylesheet rather than a note in a build script. Handoffs are not reported by
either pass.
A pair that stops colliding fails too. A licence nobody is using is where the next real collision lands and reads as company.
use makeover_webview::build::drift::{Allow, Vocabulary};
Vocabulary::in_frontend("frontend")
.generated(&["layout.css"])
// Reviewed: the card's shadow is the app's, on the hover arm only.
.allow(Allow::class("card", "box-shadow"))
.run(&makeover_webview::Emit::default());Implementations§
Source§impl<'a> Vocabulary<'a>
impl<'a> Vocabulary<'a>
Sourcepub fn in_frontend(frontend: impl AsRef<Path>) -> Self
pub fn in_frontend(frontend: impl AsRef<Path>) -> Self
Every .css under frontend/css, recursively.
Sourcepub fn files<P: AsRef<Path>>(paths: &[P]) -> Self
pub fn files<P: AsRef<Path>>(paths: &[P]) -> Self
A named list of stylesheets rather than a tree.
For a frontend whose generated and hand-written sheets share a
directory. Same trade as Breakpoints::files: the list is
hand-maintained, and a stylesheet nobody adds to it is unchecked.
Sourcepub fn generated(self, names: &[&'a str]) -> Self
pub fn generated(self, names: &[&'a str]) -> Self
Sheets this crate writes, which are skipped: the generated file setting
a generated property is the point. Relative to frontend/css for
in_frontend; matched against the end of each path
for files.
Sourcepub fn upstream(self, sheets: &[&'a str]) -> Self
pub fn upstream(self, sheets: &[&'a str]) -> Self
The text of stylesheets that sit between makeover and the app and own the classes they style as makeover owns its own.
quasi-webview’s SCREEN_CSS is the case. This crate cannot name it,
since quasi builds on this crate and not the other way round, so the app
that links both hands it in. A hand-written sheet is checked against
makeover’s sheet and every upstream sheet together. A file whose text
is an upstream sheet, the copy an app serves from its frontend, is
checked against makeover alone and never read as clashing with itself.
Sourcepub fn beneath(self, layers: &[&'a str]) -> Self
pub fn beneath(self, layers: &[&'a str]) -> Self
Cascade layers the app orders before makeover, by name.
A rule inside one loses to every generated rule whatever its selector,
so it cannot take a property from the design system and is not read.
The usual case is a reset: @layer reset, makeover, ... with
* { margin: 0; padding: 0 } inside @layer reset { } clears browser
defaults without zeroing the generated spacing. The order itself lives
in the app’s markup, which this check does not read, so naming a layer
here is the app stating that order.
Sourcepub fn allowing(self, allows: impl IntoIterator<Item = Allow<'a>>) -> Self
pub fn allowing(self, allows: impl IntoIterator<Item = Allow<'a>>) -> Self
Several reviewed exceptions, for a list the app keeps as a const.
Sourcepub fn run(&self, opts: &Emit)
pub fn run(&self, opts: &Emit)
Run the check against the stylesheet opts generates. Emits
cargo::rerun-if-changed for every file it read.
§Panics
If a directory or listed file cannot be read, if any hand-written sheet
takes a generated property without an Allow, or if an Allow no
longer collides. A build script has nowhere useful to return an error
to, and an app quietly overriding its own design system is worse than a
failed build.