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
Section titled “Surfaces”Surfaces are the backgrounds on which content lives. Tessera UI defines three surface levels:
| Token | Light | Dark | Usage |
|---|---|---|---|
--ts-color-bg-surface | #fafbff | #111318 | Default page and component background |
--ts-color-bg-elevated | #ffffff | #1b1d24 | Cards, modals — visually lifted surfaces |
--ts-color-bg-subtle | #f0f3ff | #1e2029 | Tinted 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.
Surface Hierarchy
Section titled “Surface Hierarchy”Page background (surface) └── Card (elevated + shadow) └── Nested section (subtle) └── Inline content (transparent)Each nesting level should use a distinct surface to maintain readability.
Backgrounds
Section titled “Backgrounds”Solid Backgrounds
Section titled “Solid Backgrounds”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 */Subtle Backgrounds
Section titled “Subtle Backgrounds”Low-opacity tints used for feedback, hover states, and status indicators:
| Token | Light | Dark | Usage |
|---|---|---|---|
--ts-color-interactive-primary-subtle | primary-50 | rgba(26, 115, 232, 0.15) | Primary hover/active backgrounds |
--ts-color-interactive-danger-subtle | rgba(220, 38, 38, 0.06) | rgba(220, 38, 38, 0.12) | Danger hover backgrounds |
--ts-color-info-bg | rgba(59, 130, 246, 0.06) | rgba(59, 130, 246, 0.12) | Info alert background |
--ts-color-success-bg | rgba(34, 197, 94, 0.06) | rgba(34, 197, 94, 0.12) | Success alert background |
--ts-color-warning-bg | rgba(245, 158, 11, 0.06) | rgba(245, 158, 11, 0.12) | Warning alert background |
--ts-color-danger-bg | rgba(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.
Overlay Background
Section titled “Overlay Background”--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.
Disabled Background
Section titled “Disabled Background”--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.
Frosted / Glassmorphism
Section titled “Frosted / Glassmorphism”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.
| Token | Light | Dark | Usage |
|---|---|---|---|
--ts-bg-frosted | rgba(255,255,255, 0.8) | rgba(17,19,24, 0.8) | Frosted surface background |
--ts-bg-frosted-blur | 12px | 12px | Backdrop 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 Layer System
Section titled “State Layer System”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.
| State | Opacity Token | Value | Visual Effect |
|---|---|---|---|
| Hover | --ts-state-hover-opacity | 0.08 (8%) | Subtle darkening/lightening |
| Focus | --ts-state-focus-opacity | 0.12 (12%) | Slightly stronger than hover |
| Pressed | --ts-state-pressed-opacity | 0.12 (12%) | Equal to focus (combined with scale) |
| Dragged | --ts-state-dragged-opacity | 0.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 / Loading Materials
Section titled “Skeleton / Loading Materials”Skeleton screens use placeholder shapes that pulse to indicate loading content. Two tokens define the animation colors:
| Token | Light | Dark | Usage |
|---|---|---|---|
--ts-color-skeleton-base | neutral-200 | #2a2d36 | Skeleton shape fill |
--ts-color-skeleton-pulse | neutral-100 | #1e2029 | Pulse animation target color |
Animation durations for loading states:
| Token | Value | Usage |
|---|---|---|
--ts-loading-pulse-duration | 1.5s | Standard skeleton pulse cycle |
--ts-loading-shimmer-duration | 1.8s | Shimmer/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
Section titled “Borders”Borders create structure and separation without relying on shadows:
| Token | Light | Dark | Usage |
|---|---|---|---|
--ts-color-border-default | neutral-200 | #2a2d36 | Standard input and card borders |
--ts-color-border-subtle | neutral-100 | #1e2029 | Dividers, card section separators |
--ts-color-border-strong | neutral-300 | #3b3e48 | High contrast borders, outline buttons |
Border Patterns
Section titled “Border Patterns”- Input fields use a
1px solidborder withborder-defaultcolor, transitioning toprimary-500on focus - Cards use a
1px solidborder withborder-subtlewhen theborderedattribute is set - Alerts use a
1px solidborder with the semantic color’s border token plus a3pxleft accent bar - Badges use a
1px solidborder for outline variants
Focus Indicators
Section titled “Focus Indicators”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, notoutline, for consistent rendering with border-radius - The ring is always 2px wide with 40% opacity of the primary color
Guidelines
Section titled “Guidelines”- Prefer borders over shadows for flat layouts. Bordered cards (
<ts-card bordered>) are cleaner for data-dense interfaces. - Use subtle backgrounds for grouping. Instead of adding more borders, use
--ts-color-bg-subtleto create visual zones. - Keep opacity consistent. Status backgrounds always use the 0.06/0.12 pattern — don’t improvise opacity values.
- Disabled opacity is 0.5-0.6. Applied to the entire component, not just the background, to uniformly indicate non-interactivity.