Skip to content
On this page Data attributes

Surfaces

Surface

The painted container every card and panel is built on: elevation, material (auto, solid, frosted, glass or liquid), tone, variant and radius as independent props.

<script>
  import { Surface } from "@codeatlasdev/ui";
</script>

<Surface elevation={2} tone="brand" p="4">…</Surface>
<Surface as="section" elevation={4} tone="info" p="4">…</Surface>

A Surface is one element painted by one CSS rule (.ca-surface). Each prop is a data-* attribute, and each attribute writes different --ca-surface-* variables, so the axes never fight: elevation is depth (shadow and the blur/fill tier), material is what the surface is made of, tone tints it, variant removes the fill or draws an outline, radius rounds it. Nothing is written to style.

material="auto" follows the theme: it reads the tokens that <html data-style> already rewrites, so an app on the default solid style gets opaque surfaces, and the same markup under outlined, tonal, clay, frosted or the liquid recipe takes that style, with no change to the component. Leave material unset unless one surface must differ. The explicit materials ignore data-style: solid is opaque paper, frosted a lighter blur, glass the full blur, and liquid is glass plus the SVG refraction layer, see LiquidGlass. The blurred ones need something behind them to blur, which is why the materials demo sits on a gradient.

When a Surface blurs (glass, frosted, liquid, or auto under a blurred style), the CSS rules apply to you as well when you wrap it: never will-change: transform on it or an ancestor, and never isolation: isolate on a parent of it. Both silently remove the backdrop blur. Under prefers-reduced-transparency translucent fills become opaque, and under forced-colors every surface gets a real CanvasText border, except variant="ghost".

#078

Materials

`auto` follows the theme; the others look the same under every style.

auto

solid

frosted

glass

liquid

Reference #078 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Surface } from '@codeatlasdev/ui';

  const materials = ['auto', 'solid', 'frosted', 'glass', 'liquid'] as const;
</script>

<div class="backdrop">
  {#each materials as material (material)}
    <Surface {material} elevation={3} p="4" class="tile">
      <p class="name">{material}</p>
    </Surface>
  {/each}
</div>

<style>
  .backdrop {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ca-space-3);
    width: 100%;
    padding: var(--ca-space-6);
    border-radius: var(--ca-radius-lg);
    background: linear-gradient(135deg, var(--ca-brand-main) 0%, var(--ca-semantic-info) 50%, var(--ca-semantic-success) 100%);
  }
  .backdrop :global(.tile) { flex: 1; min-width: 7rem; text-align: center; }
  .name { margin: 0; font-family: var(--ca-font-mono); font-size: var(--ca-font-size-caption); font-weight: 600; }
</style>
#076

Elevation

Steps 0 to 5: shadow and, for every material but `solid`, the blur and fill tier (0 flat, 3 regular, 5 chrome).

Elevation 0

Elevation 1

Elevation 2

Elevation 3

Elevation 4

Elevation 5

Reference #076 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Surface } from '@codeatlasdev/ui';

  const levels = [0, 1, 2, 3, 4, 5] as const;
</script>

