# Coding guidelines

Below you can find rules that needs to be applied into every component from Storefront UI to ensure that they are highly customizable, follow common format and coding standards.

# Coding conventions

The good news is that for basic coding, we have prettier run automatically on each commit made. Besides we do have eslint check enabled, so you can know exactly where you code doesn't fit our conventions.

In general, this is an open source project, hence:

  • Readability is extremely important. Please consider the next random developer who will read your code, and keep it simple for them.
  • No complex logic in template view. If there is, please use computed properties.

# Design patterns

We use atomic design concept for grouping components (without templates and pages).

Atomic design

In general, there are three groups of components:

  • Atoms - Atoms are the most basic and abstract components for building other components, such as color palletes, icons, button, input, etc.
  • Molecules - Molecules are components built from groups of atoms bonded together, take on their own properties and serve as the backbone of the design systems. Some good examples are a banner, gallery, menu item, etc.
  • Organisms - Organisms are complex and distinct components built from groups of molecules joined together, such as header, grid, accordions, etc.

IDENTIFY COMPONENT GROUP

Identifying component group is not easy, hence we save you the trouble. In the component request issue, the group directory of the component will be mentioned as part of the requirements.

# Component rules

# Naming convention

We follow Pascal Case for component naming and with a prefix Sf - hence the naming formula will be:

Sf<ComponentName>

# Component's file structure

  1. Each component should have the same files structures. Each file should be named in the format Sf<ComponentName>.<extension>:
./<ComponentName>/
|__Sf<ComponentName>.vue
|__Sf<ComponentName>.md
|__Sf<ComponentName>.spec.js
|__Sf<ComponentName>.stories.js
  • vue - template markup and component instance logic, in JavaScript.
  • md - docs for component,
  • spec.js - unit test for component, in Java Script.
  • stories.js - Storybook stories for the component.

WARNING

For the purpose of sharing our component design between projects, all the .scss files of the components will not reside within each component's directory, but will be in shared/styles/components/. Naming convention is the same as above.

# CSS Rules

# CSS Naming convention

We strictly follow BEMs for naming SCSS modifiers and CSS classes. And the CSS class names should match slot's names.

<component>__<block>--modifier

For example: .gallery__item--selected

WARNING

Keep up to 2 BEM levels (elements) at most.

# CSS Custom Properties

# Rules and tips to create and use CSS Custom Properties / CSS Variables.

  1. DO NOT use SASS VARIABLES.

  2. Create variable with default value like this:

.sf-button {
    padding: var(--button-padding, var(--spacer));
}

or empty variable without default

sf-button {
  border-radius: var(--button-border-radius);
}

TIP

Create variables for all customizable properties like padding, margin, order, z-index, color, border, border-radius, background-color.

Think of properties you would like to change it as a user and create for it a variable.

Create size variables var(--component-size, 25px) in case when you have the same width and height value

.sf-arrow{
  width: var(--component-width, var(--component-size, 266px));
  height: var(--component-height, var(--component-size, 266px));
}
  1. Use global variables for spacers, font-sizes, colors. You can find all variables here shared/styles/variables

TIP

SCSS functions and mixins you cand find here shared/styles/_helpers.scss

All global variables are here shared/styles/_variables.scss

  1. CSS Variables Naming convention
  • variable name for BLOCK var(--input-width);
  • variable name for ELEMENTS var(--input-label-padding);
.sf-input{
  width: var(--input-width, 350px)
  &__label{
    padding: var(--input-label-padding, var(--spacer-small) var(--spacer))}
  }
  1. If you want to customize some component e.g. SfButton which is nested in molecule or organism you should use defined variable from SfButton to customize it.
.sf-add-to-card {
  &__add-button {
    --button-padding: var(--spacer-medium);
  }
}
  1. Write variables always on the top of block before other properties.
.sf-arrow{
  --button-width: 20px;
  --button-height: 20px;
  outline: 0
}
  1. To create font variable use scss function font(--component-name, font-weight, font-size, line-height, font-family);
  • Use it like this:
  font: var(
    --banner-title-font,
    font(
      --banner-title,
      300,
      var(--font-size-small),
      1.6,
      var(--font-family-secondary)
    )
  );
  • If you want to customize:
.sf-banner {
    &__title {
        --banner-title-font-size: var(--font-size-regular);
    }
}
  1. You shouldn't use media queries inside block elements. Instead, use mixin for this for-desktop and write all properties at the bottom in the block scope.
.sf-banner{
  &__label{
    padding: var(--banner-label-padding, 10px 20px)
  }
  @include for-desktop{
    --banner-label-padding, 20px 40px
  }
}
  1. Most recommended NOT TO use &this
