First, clone the project Hasty at libs/frontend-libs/hasty and create a new branch of the origin/dev branch. Before doing anything else, make sure you update the version in the package.json file at the root of the project, to do that you can run:

This will ensure that new version of styleguide is properly built and no previous version is overwritten. Hasty adheres to the Semantic Versioning specification, with major versions reserved for breaking changes, patch versions for various fixes, and minor versions for everything else.

Development

To start the development server, run docker-compose up to use a Docker container, or npm ci and than npm run develop to develop locally. Then, open http://localhost:8080. All source files are found in the src directory. To add a new piece of styleguide, you first have to decide what type it is. There are objects, elements, components, and utility classes. They are all described in the namespaces section. Then, add a new .less file with the name of the new module into the appropriate folder in the src/css directory. To show the new module in the styleguide, add a new .njk file into a folder with the same name as before, but this time in the src/styleguide directory. This is an example of part of the file that generates the button element:

Do not forget to write a unit test for any new React component. Create a directory `__tests__` inside the component folder and add test file with snapshots.

---
name: Button
desc: "Button can take either the form of a link or a clickable element. The basic variant should be used sparingly in a given view, as it represents a primary action, or a CTA. If more than one button is present at once, all secondary actions should use the simple modifier. In case the primary action is destructive and cannot be taken back (e.g. delete a review), the button must use the negative modifier class."
---

{% variant 'e-button' %}
<button class="e-button" type="button">Dolorum!</button>
{% endvariant %}

{% variant ['e-button', 'e-button--simple'] %}
<button class="e-button e-button--simple" type="button">Dolorum!</button>
{% endvariant %}

Changelog

Don't forget to update CHANGELOG.md by new version entry with your changes.

Publishing

To publish a new version of styleguide, make sure you have the right version in package.json. After merging your branch to the dev branch, a new styleguide is automatically deployed to heureka.cz.stage.heu.cz/ui. To deploy a production version, merge the dev branch into the master branch. Apart from deploying the styleguide to heureka.cz/ui, this ensures that the latest version of styleguide is published to a private NPM registry npm.heu.cz. The styleguide is then available under the package name of @heureka/hasty.

Alpha publishing
Alpha unpublishing
Approval

To release your contribution into production, it has to be reviewed and approved by at least two members of the front-end technical group. Afterwards, it can be merged into the dev branch, which automatically deploys the new version to heureka.cz.stage.heu.cz/ui, where it can be checked once again. Then, create a merge request from the dev branch to the master branch and wait for another approval (one is enough). If approved, you can finally push the update to the production (both to heureka.cz/ui and the private NPM registry) by merging the branch.

How to write components

There are many well known methods for building HTML and CSS components. We base ours on a set of recommendations called BEM, or Block, Element, Modifier, which rests on a simple, but hard rule. It forbids all selectors other than those targeting classes, like .foo, and prohibits selector chaining. Both, of course, with specific exceptions. If we comply, we avoid some of the biggest problems with specificity.

The abbreviation BEM hides another principle. A component represents a so-called block, which consists of several elements and may use modifiers. To distinguish them, each of these have a different naming convention. If you create a component (or block) with the name (class) block, then its inner element must be named something like block__element. We recognize modifiers because of their block--modifier class. Here is an example of specific component:

<nav class="breadcrumbs breadcrumbs--small">
<ul class="breadcrumbs__list">
<li class="breadcrumbs__item">
<a class="breadcrumbs__link" href="">Heureka.cz</a>
</li>
<li class="breadcrumbs__item">
<a class="breadcrumbs__link" href="">E-shops</a>
</li>
<li class="breadcrumbs__item">
<span class="breadcrumbs__link">Notino</span>
</li>
</ul>
</nav>

Using multiple __ to give an element its depth is a common mistake. Instead of the class breadcrumbs__link, we would have breadcrumbs__list__item__link. This is unnecessary — and ugly — and makes it harder to change the structure of a component.

Structure

We use the CSS preprocessor Less. Preprocessors give you the basic but important option to put each component in its own file, for example breadcrumbs.less:

.breadcrumbs {


&--small
{}
&--large {}
}

.breadcrumbs__list {


.breadcrumbs--small &
{}
}

.breadcrumbs__item {


@media (max-width: @lteLayout) {

}
}

.breadcrumbs__link {


&:not([href])
{}
}

