Skip to main content

Styling

Design-system neutral by default​

Components ship without decorative visual opinions. The default appearance mirrors the UA stylesheet — buttons look like buttons, inputs look like inputs. This lets them blend into any design system without fighting existing styles.

What components always emit is structural: layout, geometry, and interaction state indicators. The decorative layer is entirely up to you.

Color scheme​

Components consume CSS system colors (Canvas, CanvasText, ButtonFace, Highlight, etc.), which the browser resolves differently in light and dark schemes. Set color-scheme on a container and every component inside it adapts automatically:

.my-app {
color-scheme: dark;
}

No class-based theming or JavaScript needed for basic color scheme support.

Global design tokens​

The optional base.css export defines semantic tokens that all components reference. Import it once at your application root to enable the token layer:

@import '@rcarls/rc-webcomponents/themes/base.css';

These tokens fall back to CSS system colors without the import — the file is optional. Override any token at your theme boundary to adjust all components at once:

:root {
--rc-accent: oklch(55% 0.2 250); /* primary action color */
--rc-border-color: oklch(75% 0 0); /* all component borders */
--rc-control-block-size: 2.5rem; /* input / button height */
--rc-control-radius: 0.5rem; /* border radius */
--rc-font-family: 'Inter', sans-serif;
}

Full token reference:

TokenDefaultControls
--rc-accentHighlight / AccentColorFocus rings, selected states
--rc-surfaceCanvasSurface backgrounds
--rc-fieldFieldInput field backgrounds
--rc-border-colorButtonBorderAll borders
--rc-border-width1pxAll borders
--rc-control-block-size2.25emControl height
--rc-control-padding-block0.25emControl vertical padding
--rc-control-padding-inline0.5emControl horizontal padding
--rc-control-radius0.125emControl corner radius
--rc-radius-md0.25emLarger radius (chips, badges)
--rc-font-familyinheritAll component text
--rc-font-size1emAll component text
--rc-shadow0 2px 8px …Popups and overlays
--rc-motion-duration120msAll transitions
--rc-disabled-opacity0.5Disabled state opacity

Per-component tokens​

Each component also exposes its own scoped tokens for fine-grained control. These are documented in the API table on each component page. Example:

/* Make the FAB match your brand */
rc-fab {
--rc-fab-bg: oklch(55% 0.2 250);
--rc-fab-color: white;
--rc-fab-radius: 0.75rem;
--rc-fab-shadow: 0 4px 12px oklch(55% 0.2 250 / 40%);
}

Adaptive layout contracts​

Choose the narrowest responsive mechanism that keeps a component's visual and behavioral state synchronized:

  1. Override one geometry token for a scalar change such as carousel slide size.
  2. Override a coordinated token set when unchanged regions need new grid tracks or placement, as with rc-card and rc-list-item.
  3. Write a property or attribute when a mode also changes ARIA, keyboard behavior, pointer calculations, focus, popup placement, rendered controls, or light-DOM slot assignment.
  4. Use a component's auto mode only when it measures an objective layout failure it owns, such as rc-chip-group exceeding max-rows.

Container queries style descendants of the query container; they do not set custom-element properties or attributes. Keep the query container on a stable consumer-owned ancestor:

.card-region {
container-type: inline-size;
}

@container (max-width: 28rem) {
.card-region rc-card {
--rc-card-grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
--rc-card-media-grid-row: 1 / -1;
--rc-card-media-grid-column: 1;
--rc-card-title-grid-row: 1;
--rc-card-title-grid-column: 2;
}
}

Do not use a CSS custom property as a hidden behavioral input that component JavaScript reads with getComputedStyle(). CSS changes have no reliable property-change notification, and server rendering, accessibility state, and interaction logic can become desynchronized.

Adapt behavior from application state​

rc-splitter.orientation affects pane geometry, drag and measurement axes, arrow-key handling, collapse icons, and aria-orientation. Set the property from application code when the containing region changes size:

const $region = document.querySelector('.workspace-region');
const $splitter = $region.querySelector('rc-splitter');

const observer = new ResizeObserver(([entry]) => {
const inlineSize = entry.borderBoxSize[0]?.inlineSize ?? entry.contentRect.width;

$splitter.orientation = inlineSize < 600 ? 'vertical' : 'horizontal';
});

observer.observe($region);

Apply the same boundary to structural states:

  • Keep rc-app-bar variant="compact|expanded" declarative because expanded mode adds measured collapse behavior.
  • Set rc-adaptive-menu.maxShown from application responsive state because it moves authored controls between toolbar and menu semantics. CSS-hiding actions would not place them in the overflow menu.
  • Keep rc-transfer-list compact, toolbar/menu orientation, slider orientation, navigation-rail expansion, search view mode, and select display mode property-driven because their rendered controls or interaction semantics change.
  • Set --rc-carousel-slide-size, card region tracks, list-item region placement, and toolbar wrapping in CSS because those alter geometry only.

CSS parts​

Components expose named parts for styling elements that are inside shadow DOM. Use the ::part() selector:

/* Style the trigger button inside rc-select */
rc-select::part(trigger) {
font-weight: 500;
letter-spacing: 0.01em;
}

/* Style the toggle indicator */
rc-select::part(toggle-indicator) {
color: var(--rc-accent);
}

Available parts are listed in the API table under the Parts section on each component's page.

Substrate reference theme​

The optional @rcarls/rc-theme-substrate package is the canonical reference theme. It is a lightweight, app-oriented foundation that maps a small orange brand palette and neutral surface system onto the RC token layer. Use it as-is, or copy its token bridge as a starting point for your own branded theme.

npm install @rcarls/rc-theme-substrate

Import the full theme bundle:

@import '@rcarls/rc-theme-substrate/theme.css';

Or import the token bridge and selected component polish:

@import '@rcarls/rc-theme-substrate/bridge.css';
@import '@rcarls/rc-theme-substrate/components.css';

Apply the rc-theme-substrate class to a container to scope the theme:

<div class="rc-theme-substrate">
<rc-select>...</rc-select>
<rc-dialog>...</rc-dialog>
</div>

Substrate includes modern CSS such as cascade layers, color-mix(), oklch(), @starting-style, and discrete transition rules where supported. View Transitions remain application opt-in: the theme exposes CSS hooks, but application code decides when to call document.startViewTransition().

Material theme​

The optional @rcarls/rc-theme-material package maps Material 3 design tokens to the RC token layer. It changes styling only — no behavior changes.

npm install @rcarls/rc-theme-material

Import the full theme bundle:

@import '@rcarls/rc-theme-material/theme.css';

Or import layers selectively if your app already provides Material system tokens:

@import '@rcarls/rc-theme-material/bridge.css';
@import '@rcarls/rc-theme-material/components.css';

components.css includes the .rc-state-layer utility. Apply that class to interactive elements that already own their border radius when you want a CSS-only Material state layer.

Apply the rc-theme-material class to a container to scope the theme:

<div class="rc-theme-material">
<rc-select>...</rc-select>
<rc-fab>...</rc-fab>
</div>

For rc-button[icon-only], Material supplies rc-button--extra-small, rc-button--medium, rc-button--large, and rc-button--extra-large size classes. Combine them with rc-button--narrow or rc-button--wide to change the container width while keeping the selected height and icon size. The unmodified size is the Material small icon button. rc-menu-button[icon-only] also supports rc-menu-button--narrow for a narrow small trigger. All visually compact variants retain the component package's independent 3rem activation area. Direct native buttons in an rc-app-bar's leading and trailing slots use the same small icon-button geometry, while nested component controls retain their own sizing.

See Theme previews to compare components with and without the Material theme applied.