Skip to content

AppHome

<caos-app-home>

an app’s landing surface: what needs attention, gathered

May be placed on an app’s home page.

Name Attribute Description Type Default Required
data property only Everything the surface shows: pending (the lead region — what is waiting on the reader), kpis (the headline tiles, each with a figure already formatted and an optional deep link), pipeline (the breakdown panel’s stages and its won/lost footer, or null when this workspace has no lifecycle to break down), and recent (the newest records with their status). Leave it unset while the read is in flight — that draws the loading state, not an empty one. pending is a tagged answer rather than a list that might be empty: ok with its items, ok with none (the reader genuinely has nothing outstanding), unreadable (the read failed — say so rather than drawing “nothing is waiting on you”), or no-lifecycle (nothing in this workspace keeps a status, so the question does not arise). json none declared Optional
userRole user-role, markup only The role named in the hero kicker, after “Overview ·”. The hero greets by role, not by name, so the surface takes no name. text Admin Optional
Name When it fires What it carries
home-ask The “Ask …” button in the hero is pressed. Opening the assistant is the host’s job. nothing
home-navigate A tile, an activity row or a panel’s “see them all” link is chosen. Only elements given an href are drawn as controls at all, so a figure with nowhere to go cannot be pressed. { href } — where the host was asked to go.

Nothing goes inside this piece.

Design token What it controls
--caos-color-accent The accented subject word in the headline and the stage bars.
--caos-color-accent-fill The Ask button, and –caos-color-accent-fill-hover under the pointer.
--caos-color-accent-contrast The label on the Ask button. White by default; a brand with a pale accent sets a darker ink here.
--caos-color-bg The ground the surface is drawn on.
--caos-color-surface The fill of the tiles and the panels.
--caos-color-border The edges of the tiles, the panels and the activity rows.
--caos-text-kpi The size of the headline figure on a tile.
--caos-font-display The typeface of the hero headline and the figures.
--caos-font-mono The typeface of the kicker and the panel links.
--caos-color-success The activity dot for a success-toned status, and the Won figure.
--caos-color-warning The activity dot for a warning-toned status.
--caos-color-info The activity dot for an info-toned status.
--caos-color-danger The activity dot for a danger-toned status, and the Lost figure.
--brand-ai-app-name What the hero button offers to ask. Read in script rather than from the stylesheet, and re-read live when data-brand changes on the document. Unset ⇒ the name the platform ships in its own token floor, so a deployment that customises nothing still reads. EMPTY ⇒ “Ask the assistant”: this one labels a button, and a button with no name cannot be identified by sight or by a screen reader, so a cleared name degrades to what the button DOES rather than putting the platform’s own brand back on a surface an org has deliberately unbranded.
--brand-wordmark The accented subject word in the headline — the name of the deployment. Read the same way, rendered exactly as the token carries it — no re-casing, so it reads the same here as it does everywhere else the token is drawn. Unset ⇒ the wordmark the platform ships in its own token floor. EMPTY ⇒ no company name anywhere in the hero, the same answer the topbar gives: an org may clear its Company Name and the surface must let it. The headline is a sentence, so the clause goes with the name rather than leaving a gap after it — it reads “What needs your attention.” and names nobody.

Nothing else drives this piece by calling it.

Nothing on this piece can be read back.

This piece exposes none of its insides to a stylesheet.

An app should open on something that says what needs attention rather than on a list a person has to interpret. Reach for it when there is more than one object worth watching and a person’s first question is “what changed and what is open” rather than “find me this record”.

  • PageHeader — You are heading an ordinary surface rather than replacing it. That is a header you build a page under; this is the page.
  • StatTile — You want ONE figure, placed on a page you are assembling yourself. This surface draws its own tiles from its data and gives you nowhere to put anything else.
  • DataTable — The landing surface should be the records themselves — an app with one object and no lifecycle worth summarising is better served by its list.
  • KanbanBoard — The pipeline is the thing being worked rather than being reported. That one is a place to move records between stages; the breakdown panel here only says how much sits in each.

Everything that leads somewhere is drawn as a real button and everything that does not is drawn as plain text, so a keyboard reaches exactly the things that can be pressed and nothing that cannot — a tile given no href is not focusable. The panel headings are real <h2>s under the hero’s <h1>, so the surface has a document outline. Left to the author: the figures arrive already worded, so their wording IS what is announced — “6” alone reads as nothing, “$124K open” reads as something. The activity status is a coloured dot with its label beside it, and the label is what carries the meaning, so a status supplied with an empty label leaves colour as the only signal.

