Skip to content
CodeAtlas UI
On this page Data attributes

Surfaces

Surface

The painted container every card and panel is built on: elevation, material, 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" material="glass" elevation={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 solid style gets opaque surfaces and one on the liquid recipe gets glass, with no change to the component. The explicit materials ignore data-style: use glass for one blurred surface in a solid app, or solid to keep one opaque in a glass app. Glass needs something behind it to blur, which is why the demos sit on a gradient.

Rules the CSS follows, and you should too when you wrap a Surface: never will-change: transform on it or an ancestor, and never isolation: isolate on a parent of a glass or frosted surface. 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.

Materials

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

auto

solid

frosted

glass

liquid

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>

Elevation

Steps 0 to 5: shadow and, for `auto`, `glass` and `frosted`, the blur tier (0 flat, 3 regular, 5 chrome).

Elevation 0

Elevation 1

Elevation 2

Elevation 3

Elevation 4

Elevation 5

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>

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

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>

Interactive

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

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>

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>`; the others 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''lg'Corner radius from the radius scale.
interactivebooleanfalseHover, press and focus-visible states and a pointer cursor.
glowbooleanfalseBrand halo behind the surface (a shadow, so it needs no isolation).
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].

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.