Tessina UI
ConfiguratorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleCheckboxChipCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFile Upload 2FlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Surface

Declares an "on-color" tone for a colored or dark region so every nested button-family component automatically switches to high-contrast, surface-adaptive styling — set the surface once, no prop-drilling.

Playground

Overview

Surface solves a common contrast problem: a button that looks correct on the page background can become unreadable on a brand-colored hero, a dark section, or an inverted card. Rather than detecting the painted background at runtime (unreliable and SSR-unsafe), Tessina UI uses an explicit, inheritable tone — the same approach taken by Material 3, Radix, and Adobe Spectrum.

Wrap a colored region in <Surface> and every nested Button, IconButton, Fab, SplitButton, ToggleButton, and ButtonGroup inherits tone="on-color". A per-component tone prop always overrides the inherited value.

Installation

pnpm add @tessinaui/ui

Usage

import { Surface } from "@tessinaui/ui";
import { Button } from "@tessinaui/ui";
{/* Recommended — `background` paints a paired bg + on-* ink and auto-enables on-color */}
<Surface background="primary" className="rounded-xl p-6">
  <Button>Get started</Button>          {/* solid inverse chip */}
  <Button variant="secondary">Docs</Button>
  <Button variant="ghost">Learn more</Button>
</Surface>

{/* Per-button override — opt one button back out */}
<Surface background="primary">
  <Button>Inherited on-color</Button>
  <Button tone="default">Forced default</Button>
</Surface>

{/* Raw mode — bring your own background + a readable ink color */}
<Surface tone="on-color" className="bg-[#3b0764] p-6 text-white">
  <Button variant="outline">Outline</Button>
</Surface>

Examples

Default

A colored surface where nested buttons inherit tone="on-color" automatically.

Backgrounds

The background sugar paints a paired fill and on-* ink — primary, error, warning, success, info, and foreground.

Rounded

Corner radius via the rounded prop — none, sm, md, lg, and full.

Per-button override

An explicit tone="default" opts a single button back out of the inherited surface tone.

Raw mode

Skip the background sugar — set tone="on-color" with your own background and a readable ink color.

How on-color styling works

Under tone="on-color", each variant renders a contrast-safe treatment derived from the surface ink (currentColor):

VariantOn-color treatment
primary (intent none)Solid neutral inverse chip (bg-foreground / text-background) — a self-contained, AA-guaranteed prominent action.
primary (semantic intent)Keeps its solid semantic fill (error, warning, …) — those already carry guaranteed on-* contrast on any surface.
secondaryQuiet: transparent rest with a faint ink border; ink-tinted hover/active.
outlineFull ink border (border-current), transparent rest, ink-tinted hover.
ghostTransparent rest, ink-tinted hover only.

Quiet variants rest transparent so their label contrast equals the surface's own on-*/surface ratio — they never render below what the palette can support. Interaction tints use color-mix(in srgb, currentColor X%, transparent), so they adapt to any surface color.

API Reference

Props

PropTypeDefaultDescription
tone"default" | "on-color""default" (or "on-color" when background is colored)Tone propagated to descendant button-family components.
background"none" | "primary" | "error" | "warning" | "success" | "info" | "foreground""none"Convenience: paints a paired bg-* + on-* ink and auto-enables on-color tone.
renderReact.ReactElement—Render into a custom element instead of a div (polymorphic).
classNamestring—Additional classes on the surface element.

Hooks & helpers

ExportDescription
useSurfaceTone()Returns the current SurfaceTone from the nearest <Surface> (or "default"). For building tone-aware components.
resolveTone(prop, ctx)Resolves a component's effective tone — explicit prop wins, else inherited surface tone.

Notes

  • Recommended path: use the background sugar — it pairs the background with its matching on-* ink so the inherited currentColor is guaranteed legible in both light and dark themes.
  • Raw mode: when you supply your own background via className, also set a readable text color (e.g. text-white) so the quiet variants' currentColor-derived fills and labels read correctly.
  • No auto-detection: Surface does not inspect the painted background; it propagates an explicit, deterministic, build-time-verifiable tone (audited against WCAG AA).

Stepper

Numeric quantity control with increment and decrement buttons. Two layout variants (pill and floating), five sizes, full intent palette, min/max/step, disabled/readOnly states, and LTR/RTL support.

Switch

A toggle control that allows users to turn a setting on or off

On this page

PlaygroundOverviewInstallationUsageExamplesDefaultBackgroundsRoundedPer-button overrideRaw modeHow on-color styling worksAPI ReferencePropsHooks & helpersNotes