Skip to content
Kappa
Activity Feed
@dicehub/kappav0.4.2

Activity Feed

Presents chronological application events with clear actors, time, status, and actions.

Today

  1. Mei Chen@mei

    Started run 184 in Wind tunnel · 32 cores

  2. API keyAutomation

    Completed surface validation · 18 checks passed

  3. Lina Haddad@lina

    Uploaded vehicle-body.step · 28.4 MB

<script setup>
import { ActivityFeed } from "@dicehub/kappa/components/activity-feed";
import { Avatar } from "@dicehub/kappa/components/avatar";
</script>

<template>
  <ActivityFeed.Root>
    <ActivityFeed.Group aria-labelledby="activity-today">
      <ActivityFeed.GroupLabel id="activity-today">Today</ActivityFeed.GroupLabel>
      <ActivityFeed.List aria-label="Project activity">
        <ActivityFeed.Item>
          <ActivityFeed.Marker variant="avatar">
            <Avatar.Root>
              <Avatar.Image src="/avatars/mei-chen.webp" alt="" />
              <Avatar.Fallback aria-hidden="true">MC</Avatar.Fallback>
            </Avatar.Root>
          </ActivityFeed.Marker>
          <ActivityFeed.Content>
            <ActivityFeed.Header>
              <ActivityFeed.Title>Mei Chen</ActivityFeed.Title>
              <ActivityFeed.Time datetime="2026-09-19T14:32:00Z">14:32</ActivityFeed.Time>
            </ActivityFeed.Header>
            <ActivityFeed.Description>
              Started run <a href="/runs/184">184</a> in Wind tunnel · 32 cores
            </ActivityFeed.Description>
          </ActivityFeed.Content>
        </ActivityFeed.Item>
      </ActivityFeed.List>
    </ActivityFeed.Group>
  </ActivityFeed.Root>
</template>

Installation

Activity Feed is part of the main Kappa package. It uses native list and time elements and has no state-machine dependency.

Barrel

import {
  ActivityFeed,
  ActivityFeedActions,
  ActivityFeedContent,
  ActivityFeedDescription,
  ActivityFeedGroup,
  ActivityFeedGroupLabel,
  ActivityFeedHeader,
  ActivityFeedItem,
  ActivityFeedList,
  ActivityFeedMarker,
  ActivityFeedRoot,
  ActivityFeedTime,
  ActivityFeedTitle,
} from "@dicehub/kappa";

Granular

import {
  ActivityFeed,
  ActivityFeedItem,
  ActivityFeedMarker,
  type ActivityFeedTone,
} from "@dicehub/kappa/components/activity-feed";

Usage

Use Activity Feed for ordered events such as run changes, project history, audit records, and review actions. Supply application data through slots.

Keep the actor or event name in Title and the event details in Description. Time aligns with the end of the header and wraps on narrow panels. Description links use the accent color and retain a visible keyboard focus ring.

  1. Simulation started

    Run 184 is preparing compute resources.

<script setup>
import { Play } from "@lucide/vue";
import { ActivityFeed } from "@dicehub/kappa/components/activity-feed";
</script>

<template>
  <ActivityFeed.Root>
    <ActivityFeed.List aria-label="Recent activity">
      <ActivityFeed.Item tone="accent">
        <ActivityFeed.Marker variant="icon"><Play aria-hidden="true" /></ActivityFeed.Marker>
        <ActivityFeed.Content>
          <ActivityFeed.Header>
            <ActivityFeed.Title>Simulation started</ActivityFeed.Title>
            <ActivityFeed.Time datetime="2026-09-19T14:32:00Z">2 min ago</ActivityFeed.Time>
          </ActivityFeed.Header>
          <ActivityFeed.Description>Run 184 is preparing compute resources.</ActivityFeed.Description>
        </ActivityFeed.Content>
      </ActivityFeed.Item>
    </ActivityFeed.List>
  </ActivityFeed.Root>
</template>

Composition

Typical part hierarchy
ActivityFeed.Root
└── ActivityFeed.Group (optional)
    ├── ActivityFeed.GroupLabel
    └── ActivityFeed.List
        └── ActivityFeed.Item
            ├── ActivityFeed.Marker
            └── ActivityFeed.Content
                ├── ActivityFeed.Header
                │   ├── ActivityFeed.Title
                │   └── ActivityFeed.Time
                ├── ActivityFeed.Description
                └── ActivityFeed.Actions (optional)