The element selectors in the example are not chained with the block and thus remain simple. They are also indented to reflect the HTML structure of the component. This is a helpful practice that — as opposed to the multiple use of __ — is easy to maintain.

In contrast, modifiers are inserted directly into a block using the & parent selector. Modifiers usually change properties of blocks, but sometimes these changes apply directly to elements. That’s a good excuse to use chaining, like the .breadcrumbs--small & { … } in the element breadcrumbs__list. We treat @media blocks similarly and put them right where they belong.

The last interesting thing to point out is the selector &:not([href]) { … }. It goes against the principle of BEM. The correct way would be to create a new modifier, for example breadcrumbs__link--current. It's fine, though. Not everything should conform exactly to the BEM method and common sense sometimes leads you a little astray.

Composition

If we want to adjust the appearance or behavior of a component depending on the context or its location in the HTML document, we often stumble. For example, two components named article and category, which represent different sections of the site and define their layout, are two different contexts. How to proceed if the breadcrumbs component should look or behave differently in each section?

We might think of using the selectors .article .breadcrumbs { … } and .category .breadcrumbs { … }. That, however, would be an unwise choice. We would quickly find out that it’s not at all clear where to put these — into breadcrumbs.less or article.less? Either way is bad, because both create an unnecessary link between the components. That, in turn, would complicate future modifications and lead to trouble with specificity.

Depending on the amount and type of adjustments, we can make a better choice. If the changes are superficial, such as background color or text size, we make a modifier. These are best named in general way, like breadcrumbs--small or breadcrumbs--large. If we know these are specific and will not be used elsewhere, it’s fine to use names such as breadcrumbs--article and breadcrumbs--category.

When it comes to major changes — at the level of structure or layout — we need to think a little more. The goal is to build components that are manageable and reusable. A component (block) is defined by its structure and inner layout, and therefore it knows if, for example, it’s a flex or grid container, or whether its elements are block or inline. It shouldn’t, however, reason or know about its location in the external layout. If, for example, we declare the breadcrumbs component as position: absolute, it will unnecessarily limit its reusability. So, it’s better to leave such declarations to components that are designed to provide the overall page layout.

In our case, that’s the article and category components’ job. So, we create two new elements article__breadcrumbs and category__breadcrumbs, whose task is to place the breadcrumbs component within the section. Since the appearance and inner structure of the component are still handled by the breadcrumbs class, the result is a composition of classes: <nav class="breadcrumbs category__breadcrumbs">. Unlike the .category .breadcrumbs { … } solution, composition doesn’t create a strong link between components and avoids specificity.

Namespaces

The original purpose of BEM was to make developers’ work easier and help them code better. Because of its clear syntax, it enhances code readability and eases orientation. We immediately know what we’re dealing with, just by looking at the code. But can we make it even better?

Using the example of the breadcrumbs and category components, we see that the first one is a reusable component, while the other lays out a particular section. They’re entirely different beasts. If that’s not clear from the names, we’ll make it obvious and add a namespace to the classes. Breadcrumbs becomes c-breadcrumbs and category takes the name of l-category. The namespace c (component) suggests that a class is a component — a closed module with a clear, specific use that is always the same. On the other hand, l (layout) gives the hint that a class provides page layout.

We use three more namespaces. The first one, named e (element), is similar to c but used only on those components that don’t have an inner structure (therefore no BEM elements). A good example is the e-button element, e.g. <button class="e-button">Send</button>. These are the simplest components.

The namespace o (object) is more complicated, but also more valuable in its meaning. It suggests a class that’s in charge of the local structure and layout. The objects o-block-list and o-inline-list are good examples. The o in the title tells us that we’re dealing with a structural element, not a specific component like a bullet list (which would have the c-bullet-list class). The o-block-list lays out its elements (called o-block-list__item) under each other, while o-inline-list puts them side by side. Also, both have a bunch of modifiers (such as o-block-list--loose), which are used to modify the spacing between elements. As objects can be used in many places and for different purposes, you can’t predict where they end up, which means objects shouldn’t be edited casually, because any change can have far-reaching consequences. It’s better (and safer) to add new modifiers.

The last namespace is the utility class with the prefix u. These classes have only one specific purpose — which should be obvious from the name — such as u-align-left or u-visually-hide. However, they should be used sparingly. Unless we need the change only in one place or situation, it’s better to create a new component or modifier.