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:
- Component styles must be placed in the component's directory.
- Components must not import other component's styles.
- Any CSS that needs to be shared across multiple components and/or global
styles must be placed in the
src/stylesdirectory.
├── 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:
@layer theme— a collection of CSS custom properties that define the look and feel of the UI.@layer foundation— ground-zero CSS for components and other styling.@layer helpers— small styling abstractions that can be used across the whole UI.@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.@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.
-
Class names must use camelCase notation to be usable in a JavaScript context.
-
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.
-
Component's top-level HTML element must have the
rootclass name. However, this rule has a few exceptions:-
When the component is a subcomponent, it's usually better to use the subcomponent's name, e.g.
itemorgroup. -
When no CSS on the root element is necessary and styling only takes place once a visual modification is invoked by component props, the
rootclass name can be omitted entirely.
-
-
Modifier class names related to the current HTML element must start with
isand contain the name of the target element, e.g.isRootLoading(modifiesroot) orisLabelHidden(modifieslabel). -
Modifier class names related to child elements must start with
hasand refer to the element in question, e.g.hasRootSmallInput(applies styling onrootbut relates toinput).
Custom Properties
RADES uses CSS custom properties to make writing and maintaining CSS more efficient. There are two kinds of custom properties used:
-
--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. -
--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;
@importis 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 ofmap-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.