Skip to main content

Cards vs Widgets

The library has two families of content surface. Picking the right one keeps detail pages and dashboards consistent and gives every full surface the same affordances.

The two families

CardWidget
What it isA single-headline / simple read-only surfaceA full content surface a user works with
ChromeCnDetailCard (title + optional icon, collapsible)CnWidgetWrapper (title + content + the shared Actions menu)
Actions menu❌ none✅ Refresh · Documentation · Request a feature (via CnActionsMenu)
ExamplesKPI cards, stats blocks, single-metric tiles, info cards, object metadatadata, related, object-table, card-grid, form-renderer, map-viewer, integration, chart

The litmus test the team uses: anything that shows a single headline number is a Card; anything a user reads a list/table/grid from or interacts with is a Widget.

What a Widget gets for free

Because every Widget renders on CnWidgetWrapper, it automatically carries the shared overflow Actions menu:

  • Refresh — always shown; broadcasts on the cn:widget:refresh event bus (page surfaces use cn:page:refresh).
  • Request a feature — always shown; opens CnSuggestFeatureModal, auto-resolving the app + repo from the CnAppRoot injects.
  • Documentation — shown only when a documentation-url is provided.
  • Per-widget extras via the #action-items slot (e.g. the Metadata item on CnObjectDataWidget).

Cards deliberately omit all of this — they are passive.

Authoring a Widget

Render CnWidgetWrapper as the component's root and put the content inside its default slot:

<template>
<CnWidgetWrapper
:title="title"
:widget-id="widgetId"
:documentation-url="documentationUrl">
<template #actions><!-- inline buttons (left of the menu) --></template>
<template #action-items><!-- extra menu items --></template>
<!-- widget content -->
</CnWidgetWrapper>
</template>

In the JSON manifest grid, the entry-level title and documentationUrl flow into the widget so the chrome renders them without per-widget props:

{ "widgetKey": "related", "title": "Related", "gridWidth": 12 }

Authoring a Card

Use CnDetailCard (or a card primitive like CnKpiGrid / CnStatsBlock). No Actions menu:

<template>
<CnDetailCard :title="title" :collapsible="true">
<!-- single-headline / read-only content -->
</CnDetailCard>
</template>

The default detail page

A schema-driven type: "detail" page renders the data Widget with the related Widget beneath it. Object metadata is a Card — it is reached on demand from the data Widget's Metadata action item (a small modal), rather than occupying a permanent slot. See CnDetailPage, CnObjectDataWidget, CnRelatedObjectsWidget and CnObjectMetadataModal.