Design system

Recommendation card

One recommendation on Home, as a card in the grid or a row in the list, with what it is, what it says, how soon and where.

import { RecCard, RecRow } from "@/pages/harness/HomePage";

Home shows the first six recommendations that are waiting for a decision, either as a grid of cards or as a list. RecCard and RecRow are the same recommendation in those two layouts. Both are a single button that opens the recommendation on the Recommendations page, and both show the same four parts:

  1. Type: what the recommendation makes or changes, with an icon in --color-primary-500.
  2. Title and basis: what it says, and the evidence in one or two lines.
  3. Urgency: how soon to decide, as coloured text with an icon.
  4. Channel: the ad platform it acts on, with its source icon. Left out when the recommendation has no platform.

Card

<div className="grid grid-cols-3 gap-4 items-stretch">
  {top.map((r) => (
    <RecCard key={r.id} rec={r} onOpen={() => navigate(`/recommendations?rec=${r.id}`)} />
  ))}
</div>

Home puts the cards in three columns, 16px apart. The example uses two to fit this page. items-stretch keeps cards in a row the same height, and the footer is pushed to the bottom of each one.

PartStyle
CardWhite, 16px padding, 12px between parts, 8px radius, --color-grey-100 border. On hover the border turns --color-grey-200 and a soft shadow appears.
Type12px medium, --text-secondary, 15px icon in --color-primary-500.
Title14px medium, --text-primary, one line. When it is cut short, hovering it shows the whole title.
Basis12px, --text-secondary, at most two lines.
Footer12px top padding above a --color-grey-100 rule. Urgency on the left, channel on the right with a 13px icon.

List row

The list view is a table: a 36px header row and one 60px row per recommendation, with the same columns in both.

const LIST_COLS = "168px minmax(0,1fr) 150px 116px 24px";

<div className="overflow-hidden rounded-lg border border-solid border-[var(--color-grey-100)] bg-white">
  <div
    className="grid items-center gap-4 h-9 px-4 bg-[var(--color-grey-50)] text-[12px] font-medium uppercase tracking-[0.04em] text-[var(--text-muted)]"
    style={{ gridTemplateColumns: LIST_COLS }}
  >
    <span>Type</span><span>Recommendation</span><span>Channel</span><span>When</span><span />
  </div>
  {top.map((r) => (
    <RecRow key={r.id} rec={r} onOpen={() => navigate(`/recommendations?rec=${r.id}`)} />
  ))}
</div>

The header row is not part of RecRow, and LIST_COLS is not exported from HomePage.jsx. If you use RecRow anywhere else, copy the column template exactly, or the header and the rows will not line up.

Each row is at least 60px tall, with 16px side padding and a --color-grey-100 rule on top. Title and basis are one line each. On hover the row turns --color-grey-50 and a 13px arrow appears in the last column.

Urgency

rec.urgencyLabelIconColour
"act-now"Act nowLightning#E11D48
"this-week"This weekWarning#B45309
"monitor", or anything elseThis monthEye#1D4ED8

These three colours are written into HomePage.jsx as hex values and are not product tokens. The Recommendations page shows the same urgencies through its own .rec-card-status classes and calls "monitor" "Next run", and src/mocks/recommendations.js calls it "Monitor". One urgency has three names today.

Type

The type comes from the recommendation's fields, checked in this order:

ConditionTypeIcon
rec.draftPage is setLanding pageLayout
rec.draftCreative is setAd creativeImageSquare
rec.workflowId === "paid-media-roi"DashboardSquaresFour
rec.type === "handoff"MemoNote
Anything elseCampaign changeSlidersHorizontal

These are the icons the prompts under the Home composer use, so a "Landing page" card and the "Demo page for the Q4 campaign" prompt share a mark. The MAKES map also has Report (FileText), but no recommendation is given that type.

<RecCard rec={{ ...rec, draftPage: { name: "Forecast review demo page" } }} onOpen={open} />
<RecCard rec={{ ...rec, draftCreative: { name: "Retargeting ad refresh" } }} onOpen={open} />

Every recommendation from the paid-media-roi workflow shows as Dashboard, including campaign changes such as Raise weekly budget on "G_Search_Brand_Core", as in the grid above. The workflow builds a dashboard, but the recommendation itself is a campaign change.

Long text

The title stays on one line and the basis stops at two. The card keeps its height, so a row of cards stays even.

<RecCard rec={rec} onOpen={open} />

Channel

rec.platform is looked up with platformOf from src/mocks/agentWorkflows.js: "google-search" shows Google Ads, "linkedin" and "linkedin-web" show LinkedIn Ads, and "meta" shows Meta Ads. Any other id is shown as written. With no platform, the card leaves the right of the footer empty and the row leaves the Channel column empty.

Props

Both components take the same props.

PropTypeDefaultNotes
recobjectA recommendation as /api/recommendations returns it. Reads title, basis, urgency, platform, type, workflowId, draftPage and draftCreative.
onOpen() => voidCalled on click. Home navigates to /recommendations?rec=<id>.

Accessibility

  • Each card and row is one native <button type="button">, so it is reachable with Tab and opens with Enter or Space. Its accessible name is all of its text: type, title, basis, urgency and channel.
  • There is no focus style of its own; it relies on the browser's focus ring. The row's arrow appears on hover only, not on keyboard focus.
  • The full-title tooltip on a card opens on hover of the title. Keyboard users get the full title from the button's name instead.
  • Urgency is shown with a label as well as a colour, so it does not rely on colour alone.

Do and don't

  • Do use these for a short list of recommendations that each open somewhere else. On the Recommendations page itself, the queue uses its own QueueRow.
  • Do keep the order of parts. People compare cards by scanning the same place on each.
  • Don't put buttons inside a card. The whole card is already a button, and a button inside a button is invalid HTML. Decisions are made on the Recommendations page.
  • Don't use RecCard as a general card. For other content, use Card.

Where it is used

FileUse
src/pages/harness/HomePage.jsxThe Recommendations section under the Home composer: up to six cards in a three-column grid, or the same six as list rows. The choice is saved in localStorage under home-recs-view.

Both functions are defined in HomePage.jsx and exported from it for these docs. Nothing else imports them.

On this page