Component library

Showing 21 components in the shared kit.

Narrative

  • <Lead>

    sharedContentNarrative

    Opening paragraph of a section. Larger and slightly muted, sets the tone.

    When to use: Use exactly once per section, immediately after the section's H1, to summarise the section's headline finding in 1–2 sentences.

    Preview

    Across two days with Acme's operations and finance teams, we mapped twelve candidate automation use cases. Six are recommended for the first wave.

    PropTypeNotes
    childrenReactNodeRequired. The opener prose.
    classNamestringOptional. Tailwind classes appended to the wrapper.

    Examples

    <Lead>
        Across two days with Acme's operations and finance teams, we mapped
        twelve candidate automation use cases. Six are recommended for the
        first wave.
    </Lead>
  • <Callout>

    sharedContentStatementNarrative

    Generic emphasis box for asides, important context, or warnings.

    When to use: Use when text needs visual separation from surrounding prose but isn't a finding or recommendation. Common: 'assumptions to validate', 'what we mean by X', 'next steps'.

    Preview

    PropTypeNotes
    tone"info" | "success" | "warning" | "danger"Optional. Default: info. Colour and icon.Values: info, success, warning, dangerExample: warning
    titlestringOptional. Optional headline.Example: Two assumptions to validate in week 1
    childrenReactNodeRequired. Callout body.

    Examples

    <Callout tone="warning" title="Two assumptions to validate in week 1">
        The projection above assumes the ERP middleware can be exposed within
        the first three weeks, and that the AP team is willing to triage
        exceptions in a Lunnoa queue rather than email.
    </Callout>
  • <Quote>

    sharedQuoteNarrative

    Pull-quote from an interview, with attribution.

    When to use: Use for high-signal, verbatim statements that capture how stakeholders actually framed a problem. One quote per finding is common.

    Preview

    We do the same five-step approval on every supplier invoice. The five steps make sense. Doing them all by hand five hundred times a month does not.

    Maria Fernandes, Head of Finance
    PropTypeNotes
    attributionstringOptional. Person's name.Example: Maria Fernandes
    rolestringOptional. Their role and/or department.Example: Head of Finance
    childrenReactNodeRequired. The quoted text. Do not include surrounding quotation marks — the component adds them.

    Examples

    <Quote attribution="Maria Fernandes" role="Head of Finance">
        We do the same five-step approval on every supplier invoice. The five
        steps make sense. Doing them all by hand five hundred times a month
        does not.
    </Quote>
  • <TeamMember>

    sharedListContentNarrative

    One team member tile with avatar, name, role, department, and optional affiliation badge.

    When to use: Always inside `<TeamGrid>`. Use for delivery teams, workshop squads, or mixed client/Lunnoa rosters (up to six per grid).

    Often with: TeamGridPersonCard

    Preview

    Maria Fernandes

    Head of Finance

    Finance

    Client

    PropTypeNotes
    namestringRequired. Full name.Example: Maria Fernandes
    rolestringOptional. Job title.Example: Head of Finance
    departmentstringOptional. Team or department.Example: Finance
    avatarUrlstringOptional. Optional headshot URL.
    initialsstringOptional. Avatar fallback when no image; defaults to initials from name.Example: MF
    affiliation"Client" | "Lunnoa" | "Partner"Optional. Optional org badge for mixed teams.Values: Client, Lunnoa, PartnerExample: Client

    Examples

    <TeamMember name="Maria Fernandes" role="Head of Finance" department="Finance" affiliation="Client" initials="MF" />
  • <TeamGrid>

    sharedListContentNarrative

    Grid of up to six TeamMember cards with optional heading.

    When to use: Delivery team, workshop squad, or steering group in document reports. Use 3 columns by default; 2 for wider cards.

    Often with: TeamMemberPersonCard

    Preview

    Wave 1 delivery team

    Client sponsors and Lunnoa delivery leads

    Maria Fernandes

    Head of Finance

    Finance

    Client

    James Okafor

    Operations Manager

    Operations

    Client

    Elena Vasquez

    Engagement lead

    Lunnoa

    Lunnoa

    PropTypeNotes
    titlestringOptional. Section heading above the grid.
    descriptionstringOptional. Subtitle or roster note.
    columns2 | 3Optional. Default: 3. Desktop column count.Values: 2, 3Example: 3
    childrenReactNodeRequired. One to six <TeamMember> elements.

    Examples

    <TeamGrid title="Wave 1 delivery team" description="Client sponsors and Lunnoa delivery leads">
      <TeamMember name="Maria Fernandes" role="Head of Finance" department="Finance" affiliation="Client" initials="MF" />
      <TeamMember name="James Okafor" role="Operations Manager" department="Operations" affiliation="Client" initials="JO" />
      <TeamMember name="Elena Vasquez" role="Engagement lead" department="Lunnoa" affiliation="Lunnoa" initials="EV" />
    </TeamGrid>
  • <TwoColumn>

    sharedSplitContentNarrative

    Side-by-side blocks. Stacks vertically on mobile.

    When to use: Use to balance two related blocks (e.g., two quotes, two side notes). For 'before vs. after' use BeforeAfter instead.

    Preview

    Maria Fernandes, Head of Finance
    James Okafor, Operations Manager
    PropTypeNotes
    reversebooleanOptional. Default: false. Swap column order on desktop.
    childrenReactNodeRequired. Two children, each rendered as one column.

    Examples

    <TwoColumn>
        <Quote attribution="Maria Fernandes" role="Head of Finance">…</Quote>
        <Quote attribution="James Okafor" role="Operations Manager">…</Quote>
    </TwoColumn>
  • <SectionDivider>

    sharedChapterNarrative

    Visual divider between sub-sections, optionally labelled.

    When to use: Use sparingly to mark a clear topic shift inside one section, especially before participant lists or appendix-style content.

    Preview


    Contributors
    PropTypeNotes
    labelstringOptional. Small label centred on the divider.Example: Contributors

    Examples

    <SectionDivider label="Contributors" />

