Skip to content
Kappa
Layer Card
@dicehub/kappav0.4.2

Layer Card

Stacks a supporting layer behind one raised primary surface.

Run 214Converged
Steady-state thermal analysisResidual 2.1e-05 after 42 iterations.
<script setup>
import { Badge } from "@dicehub/kappa/components/badge";
import { LayerCard } from "@dicehub/kappa/components/layer-card";
</script>

<template>
  <LayerCard style="width: 23rem;">
    <LayerCard.Secondary as="header">
      <span>Run 214</span>
      <Badge variant="success">Converged</Badge>
    </LayerCard.Secondary>
    <LayerCard.Primary>
      <strong>Steady-state thermal analysis</strong>
      <span>Residual 2.1e-05 after 42 iterations.</span>
    </LayerCard.Primary>
  </LayerCard>
</template>

Installation

Layer Card is part of the main Kappa package. It uses native semantic elements and adds no state-machine dependency.

Barrel

import {
  LayerCard,
  LayerCardPrimary,
  LayerCardRoot,
  LayerCardSecondary,
} from "@dicehub/kappa";

Granular

import {
  LayerCard,
  LayerCardPrimary,
  LayerCardRoot,
  LayerCardSecondary,
} from "@dicehub/kappa/components/layer-card";

Usage

Place Secondary first for context such as a run identifier, resource name, or category. Primary then carries the raised content and stays flush with the outer surface.

Solver configuration
Rotor refinementMesh 4.2M cells · k-ω SST · 8 workers
Iterations
42
Wall time
4 min 12 s
<script setup>
import { LayerCard } from "@dicehub/kappa/components/layer-card";
</script>

<template>
  <LayerCard as="article" style="width: 23rem;">
    <LayerCard.Secondary as="header">Solver configuration</LayerCard.Secondary>
    <LayerCard.Primary>
      <strong>Rotor refinement</strong>
      <span>Mesh 4.2M cells · k-ω SST · 8 workers</span>
      <dl>
        <div><dt>Iterations</dt><dd>42</dd></div>
        <div><dt>Wall time</dt><dd>4 min 12 s</dd></div>
      </dl>
    </LayerCard.Primary>
  </LayerCard>
</template>

Composition

Typical part hierarchy
LayerCard.Root <div | article | section | aside | a | form>
├── LayerCard.Secondary (optional)
│   └── supporting label or context
└── LayerCard.Primary
    └── raised content

LayerCard.Root (simple surface, direct content)
└── default slot

The root owns the outer radius and clips both layers. The primary surface stays square, so the outer silhouette supplies every visible corner. Ark UI has no static layer primitive, so Kappa owns this native presentation and its semantic element choices.

  • Use Secondary and Primary together for two clear surfaces.
  • Use direct root content when one surface is enough.
  • Place dense toolbars, filters, and tables inside Primary; keep Secondary to one line of context.
  • Keep one primary target per layer. Do not nest other controls inside a linked surface.
  • Do not nest Layer Cards to fake depth. Use Grid or another layout container.

Examples

Layered Content

Pair a secondary context layer with one raised primary surface.

Deployment window
Tuesday, 18:00–19:00 UTC Four services, twelve checks, no maintenance window conflicts.
Services
4
Checks
12 / 12
<script setup>
import { LayerCard } from "@dicehub/kappa/components/layer-card";
</script>

<template>
  <LayerCard as="article" style="width: 23rem;">
    <LayerCard.Secondary as="header">Deployment window</LayerCard.Secondary>
    <LayerCard.Primary>
      <strong>Tuesday, 18:00–19:00 UTC</strong>
      <span>Four services, twelve checks, no maintenance window conflicts.</span>
      <dl>
        <div><dt>Services</dt><dd>4</dd></div>
        <div><dt>Checks</dt><dd>12 / 12</dd></div>
      </dl>
    </LayerCard.Primary>
  </LayerCard>
</template>

Simple Surface

Use direct root content when one surface is enough.

Solver diagnostics

All convergence checks passed. The solution is ready to export.

<script setup>
import { LayerCard } from "@dicehub/kappa/components/layer-card";
</script>

<template>
  <LayerCard as="section" style="width: 23rem;">
    <p><strong>Solver diagnostics</strong></p>
    <p>All convergence checks passed. The solution is ready to export.</p>
  </LayerCard>
</template>

Interactive Primary Link

Render Primary as an anchor when the whole layer has one target.

<script setup>
import { ArrowRight } from "@lucide/vue";
import { LayerCard } from "@dicehub/kappa/components/layer-card";
</script>

<template>
  <LayerCard style="width: 23rem;">
    <LayerCard.Secondary as="header">Release notes</LayerCard.Secondary>
    <LayerCard.Primary as="a" href="/release-notes">
      <strong>Read the component model <ArrowRight aria-hidden="true" /></strong>
      <span>The primary layer is one semantic anchor.</span>
    </LayerCard.Primary>
  </LayerCard>
</template>

Layered States

Hover and focus-visible styles cover linked roots and linked primaries.

<script setup>
import { LayerCard } from "@dicehub/kappa/components/layer-card";
</script>