The whole surface with live figures — tiles, a $-weighted breakdown with its won/lost footer, and the records that moved most recently.

The page around it: The page around it owns navigation: it listens for the two events this surface raises and reports what it was asked to do.

{
"id": "example",
"section": "The landing surface",
"columns": 1,
"items": [
{
"id": "app_home_1",
"type": "component",
"key": "app_home",
"inputs": {
"user_role": "Manager",
"data": {
"pending": {
"status": "ok",
"title": "Waiting on you",
"items": [
{
"id": "p9",
"label": "PRO-2210 · Harlow Marine",
"objectLabel": "Proposal",
"statusLabel": "Presented",
"tone": "info",
"waitingLabel": "Updated 3 months ago",
"href": "/app/proposals/p9"
},
{
"id": "o7",
"label": "ORD-4460 · Pike & Sons",
"objectLabel": "Order",
"statusLabel": "Activated",
"tone": "warning",
"waitingLabel": "Updated 12 days ago",
"href": "/app/orders/o7"
},
{
"id": "p4",
"label": "PRO-2231 · Ashcroft Group",
"objectLabel": "Proposal",
"statusLabel": "Draft",
"tone": "muted",
"waitingLabel": "Updated yesterday",
"href": "/app/proposals/p4"
}
]
},
"kpis": [
{
"label": "Open orders",
"value": "6",
"sub": "$124K open",
"href": "/app/orders"
},
{
"label": "Won this quarter",
"value": "$86.9K",
"sub": "4 won · 1 lost"
},
{
"label": "Win rate",
"value": "80%",
"sub": "4 of 5 decided"
}
],
"pipeline": {
"title": "Pipeline by stage",
"linkLabel": "All orders",
"linkHref": "/app/orders",
"stages": [
{
"status": "draft",
"label": "Draft",
"count": 3,
"value": 24000
},
{
"status": "review",
"label": "In review",
"count": 2,
"value": 40000
},
{
"status": "scheduled",
"label": "Scheduled",
"count": 1,
"value": 60000
}
],
"winLoss": {
"won": {
"count": 4,
"value": 86900
},
"lost": {
"count": 1,
"value": 12400
}
}
},
"recent": {
"title": "Recent activity",
"rows": [
{
"id": "o1",
"label": "ORD-4471 · Meridian Supply",
"sub": "Updated 2 hours ago",
"status": "review",
"statusLabel": "In review",
"tone": "info",
"href": "/app/orders/o1"
},
{
"id": "o2",
"label": "ORD-4472 · Calder & Finch",
"sub": "Updated yesterday",
"status": "won",
"statusLabel": "Won",
"tone": "success",
"href": "/app/orders/o2"
}
]
}
}
}
}
]
}
<caos-app-home id="appHome1" user-role="Manager"></caos-app-home>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const appHome1 = document.getElementById('appHome1');
appHome1.data = {
"pending": {
"status": "ok",
"title": "Waiting on you",
"items": [
{
"id": "p9",
"label": "PRO-2210 · Harlow Marine",
"objectLabel": "Proposal",
"statusLabel": "Presented",
"tone": "info",
"waitingLabel": "Updated 3 months ago",
"href": "/app/proposals/p9"
},
{
"id": "o7",
"label": "ORD-4460 · Pike & Sons",
"objectLabel": "Order",
"statusLabel": "Activated",
"tone": "warning",
"waitingLabel": "Updated 12 days ago",
"href": "/app/orders/o7"
},
{
"id": "p4",
"label": "PRO-2231 · Ashcroft Group",
"objectLabel": "Proposal",
"statusLabel": "Draft",
"tone": "muted",
"waitingLabel": "Updated yesterday",
"href": "/app/proposals/p4"
}
]
},
"kpis": [
{
"label": "Open orders",
"value": "6",
"sub": "$124K open",
"href": "/app/orders"
},
{
"label": "Won this quarter",
"value": "$86.9K",
"sub": "4 won · 1 lost"
},
{
"label": "Win rate",
"value": "80%",
"sub": "4 of 5 decided"
}
],
"pipeline": {
"title": "Pipeline by stage",
"linkLabel": "All orders",
"linkHref": "/app/orders",
"stages": [
{
"status": "draft",
"label": "Draft",
"count": 3,
"value": 24000
},
{
"status": "review",
"label": "In review",
"count": 2,
"value": 40000
},
{
"status": "scheduled",
"label": "Scheduled",
"count": 1,
"value": 60000
}
],
"winLoss": {
"won": {
"count": 4,
"value": 86900
},
"lost": {
"count": 1,
"value": 12400
}
}
},
"recent": {
"title": "Recent activity",
"rows": [
{
"id": "o1",
"label": "ORD-4471 · Meridian Supply",
"sub": "Updated 2 hours ago",
"status": "review",
"statusLabel": "In review",
"tone": "info",
"href": "/app/orders/o1"
},
{
"id": "o2",
"label": "ORD-4472 · Calder & Finch",
"sub": "Updated yesterday",
"status": "won",
"statusLabel": "Won",
"tone": "success",
"href": "/app/orders/o2"
}
]
}
};

