Skip to main content

rc-button

Progressive-enhancement button wrapper for a direct native <button>.

rc-button keeps the real button in light DOM for forms, labels, keyboard behavior, and no-JavaScript fallback. The custom element adds reflected state attributes, icon/label presence hooks, and overlay parts for state-layer or progress effects.

Package
@rcarls/rc-button
Element
<rc-button>
Native dependency
Direct-child <button> element
State model
Controlled or uncontrolled toggle state
Main events
rc-button-toggle

Installation

npm install @rcarls/rc-button
import '@rcarls/rc-button/define';

Markup

<rc-button toggle default-selected>
<button type="button">
<span data-rc-button-icon aria-hidden="true"></span>
<span data-rc-button-selected-icon aria-hidden="true"></span>
<span data-rc-button-label>Save</span>
</button>
</rc-button>

Place icons, selected icons, progress-adjacent content, and visible labels inside the native button. rc-button never moves author nodes.

Mark custom progress-adjacent content with data-rc-button-progress so it is excluded from label detection, the same way data-rc-button-icon and data-rc-button-selected-icon are.

States

Use reflected attributes such as pending, progress, selected, icon-only, and full-width as declarative hooks. pending and progress disable the native button and expose aria-busy when the author has not set it. Use pending or boolean progress for an indeterminate affordance. Pair progress with progress-value for a determinate percentage from 0 through 100; out-of-range values are clamped visually.

Use [data-rc-button-icon] and [data-rc-button-selected-icon] inside the button for theme-driven selected icon switching.

Add toggle to opt into the APG toggle-button pattern. In uncontrolled usage, default-selected supplies the initial state and user activation updates selected, aria-pressed, and the displayed icon. In controlled usage, set selected and update it from rc-button-toggle; the event's selected detail contains the requested next state. Host property writes are silent. Removing toggle removes component-owned toggle semantics.

const favoriteButton = document.querySelector('rc-button[toggle]');

favoriteButton.addEventListener('rc-button-toggle', (event) => {
favoriteButton.selected = event.detail.selected;
});

Accessibility

Keep the native button's accessible label unchanged between states. The icons may change because they are presentational and marked aria-hidden, while the visible “Save recipe” label remains stable as required by the APG button pattern.

rc-button writes aria-pressed="true" or "false" only after toggle opts the native child into toggle-button semantics. Enter and Space continue to use the native button's activation behavior, and focus remains on the button.

Without a theme, the direct child keeps its native button appearance. The wrapper adds only layout and behavior until component tokens or a packaged theme opt into decorative styling.

Substrate supplies hover, focus, and pressed state layers, and presents determinate progress as a numeric percentage. Material supplies the same interaction states, adds a reduced-motion-aware ripple originating at the pointer or touch position, and uses a compact circular progress affordance.

Live demo

The Save recipe button is controlled: both native button activation and the Selected checkbox update the same selected state.

API

Properties

PropertyMarkupTypeDefaultDescription
disableddisabledbooleanfalseMirror disabled state to the native child.
pendingpendingbooleanfalseShow an indeterminate progress affordance and disable the native button.
progressprogressbooleanfalseShow a progress affordance and disable the native button.
progressValueprogress-valuenumber | undefinedNot specifiedOptional determinate progress percentage, clamped from 0 through 100.
toggletogglebooleanfalseOpt the native child into APG toggle-button behavior.
selectedselectedbooleanNot specifiedControlled selected state. Host writes are silent.
defaultSelecteddefault-selectedbooleanNot specifiedInitial selected state for uncontrolled toggle usage.
iconOnlyicon-onlybooleanfalseIcon-only layout hint. May also be reflected by child classification.
fullWidthfull-widthbooleanfalseStretch the native child button to the host inline size.

Methods

No public methods are documented in the custom elements manifest.

Events

EventDetail typeDescription
rc-button-toggleCustomEventFired when a user activates a button with `toggle`.

Slots

NameDescription
(default)A direct native `<button>` child.

CSS Custom Properties

PropertyDefaultDescription
--rc-button-gapNot specifiedGap between icon and label content.
--rc-button-block-sizeNot specifiedMinimum button block size.
--rc-button-min-inline-sizeNot specifiedMinimum button inline size.
--rc-button-inline-sizeNot specifiedButton inline size.
--rc-button-padding-blockNot specifiedButton block-axis padding (defers to native button padding when unset).
--rc-button-padding-inlineNot specifiedButton inline-axis padding (defers to native button padding when unset).
--rc-button-borderNot specifiedButton border (defers to native button border when unset).
--rc-button-radiusNot specifiedButton and overlay border radius (defers to native button radius when unset).
--rc-button-bgNot specifiedButton background (defers to native button background when unset).
--rc-button-colorNot specifiedButton text color (defers to native button color when unset).
--rc-button-shadowNot specifiedButton box shadow (defers to native button shadow when unset).
--rc-button-fontNot specifiedButton font shorthand (defers to native button font when unset).
--rc-button-transitionNot specifiedButton transition shorthand (defers to native button transition when unset).
--rc-button-icon-sizeNot specifiedButton inline and min-inline size when `icon-only`. Falls back to `--rc-button-block-size`, then `--rc-control-block-size`, then `2.5rem`.
--rc-button-disabled-opacityNot specifiedDisabled button opacity (defers to native disabled styling when unset).
--rc-button-busy-content-colortransparentButton text color while `pending` or `progress`.
--rc-button-state-layer-bgcurrentColorState-layer overlay color.
--rc-button-state-layer-duration150msState-layer opacity transition duration.
--rc-button-state-layer-easingeaseState-layer opacity transition easing.
--rc-button-hover-state-layer-opacity0State-layer opacity on hover.
--rc-button-focus-state-layer-opacity0State-layer opacity on focus-within.
--rc-button-pressed-state-layer-opacity0State-layer opacity on active press.
--rc-button-progress-colorcurrentColorProgress affordance color.
--rc-button-progress-font600 0.75rem / 1 sans-serifDeterminate progress percentage font.
--rc-button-progress-size1.25remIndeterminate progress spinner diameter.
--rc-button-progress-track-width2pxIndeterminate progress spinner stroke width.
--rc-button-progress-track-colorcolor-mix(in srgb, currentColor 24%, transparent)Indeterminate progress spinner track color.
--rc-button-progress-active-colorcurrentColorIndeterminate progress spinner active arc color.

CSS Parts

PartDescription
state-layerOverlay layer for hover, focus, pressed, ripple, or design-system effects.
progressNon-interactive progress affordance overlay shown for `pending` or `progress`.