Skip to content

CSS Guidelines

Basic rules to follow when authoring RADES CSS.

Coding Style

RADES uses Stylelint to avoid errors in CSS and enforce a unified coding style across all stylesheets. The configuration is the shared stylelint-config-racom preset from @racom/frontend_tooling; the repository config only extends it.

CSS Architecture

Without Web Components, there is no such thing as a complete encapsulation of a component. Inside the 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.

RADES builds on top of @react-ui-org/react-ui and follows the same principles: all CSS is written in specificity order, organized into cascade layers, and authored in Sass.

File Structure

There are three simple rules to follow when organizing RADES CSS:

  1. Component styles must be placed in the 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.
├── src
    ├── components                        React components with their stylesheets
    │   └── <Component>
    │       ├── <Component>.module.scss   Component's stylesheet, loaded as a CSS Module (only when needed)
    │       └── …
    └── styles                            Partials for top-level Sass endpoints and shared styles
        ├── elements                      Styles for unclassed HTML elements (type selectors)
        ├── exported-properties           CSS custom properties exported for consumers
        ├── helpers                       Helper classes
        ├── settings                      Design tokens (colors, spacing, typography, …) shared across styles
        ├── themes                        Theme values exposed as CSS custom properties
        ├── tools                         Sass mixins and functions shared across styles
        ├── _layers.scss                  Declares the cascade layers
        └── main.scss                     Top-level Sass endpoint

Cascade Layers

RADES CSS is organized into cascade layers, each with its own specificity level. The layers are declared in src/styles/_layers.scss as follows:

  1. @layer theme — a collection of CSS custom properties that define the look and feel of the UI.
  2. @layer foundation — ground-zero CSS for components and other styling.
  3. @layer helpers — small styling abstractions that can be used across the whole UI.
  4. @layer components — component styles are written as CSS Modules which output into this layer. Also, each component has its own cascade layer, e.g. @layer components.dialog.
  5. @layer utilities — tiny classes to control selected CSS properties, forcing them with !important.

Any custom CSS can be added to the end of the cascade, but it's recommended to use any of the existing layers to keep the CSS organized.

👉 With !important styles, layered styles take precedence over unlayered styles.

CSS Modules

For components, RADES leverages CSS modules (not to be confused with the modular CSS specification of the same name) to take advantage of writing native CSS. 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 the global context of the whole web app.

For example, this JSX:

// Dialog.jsx

<div className={styles.root}>
  <div className={styles.title}>{title}</div>
</div>

… with this SCSS:

// Dialog.module.scss

@layer components.dialog {
    .root {
        // …
    }

    .title {
        // …
    }
}

… produces unique, human-readable class names such as Dialog-module__root__2yVxr5IZ, which are further shortened and obfuscated for production environments.

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 a 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.

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

    1. When the component is a subcomponent, it's usually better to use the subcomponent's name, e.g. item or group.

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

  4. 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).

  5. 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

RADES uses CSS custom properties to make writing and maintaining CSS more efficient. There are two kinds of custom properties used:

  1. --rui-* for theme-related custom properties. RADES builds on the React UI theme system, so theme tokens keep the --rui-* prefix. They are part of the public API and are designed to be customized.

  2. --rades-custom-* for any custom properties whose value comes from a component's API.

Helpers and Utilities

There are also global helper and utility classes (both documented as CSS Helpers for the sake of comprehensibility for non-CSS folks) that can be used by developers and thus remain unaltered by CSS modules.

Class Naming Rules

Class names must use kebab-case notation to be usable in an HTML context.

Preprocessing with Sass

All RADES CSS source is written in the SCSS syntax of the 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.

Postprocessing with PostCSS

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