Skip to content

Material

Material defines how surfaces look and feel — their color, opacity, border treatment, and visual weight. Tessera UI uses a layered material system that adapts seamlessly between light and dark themes.

Surfaces are the backgrounds on which content lives. Tessera UI defines three surface levels:

TokenLightDarkUsage
--ts-color-bg-surface#fafbff#111318Default page and component background
--ts-color-bg-elevated#ffffff#1b1d24Cards, modals — visually lifted surfaces
--ts-color-bg-subtle#f0f3ff#1e2029Tinted sections, alternate rows, hover backgrounds

In light mode, surfaces rely on shadows to create visual separation. In dark mode, surfaces use slightly different background values — elevated surfaces are lighter than the base surface.

Page background (surface)
└── Card (elevated + shadow)
└── Nested section (subtle)
└── Inline content (transparent)

Each nesting level should use a distinct surface to maintain readability.

Used for primary interactive elements and filled components:

/* Primary button */
background-color: var(--ts-color-interactive-primary); /* #1a73e8 */
/* Neutral button */
background-color: var(--ts-color-neutral-800); /* #2c3038 */

Low-opacity tints used for feedback, hover states, and status indicators:

TokenLightDarkUsage
--ts-color-interactive-primary-subtleprimary-50rgba(26, 115, 232, 0.15)Primary hover/active backgrounds
--ts-color-interactive-danger-subtlergba(220, 38, 38, 0.06)rgba(220, 38, 38, 0.12)Danger hover backgrounds
--ts-color-info-bgrgba(59, 130, 246, 0.06)rgba(59, 130, 246, 0.12)Info alert background
--ts-color-success-bgrgba(34, 197, 94, 0.06)rgba(34, 197, 94, 0.12)Success alert background
--ts-color-warning-bgrgba(245, 158, 11, 0.06)rgba(245, 158, 11, 0.12)Warning alert background
--ts-color-danger-bgrgba(220, 38, 38, 0.06)rgba(220, 38, 38, 0.12)Danger alert background

The 0.06 / 0.12 opacity pattern: Alert and status backgrounds use a very low opacity of the semantic color to create a tinted surface that remains readable in both light and dark modes. Dark mode uses slightly higher opacity (0.12) because the contrast range is narrower.

--ts-color-bg-overlay: rgba(0, 0, 0, 0.45); /* Light */
--ts-color-bg-overlay: rgba(0, 0, 0, 0.65); /* Dark */

Used behind modals and drawers to dim underlying content. Dark mode uses a stronger overlay since the base surface is already dark.

--ts-color-bg-disabled: var(--ts-color-neutral-100); /* Light */
--ts-color-bg-disabled: #1e2029; /* Dark */

Applied to disabled inputs and interactive elements to visually communicate non-interactivity.

Tessera UI provides opt-in tokens for frosted glass surfaces — translucent backgrounds with a blur effect. These are useful for floating toolbars, sticky headers, and overlay panels that need to remain legible while hinting at the content beneath.

TokenLightDarkUsage
--ts-bg-frostedrgba(255,255,255, 0.8)rgba(17,19,24, 0.8)Frosted surface background
--ts-bg-frosted-blur12px12pxBackdrop blur radius

Usage:

.frosted-header {
background-color: var(--ts-bg-frosted);
backdrop-filter: blur(var(--ts-bg-frosted-blur));
-webkit-backdrop-filter: blur(var(--ts-bg-frosted-blur));
}

Guidelines:

  • Frosted surfaces are opt-in — they are not used by default in any Tessera UI component
  • Always provide a solid fallback for browsers that do not support backdrop-filter
  • Test contrast of text on frosted surfaces with varying background content beneath

State layers are semi-transparent overlays applied on top of a component’s surface to indicate interaction state. The overlay color matches the component’s key color (e.g., primary, danger) at a prescribed opacity.

StateOpacity TokenValueVisual Effect
Hover--ts-state-hover-opacity0.08 (8%)Subtle darkening/lightening
Focus--ts-state-focus-opacity0.12 (12%)Slightly stronger than hover
Pressed--ts-state-pressed-opacity0.12 (12%)Equal to focus (combined with scale)
Dragged--ts-state-dragged-opacity0.16 (16%)Strongest overlay for active drag

Implementation pattern:

.interactive-element::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
background: currentColor;
opacity: 0;
transition: opacity var(--ts-transition-fast);
pointer-events: none;
}
.interactive-element:hover::before {
opacity: var(--ts-state-hover-opacity);
}
.interactive-element:active::before {
opacity: var(--ts-state-pressed-opacity);
}

This approach ensures consistent state feedback regardless of the element’s base color.

Skeleton screens use placeholder shapes that pulse to indicate loading content. Two tokens define the animation colors:

TokenLightDarkUsage
--ts-color-skeleton-baseneutral-200#2a2d36Skeleton shape fill
--ts-color-skeleton-pulseneutral-100#1e2029Pulse animation target color

Animation durations for loading states:

TokenValueUsage
--ts-loading-pulse-duration1.5sStandard skeleton pulse cycle
--ts-loading-shimmer-duration1.8sShimmer/sweep animation cycle
@keyframes ts-skeleton-pulse {
0%, 100% { background-color: var(--ts-color-skeleton-base); }
50% { background-color: var(--ts-color-skeleton-pulse); }
}
.skeleton {
animation: ts-skeleton-pulse var(--ts-loading-pulse-duration) ease-in-out infinite;
border-radius: var(--ts-radius-sm);
}

Borders create structure and separation without relying on shadows:

TokenLightDarkUsage
--ts-color-border-defaultneutral-200#2a2d36Standard input and card borders
--ts-color-border-subtleneutral-100#1e2029Dividers, card section separators
--ts-color-border-strongneutral-300#3b3e48High contrast borders, outline buttons
  • Input fields use a 1px solid border with border-default color, transitioning to primary-500 on focus
  • Cards use a 1px solid border with border-subtle when the bordered attribute is set
  • Alerts use a 1px solid border with the semantic color’s border token plus a 3px left accent bar
  • Badges use a 1px solid border for outline variants

Focus rings are critical for keyboard accessibility. Tessera UI uses a consistent ring pattern:

--ts-focus-ring: 0 0 0 2px var(--ts-color-focus-ring);
/* --ts-color-focus-ring: rgba(26, 115, 232, 0.4) */

A variant exists for error-state focus:

--ts-color-focus-ring-danger: rgba(220, 38, 38, 0.3);

Rules:

  • Every interactive element must show a focus ring on :focus-visible
  • Focus rings use box-shadow, not outline, for consistent rendering with border-radius
  • The ring is always 2px wide with 40% opacity of the primary color
  1. Prefer borders over shadows for flat layouts. Bordered cards (<ts-card bordered>) are cleaner for data-dense interfaces.
  2. Use subtle backgrounds for grouping. Instead of adding more borders, use --ts-color-bg-subtle to create visual zones.
  3. Keep opacity consistent. Status backgrounds always use the 0.06/0.12 pattern — don’t improvise opacity values.
  4. Disabled opacity is 0.5-0.6. Applied to the entire component, not just the background, to uniformly indicate non-interactivity.