- Published on
ACE Card View vs Quick View — Design Patterns
The biggest mistake developers make with their first ACE is treating the card view as a miniature web part — stuffing it with too much information, too many buttons, and too much text.
The card view is a glanceable status indicator, not a compact page. The quick view is where detail and action live. Getting this boundary right is the difference between an ACE users check every morning and one they immediately hide from their dashboard.
This article covers the design patterns for each view, with Adaptive Card JSON templates for the most common scenarios.
🗺️ The Design Principle: One Glance, One Action
The Viva Connections Dashboard is designed for mobile-first, ambient consumption. Users see it while walking to a meeting, between tasks, or on their phone during commute. The card view must answer one question in under two seconds:
"Do I need to act on this?"
If the answer is yes, the card shows a count or status and a single button to open the quick view. If the answer is no, the card confirms that with a green indicator or a "nothing new" message. The quick view provides the list, form, or detail needed to act.
🧩 Card View Patterns
Pattern 1 — Count Badge (Approval / Task / Notification)
Best for: approvals, tasks, tickets, unread messages — anything with a count of pending items.
public get data(): IPrimaryTextCardParameters {
const count = this.state.pendingApprovals.length;
return {
primaryText: count === 0
? 'All caught up'
: `${count} Pending Approval${count !== 1 ? 's' : ''}`,
description: count === 0
? 'No items waiting for your review'
: `Oldest: ${this.state.pendingApprovals[0]?.title ?? ''}`,
title: 'Approvals'
};
}
Rule: Show the count prominently. Show the most urgent item's title as the description. If count is 0, show a positive confirmation — not a blank card.
Pattern 2 — Status Indicator (Health / System / Availability)
Best for: system health, room availability, shift status, connection status.
public get data(): IPrimaryTextCardParameters {
const status = this.state.systemStatus; // 'Operational' | 'Degraded' | 'Down'
const statusMap = {
'Operational': { text: '✓ All Systems Operational', desc: 'Last checked 5 min ago' },
'Degraded': { text: '⚠ Partial Outage', desc: 'Some services affected' },
'Down': { text: '✗ Service Disruption', desc: 'IT team notified' }
};
return {
primaryText: statusMap[status]?.text ?? 'Status Unknown',
description: statusMap[status]?.desc ?? ''
};
}
Rule: Use plain language. Users should not need to interpret a status code — "All Systems Operational" beats "STATUS_OK".
Pattern 3 — Next Item (Calendar / Schedule / Due Date)
Best for: next meeting, next shift, next deadline, next event.
public get data(): IPrimaryTextCardParameters {
const next = this.state.nextMeeting;
if (!next) {
return {
primaryText: 'No upcoming meetings',
description: 'Your calendar is clear for today'
};
}
return {
primaryText: next.subject,
description: `${next.startTime} — ${next.location ?? 'No location'}`
};
}
Rule: Show the next item only — not a list. The quick view shows the list. One item in the card creates focus; a list creates overwhelm.
🧩 Quick View Patterns
Pattern 1 — Numbered List with Actions
The most common quick view: a list of items, each with a primary action button.
QuickViewTemplate.json
{
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Pending Approvals",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "Container",
"$data": "${items}",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "${title}",
"weight": "Bolder",
"wrap": true
},
{
"type": "TextBlock",
"text": "Requested by ${requestedBy} · ${daysAgo} days ago",
"isSubtle": true,
"size": "Small",
"spacing": "None"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.OpenUrl",
"title": "Review",
"url": "${url}",
"style": "positive"
}
]
}
]
}
]
}
],
"separator": true
}
]
}
Pattern 2 — Summary + Detail Toggle
For items where the user needs a summary first, then can expand to details:
{
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "IT Service Tickets",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "Container",
"$data": "${tickets}",
"items": [
{
"type": "TextBlock",
"text": "#${id} — ${title}",
"weight": "Semibold",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{ "title": "Status", "value": "${status}" },
{ "title": "Priority", "value": "${priority}" },
{ "title": "Opened", "value": "${openedDate}" }
]
}
],
"separator": true,
"selectAction": {
"type": "Action.OpenUrl",
"url": "${url}"
}
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Open Service Portal",
"url": "${portalUrl}"
}
]
}
Pattern 3 — Confirmation Form
For quick actions that need one confirmation field — approving a request, updating a status, submitting a brief response:
{
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Approve Leave Request",
"weight": "Bolder",
"size": "Medium"
},
{
"type": "FactSet",
"facts": [
{ "title": "Employee", "value": "${employeeName}" },
{ "title": "Dates", "value": "${leaveDates}" },
{ "title": "Type", "value": "${leaveType}" }
]
},
{
"type": "Input.Text",
"id": "comments",
"placeholder": "Add a comment (optional)",
"isMultiline": true
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Approve",
"data": { "action": "approve", "itemId": "${itemId}" },
"style": "positive"
},
{
"type": "Action.Submit",
"title": "Reject",
"data": { "action": "reject", "itemId": "${itemId}" },
"style": "destructive"
}
]
}
Handling the submit action in the quick view class:
public async onAction(action: IActionArguments): Promise<void> {
if (action.type === 'Submit') {
const { actionId, itemId, comments } = action.data;
if (actionId === 'approve') {
await this._approvalService.approve(itemId, comments);
// Navigate back and refresh state
this.quickViewNavigator.close();
await this.adaptiveCardExtensionContext.setState(
await this._approvalService.getPendingApprovals()
);
}
}
}
📐 Card View Sizing Guidelines
The Viva Connections Dashboard supports three card sizes:
| Size | Use case |
|---|---|
| Medium (default) | Most ACEs — sufficient for a count + description + one button |
| Large | When the quick view equivalent is too heavy — show a list of 3 items directly in the card |
| Small | Status-only cards — single indicator, no description, no button |
Declare supported sizes in the manifest:
{
"preconfiguredEntries": [
{
"groupId": "...",
"group": { "default": "Cards" },
"title": { "default": "Approvals" },
"description": { "default": "Pending approvals card" },
"officeFabricIconFontName": "Checkmark",
"cardSize": "Medium",
"supportedCardSizes": ["Medium", "Large"]
}
]
}
✅ Summary
- The card view answers one question: "Do I need to act?" — count, status, or next item only.
- The quick view is where lists, detail, and forms live — users come here to act, not just to read.
- Show a positive "all clear" state when there are no pending items — a blank or empty card confuses users.
- Use
ColumnSetin quick view templates to align action buttons to the right of each list item. Action.Submithandles in-card form submissions;Action.OpenUrlopens links in a new browser tab.- Support at least Medium and Large card sizes for flexibility — admins configure the size per dashboard layout.
Happy coding!
Author
Ravichandran@Hi_Ravichandran