The same surface with no data set — four placeholder tiles and three waiting panels, holding the page’s real proportions so nothing moves under the reader when the figures arrive.

{
"id": "example",
"section": "Before the figures land",
"columns": 1,
"items": [
{
"id": "app_home_1",
"type": "component",
"key": "app_home",
"inputs": {
"user_role": "Manager"
}
}
]
}
<caos-app-home user-role="Manager"></caos-app-home>

The read settled and genuinely found no object worth a tile — no app placed here declares a lifecycle. This is the shape a workspace with no sales-shaped (or any status-bearing) app installed actually gets: a real page saying so, never an empty grid of zeroed sales tiles.

{
"id": "example",
"section": "Nothing on this workspace to measure",
"columns": 1,
"items": [
{
"id": "app_home_1",
"type": "component",
"key": "app_home",
"inputs": {
"user_role": "Manager",
"data": {
"pending": {
"status": "no-lifecycle"
},
"kpis": [],
"pipeline": null,
"recent": {
"title": "Recent activity",
"rows": []
}
}
}
}
]
}
<caos-app-home id="appHome1" user-role="Manager"></caos-app-home>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const appHome1 = document.getElementById('appHome1');
appHome1.data = {
"pending": {
"status": "no-lifecycle"
},
"kpis": [],
"pipeline": null,
"recent": {
"title": "Recent activity",
"rows": []
}
};

The workspace does keep a lifecycle and the read succeeded — the reader simply has nothing outstanding. This is a real answer and says so, which is why pending is a tagged state rather than a list: an empty list and a failed read would otherwise draw the same page.

{
"id": "example",
"section": "A clear desk",
"columns": 1,
"items": [
{
"id": "app_home_1",
"type": "component",
"key": "app_home",
"inputs": {
"user_role": "Manager",
"data": {
"pending": {
"status": "ok",
"title": "Waiting on you",
"items": []
},
"kpis": [
{
"label": "Open orders",
"value": "0",
"sub": "nothing open"
}
],
"pipeline": null,
"recent": {
"title": "Recent activity",
"rows": []
}
}
}
}
]
}
<caos-app-home id="appHome1" user-role="Manager"></caos-app-home>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const appHome1 = document.getElementById('appHome1');
appHome1.data = {
"pending": {
"status": "ok",
"title": "Waiting on you",
"items": []
},
"kpis": [
{
"label": "Open orders",
"value": "0",
"sub": "nothing open"
}
],
"pipeline": null,
"recent": {
"title": "Recent activity",
"rows": []
}
};

The figures landed and the reader’s own outstanding work did not. The surface says that rather than drawing an empty list, because “nothing is waiting on you” is the most confident way this page could be wrong.

{
"id": "example",
"section": "The waiting list could not be read",
"columns": 1,
"items": [
{
"id": "app_home_1",
"type": "component",
"key": "app_home",
"inputs": {
"user_role": "Manager",
"data": {
"pending": {
"status": "unreadable"
},
"kpis": [
{
"label": "Open orders",
"value": "6",
"sub": "$124K open"
}
],
"pipeline": null,
"recent": {
"title": "Recent activity",
"rows": []
}
}
}
}
]
}
<caos-app-home id="appHome1" user-role="Manager"></caos-app-home>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const appHome1 = document.getElementById('appHome1');
appHome1.data = {
"pending": {
"status": "unreadable"
},
"kpis": [
{
"label": "Open orders",
"value": "6",
"sub": "$124K open"
}
],
"pipeline": null,
"recent": {
"title": "Recent activity",
"rows": []
}
};