Signal Feed Card
v0.1.0Item-driven feed row for prioritised work queues: severity and status chips, a stage progress chain, act-by countdown, assignee, and priority score. Takes one flat item and owns every presentation decision, so screens map their domain record and nothing else.
Install
npx shadcn@latest add @signalos/signal-feed-cardRequires a configured @signalos registry and a valid SIGNALOS_REGISTRY_TOKEN - get access. Registry dependencies (@signalos/tokens, @signalos/utils, @signalos/ranked-list-card, @signalos/status-badge) are pulled automatically.
Preview
Example & code
signal-feed-card.example.tsx
// Example: a work-queue row mapped from an arbitrary domain record.
// The screen formats every value; the card owns the layout.
import { Route, Zap } from "lucide-react"
import { SignalFeedCard } from "@/components/widgets/signal-feed-card/SignalFeedCard"
import type { SignalFeedItem } from "@/components/widgets/signal-feed-card/SignalFeedCard.types"
interface QueueRecord {
id: string
headline: string
priority: number
confidence: number
severity: "critical" | "high" | "medium" | "low"
owner: string | null
team: string
dueInHours: number
}
function toItem(record: QueueRecord): SignalFeedItem {
return {
id: record.id,
rank: record.priority,
confidence: record.confidence,
title: record.headline,
severity: record.severity,
status: "IN PROGRESS",
assignee: { name: record.owner ?? undefined, role: record.team },
deadline: {
label:
record.dueInHours < 0
? `Past due by ${Math.abs(record.dueInHours)}h`
: `${record.dueInHours}h`,
tone:
record.dueInHours < 0
? "past-due"
: record.dueInHours < 4
? "urgent"
: "normal",
},
stages: [
{
id: "detected",
label: "Detected",
icon: <Zap className="h-2.5 w-2.5" />,
},
{
id: "routed",
label: "Routed",
value: "+2m",
icon: <Route className="h-2.5 w-2.5" />,
emphasized: true,
},
],
}
}
const records: QueueRecord[] = [
{
id: "rec-1",
headline: "Coverage collapse across multiple tenants",
priority: 1,
confidence: 62,
severity: "high",
owner: "Priya N.",
team: "Integration",
dueInHours: 3,
},
]
export default function Example() {
return (
<div className="flex flex-col gap-3">
{records.map((record) => (
<SignalFeedCard
key={record.id}
item={toItem(record)}
onOpen={(item) => console.info("open", item.id)}
/>
))}
</div>
)
}
Props
SignalFeedStage
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | - | Stable key for the stage. |
| label* | ReactNode | - | Stage name, e.g. "Detected" or "Assigned". |
| value | ReactNode | - | Pre-formatted elapsed/relative value shown next to the label, e.g. "+37s". |
| icon | ReactNode | - | Optional leading icon (a ~10px glyph reads best). |
| emphasized | boolean | undefined | - | Renders the stage with the primary accent — use for the current stage. |
SignalFeedDeadline
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | ReactNode | - | Pre-formatted countdown, e.g. "1d 21h 55m", "Past due by 2h", "No deadline". |
| tone | SignalFeedDeadlineTone | undefined | "normal" | Urgency tone. |
SignalFeedAssignee
| Prop | Type | Default | Description |
|---|---|---|---|
| name | ReactNode | - | Display name of the person. |
| role | ReactNode | - | Secondary line — the role, team, or queue the item was routed to. |
SignalFeedHighlight
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | ReactNode | - | Pre-formatted value, e.g. "max 12.4σ" or "peak 3.1x". |
| tone | SignalFeedHighlightTone | undefined | "default" | Emphasis tone. |
SignalFeedItem
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | - | Stable identifier; also used as the React key by list callers. |
| title* | ReactNode | - | Primary headline (clamped to two lines). |
| rank | ReactNode | - | Left-column rank. A `number` renders as `#2`; any other node renders verbatim. Omit to hide the entire left column. |
| confidence | number | undefined | - | Confidence/progress arc under the rank, 0–100. |
| confidenceColorClass | string | undefined | "text-primary" | Tailwind `text-*` class driving the arc color. |
| severity | Severity | null | undefined | - | Severity chip and accent border. `null` renders a neutral "UNSET" chip. |
| status | ReactNode | - | Workflow status chip, e.g. "IN PROGRESS". |
| reference | ReactNode | - | Short mono identifier shown beside the chips, e.g. "A1B2C3D4". |
| category | ReactNode | - | Grouping label in the meta row, e.g. a domain or product area. |
| metric | ReactNode | - | Primary measurement name in the meta row, rendered as `<code>`. |
| signalCount | number | undefined | - | Number of underlying signals; pluralised with `labels.signalNoun`. |
| highlight | SignalFeedHighlight | undefined | - | Emphasised measurement in the meta row. |
| escalated | boolean | undefined | false | Shows the corner ribbon (e.g. an escalation flag). |
| deadline | SignalFeedDeadline | undefined | - | Act-by countdown; its tone also drives the card's urgency border. |
| assignee | SignalFeedAssignee | undefined | - | Current owner shown in the trailing column and mobile footer. |
| score | ReactNode | - | Pre-formatted priority score shown in the trailing footer, e.g. "0.746". |
| stages | readonly SignalFeedStage[] | undefined | - | Horizontal progress chain under the title. |
SignalFeedCardLabels
| Prop | Type | Default | Description |
|---|---|---|---|
| rank | ReactNode | "Rank" | Caption above the rank number. |
| deadline | ReactNode | "Act by" | Caption above the countdown. |
| assignee | ReactNode | "Routed to" | Caption above the assignee. |
| escalated | ReactNode | "Escalated" | Corner ribbon text when `item.escalated` is set. |
| signalNoun | string | undefined | "signal" | Singular noun for `item.signalCount`; an "s" is appended when != 1. |
| scorePrefix | ReactNode | "score" | Prefix before `item.score` in the trailing footer. |
SignalFeedCardProps
| Prop | Type | Default | Description |
|---|---|---|---|
| item* | SignalFeedItem | - | The record to render. |
| selected | boolean | undefined | false | Selected state (primary ring + border). |
| onOpen | ((item: SignalFeedItem) => void) | undefined | - | Invoked on click / Enter / Space. Omit to render a non-interactive card. |
| labels | SignalFeedCardLabels | undefined | - | Caption overrides. |
| className | string | undefined | - | |
| data-testid | string | undefined | "signal-feed-card" | Root test id; inner elements derive from it (`-deadline`, `-deadline-mobile`, `-assignee`, `-score`, `-escalated`, `-stage-<id>`). |
SignalFeedCardSkeletonProps
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | undefined | - | |
| data-testid | string | undefined | "signal-feed-card-skeleton" |
Supporting types
export type SignalFeedDeadlineTone = "normal" | "urgent" | "past-due"
export type SignalFeedHighlightTone = "default" | "warning" | "error"
npm dependencies
lucide-react@^1.7.0Changelog
signal-feed-card
0.1.0
- Initial release: item-driven feed card for prioritised queues, built on
ranked-list-card. Renders severity/status chips, a reference, a meta row (category, metric, signal count, highlight), a stage progress chain, an act-by countdown with urgency tone, assignee, and a priority score, plus a mobile footer and a loading skeleton. Every value arrives pre-formatted so the widget never guesses a locale or unit.