Kappa owns this native semantic composition because Ark UI has no Activity Feed primitive. Activity Feed does not fetch data, format events, or depend on an application store.

  • Use Group and GroupLabel when events are divided by date or category.
  • Use one List for each group and preserve a meaningful event order.
  • Use dot markers for neutral system events, icons for typed events, and avatars for people.
  • Use Actions only for controls that act on the associated event.
  • Compose with Collapsible to reveal long event details.

Examples

Compact

Use compact density for audit trails and high-frequency configuration events.

Configuration history

  1. Boundary condition updated

    inlet.velocity · 14.2 m/s

  2. Geometry synchronized

    vehicle-body.step · 28.4 MB

  3. Run settings saved

    32 cores · 48 GB memory

<script setup>
import { ActivityFeed } from "@dicehub/kappa/components/activity-feed";

const events = [
  { title: "Boundary condition updated", time: "14:32" },
  { title: "Geometry synchronized", time: "14:29" },
];
</script>

<template>
  <ActivityFeed.Root size="compact">
    <ActivityFeed.List aria-label="Configuration history">
      <ActivityFeed.Item v-for="event in events" :key="event.time">
        <ActivityFeed.Marker />
        <ActivityFeed.Content>
          <ActivityFeed.Header>
            <ActivityFeed.Title>{{ event.title }}</ActivityFeed.Title>
            <ActivityFeed.Time>{{ event.time }}</ActivityFeed.Time>
          </ActivityFeed.Header>
        </ActivityFeed.Content>
      </ActivityFeed.Item>
    </ActivityFeed.List>
  </ActivityFeed.Root>
</template>

Markers

Distinguish system events, typed actions, and people with dot, icon, and avatar markers.

  1. Automatic system event

  2. Agent prepared a configuration

  3. Lina added a review note

<script setup>
import { Bot } from "@lucide/vue";
import { ActivityFeed } from "@dicehub/kappa/components/activity-feed";
import { Avatar } from "@dicehub/kappa/components/avatar";
</script>

<template>
  <ActivityFeed.Root>
    <ActivityFeed.List aria-label="Marker examples">
      <ActivityFeed.Item>
        <ActivityFeed.Marker />
        <ActivityFeed.Content><ActivityFeed.Title>Automatic system event</ActivityFeed.Title></ActivityFeed.Content>
      </ActivityFeed.Item>
      <ActivityFeed.Item tone="accent">
        <ActivityFeed.Marker variant="icon"><Bot aria-hidden="true" /></ActivityFeed.Marker>
        <ActivityFeed.Content><ActivityFeed.Title>Agent prepared a configuration</ActivityFeed.Title></ActivityFeed.Content>
      </ActivityFeed.Item>
      <ActivityFeed.Item>
        <ActivityFeed.Marker variant="avatar">
          <Avatar.Root><Avatar.Fallback aria-hidden="true">LH</Avatar.Fallback></Avatar.Root>
        </ActivityFeed.Marker>
        <ActivityFeed.Content><ActivityFeed.Title>Lina added a review note</ActivityFeed.Title></ActivityFeed.Content>
      </ActivityFeed.Item>
    </ActivityFeed.List>
  </ActivityFeed.Root>
</template>

Tones

Apply restrained semantic color to the marker without coloring the full event row.

  1. Configuration saved

  2. Simulation started

  3. Mesh validation passed

  4. Residual target not reached

  5. Solver stopped unexpectedly

<script setup lang="ts">
import { ActivityFeed, type ActivityFeedTone } from "@dicehub/kappa/components/activity-feed";

const tones: ActivityFeedTone[] = ["neutral", "accent", "success", "warning", "danger"];
</script>

<template>
  <ActivityFeed.Root size="compact">
    <ActivityFeed.List aria-label="Activity tones">
      <ActivityFeed.Item v-for="tone in tones" :key="tone" :tone="tone">
        <ActivityFeed.Marker />
        <ActivityFeed.Content><ActivityFeed.Title>{{ tone }} event</ActivityFeed.Title></ActivityFeed.Content>
      </ActivityFeed.Item>
    </ActivityFeed.List>
  </ActivityFeed.Root>
</template>

Long activity

Keep a long update collapsed until it is needed. Select the arrow at the bottom center to expand the card and read the full review. The arrow stays below the content and points up when open. Collapsible supplies keyboard controls, height animation, and reduced-motion support.

  1. Mei Chen

    Published a detailed review of run 184. Three changes are required before the next wind tunnel comparison.

<script setup lang="ts">
import { ref } from "vue";
import { ChevronDown } from "@lucide/vue";
import { ActivityFeed } from "@dicehub/kappa/components/activity-feed";
import { Avatar } from "@dicehub/kappa/components/avatar";
import { Collapsible } from "@dicehub/kappa/components/collapsible";

