Components


Components are reusable building blocks for web applications with a canonical implementation in both HTML and CSS. While themes make sure that all of your elements conform to a few general guidelines regarding colors and typography, components must implement the exact styles prescribed in the pattern library and can only have child elements with the exact styles prescribed in the pattern library.

Components through Warhol's eyes #

Consider the following example component definition:

<!-- "Icon button" component defined in pattern library -->

<button class="iconButton">
  <span class="iconButton__icon">
    <img class="iconButton__icon__image" src="checkmark.png" />
  </span>
  <span class="iconButton__text">OK</span>
</button>

<style>
.iconButton {
  width: 200px;
  padding: 0.5em;
  position: relative;
}
.iconButton::after { /* add a two-tone underline */
  content: "";
  position: absolute;
  bottom: 0.25em;
  left: 0.5em;
  width: calc(100% - 1em);
  border-top: 2px solid #CCC;
  border-bottom: 1px solid #AAA;
}
.iconButton__icon {
  font-size: 1.2em;
}
.iconButton__text {
  font-weight: bold;
}
</style>

Warhol will deduce the following rules from this component definition:

  1. An element that matches the selector .button and not any other component selector with a higher specificity is an instance of the Icon button component
  2. As such, its computed width property be equal to 200px, the padding must be 0.5em (or whatever number of pixels this value computes to) and the position value must be relative.
  3. The component must also have the pseudo element ::after defined with all of its styles
  4. An icon button may have child elements that use styles that either match the styles of .button__icon or the styles of .button__text

These rules are what Warhol saves as data about your pattern library. When Warhol tests a production web page against the data it extracted from a pattern library, it essentially reverses the process and complains if the data from the pattern library and the data from the production web site don't match.

It is important to stress that

  1. Warhol does not care much about HTML tags, text content, image URLs or the specific classes, ids and other attributes that are being used to implement a component. Selectors and tags are helpful when figuring out which element is which, but when push comes to shove, only the specified styles count. You could swap out the img element with an svg and nothing would change, at least from Warhol's point of view.
  2. Components can be nested inside other components in your pattern library, but only components that are not nested in other components count as canonical component definitions. Nested components do not contribute anything to Warhol's analysis of your pattern library.
  3. An element's styles in production must match the element's styles in the pattern library exactly. The computed values must match for each property explicitly defined by CSS. Very few properties can be whitelisted for testing using the layout-mode directive.
  4. if a component or component child has a pseudo element like ::before and ::after in the pattern library, the same pseudo elements are mandatory when testing the production web page and the pseudo elements are required to have styles that match the pattern library.
  5. Warhol has currently no concept of any sort of ordering of component children or of mandatory component children. Any component child can appear in any place in its parent and in any number, as long as its styles match. The same is true for component children's children. There will be ways to change this behavior to something more strict in the future.
  6. Warhol currently has no concept dynamic component states, but this will also change in the future.
  7. a pseudo element can't take the place of a component child or vice versa, even if their styles are equal.
  8. issues that are not directly related to visual design, like HTML validity, SEO and accessibility are firmly outside of Warhol's scope, as is the inner construction of <svg> elements, the pixels on a <canvas> and so on.
  9. Warhol simply ignores invisible child elements inside components as well as line breaks. Additional line breaks or stray script elements do not count as errors.

All these limitations are by design and there will be configuration options to make the testing more strict in the future and to expand testing to dynamic states (which covers CSS pseudo classes in the vain of :hover as well as custom states like an .isOpen class on a collapsible element). By design Warhol only points out design discrepancies that are obvious problems, such as a wrong color. But Warhol can't know everything from one example element in the pattern library. Can an .iconButton have more than one icon? Is more than one iconButton__text allowed? Is iconButton__text even required or are icon-only buttons OK? Does the icon always have to be the first child element in an .iconButton? There is no way to know by just looking at an example, so Warhol defaults to not complaining about anything that is at least plausible (which includes all of the above points about the icon button).

Future improvements to Warhol will allow you to tighten up its test algorithms. The only way to do so right now is with the directives must-match and must-not-match.

Utility classes #

If you want to use helper or utility classes like .box-shadow or .alignLeft on your components without having them cause unexpected CSS declaration errors, check out style utilities!

Configuring components #

The components field in your Warhol configuration can contain a list of component definitions.

Field source (string, required, non-empty) #

The selector for the component example(s) in the pattern library. The selector can target more than one element.

Field target (string, optional, non-empty, defaults to source) #

The selector for the component implementations in the production web page. You can omit the target field if its value is equal to source.

Field name (string or null, optional, non-empty, defaults to target) #

An optional human-readable name for the component. Defaults to the target selector if name is not specified or null.

Field componentUrl (string or null, optional or required depending on the pattern lib URL) #

This component's own URL, if any. Defaults to the pattern lib URL. If no pattern lib URL was specified, this field is required for each component.

Note that you do not need to specify a component URL if the component definition is embedded in an iframe in the page at the pattern lib URL. Warhol can extract components from iframes as long as it encounters the iframe on some other page.

Example configuration #

Full example:

{
  "patternLibUrl": "https://warhol.io/patterns",
  "breakpoints": [800, 1000],
  "components": [{
    "source": ".iconButton",
    "target": ".icoBtn",
    "name": "Icon button",
    "componentUrl": "https://warhol.io/patterns/iconButton",
  }]
}

Minimal example:

{
  "patternLibUrl": "https://warhol.io/patterns",
  "breakpoints": [800, 1000],
  "components": [{
    "source": ".iconButton"
  }]
}

FAQ #

  • How can I test dynamic components, such as collapsible boxes? At the moment there is no built-in way for Warhol to test dynamic components or anything interactive (like :hover). This feature is on our roadmap and in even implemented in parts but it not yet ready for prime time. As a workaround you can define different component states as separate components (e.g. a component for .collapsible and an extra component for .collapsible.isOpen) and re-run tests to check the component in both states. This is just a temporary workaround! In the future Warhol will be able to automatically predict the styles in component's dynamic states with only minimal configuration overhead.

Join our Slack Community

Any question not answered yet? You want to get the latest information on Warhol and discuss new features? Join our Slack Community.