Skip to main content

Vocabulary

Struct Vocabulary 

Source
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: .field does not beat input[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>

Source

pub fn in_frontend(frontend: impl AsRef<Path>) -> Self

Every .css under frontend/css, recursively.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn allow(self, allow: Allow<'a>) -> Self

A reviewed exception. See Allow.

Source

pub fn allowing(self, allows: impl IntoIterator<Item = Allow<'a>>) -> Self

Several reviewed exceptions, for a list the app keeps as a const.

Source

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.

Trait Implementations§

Source§

impl<'a> Clone for Vocabulary<'a>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for Vocabulary<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a> Forget for Vocabulary<'a>

§

impl<'a> Freeze for Vocabulary<'a>

§

impl<'a> Move for Vocabulary<'a>

§

impl<'a> RefUnwindSafe for Vocabulary<'a>

§

impl<'a> Send for Vocabulary<'a>

§

impl<'a> Sync for Vocabulary<'a>

§

impl<'a> Unpin for Vocabulary<'a>

§

impl<'a> UnsafeUnpin for Vocabulary<'a>

§

impl<'a> UnwindSafe for Vocabulary<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.