AppHome
<caos-app-home>
an app’s landing surface: what needs attention, gathered
May be placed on an app’s home page.
Inputs
Section titled “Inputs”| 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 |
Events
Section titled “Events”| 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.
Styling hooks
Section titled “Styling hooks”| 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. |
Methods
Section titled “Methods”Nothing else drives this piece by calling it.
Readable state
Section titled “Readable state”Nothing on this piece can be read back.
Styling parts
Section titled “Styling parts”This piece exposes none of its insides to a stylesheet.
When to use it
Section titled “When to use it”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”.
What to use instead
Section titled “What to use instead”- 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.
Accessibility
Section titled “Accessibility”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.
Examples
Section titled “Examples”The landing surface
Section titled “The landing surface”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" } ] }};Before the figures land
Section titled “Before the figures land”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>Nothing on this workspace to measure
Section titled “Nothing on this workspace to measure”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": [] }};A clear desk
Section titled “A clear desk”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 waiting list could not be read
Section titled “The waiting list could not be read”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": [] }};