<template>
  <LayerCard as="a" href="/reports/run-214" style="width: 23rem;">
    <span>Linked root surface</span>
    <span>Hover and keyboard focus are visible.</span>
  </LayerCard>

  <LayerCard style="width: 23rem;">
    <LayerCard.Secondary as="header">Primary link</LayerCard.Secondary>
    <LayerCard.Primary as="a" href="/reports/run-214">
      <strong>Open the run report</strong>
      <span>Focus stays inside the layered surface.</span>
    </LayerCard.Primary>
  </LayerCard>
</template>

Filter Toolbar with Small Tabs

Compose small tabs, a search input, and a status table inside one dense primary layer.

Request log6 / 6
OriginStatusDuration
api.dicehub.com /v1/runs2xx · 1842118 ms
cdn.dicehub.com /preview/*2xx · 320842 ms
assets.dicehub.com /meshes/*3xx · 41264 ms
legacy.dicehub.com /v0/solve4xx · 96812 ms
telemetry.dicehub.com /ingest4xx · 58310 ms
gateway.dicehub.com /solver/health5xx · 122.4 s
<script setup>
import { computed, ref } from "vue";
import { Badge } from "@dicehub/kappa/components/badge";
import { Input } from "@dicehub/kappa/components/input";
import { LayerCard } from "@dicehub/kappa/components/layer-card";
import { Tabs } from "@dicehub/kappa/components/tabs";

const statusFilters = [
  { value: "all", label: "All" },
  { value: "2xx", label: "2xx" },
  { value: "3xx", label: "3xx" },
  { value: "4xx", label: "4xx" },
  { value: "5xx", label: "5xx" },
];
const statusVariants = { "2xx": "success", "3xx": "info", "4xx": "warning", "5xx": "error" };
const requests = [
  { origin: "api.dicehub.com /v1/runs", status: "2xx", count: 1842, duration: "118 ms" },
  { origin: "legacy.dicehub.com /v0/solve", status: "4xx", count: 96, duration: "812 ms" },
  { origin: "gateway.dicehub.com /solver/health", status: "5xx", count: 12, duration: "2.4 s" },
  // …more origins
];

const search = ref("");
const status = ref("all");
const visible = computed(() => {
  const query = search.value.trim().toLowerCase();
  return requests.filter((request) =>
    (status.value === "all" || request.status === status.value) &&
    (!query || request.origin.toLowerCase().includes(query)),
  );
});
</script>

<template>
  <LayerCard as="section" style="width: 34rem;">
    <LayerCard.Secondary as="header">
      <span>Request log</span>
      <span>{{ visible.length }} / {{ requests.length }}</span>
    </LayerCard.Secondary>
    <LayerCard.Primary>
      <div class="toolbar">
        <Input v-model="search" aria-label="Filter origins" size="sm" placeholder="Filter origins..." />
        <Tabs.Root v-model="status">
          <Tabs.List aria-label="Filter by status" size="sm">
            <Tabs.Trigger v-for="filter in statusFilters" :key="filter.value" :value="filter.value">
              {{ filter.label }}
            </Tabs.Trigger>
            <Tabs.Indicator />
          </Tabs.List>
        </Tabs.Root>
      </div>
      <table aria-label="Request origins">
        <thead>
          <tr><th scope="col">Origin</th><th scope="col">Status</th><th scope="col">Duration</th></tr>
        </thead>
        <tbody>
          <tr v-for="request in visible" :key="request.origin">
            <th scope="row">{{ request.origin }}</th>
            <td><Badge :variant="statusVariants[request.status]">{{ request.status }} · {{ request.count }}</Badge></td>
            <td>{{ request.duration }}</td>
          </tr>
        </tbody>
      </table>
      <p v-if="visible.length === 0" role="status">No origins match the current filter.</p>
      <p>Showing {{ visible.length }} of {{ requests.length }} requests</p>
    </LayerCard.Primary>
  </LayerCard>
</template>

Accessibility

  • Layer Card adds no widget role or keyboard behavior.
  • Choose article, section, aside, and header only when the content meets native HTML sectioning rules.
  • Use as="a" on the root or on Primary only when the complete surface has one target. Do not place other controls inside that anchor.
  • Linked roots and linked primaries keep a visible focus indicator and a forced-colors boundary.
  • HTML, ARIA, data attributes, and event listeners pass to the selected native element.

API Reference

LayerCard

Outer surface for layered or direct content.

PropTypeDefaultDescription
as"div" | "article" | "section" | "aside" | "a" | "form""div"Native element rendered by the outer surface.
default slotslot—Secondary and Primary layers, or direct surface content.

Parts

PartSupported asDefaultDescription
LayerCard.Secondary"div" | "header" | "p""div"Supporting layer behind the primary surface.
LayerCard.Primary"div" | "article" | "section" | "a""div"Raised content surface with square corners.

Data Slots

SlotDescription
layer-cardOuter surface
layer-card-secondarySupporting layer
layer-card-primaryRaised primary layer

Exports

ExportDescription
LayerCardCompound root with the Root, Primary, and Secondary parts.
LayerCardRootNamed outer surface export.
LayerCardPrimary / LayerCardSecondaryNamed layered-surface exports.
LayerCard*Props / LayerCard*SlotsPublic prop and slot contracts.
resolveLayerCard*Safe runtime resolvers for the semantic element props.