rc-app-bar
App bar modeled after the Material 3 Top app bar, with leading, title, center, trailing regions and optional scroll behavior.
- Package
@rcarls/rc-app-bar- Element
<rc-app-bar>- Native dependency
- Structural slots only; consumer supplies controls, icons, and landmarks
- State model
- Controlled or uncontrolled scrolled state
- Main events
rc-app-bar-scroll- Related
- Usage rulesTheme previews
Installation
- npm
- Yarn
- pnpm
npm install @rcarls/rc-webcomponents@next
yarn add @rcarls/rc-webcomponents@next
pnpm add @rcarls/rc-webcomponents@next
import '@rcarls/rc-webcomponents/rc-app-bar/define';
Usage
Put the title in the default slot and controls in the leading and trailing slots. Wrap a
page-level bar in <header>, since the element adds no landmark of its own.
<header>
<rc-app-bar scroll-target="#content">
<button slot="leading" aria-label="Back">←</button>
<span>Inbox</span>
<rc-toolbar slot="trailing" label="Actions">...</rc-toolbar>
</rc-app-bar>
</header>
<div id="content" class="scroll-area">...</div>
Live demo
Title app bar
Centered search
The center slot fills the flexible space between leading and trailing by
default, capped by --rc-app-bar-center-max-inline-size before it starts
centering within the available space instead of continuing to stretch (the
demo above sets a cap so the search field doesn't stretch edge-to-edge).
Add center-symmetric when a control must sit at the bar's exact geometric
midpoint regardless of asymmetric leading/trailing widths instead:
<rc-app-bar center-symmetric>
<button slot="leading">Long leading label</button>
<rc-search-bar slot="center">...</rc-search-bar>
<button slot="trailing" aria-label="Account">Account</button>
</rc-app-bar>
Accessibility
- The bar has no implicit landmark role; wrap a page-level instance in
<header>. - Slotted controls keep their native semantics and keyboard behavior.
- The title stays connected and exposed to assistive technology in every visual state; it is never duplicated as the layout changes.
- Movement and fades are off under
prefers-reduced-motion: reduce. - The scrolled divider defaults to
GrayText, so it stays visible in forced colors.
Reclaiming space with hide
scroll-behavior="hide" only translates the bar. While hiding, the bar
publishes --rc-app-bar-block-size and --rc-app-bar-visible-block-size
(0px while hidden) onto its scroll target. Stack the bar over its scroll
container and reserve that size, so content scrolls behind the bar and fills
its space while it is hidden:
.screen {
display: grid;
grid-template: 'stack' 1fr / 1fr;
}
.screen > header,
.screen > .content {
grid-area: stack;
}
.screen > header {
z-index: 1;
align-self: start;
}
.content {
overflow-y: auto;
padding-block-start: var(--rc-app-bar-block-size, 4rem);
scroll-padding-block-start: var(--rc-app-bar-block-size, 4rem);
}
.content .sticky-filters {
position: sticky;
/* Sticky offsets are measured inside the scroller's padding. */
inset-block-start: calc(
var(--rc-app-bar-visible-block-size, 4rem) - var(--rc-app-bar-block-size, 4rem)
);
transition: inset-block-start 200ms ease;
}
The bar reveals itself when focus enters and remains visible while it contains focus.
Theming
The default demo mode shows the component without a package theme. Use the shared preview controls on this page to compare inherited, light, and dark color schemes or to apply the optional Material theme only inside the demo frames.
Material top app bar sizes are theme presets, not public component variants.
Keep the component structural with variant="compact" or variant="expanded",
then use rc-theme-material modifier classes to tune the token mapping:
<rc-app-bar class="rc-app-bar--large" variant="expanded" scroll-behavior="collapse">
<button slot="leading" type="button" aria-label="Back">...</button>
<h1>Reading list</h1>
</rc-app-bar>
Available Material helper classes are rc-app-bar--center-aligned,
rc-app-bar--medium, and rc-app-bar--large. For custom collapse animation,
read the component-owned --rc-app-bar-collapse-progress value in CSS:
rc-app-bar .brand-mark {
opacity: calc(1 - var(--rc-app-bar-collapse-progress, 0));
}
API
Properties
| Property | Markup | Type | Default | Description |
|---|---|---|---|---|
variant | variant | RCAppBarVariant | 'compact' | Structural variant. `expanded` adds a flexible title row. |
centerSymmetric | center-symmetric | boolean | false | Opt-in: mirrors the leading/trailing edge widths so `slot="center"` content stays exactly viewport-centered regardless of asymmetric side content. Default (absent): center content fills the flexible middle column like a plain title, capped and self-centered within it via `--rc-app-bar-center-max-inline-size` -- centered within the available space between leading and trailing, not mirrored to the viewport. |
scrollBehavior | scroll-behavior | RCAppBarScrollBehavior | 'pinned' | Visual response to observed scrolling. |
scrollTarget | scroll-target | ScrollObserverTarget | string | null | null | Scroll container to observe. The attribute accepts a CSS selector or `window`; the property also accepts an element, document, or window. |
scrollThreshold | scroll-threshold | number | 4 | Scroll offset in px past which the bar is scrolled (strict `>`). |
hideDistance | hide-distance | number | 32 | With `scroll-behavior="hide"`, how far in px the target must scroll in one direction before the bar hides or reveals, so a small reversal while reading or a fling settling does not toggle it. Within `scrollThreshold` of the top the bar always shows. |
scrolled | JS property only | boolean | Not specified | Whether the bar is past its scroll threshold. Host writes are silent and controlled; assigning `undefined` releases observation. |
collapsed | JS property only | boolean | Not specified | Whether the expanded title row is currently collapsed. |
Events
| Event | Detail type | Description |
|---|---|---|
rc-app-bar-scroll | No detail type documented | When observed scroll state crosses the threshold; `detail: { scrolled }` |
Slots
| Name | Description |
|---|---|
leading | Leading navigation or controls; accepts multiple children |
(default) | The single title region; may contain title and subtitle markup |
center | Content such as search, kept within the flexible middle column between leading and trailing. Fills that column by default, capped and self-centered within it via `--rc-app-bar-center-max-inline-size`; add `center-symmetric` for exact viewport centering regardless of asymmetric leading/trailing content instead. |
trailing | Trailing action controls |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--rc-app-bar-bg | Canvas | Bar background |
--rc-app-bar-color | CanvasText | Bar text color |
--rc-app-bar-shadow | none | Bar box shadow |
--rc-app-bar-compact-min-height | 3rem | Compact row minimum height |
--rc-app-bar-expanded-padding-block | 0.75em | Expanded title padding |
--rc-app-bar-padding-inline | 0.75em | Horizontal padding |
--rc-app-bar-gap | 0.5em | Gap between regions |
--rc-app-bar-center-max-inline-size | 100% | Max width of `slot="center"` content before it centers within the available space instead of filling it. No effect when `center-symmetric` is set. |
--rc-app-bar-title-start-padding | 0px | Extra title inline-start padding used only when the leading slot is empty (M3: align with content below when there's no navigation icon) |
--rc-app-bar-transition-duration | 200ms | Endpoint and hide duration |
--rc-app-bar-scroll-divider | 1px solid GrayText | Scrolled divider |
--rc-app-bar-collapse-progress | 0 | Collapse animation progress 0–1; read-only |
--rc-app-bar-block-size | Not specified | Read-only output on the scroll target in hide mode: the bar's measured block size. |
--rc-app-bar-visible-block-size | Not specified | Read-only output on the scroll target in hide mode: the bar's block size while shown, `0px` while hidden. |
--rc-app-bar-title-font-size | Not specified | Compact-row title font size (opt-in; inherits when unset) |
--rc-app-bar-expanded-title-font-size | Not specified | Expanded-row title font size (opt-in) |
CSS Parts
| Part | Description |
|---|---|
root | The grid container |
leading | Wrapper around the leading slot |
title | Wrapper around the single title slot |
center | Wrapper around the centered slot |
trailing | Wrapper around the trailing slot |
scroll-shadow | Scrolled-state separator |