.sf-product-card {
  &:hover {
    #{$this}__add-button {
      opacity: 1;
    }
  }

When it is possible try to use variable

TIP

.sf-product-card {
  &:hover {
    --product-card-add-button-opacity: 1;
  }
  &__add-button {
      opacity: var(--product-card-add-button-opacity, 0);
  }

# CSS Structure

  1. Use flexbox
  2. Always import a global helpers at the top @import "../../helpers";.
  3. Start with mobile view and write mobile-first CSS. It means that media queries should be only for a desktop view.
  4. DO NOT use scoped styles.
  5. Use CSS Custom Properties.
  6. Use global css variables whenever it's possible.
  7. Try to order css properties according to “Grouped by type” example: Poll results how do you order your css properties

WARNING

Use global CSS variables for common properties like colors. All the global variables are found in shared/styles/global/_variables/.

  1. Properties that may broke the design in future changes shouldn't be customizable.

TIP

A safe set of properties to customize are: font-size, text-align, color, background-color, background-image, padding (not always), margin (not always), text-transform, font-weight, font-family, background-size, align-content (not always), align-items(not always)

  1. Provide CSS modifiers for most common modifications. (component__black--modifier)
  2. DO NOT use any outer positioning for components (like outer margins). The way they're positioned in layout should be determined in outer environment.
  3. Try to use rems for 10px up.

SAMPLE CSS FILE

Here you can find an example of properly styled component with all rules applied.

# Template rules

  1. HTML should be semantic and not tied to CSS implementation. Total repalcement of CSS should be possible without using semantic meaning of markup.
  2. We use slots and props for content composition.
  • Props should be used to fulfil most common use case
  • Instead of providing multiple props and configurations for different use cases try to add slots with general usage
  • Slots are also meant to be used for markup replacement. In other words every default markup should be replaceable with slots.
  • Always provide proper slot-scope. For example when we have pagination component with slot next allowing to replace arrow to next page we would like to provide things like next() function or canGoFurther variable. Then if someone will choose to replace slot content with his/her own it's much easier to know how to keep same behavior.
  1. One slot is usually a single BEM element.
  2. DO NOT use props for setting properties that can be set by css (except for background images).
  3. Provide a default slot (usually empty) for a full customization support.

WARNING

Try to make components as customizable as possible without complicating them. Think about the parts that are usually customized and allow simple way to change their look.

# Deprecation guidelines

StorefrontUI cares about providing new features and keeping up-to-date with new best practices. Sometimes it can become necessary to make changes to APIs or to prepare new features in order to stay current. To make this process easier for everyone, we deprecate APIs and features for a period of time before the actual removal by adding proper information about deprecations in the next major version.

In summary:

  • we don't remove implemented features before the next major version
  • we add deprecation infos, so users can prepare for replacements and have time to update their dependencies to the latest version
  • on our side: before a release, we can spot deprecated features more easily, remove them and prepare migration documentation

To standardize this process, we stick with a few rules:

  • add this note next to the deprecated feature/APIs -> @deprecated will be removed in 1.0.0 use ___ prop instead

  • add this note next to deprecated code that should be removed // TODO remove in 1.0.0 / BEGIN -> // END

  • import helper from utitilies import { deprecationWarning } from "@/utilities/helpers/deprecation-warning.js";

  • add info about deprecation by using the helper method deprecationWarning(this.$options.name, "your deprecation message")

deprecationWarning(componentName, message)

componentName is a string argument that you have to pass for the component's name to appear in front of the deprecation warning – in general, this.$options.name returns the name of the current Vue component.

As a result the console will show a warning like the following: [StorefrontUI][SfComponent] ____ prop has been deprecated and will be removed in 1.0.0. Use ____ instead.

DEPRECATION INFO

Here you can see an example how it looks like in SfAccordion:

props: {
  /**
   * Opens the first accordion item if set to "true"
   * @deprecated will be removed in 1.0.0 use open prop instead
   */
  firstOpen: {
    type: Boolean,
    default: false
  },
}
// TODO remove in 1.0.0 / BEGIN
if (this.firstOpen) {
  this.$children[0].isOpen = this.firstOpen;
  console.warn(
    "[StorefrontUI][SfAccordion] Prop 'firstOpen' has been deprecated and will be removed in v1.0.0. Use 'open' instead."
  );
  return;
}
// <- END

# Unit tests

The minimum set of tests should contain component:

  • External API: props, slots, $events
  • Internal API: methods

# Stories for Storybook

Component stories should contain at least these following stories:

  1. Default called basic for all props, with props and CSS modifiers fillable as knobs.
  2. One for every customisable slot in the component

TIP

A good example is SfBanner stories.

You can learn more about Storybook syntax and how to use here.

Check our storybook for examples.