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:
| Token | Default | Controls |
|---|---|---|
--rc-accent | Highlight / AccentColor | Focus rings, selected states |
--rc-surface | Canvas | Surface backgrounds |
--rc-field | Field | Input field backgrounds |
--rc-border-color | ButtonBorder | All borders |
--rc-border-width | 1px | All borders |
--rc-control-block-size | 2.25em | Control height |
--rc-control-padding-block | 0.25em | Control vertical padding |
--rc-control-padding-inline | 0.5em | Control horizontal padding |
--rc-control-radius | 0.125em | Control corner radius |
--rc-radius-md | 0.25em | Larger radius (chips, badges) |
--rc-font-family | inherit | All component text |
--rc-font-size | 1em | All component text |
--rc-shadow | 0 2px 8px … | Popups and overlays |
--rc-motion-duration | 120ms | All transitions |
--rc-disabled-opacity | 0.5 | Disabled 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:
- Override one geometry token for a scalar change such as carousel slide size.
- Override a coordinated token set when unchanged regions need new grid
tracks or placement, as with
rc-cardandrc-list-item. - 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.
- Use a component's
automode only when it measures an objective layout failure it owns, such asrc-chip-groupexceedingmax-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.maxShownfrom 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.