Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsEssentials

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>

Showcase

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).

Skeleton

Animated placeholder for loading states. Wave shimmer (default) and pulse variants, five rounded options, and composable with any layout.

Progress

Communicate operation status with a linear bar or circular ring. Four variants (default/success/warning/error), three sizes, five rounding options, indeterminate animation, optional label with inside/outside positioning, and LTR/RTL support.

On this page

PlaygroundOverviewInstallationUsageShowcaseHow on-color styling worksAPI ReferencePropsHooks & helpersNotes