const open = ref(false);
</script>

<template>
  <ActivityFeed.Root>
    <ActivityFeed.List aria-label="Detailed project activity">
      <ActivityFeed.Item>
        <ActivityFeed.Marker variant="avatar">
          <Avatar.Root>
            <Avatar.Image src="/avatars/mei-chen.webp" alt="" />
            <Avatar.Fallback aria-hidden="true">MC</Avatar.Fallback>
          </Avatar.Root>
        </ActivityFeed.Marker>
        <ActivityFeed.Content>
          <Collapsible.Root v-model:open="open" class="activity-feed-expandable">
            <ActivityFeed.Header>
              <ActivityFeed.Title>Mei Chen</ActivityFeed.Title>
              <ActivityFeed.Time datetime="2026-09-19T12:06:00Z">12:06</ActivityFeed.Time>
            </ActivityFeed.Header>
            <ActivityFeed.Description>
              Published a detailed review of run 184. Three changes are required before the next
              wind tunnel comparison.
            </ActivityFeed.Description>
            <Collapsible.Content class="activity-feed-expandable__content">
              <div class="activity-feed-expandable__report">
                <p>
                  I compared the production mesh with the reference case at 14.2 m/s. The mean
                  drag coefficient is within the target range, but the pressure distribution
                  around the rear wheel still changes between the final sampling windows. Keep
                  this run as a baseline while we check the remaining differences.
                </p>
                <h4>Boundary conditions</h4>
                <p>
                  Change the inlet turbulence intensity from 1% to 0.5% to match the measured
                  tunnel conditions. Keep the velocity profile and air temperature unchanged.
                  The outlet pressure and symmetry boundaries already match the reference
                  setup, so they do not need an update.
                </p>
                <h4>Mesh and convergence</h4>
                <p>
                  Add one refinement level around the rear wheel and the wake. The current mesh
                  passes all 18 geometry checks, but the local cell size is too large to resolve
                  the pressure gradient. Preserve the existing prism layers and check the wall
                  distance again after the new volume mesh is generated.
                </p>
                <p>
                  Extend the solver by 500 iterations after the residuals settle. Compare the
                  final three force averages instead of the last sample alone. Record the
                  maximum Courant number and the mass imbalance with the exported results so
                  the next review can use the same acceptance checks.
                </p>
                <h4>Next steps</h4>
                <p>
                  Save the updated configuration as a new revision, attach the mesh quality
                  report, and run the comparison on the same 32-core allocation. Share the
                  pressure and force plots with Lina before marking the case ready for release.
                  The original run remains available for comparison.
                </p>
              </div>
            </Collapsible.Content>
            <div class="activity-feed-expandable__footer">
              <Collapsible.Trigger
                class="activity-feed-expandable__trigger"
                :aria-label="open ? 'Collapse review details' : 'Expand review details'"
              >
                <template #indicator>
                  <ChevronDown class="activity-feed-expandable__arrow" aria-hidden="true" />
                </template>
              </Collapsible.Trigger>
            </div>
          </Collapsible.Root>
        </ActivityFeed.Content>
      </ActivityFeed.Item>
    </ActivityFeed.List>
  </ActivityFeed.Root>
</template>

<style scoped>
.activity-feed-expandable {
  border: 0;
  border-radius: 0;
  background: transparent;
}

.activity-feed-expandable__footer {
  display: flex;
  justify-content: center;
  margin-block-start: 0.5rem;
  margin-block-end: calc(
    0.25rem - var(--kappa-activity-feed-item-padding-block)
  );
  /* Include the marker column so the arrow is centered on the whole card. */
  margin-inline-start: calc(
    0px - var(--kappa-activity-feed-marker-size) - var(--kappa-activity-feed-item-gap)
  );
}

.activity-feed-expandable__trigger {
  inline-size: 1.75rem;
  min-block-size: 1.75rem;
  justify-content: center;
  padding: 0;
  border-radius: 0.25rem;
  color: var(--kappa-subtle);
}

.activity-feed-expandable__trigger[data-state="open"] {
  background: transparent;
}

.activity-feed-expandable__arrow {
  inline-size: 1rem;
  block-size: 1rem;
  transition: transform 160ms ease;
}

.activity-feed-expandable__trigger[data-state="open"] .activity-feed-expandable__arrow {
  transform: rotate(180deg);
}

.activity-feed-expandable__content {
  border: 0;
}

.activity-feed-expandable__content :deep([data-slot="collapsible-content-body"]) {
  padding: 0.875rem 0 0;
}

