Warhol

must-match/must-not-match


--warhol-must-match and --warhol-must-not-match are two directives that require certain elements to match (or not match) CSS selectors of your choice. This is useful to contrain the ordering of component children. Consider the following example component definiton:

<!-- "Icon button" component defined in pattern library -->
<button class="iconButton">
  <img class="iconButton__icon" src="checkmark.png" />
  <span class="iconButton__text">OK</span>
</button>

Warhol's default laissez-faire approach to componen children means that it allows accepts number and any combination of child elements that are present in the pattern library. The directives --warhol-must-match and --warhol-must-not-match are one way to tighten up the requirements for the component child elements.

If we wanted to make sure that there can at most be one icon and to ensure that it is always the first child element inside an .iconButton component, we could change the component definition in the pattern library as follows:

<!-- "Icon button" component defined in pattern library -->
<button class="iconButton">
  <img style="--warhol-must-match: ':first-child'" class="iconButton__icon" src="checkmark.png" />
  <span class="iconButton__text">OK</span>
</button>

The use of the style attribute is optional - the could just as well be defined in a CSS file together with all of the other style directives. The quotes around the selector are required for all but the simplest CSS selectors (like div). The property --warhol-must-match does not count as style property but instructs Warhol tocomplain if it encounters an .iconButton component where the icon is not the first child element:

<!-- "Icon button" components implemented in a production web site -->

<button class="iconButton">
  <img class="icon" src="checkmark.png" /> <!-- no problem -->
  <span class="text">OK</span>
</button>

<button class="iconButton">
  <img class="icon" src="info.png" /> <!-- no problem -->
</button>

<button class="iconButton">
  <span class="text">Cancel</span>
  <img class="icon" src="cross.png" /> <!-- causes a "must match violation" error -->
</button>

Unsurprisingly, --warhol-must-match requires an element to not match a certain selector. We could use this directive to require the icon in an .iconButton component to be followed up by some other element:

<!-- "Icon button" component defined in pattern library -->
<button class="iconButton">
  <img
    style="--warhol-must-match: ':first-child'; --warhol-must-not-match: ':only-child'"
    class="iconButton__icon"
    src="checkmark.png" />
  <span class="iconButton__text">OK</span>
</button>

This effectivly forces every .iconButton component to contain an icon and a text element in exectly this order:

<!-- "Icon button" components implemented in a production web site -->

<button class="iconButton">
  <img class="icon" src="checkmark.png" /> <!-- no problem -->
  <span class="text">OK</span>
</button>

<button class="iconButton">
  <img class="icon" src="info.png" /> <!-- causes a "must match violation" error -->
</button>

<button class="iconButton">
  <span class="text">Cancel</span>
  <img class="icon" src="cross.png" /> <!-- causes a "must match violation" error -->
</button>

The directives --warhol-must-match and --warhol-must-not-match are at the moment the only way to restrict the order or force the presence of of component children. Other features like this will be added in the future.

Tips for debugging

  • Read the error message for any must-match violation carefully, as it explains precisely which selector does not (or, in violation of --warhol-must-not-match, does) match the element that is being reported.
  • Check the snapshot error log in browser extension or the web app to make sure that Warhol was able to parse the selectors in your directives.
  • Keep it simple! --warhol-must-match and --warhol-must-not-match are a somewhat clunky way to express complex constraints. Better features for this exact purpose are on their way!

See also