<div class="row">
  {#each levels as elevation (elevation)}
    <Surface material="solid" {elevation} p="4" class="tile">
      <p class="name">Elevation {elevation}</p>
    </Surface>
  {/each}
</div>

<style>
  .row { display: flex; flex-wrap: wrap; gap: var(--ca-space-4); width: 100%; padding: var(--ca-space-4); }
  .row :global(.tile) { flex: 1; min-width: 7rem; text-align: center; }
  .name { margin: 0; font-size: var(--ca-font-size-caption); font-weight: 600; }
</style>
#079

Tone and variant

Tone tints the fill, or colors the outline of `variant="outlined"`.

neutral
filled

brand
filled

success
filled

warning
filled

error
filled

info
filled

neutral
outlined

brand
outlined

success
outlined

warning
outlined

error
outlined

info
outlined

neutral
ghost

brand
ghost

success
ghost

warning
ghost

error
ghost

info
ghost

Reference #079 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Surface } from '@codeatlasdev/ui';

  const tones = ['neutral', 'brand', 'success', 'warning', 'error', 'info'] as const;
  const variants = ['filled', 'outlined', 'ghost'] as const;
</script>

<div class="grid">
  {#each variants as variant (variant)}
    {#each tones as tone (tone)}
      <Surface material="solid" elevation={1} {tone} {variant} p="3" class="tile">
        <p class="name">{tone}<br />{variant}</p>
      </Surface>
    {/each}
  {/each}
</div>

<style>
  .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: var(--ca-space-3); width: 100%; padding: var(--ca-space-4); }
  .name { margin: 0; font-size: var(--ca-font-size-caption); text-align: center; }
</style>
#077

Interactive

`interactive` adds hover, press and a focus-visible ring. Render a real `button` or `a` (`as`), so it is focusable and named.

Reference #077 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Surface } from '@codeatlasdev/ui';

  let count = $state(0);
</script>

<Surface
  as="button"
  type="button"
  interactive
  elevation={2}
  material="solid"
  radius="md"
  p="4"
  onclick={() => count++}
>
  Pressed {count} times
</Surface>
#543

Paper grain

`grain` multiplies a noise texture over the surface; `grain="animated"` shifts it and stays still under reduced motion.

grain

grain="animated"

no grain

Reference #543 · Pick in the gallery

Code
svelte
<script lang="ts">
	import { Surface } from "@codeatlasdev/ui";
</script>

<div class="row">
	<Surface material="solid" elevation={2} grain p="4" class="tile"><p class="name">grain</p></Surface>
	<Surface material="solid" elevation={2} grain="animated" p="4" class="tile"><p class="name">grain="animated"</p></Surface>
	<Surface material="solid" elevation={2} p="4" class="tile"><p class="name">no grain</p></Surface>
</div>

<style>
	.row {
		display: flex;
		flex-wrap: wrap;
		gap: var(--ca-space-3);
		width: 100%;
	}
	.row :global(.tile) {
		flex: 1;
		min-width: 9rem;
	}
	.name {
		margin: 0;
		font-size: var(--ca-font-size-body2);
		color: var(--ca-text-primary);
	}
</style>

Props

PropTypeDefaultDescription
asstring'div'Element to render (`section`, `a`, `button`, `li`...).
elevation0 | 1 | 2 | 3 | 4 | 51Depth: drop shadow, and the blur/fill tier of the material.
material'auto' | 'solid' | 'frosted' | 'glass' | 'liquid''auto'`auto` follows `<html data-style>`; `solid`, `frosted`, `glass` and `liquid` are absolute. `liquid` adds the SVG refraction layer.
tone'neutral' | 'brand' | 'success' | 'warning' | 'error' | 'info''neutral'Semantic tint over the fill; the outline color of `variant="outlined"`.
variant'filled' | 'outlined' | 'ghost''filled'`outlined` has an edge and no fill, `ghost` neither.
radius'none' | 'sm' | 'md' | 'lg' | 'xl' | 'pill'undefinedCorner radius from the radius scale. Omit for the large radius (`--ca-radius-lg`).
interactivebooleanfalseHover, press and focus-visible states and a pointer cursor. Render a link or button, or give it a role and a name.
glowbooleanfalseBrand halo behind the surface (a shadow, so it needs no isolation). It uses `--ca-glow-primary`, which is transparent under the `solid` and `flat` styles, so it only shows under styles that keep a glow (`glass`, `frosted`, `chrome`).
grainboolean | 'animated'falsePaper grain: a multiplied noise overlay clipped to the surface radius. `'animated'` shifts it, and it is still under reduced motion. Strength: `--ca-grain-opacity` (0.08), tile size: `--ca-grain-size` (160px).
p, px, pySpace | responsiveundefinedPadding as a spacing step (`"4"` = 16px). See [Layout primitives](/docs/layout/layout-primitives/).
classstring''Extra class names. Other attributes are forwarded to the element.
childrenSnippetundefinedContent.

Data attributes

The attributes are part of the public CSS contract, so an app can restyle one axis without touching the component: [data-elevation="0"], [data-material="glass"], [data-tone="error"], [data-variant="ghost"], [data-radius="pill"], [data-interactive], [data-glow], [data-grain].

SurfacePanel

SurfacePanel is a Surface at elevation 2 that renders a <section>. With an accessible name (aria-label or aria-labelledby) it is a region landmark: a filter bar, a settings group, a summary beside a table. It is not the AppShell sidebar Panel.