.activity-feed-expandable__report {
  border-block-start: 1px solid var(--kappa-line);
  padding-block-start: 0.875rem;
  color: var(--kappa-subtle);
  font-size: 0.8125rem;
  line-height: 1.6;
  overflow-wrap: anywhere;
}

.activity-feed-expandable__report p {
  margin: 0;
}

.activity-feed-expandable__report h4 {
  margin: 1rem 0 0.25rem;
  color: var(--kappa-default);
  font-size: inherit;
  font-weight: 600;
}

.activity-feed-expandable__report p + p {
  margin-block-start: 0.75rem;
}

@media (prefers-reduced-motion: reduce) {
  .activity-feed-expandable__arrow {
    transition: none;
  }
}
</style>

Actions

Place event-specific controls after the description while keeping the title readable.

  1. Lina requested changes to the inlet setup

    The turbulence intensity must match the reference case.

<script setup>
import { ActivityFeed } from "@dicehub/kappa/components/activity-feed";
import { Button } from "@dicehub/kappa/components/button";
</script>

<template>
  <ActivityFeed.Root>
    <ActivityFeed.List aria-label="Review activity">
      <ActivityFeed.Item tone="warning">
        <ActivityFeed.Marker />
        <ActivityFeed.Content>
          <ActivityFeed.Title>Lina requested changes</ActivityFeed.Title>
          <ActivityFeed.Description>Review the inlet setup.</ActivityFeed.Description>
          <ActivityFeed.Actions>
            <Button size="sm" variant="secondary">Open comment</Button>
            <Button size="sm" variant="ghost">Resolve</Button>
          </ActivityFeed.Actions>
        </ActivityFeed.Content>
      </ActivityFeed.Item>
    </ActivityFeed.List>
  </ActivityFeed.Root>
</template>

Accessibility

  • List renders a native ordered list and Item renders a native list item.
  • Place aria-label or aria-labelledby on List to name the event history.
  • Time renders a native time element. Supply datetime whenever the value is known.
  • Connect each Group to its GroupLabel with aria-labelledby.
  • Decorative marker icons need aria-hidden="true". Meaningful images need alternative text.
  • For live events, set aria-live="polite" on a small changing region. Do not announce the full history again.
  • HTML, ARIA, data attributes, and event listeners pass to every part.
  • The long activity example uses a named disclosure button with aria-expanded and aria-controls. Its height animation respects reduced motion.

API Reference

ActivityFeed.Root

PropTypeDefaultDescription
size"default" | "compact""default"Controls event spacing, marker dimensions, and type scale.

ActivityFeed.Item

PropTypeDefaultDescription
tone"neutral" | "accent" | "success" | "warning" | "danger""neutral"Applies semantic color to the event marker.

ActivityFeed.Marker

PropTypeDefaultDescription
variant"dot" | "icon" | "avatar""dot"Selects marker geometry for automatic, typed, or person events.

Semantic Parts

PartPropTypeDefaultDescription
GroupLabel / Titleas"h2" | "h3" | "h4" | "h5" | "h6" | "p" | "div""h3" / "p"Selects a safe native heading or text element.
TimedatetimestringundefinedSets the machine-readable value on the native time element.

Parts

PartElementDescription
ActivityFeed.RootdivRoot density and token scope.
ActivityFeed.GroupsectionOptional set of events for one date or category.
ActivityFeed.GroupLabelh3Visible label for a group.
ActivityFeed.ListolNative ordered event list.
ActivityFeed.ItemliOne chronological event and its semantic tone.
ActivityFeed.MarkerdivDot, icon, or avatar beside the event content.
ActivityFeed.ContentdivFlexible event content column.
ActivityFeed.HeaderdivTitle and time row.
ActivityFeed.TitlepPrimary event statement.
ActivityFeed.DescriptionpSupporting event detail.
ActivityFeed.TimetimeVisible and machine-readable event time.
ActivityFeed.ActionsdivOptional event actions.

Exports

ExportDescription
ActivityFeedCompound API exposing all Activity Feed parts.
ActivityFeedRootUnaugmented root component.
ActivityFeedGroup / ActivityFeedGroupLabelOptional event grouping parts.
ActivityFeedList / ActivityFeedItemNative ordered list and list-item parts.
ActivityFeedMarkerDot, icon, or avatar marker.
ActivityFeedContent / ActivityFeedHeaderEvent content layout parts.
ActivityFeedTitle / ActivityFeedDescriptionPrimary and supporting event copy.
ActivityFeedTime / ActivityFeedActionsEvent metadata and controls.
ActivityFeedSize / ActivityFeedTone / ActivityFeedMarkerVariantPublic option unions.