Dashboard

  • <KPI>

    sharedStatsDashboard

    Single metric tile with optional trend indicator.

    When to use: Use for headline numbers (manual hours, error rate, ROI). Always inside a <KPIGrid>.

    Often with: KPIGrid

    Preview

    Manual hours / week
    142
    Across operations, finance and HR
    PropTypeNotes
    labelstringRequired. Short metric label.Example: Manual hours / week
    valueReactNodeRequired. The displayed value.Example: 142
    trend"up" | "down" | "flat" | "baseline"Optional. Default: baseline. Movement direction. 'baseline' means this is a starting measurement.Values: up, down, flat, baselineExample: down
    trendValuestringOptional. Magnitude of the trend.Example: −42%
    isPositiveDirection"higher" | "lower"Optional. Default: lower. Whether higher is better. Affects whether up/down arrow is green or red.Values: higher, lowerExample: lower
    descriptionstringOptional. Optional supporting line beneath the value.Example: Across operations, finance and HR

    Examples

    <KPI label="Manual hours / week" value="142" description="Across operations, finance and HR" trend="baseline" />
  • <KPIGrid>

    sharedStatsDashboard

    Grid of KPI tiles. Responsive: collapses to 1 column on mobile.

    When to use: Wrap 2–4 KPIs together. Place at the top of a section under '## At a glance'. In SEO reports, typical KPIs are organic sessions, keywords in top 10, referring domains, and share of non-branded traffic.

    Often with: KPI

    Preview

    Use cases identified
    12
    First-wave candidates
    6
    Hours saved / month
    380
    −42% manual effort
    PropTypeNotes
    columns2 | 3 | 4Optional. Default: 4. Desktop column count.Values: 2, 3, 4Example: 3
    childrenReactNodeRequired. One or more <KPI> elements.

    Examples

    <KPIGrid columns={3}>
        <KPI label="Use cases identified" value="12" />
        <KPI label="First-wave candidates" value="6" />
        <KPI label="Hours saved / month" value="380" trend="down" trendValue="−42% manual effort" isPositiveDirection="lower" />
    </KPIGrid>
  • <MaturityRadar>

    sharedChartContentDashboard

    Radar chart of dimensions (axes) vs. maturity score (radial). Optional target overlay.

    When to use: Use to show a multi-dimensional capability assessment, typically in 'current state' or 'executive summary' sections. Works for SEO maturity too (e.g. Technical, Content, Authority, Measurement, Governance as axes), where the pillar bars of <PillarScoreCard> would be too coarse.

    Preview

    Acme automation maturity — current vs. 6-month target

    Scores from 11 stakeholder interviews using the Lunnoa maturity framework (1 = ad hoc, 5 = optimised).

    Current Target
    PropTypeNotes
    dimensionsstring[]Required. Axis labels, in order.Example: ["Data", "Tooling", "Process", "Governance", "Skills"]
    currentnumber[]Required. Current scores, same order as dimensions.Example: [3, 2, 3, 2, 2]
    targetnumber[]Optional. Optional target scores, drawn as a dashed overlay.
    maxnumberOptional. Default: 5. Maximum value on the radial axis.
    titlestringOptional. Chart title.
    descriptionstringOptional. Subtitle / methodology note.

    Examples

    <MaturityRadar
        dimensions={["Data quality", "Tooling", "Process clarity", "Governance", "Automation skills"]}
        current={[3, 2, 3, 2, 2]}
        target={[4, 4, 4, 4, 3]}
        title="Acme automation maturity — current vs. 6-month target"
        description="Scores from 11 stakeholder interviews using the Lunnoa maturity framework (1 = ad hoc, 5 = optimised)."
    />
  • <MaturityHeatmap>

    sharedChartContentDataDashboard

    Stage-based maturity table (rows = dimensions, columns = levels). One highlighted cell per row.

    When to use: Use as an alternative to MaturityRadar when the maturity framework is stage-based (e.g. CMMI-style: ad hoc / repeatable / defined / managed / optimised).

    Preview

    Current vs 6-month target

    DimensionAd hocRepeatableDefinedManagedOptimised
    Data quality
    Tooling
    Current Target
    PropTypeNotes
    rowsstring[]Required. Row labels (dimensions).Example: ["Data quality", "Tooling", "Process clarity"]
    columnsstring[]Required. Column labels (maturity levels).Example: ["Ad hoc", "Repeatable", "Defined", "Managed", "Optimised"]
    currentboolean[][]Required. 2D array [row][col]. Each row should have at most one true cell.
    targetboolean[][]Optional. Optional target cells, drawn as a ring.
    titlestringOptional. Chart title.

    Examples

    <MaturityHeatmap
        rows={["Data quality", "Tooling"]}
        columns={["Ad hoc", "Repeatable", "Defined", "Managed", "Optimised"]}
        current={[[false, false, true, false, false], [false, true, false, false, false]]}
        target={[[false, false, false, true, false], [false, false, false, true, false]]}
        title="Current vs 6-month target"
    />
  • <OpportunityMatrix>

    sharedChartCompareContentDashboard

    Effort × impact scatter plot (effort increases left to right, impact increases bottom to top). Top-left (low effort, high impact) is the usual quick-win zone. Optional category (or name) tabs match the DataTable filter pattern.

    When to use: Use to visualise a portfolio of candidate use cases / opportunities scored on two dimensions. Typically immediately before the recommended use case grid. Also fits SEO analyses: plot keyword clusters or content opportunities as effort × impact, with categories like Technical, Content, and Authority as tabs.

    Preview

    Twelve use cases scored on effort and impact

    FinanceOperationsPeople

    Portfolio by function

    FinanceOperations
    PropTypeNotes
    items{ name: string; effort: number; impact: number; category?: string }[]Required. Each item is plotted as a dot. effort and impact are 1–5.
    titlestringOptional. Chart title.
    descriptionstringOptional. Subtitle.
    tabs{ id: string; label: string }[]Optional. Optional filter tabs (same pattern as DataTable). Include `{ id: "all", label: "All" }` first. Other tab ids slug-match `tabFilterKey` on each item (default: category names, e.g. `finance` for Finance).
    tabFilterKey"category" | "name"Optional. Default: category. Which item field tab ids match after slugify.Values: category, name

    Examples

    <OpportunityMatrix
        title="Twelve use cases scored on effort and impact"
        items={[
            { name: "Supplier invoice intake", effort: 2, impact: 5, category: "Finance" },
            { name: "Order acknowledgements", effort: 2, impact: 4, category: "Operations" },
            { name: "HR ticket triage", effort: 1, impact: 3, category: "People" }
        ]}
    />
    <OpportunityMatrix
        title="Portfolio by function"
        tabs={[
            { id: "all", label: "All" },
            { id: "finance", label: "Finance" },
            { id: "operations", label: "Operations" }
        ]}
        items={[
            { name: "Invoice intake", effort: 2, impact: 5, category: "Finance" },
            { name: "Order acknowledgements", effort: 2, impact: 4, category: "Operations" }
        ]}
    />
  • <RoadmapTimeline>

    sharedTimelineAgendaContentDashboard

    Vertical phased timeline with status badges per phase.

    When to use: Use in roadmap / delivery plan sections to sequence work across phases (typically 3–5 phases). In SEO reports, use it for the remediation and content roadmap (e.g. technical fixes → content production → authority building).

    Preview

    Wave 1 — 12-week delivery

    1. Phase 1 — Foundation
      Weeks 1–3
      • ERP middleware exposed
      • Lunnoa platform deployed
    2. Phase 2 — Build the three flows
      Weeks 4–8
      • HR triage live week 5
      • Invoice intake live week 8
    PropTypeNotes
    phases{ label: string; when: string; status?: 'done'|'active'|'planned'; items?: string[] }[]Required. Phases in chronological order.
    titlestringOptional. Section title for the timeline.
    descriptionstringOptional. Subtitle.

    Examples

    <RoadmapTimeline
        title="Wave 1 — 12-week delivery"
        phases={[
            { label: "Phase 1 — Foundation", when: "Weeks 1–3", status: "active", items: ["ERP middleware exposed", "Lunnoa platform deployed"] },
            { label: "Phase 2 — Build the three flows", when: "Weeks 4–8", status: "planned", items: ["HR triage live week 5", "Invoice intake live week 8"] }
        ]}
    />
  • <ComparisonBar>

    sharedChartCompareDashboard

    Horizontal grouped bar chart: one row per process, with a grey Before bar and a coloured After bar. Before/After legend is rendered automatically below the chart.

    When to use: Use to project the impact of a wave of automation across multiple processes. Most useful immediately after a use case grid. Put the process or use-case name in each row's `label` (Y-axis). Do not use Before/After wording such as "Manual today" or "After automation" in `label`; those states are already shown by bar colour and the chart legend. In SEO reports, use it to benchmark against a competitor per metric or to project measured baseline vs target (e.g. clicks per topic cluster).

    Often with: UseCaseGridBeforeAfter

    Preview

    Manual hours per month — before vs. projected after Wave 1

    BeforeAfter
    PropTypeNotes
    rows{ label: string; before: number; after: number }[]Required. One object per process being compared. Each row renders once on the Y-axis with two grouped bars (before, then after).
    rows[].labelstringRequired. Process or use-case name shown on the Y axis (e.g. "Supplier invoice intake"). Not the Before/After state.Example: Order acknowledgements
    rows[].beforenumberRequired. Current / baseline numeric value. Rendered as the grey bar.Example: 280
    rows[].afternumberRequired. Projected / proposed numeric value. Rendered as the coloured After bar.Example: 60
    unitstringOptional. Default: "". Suffix for tick labels and tooltips.Example: hrs
    isPositiveDirection"higher" | "lower"Optional. Default: lower. Whether smaller numbers are better. Drives the After bar colour.Values: higher, lowerExample: lower
    titlestringOptional. Chart title.
    descriptionstringOptional. Subtitle / methodology note.

    Examples

    <ComparisonBar
        title="Manual hours per month — before vs. projected after Wave 1"
        unit=" hrs"
        isPositiveDirection="lower"
        rows={[
            { label: "Supplier invoice intake", before: 280, after: 60 },
            { label: "Order acknowledgements", before: 110, after: 20 }
        ]}
    />
  • <DataTable>

    sharedDataListDashboard

    Card-style data grid with optional status tabs, sortable columns, status badges, row checkboxes (illustrative), filter affordance, and client-side pagination.

    When to use: Use for tabular report appendices (delivery lists, backlog queues, milestone trackers) where readers benefit from filtering and paging without leaving the report. Keep row counts modest for print; prefer `pageSize` 5–10. In SEO reports, use it for page-level appendices (top pages by clicks, redirect maps, content inventory); for keyword rankings prefer the purpose-built <KeywordRankingTable>.

    Preview

    Delivery activities

    Track your recent shipping activities

    SelectRouteStatus
    #324112ElectronicsAcme10 Apr 2028 2:15 pmBerlin–Milan$1,250.00Delivered

    Showing 1 to 1 of 1

    PropTypeNotes
    titlestringRequired. Table card title.Example: Delivery activities
    descriptionstringOptional. Subtitle under the title.Example: Track your recent shipping activities
    columns{ key: string; label: string; sortable?: boolean }[]Required. Column definitions; `key` matches keys on each row object.
    rowsRecord<string, string>[]Required. Row data; values are strings so they serialise cleanly inside MDX.
    tabs{ id: string; label: string }[]Optional. Optional filter tabs. Include `{ id: "all", label: "All" }` first for an unfiltered view. Other `id` values slug-match `tabFilterKey` on each row (e.g. status "In Transit" matches tab `in-transit`).
    tabFilterKeystringOptional. Default: status. Row field used when filtering by tab.
    badgeColumnKeystringOptional. Default: status. Column whose cells render as coloured status pills.
    showCheckboxesbooleanOptional. Default: true. Show a leading illustrative checkbox column.
    showFilterButtonbooleanOptional. Default: true. Show a secondary Filter button (illustrative; no sheet wired).
    filterButtonLabelstringOptional. Default: "Filter". Label for the filter button.
    pageSizenumberOptional. Default: 5. Rows per page; set `0` to disable pagination footer.

    Examples

    <DataTable
        title="Delivery activities"
        description="Track your recent shipping activities"
        tabs={[
            { id: "all", label: "All" },
            { id: "delivered", label: "Delivered" },
            { id: "in-transit", label: "In transit" },
            { id: "pending", label: "Pending" },
            { id: "processing", label: "Processing" }
        ]}
        columns={[
            { key: "orderId", label: "Order ID", sortable: true },
            { key: "category", label: "Category", sortable: true },
            { key: "company", label: "Company", sortable: true },
            { key: "arrival", label: "Arrival", sortable: true },
            { key: "route", label: "Route", sortable: false },
            { key: "price", label: "Price", sortable: true },
            { key: "status", label: "Status", sortable: false }
        ]}
        rows={[{ orderId: "#324112", category: "Electronics", company: "Acme", arrival: "10 Apr 2028 2:15 pm", route: "Berlin–Milan", price: "$1,250.00", status: "Delivered" }]}
    />
  • <SimpleDataTable>

    sharedDataDashboard

    Read-only text grid: a header row from string labels and body rows from string arrays. Bordered card, optional caption, optional zebra striping, optional bold emphasis on chosen body rows (totals, subtotals). No sorting, filters, or pagination.

    When to use: Use when you only need to present a small matrix (assumption register, RACI, scorecard, inventory snapshot) without the interactive chrome of `<DataTable>`. Prefer markdown tables for narrative prose; use this when MDX expressions make row data easier to maintain.

    Often with: DataTable

    Preview

    Session attendance (illustrative)
    NameTeamDay 1Day 2
    Alex ExampleOperationsYesYes
    Jordan ExampleITYesNo
    Hours by workstream (illustrative)
    WorkstreamManual hrs / week
    Invoice intake42
    Order acknowledgements28
    Total70
    PropTypeNotes
    columnsstring[]Required. Header labels in column order.Example: ["Role", "Name", "Availability"]
    rowsstring[][]Required. Each entry is one row: an array of cell strings aligned to `columns`.Example: [["Sponsor", "Alex Example", "Weekly"], ["Owner", "Jordan Example", "Daily"]]
    captionstringOptional. Optional title or context line above the table.
    stripedbooleanOptional. Default: true. Whether odd body rows use a subtle banded background.
    boldRowIndicesnumber[]Optional. 0-based indices of body rows to render with semibold text (e.g. `[0, 2]`). Indices outside the row range are ignored.Example: [1]
    boldLastRowbooleanOptional. Default: false. When true, semibold the last body row (typical for a totals or summary line). Combines with `boldRowIndices`.

    Examples

    <SimpleDataTable
        caption="Session attendance (illustrative)"
        columns={["Name", "Team", "Day 1", "Day 2"]}
        rows={[
            ["Alex Example", "Operations", "Yes", "Yes"],
            ["Jordan Example", "IT", "Yes", "No"]
        ]}
    />
    <SimpleDataTable
        caption="Hours by workstream (illustrative)"
        columns={["Workstream", "Manual hrs / week"]}
        rows={[
            ["Invoice intake", "42"],
            ["Order acknowledgements", "28"],
            ["Total", "70"]
        ]}
        boldLastRow
    />
  • <AnalyticsCard>

    sharedStatsDashboard

    Single-card analytics summary: title and subtitle, optional kebab affordance, a hero row (icon + label + large value), and a 2×2 grid of sub-metrics with tinted icon chips, values, and either a static caption or a success-styled trend line.

    When to use: Use for report readouts that mirror product analytics tiles (adoption, pipeline, programme health). Pair two cards inside `<TwoColumn>` for side-by-side comparisons. Keep copy factual; do not invent metrics.

    Preview

    User analytics

    Platform user insights

    Total users

    10,590

    Free users

    10,074

    95% of total users

    Paid users

    516

    +2.5% from last month

    New users

    1,200

    +5,238 from last month

    Most active

    480

    Weekly active

    PropTypeNotes
    titlestringRequired. Card title.Example: User analytics
    subtitlestringOptional. Muted line under the title.Example: Platform user insights
    heroIcon"users" | "folder" | "projects"Required. Icon in the hero row.Values: users, folder, projectsExample: users
    heroLabelstringRequired. Label beside the hero icon.Example: Total users
    heroValuestringRequired. Large headline figure.Example: 10,590
    statsAnalyticsCardStat[]Required. Exactly four objects (2×2 grid). Each stat: `label`, `value`, `icon` (users|folder|pie|trend|clock|target|percent|building), `iconTone` (brand|warning|success|indigo|emerald|rose|slate), optional `caption`, or `trendValue` + optional `trendSuffix` for a green trend line.
    showMenubooleanOptional. Default: true. Show the illustrative kebab button.

    Examples

    <AnalyticsCard
        title="User analytics"
        subtitle="Platform user insights"
        heroIcon="users"
        heroLabel="Total users"
        heroValue="10,590"
        stats={[
            { label: "Free users", value: "10,074", icon: "pie", iconTone: "indigo", caption: "95% of total users" },
            { label: "Paid users", value: "516", icon: "percent", iconTone: "emerald", trendValue: "+2.5%", trendSuffix: "from last month" },
            { label: "New users", value: "1,200", icon: "users", iconTone: "brand", trendValue: "+5,238", trendSuffix: "from last month" },
            { label: "Most active", value: "480", icon: "clock", iconTone: "warning", caption: "Weekly active" }
        ]}
    />
  • <UsageDonutCard>

    sharedChartDashboard

    Donut chart with centre headline figures, a breakdown list (avatar initials, title, subtitle, coloured dot and usage line), and an optional footer link.

    When to use: Use when a report needs a share-style visual (usage by vendor, cost by workstream, incidents by severity). Pass numeric `segments[].value` for slice proportions; `rows[].usageDisplay` is free-form text for the right column. Prefer 3–5 segments for legibility. In SEO reports, use it for traffic share by channel (organic, direct, paid, referral) or organic traffic share by site section.

    Preview

    API token usages

    13.5M

    2 450 keys

    • GPT

      2 API keys configured

      7M

      Tokens used

    • Gemini

      1 API key configured

      2.5M

      Tokens used

    • xAI

      2 API keys configured

      4.5M

      Tokens used

    PropTypeNotes
    titlestringRequired. Card title.Example: API token usages
    centerValuestringRequired. Large text in the donut centre.Example: 13.5M
    centerSubtextstringOptional. Muted line under the centre value.Example: 2 450 API keys
    segments{ id: string; value: number; color: "brand"|"purple"|"sky"|"fuchsia"|"emerald" }[]Required. Slice weights and semantic colours (maps to theme tokens).
    rows{ segmentId: string; title: string; subtitle: string; usageDisplay: string; avatarText?: string; usageCaption?: string }[]Required. One row per slice; `segmentId` must match a `segments[].id` for the dot colour.
    footerLabelstringOptional. Link text below the list.
    footerHrefstringOptional. URL for the footer link (internal or external).
    showMenubooleanOptional. Default: true. Show the illustrative kebab button.

    Examples

    <UsageDonutCard
        title="API token usages"
        centerValue="13.5M"
        centerSubtext="2 450 keys"
        segments={[
            { id: "gpt", value: 7, color: "brand" },
            { id: "gemini", value: 2.5, color: "purple" },
            { id: "xai", value: 4.5, color: "sky" }
        ]}
        rows={[
            { segmentId: "gpt", avatarText: "GP", title: "GPT", subtitle: "2 API keys configured", usageDisplay: "7M" },
            { segmentId: "gemini", avatarText: "Ge", title: "Gemini", subtitle: "1 API key configured", usageDisplay: "2.5M" },
            { segmentId: "xai", avatarText: "xA", title: "xAI", subtitle: "2 API keys configured", usageDisplay: "4.5M" }
        ]}
        footerLabel="View all usage details"
        footerHref="#"
    />
  • <MiniStatCard>

    sharedStatsDashboard

    Compact metric card with a headline value, a coloured label, and a badge icon. The body below the header is configurable via `display`: a sparkline/donut chart, a short supporting paragraph, or empty (header only). Use inside `<MiniStatGrid>`.

    When to use: Use when a report needs a 2×2 (or wider) grid of at-a-glance metrics. Mix `display="chart"` for tiles that show a trend, `display="text"` for tiles that need a short qualitative note (e.g. "Up from £8,100 last quarter"), and `display="none"` for compact value-only tiles. Pair with `<MiniStatGrid>` to lay out tiles side by side.

    Often with: MiniStatGridKPIKPIGrid

    Preview

    $10,230

    Expense

    $65,432

    Sales

    £48k

    Pipeline

    Up from £42k last month, driven by Q3 supplier renewals.

    3

    Active reports

    PropTypeNotes
    valuestringRequired. Main headline metric, e.g. "$10,230".Example: $10,230
    labelstringRequired. Short label below the value, e.g. "Expense".Example: Expense
    icon"credit-card" | "shopping-bag" | "shopping-cart" | "bar-chart" | "trend-up" | "line-chart" | "wallet"Required. Icon displayed in the top-right badge.Values: credit-card, shopping-bag, shopping-cart, bar-chart, trend-up, line-chart, walletExample: credit-card
    display"chart" | "text" | "none"Optional. What to render below the header. `chart` requires `chart` + `data`; `text` requires `text`; `none` renders the header only. If omitted, the type is inferred from which props are present (chart+data → chart, otherwise text → text, otherwise none).Values: chart, text, noneExample: chart
    chart"donut" | "bar" | "line"Optional. Type of embedded chart (only used when `display="chart"`). `donut` = ring chart (2+ segments); `bar` = vertical bar sparkline; `line` = monotone line sparkline.Values: donut, bar, lineExample: donut
    datanumber[]Optional. Data for the chart (only used when `display="chart"`). Donut: two or more segment values (proportional), e.g. [70, 30]. Bar/Line: ordered series of values, e.g. [3, 8, 5, 12, 7, 15, 9].Example: [70, 30]
    textstringOptional. Short supporting paragraph (only used when `display="text"`). Keep it to one or two sentences; longer copy is cropped visually.Example: Up from £8,100 last quarter, driven by Q3 supplier renewals.

    Examples

    <MiniStatGrid columns={2}>
      <MiniStatCard value="$10,230" label="Expense" icon="credit-card" display="chart" chart="donut" data={[70, 30]} />
      <MiniStatCard value="$65,432" label="Sales" icon="shopping-bag" display="chart" chart="bar" data={[8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15]} />
      <MiniStatCard value="£48k" label="Pipeline" icon="trend-up" display="text" text="Up from £42k last month, driven by Q3 supplier renewals." />
      <MiniStatCard value="3" label="Active reports" icon="bar-chart" display="none" />
    </MiniStatGrid>
  • <MiniStatGrid>

    sharedStatsDashboard

    Responsive grid wrapper for `<MiniStatCard>` tiles, defaulting to a 2-column layout.

    When to use: Wrap two or more `<MiniStatCard>` components inside `<MiniStatGrid>` to produce the 2×2 (or wider) tile layout. Set `columns` to 3 or 4 if you have more tiles.

    Often with: MiniStatCard

    Preview

    $10,230

    Expense

    $65,432

    Sales

    £48k

    Pipeline

    Up from £42k last month, driven by Q3 supplier renewals.

    3

    Active reports

    PropTypeNotes
    childrenReactNodeRequired. `<MiniStatCard>` elements.
    columns2 | 3 | 4Optional. Default: 2. Number of columns at the desktop breakpoint.Values: 2, 3, 4Example: 2

    Examples

    <MiniStatGrid columns={2}>
      <MiniStatCard value="$10,230" label="Expense" icon="credit-card" display="chart" chart="donut" data={[70, 30]} />
      <MiniStatCard value="$65,432" label="Sales" icon="shopping-bag" display="chart" chart="bar" data={[8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15]} />
      <MiniStatCard value="£48k" label="Pipeline" icon="trend-up" display="text" text="Up from £42k last month, driven by Q3 supplier renewals." />
      <MiniStatCard value="3" label="Active reports" icon="bar-chart" display="none" />
    </MiniStatGrid>
  • <TrendChart>

    sharedChartDashboard

    Multi-series line or combo bar-and-line chart over time for document reports, with optional dual Y-axes, grouped bars, and a source caption.

    When to use: Operational and analytical reports: trends over weeks or quarters, adoption curves, revenue vs margin. Set `type="bar"` on a series for columns; `yAxis="right"` for a secondary scale (e.g. £m bars + % line). Prefer `<TrafficTrendChart>` for SEO traffic area charts. For presentation slides use `<TrendLineChart>` (`kind: "presentation"`).

    Often with: TrafficTrendChartComparisonBarFinanceOverviewCardTrendLineChart

    Preview

    Manual hours per week

    ActualTargetBaseline measured in week 1

    Revenue and EBITDA margin

    RevenueEBITDA margin · right axisSource: management accounts, FY26
    PropTypeNotes
    titlestringOptional. Chart heading.
    descriptionstringOptional. Muted subheading under the title.
    labelsstring[]Required. X-axis labels in order (months, weeks, quarters).
    series{ name: string; values: number[]; color?: "brand" | "sky" | "emerald" | "purple" | "slate" | "success" | "warning" | "error"; type?: "line" | "bar"; yAxis?: "left" | "right"; unit?: string }[]Required. One or more series aligned to `labels`. Set `type="bar"` for columns; `yAxis="right"` for the secondary axis.
    unitstringOptional. Default: "". Default unit suffix for the left axis and series without their own `unit`.
    yAxes{ left?: { unit?: string; maxValue?: number; minValue?: number }; right?: { unit?: string; maxValue?: number; minValue?: number } }Optional. Per-axis unit and scale overrides for dual-axis charts.
    sourceNotestringOptional. Caption under the legend naming tool and date.Example: Source: ERP export, FY26 Q1–Q4

    Examples

    <TrendChart
      title="Manual hours per week"
      unit="h"
      labels={["Wk 1", "Wk 2", "Wk 3", "Wk 4", "Wk 5", "Wk 6"]}
      series={[
        { name: "Actual", values: [142, 138, 121, 98, 76, 60], color: "brand" },
        { name: "Target", values: [120, 115, 100, 80, 60, 45], color: "slate" }
      ]}
      sourceNote="Baseline measured in week 1"
    />
    <TrendChart
      title="Revenue and EBITDA margin"
      labels={["Q1", "Q2", "Q3", "Q4"]}
      yAxes={{ left: { unit: "m" }, right: { unit: "%", maxValue: 40 } }}
      series={[
        { name: "Revenue", type: "bar", values: [18, 22, 24, 26], color: "brand", unit: "m" },
        { name: "EBITDA margin", type: "line", yAxis: "right", values: [22, 24, 25, 27], color: "success", unit: "%" }
      ]}
      sourceNote="Source: management accounts, FY26"
    />