Common variable patterns¶
Some of River's variables work together to achieve bigger interface changes, others have unique behaviours that aren't always intuitive.
Auto-generated variables¶
By default River automates some colours, using either color-mix or HSL. These auto-overrides can be replaced as most are set as variables. For example:
- A second background color:
--crm-layer2-bg-coloris auto-generated with:--crm-layer2-bg-color: color-mix(in srgb,var(--crm-layer1-bg-color) 95%,#000 5%); - Hover states for buttons are generated from the colour of the button:
--crm-primary-hover-color: color-mix(in srgb,var(--crm-primary-color) 75%,#000 25%); - Semantic emphasis colours (success, warning, info, danger) both cascade into and auto-generate related colour variables. Once you set
--crm-success-coloran HSL transform creates--crm-success-light-colorwhich is also used for--crm-alert-success-bg-color, which in turn generates with color mix--crm-alert-success-border-color. Another HSL transform creates--crm-notify-success-color.
Therefore once you've chosen a colour for --crm-success-color in your Stream you don't need to worry about all the other success related variables as they will be auto-generated from the colour you chose.
But perhaps you need to tweak --crm-notify-success-color because you've chosen a lighter colour for --crm-notify-bg-color rather than the dark default and it's no longer legible. Your stream can just define that specific variable, or point to a different semantic variable (such as crm-success-ink-color, which is the variable for the success colour on the normally light page background).
Dark mode variables¶
The variables for dark mode are defined in core/css/_dark.css and the related Stream dark.css overrides (streams/[stream-name]/_dark.css). They should follow the same category name and order.
To swap light and dark colours over, do not swap the values of primitive variables like --crm-c-amber-light and --crm-c-amber-dark; but instead change semantic and component variables to point to different colours.
Because of auto-generated variables (see above), changing one variable in _dark.css sometimes flows into multiple other auto-generated variables.
Variable pairs for contrast¶
Many colour variables exist in pairs of background/foreground colours to ensure a legible contrast ratio. For Semantic variables the foreground colour is marked with -text- while for Component variables the background colour is marked with -bg-. For example:
/* Primary colour pattern (semantic) */
--crm-primary-color: var(--crm-inverse-bg-color);
--crm-primary-text-color: var(--crm-text-light-color);
/* Info colour pattern (semantic) */
--crm-info-color: var(--crm-c-blue-dark);
--crm-info-text-color: var(--crm-text-light-color);
/* Accordion header (component) */
--crm-accordion-header-bg-color: var(--crm-secondary-color);
--crm-accordion-header-color: var(--crm-secondary-text-color);
/* Table header (component) */
--crm-table-header-bg-color: var(--crm-paper);
--crm-table-header-color: var(--crm-text-color);
/* Tabs (component) */
--crm-tab-bg-color: var(--crm-layer1-bg-color);
--crm-tab-color: var(--crm-text-color);
/* Dropdown (component) */
--crm-dropdown-bg-color: var(--crm-secondary-hover-color);
--crm-dropdown-color: var(--crm-text-light-color);
--crm-dropdown-hover-bg-color: var(--crm-paper);
--crm-dropdown-hover-text-color: var(--crm-text-color);
Border variables¶
--crm-border is a Semantic variable used across multiple components for a consistent colour, style and width, which can then be replaced on a component-by-component basis.
Some border variable are designed so the Stream can define which side they appear on. This is done with separate variables for border colour/style variables and the width, which sets a value for each side — top, right, bottom, left — while '0' removes the border. For example:
/* In Walbrook, adds a border to the left of notification boxes */
--crm-notify-accent-border: 0 0 0 3px;
/* In Thames, adds a large border to the top of notifications */
--crm-notify-accent-border: 0.875rem 0 0 0;
/* Adds a border to the bottom of accordion headers */
--crm-accordion-header-border-width: 0 0 1px 0;
/* Removes the border from the top of accordion body to match the border with the header */
--crm-accordion-border-width: 0 1px 1px 1px;
/* Applies different colours to different sizes of the dialog header */
--crm-dialog-header-border-color: transparent transparent var(--crm-border-color) transparent
Background variables¶
There are multiple background variables, so to help decide which one(s) you want to tweak, their relationship is illustrated below – gradating darker in light mode and lighter in dark mode:

Tabs variables¶
--crm-tabs- defines the collection of tabs, and --crm-tab- defines the individual tab. Two patterns are supported: tabs in the 'pill' style in the middle of a background block, and traditional tabs flush to one side with the active overlapping (as seen on the Search Kit Builder layout, and Hackney Brook Contact Layout, and below). To achieve this flush tab type you need a combination of --crm-tabs-padding with 0 one side, --crm-tab-hang as the width of the --crm-tabs-border, and --crm-tab-border-width and --crm-tab-radius both setting to 0 the side that's flush to the edge.

Contact Layout tabs position¶
Contact Layout has its own set of variables to define the tabs there and can do everything that regular tabs can do with flush or centred tabs. Furthermore, several of the --crm-contact variables can be used together to swap the contact layout view tabs from being at the top (as in Greenwich/Minetta) to being on the left (Walbrook/Hackney/Thames). These need to be changed together:
--crm-contact-directionchoose 'flex' for tabs at top, or 'grid' for tabs at side.--crm-side-tabs-widthset a value for the width of the tabs if using grid for tabs at the side.--crm-contact-tabs-flow, set 'row' for inline tabs at top, 'column' for stacked tabs on the side.