Skip to content

CSS Guidelines

Basic rules to follow when authoring React UI CSS.

Coding Style

React UI uses Stylelint to avoid errors in CSS and enforce unified coding style across all stylesheets.

Configurations used:

CSS Architecture

Without Web Components, there is no such thing as a complete encapsulation of a component. Inside browser window, a React app is still a bunch of HTML and CSS (and JS, of course) living in the same global context. Developers can fight against CSS cascade, inheritance and specificity, or accept these principles, and even benefit from them. Understanding how CSS works and making use of this knowledge leads towards smaller stylesheets, easier maintenance, and better performance.

To be able to handle CSS at such a large scale of a UI library, React UI works in harmony with fundamental CSS principles. Most importantly, all CSS is written in specificity order, i.e. from lowest to highest specificity. This idea was most famously shaped and popularized by Harry Roberts in his ITCSS architecture. React UI draws inspiration from ITCSS which can be seen the best in the foundation CSS layer (and its source).

File Structure

There are three simple rules to follow when organizing React UI CSS:

  1. Component styles must be placed in component's directory.
  2. Components must not import other component's styles.
  3. Any CSS that needs to be shared across multiple components and/or global styles must be placed in the src/styles directory.

👉 All React UI CSS is written in Sass. Learn more about preprocessing with Sass.

├── src
    ├── …
    └── lib                       Main source directory, contains global Sass endpoints
        ├── components            React components with their stylesheets
            ├── *
                ├── <Component>
                    ├── …
                    ├── _settings.scss    Component's non-themeable Sass variables
                    ├── _theme.scss       Component's Sass interface to its CSS custom properties in `theme.scss`
                    ├── _tools.scss       Component's Sass mixins and functions
                    ├── Component.scss    Component's main stylesheet
                    └── …
        ├── …
        ├── styles                Partials for top-level Sass endpoints and shared styles
            ├── elements          Styles for unclassed HTML elements (type selectors)
            ├── generic           Global ground-zero styles
            ├── helpers           Helper classes
            ├── settings          Sass variables shared across global styles and/or multiple components
            ├── theme             Sass interface to `theme.scss`, used in global styles or across multiple components
            ├── theme-constants   Sass variables for use only within `theme.scss`
            ├── tools             Sass mixins and functions shared across global styles and/or multiple components
            └── _utilities.scss   Sass loop that generates utility classes from `settings/_utilities.scss` config
        ├── …
        ├── foundation.scss       Mandatory themeable CSS layer, ground-zero for React components
        ├── helpers.scss          Optional set of helper and utility classes
        └── theme.scss            Default theme, a collection of hundreds of CSS custom properties

CSS Modules

React UI leverages CSS modules (not to be confused with modular CSS specification of the same name) to take advantage of writing native CSS (meaning “not JSS or CSS in JS”). Together with Sass, CSS modules represent flexibility and popular programming features needed to author modern stylesheets perfectly familiar to traditional CSS developers.

Components

CSS modules help keeping source class names short and clear. The same class name can be used in another component with different styling. Final class names are converted by tooling and composed of component name, original class name, and a random suffix which makes them unique in global context of the whole web app.

For example, this JSX:

// Button.jsx

<button className={styles.root}>
  <span className={styles.beforeLabel}>{beforeLabel}</span>
  <span className={styles.label}>{label}</span>
  <span className={styles.afterLabel}>{afterLabel}</span>
</button>

… with this SCSS:

// Button.scss

.root {
 // …
}

.beforeLabel {
 // …
}

.afterLabel {
 // …
}

.label {
  // …
}

… produces following CSS class names:

  • Button__root__2yVxr5IZ
  • Button__beforeLabel__1rrmrrWj
  • Button__afterLabel__38eMTilM
  • Button__label__23iTNlfS

Resulting CSS class names are both unique and human-readable at the same time which is convenient for development. Class names are further shortened and obfuscated for production environments.

Helpers and Utilities

There are also global helper and utility classes (both documented as CSS Helpers for the sake of comprehensibility for non-CSS guys) that can be used by developers and thus remain unaltered by CSS modules. For example, :global(.display-block) selector produces display-block CSS class.

Class Naming Rules

Following rules make it clear both in JSX and CSS what is affected by a CSS class.

  1. Class names must use camelCase notation to be usable in JavaScript context.

  2. Short, preferably single-word names should be chosen for all component elements. No naming convention like BEM or SUIT CSS needs to be applied since class names are unique in the global scope thanks to CSS modules. Conventions for modifier classes are covered by the rules below.

  3. Component's top-level HTML element must have root class name. However, this rule has a few exceptions:

  4. When the component is a subcomponent, it's usually better to use subcomponent's name, e.g. item or group. This enables us to keep related CSS of both the main component and its subcomponents in a single file and see the big picture during development.

  5. When no CSS on the root element is necessary and styling only takes place once a visual modification is invoked by component props, root class name can be omitted entirely.

  6. Modifier class names related to the current HTML element must start with is and contain the name of the target element, e.g. isRootLoading (modifies root) or isLabelHidden (modifies label). However, child elements may be modified as well, e.g. by a CSS selector like .isRootRequired > .label (root is marked as required but the label is what needs to be visually modified).

  7. Modifier class names related to child elements must start with has and refer to the element in question, e.g. hasRootSmallInput (applies styling on root but relates to input).

Custom Properties

React UI takes advantage of supporting modern browsers and uses CSS custom properties to make writing and maintaining CSS more efficient.

There are three kinds of custom properties used:

  1. --rui-local-* for internal (component-scoped, local) custom properties. May reuse other custom property types.

  2. --rui-custom-* for any custom properties whose value comes from component's API. May reuse other custom property types.

  3. --rui-* (unscoped for the sake of brevity) for theme-related custom properties. Part of public API, designed to be customized. Must not reuse other custom property types. Refer to the theming overview to learn how their names are created.

Preprocessing with Sass

All React UI CSS source is written in SCSS syntax of Sass preprocessor.

  • Sass variables, mixins and functions must use kebab-case notation.

  • Only Sass modules must be used to organize Sass source files, @import is deprecated. Using scoped variables, mixins and functions (those starting with _) is highly recommended whenever appropriate.

  • Built-in Sass modules should be preferred over older Sass functions that are deprecated, e.g. map.get() instead of map-get().

  • Mixins that lead to duplicate CSS should be avoided. If possible, combine multiple CSS selectors for the desired rule set to achieve the same result.

  • Extend functionality should be avoided entirely due to its hardly predictable behavior.

  • Classes that are automatically generated by Sass loops should be handled with care. With loops, it's easy to produce a lot of CSS and negatively impact performance.

Postprocessing with PostCSS

All styles are automatically prefixed by Autoprefixer plugin for PostCSS according to Browserslist configuration stored in .browserslistrc.