Customizing CSS
Style the Ridu admin with your own CSS, theme variables, component classes, and optional Sass utilities.
Change the admin’s colors, typography, spacing, and built-in controls with a stylesheet in your application. Ridu exposes CSS variables and named classes, so you can style the existing screens without replacing their Svelte components.
Use custom components when you need different content or behavior, such as a new dashboard panel or field editor.
Add your stylesheet
Create admin/src/custom.css. This example rounds the controls and dashboard cards and makes
the login form wider:
@import '@riducms/ui/layers.css';
@layer app {
:root {
--control-radius: 6px;
}
.ridu-auth {
--auth-width: 520px;
}
.ridu-dashboard__card {
border-radius: 10px;
}
}Import it from the admin entry:
import { createAdminClient, mountAdmin } from '@riducms/admin';
import adminConfig from '@/admin.config';
import './custom.css';
const target = document.getElementById('app');
if (target === null) throw new Error('Missing admin mount element.');
mountAdmin({
target,
clientFactory: createAdminClient,
...adminConfig
});Run ridu dev and open /admin to see your changes. The entry already loads Ridu’s base theme
through @riducms/admin. Your stylesheet is bundled into the admin when you run ridu build.
Override built-in styles
Ridu puts its styles in CSS cascade layers: named groups that determine which styles take precedence. For normal declarations, the order from lowest to highest is:
ridu— the framework’s reset, theme, base styles, components, and utilities.ridu-plugins— styles added by plugins.app— your application’s overrides.- Unlayered CSS — ordinary rules outside any
@layer.
The layers.css import establishes this order before components load. Use @layer app as in
the example above, or write ordinary unlayered CSS. A selector in app can override a more
specific framework selector without adding !important. Within the same layer, normal CSS
specificity and source order apply. Inline styles and !important declarations follow their
own cascade rules.
Keep global overrides in custom.css. For a custom Svelte component, put its own presentation
in a <style> block or import a stylesheet beside the component.
Change colors and typography
CSS variables let many components share the same value. Set them on :root to change the whole
admin, including menus and dialogs rendered outside the current page.
For example, give primary actions different colors in each theme:
@import '@riducms/ui/layers.css';
@layer app {
:root {
--font-sans: 'Trebuchet MS', sans-serif;
--control-radius: 6px;
/* Dark theme: keep text readable on the new action color. */
--primary: #a5b4fc;
--primary-foreground: #1e1b4b;
--accent-hover: #c7d2fe;
}
:root[data-theme='light'] {
--primary: #4338ca;
--primary-foreground: #ffffff;
--accent-hover: #3730a3;
}
}The admin applies data-theme="light" or data-theme="dark" to the root <html> element,
including when the user chooses the system theme. Ridu’s default variables adapt to that choice.
Your literal color overrides need their own light and dark values when the same color would
be unreadable in one theme. Check both from the account settings.
Colors
--backgroundand--foreground- Page background and main text.
--background-surfaceand--background-layer- Cards, controls, and raised surfaces.
--foreground-muted- Secondary text.
--primaryand--primary-foreground- Primary action background and text. Set both so text stays readable.
--accent-hover- Primary action hover color.
--control-surfaceand--control-border- Input backgrounds and borders. Use
--control-surface-hoverand--control-border-hoverfor hover states. --field-error-surfaceand--field-error-foreground- Field error bubble background and text.
--focus-outlineand--focus-outline-offset- Keyboard focus outline and its distance from the control.
Fonts and control sizes
--font-sans- Admin text and controls. Defaults to the system sans-serif stack.
--font-mono- Code and structured values. Defaults to the system monospace stack.
--font-richtext-body- Rich-text editor content. Defaults to a serif stack beginning with Georgia.
--font-size-bodyand--line-height-body- Controls and admin areas that use the body-size variables. Defaults to
13pxtext with a20pxline height. --control-height- Inputs and large buttons. Defaults to
40px. --control-radius- Input and button corners. Defaults to
3px. --control-padding-inline- Horizontal padding in controls. Defaults to
15px. --admin-nav-width- Desktop navigation width. Defaults to
275px. --admin-gutter- Page gutters. Defaults to
60pxon desktop,40pxat 1024px, and16pxat 768px. --admin-sticky-offset- Set by the document editor to the height of its sticky action bar. Use it as
topfor your own sticky content so it stays below the bar. It is0pxin the live-preview editor pane, which scrolls on its own.
The installed @riducms/ui/src/theme.css contains the complete theme. Feature styles can set more local values:
for example, set --auth-width on .ridu-auth, where the login layout defines it. A value set
directly on an element takes precedence over a value inherited from an ancestor, even when the
ancestor’s rule is in a higher layer.
Style one part of the admin
Inspect the element in your browser and use its named ridu- class. Classes such as
.ridu-dashboard__card identify a component part; modifiers such as .ridu-button--primary
identify a variant. Use these names instead of generated svelte-… hashes or utility classes.
For example, keep the rest of the theme and add an accent to the active navigation link:
/* Inside your existing @layer app block. */
.ridu-nav__link--active {
color: var(--primary);
}
.ridu-nav__link--active::before {
background: currentColor;
}Useful starting selectors include:
- Login and recovery
.ridu-auth,.ridu-auth__content,.ridu-auth__heading- Navigation
.ridu-nav__link,.ridu-nav__link--active- Dashboard
.ridu-dashboard,.ridu-dashboard__card- Collection list
.ridu-list,.ridu-list-results,.ridu-list-table__scroll- Document editor
.ridu-document,.ridu-document-header,.ridu-document-fields- Inputs and buttons
.ridu-input,.ridu-textarea,.ridu-button- Field labels and feedback
.ridu-field-label,.ridu-field-help,.ridu-field-error-tooltip- Rich text
.ridu-richtext-content,.ridu-richtext-floating-toolbar- SEO
.ridu-seo-overview,.ridu-seo-field,.ridu-seo-preview
Field wrappers also expose data-field-path. Use the path shown in the DOM to narrow an override
to an individual field:
/* Inside your existing @layer app block. */
[data-field-path='title'] .ridu-input {
font-weight: 600;
}This matches the title field wherever that path appears. Use a wrapper class from a
custom view when the change belongs to only one collection.
Menus and drawers may be mounted outside that field wrapper. Target their own classes for popup
presentation; use :root variables when the value should apply throughout the admin.
Use Sass in your own components
Plain CSS is enough for theme overrides. If you prefer Sass, rename the file to custom.scss and
change the entry import to import './custom.scss'. Generated projects use
@riducms/build, which already includes the Sass compiler.
Import @riducms/ui/styles.scss with @use to reuse control styles and breakpoints. It also
declares Ridu’s layer order, so this file needs no separate layers.css import:
@use '@riducms/ui/styles.scss' as ui;
@layer app {
.editor-note {
@include ui.control-surface;
padding: 12px var(--control-padding-inline);
color: var(--foreground);
}
@media (max-width: ui.$small) {
.editor-note {
padding: 10px;
}
}
}Apply class="editor-note" to an element in your component. The mixin adds its border, radius,
background, and shadow. It does not create an input or supply keyboard behavior; use the
UI components for interactive controls.
control-surface- Shared resting control border, radius, background, and shadow.
focus-outline($offset: var(--focus-outline-offset))- Outline declarations to include inside your
:focus-visiblerule. Pass an offset to change its distance from the control. visually-hidden- Hide content visually while keeping it available to assistive technology.
confirmation-overlay,confirmation-content,confirmation-title- Shared confirmation dialog presentation. Use
ConfirmationDialogwhen you also need its behavior. $small,$medium,$large- Inclusive
max-widthbreakpoints:768px,1024px, and1440px.
These Sass definitions reuse the runtime CSS variables. To change a color or size, override the variable in CSS; there is no separate Sass theme to configure.
If an override does not appear
- Check that
admin/src/main.tsimports the stylesheet and that the selector matches the rendered element. Creating a file alone does not load it. - Put application rules in
appor leave them unlayered. Importlayers.cssbefore declaring custom layers; the Sass entry does this for you. - Check the element’s computed styles. A local variable, inline style, or another application rule may be winning. Put a local variable on the component that defines it.
- Svelte
<style>blocks are scoped to their component. Use the imported global stylesheet to target built-in admin elements. - Check light and dark mode and a narrow viewport. If you override
--admin-gutterat:root, add your own media queries to retain smaller gutters on mobile.