Component library

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>
  • <KeyFinding>

    workshopStatementContentNarrative

    Highlighted observation from the engagement, with severity colour-coding.

    When to use: Call out an insight that materially affects the client's decisions. A 12-page report typically has 4–8 of these.

    Preview

    Key findingHigh impact

    Order intake passes through five inboxes before reaching ERP

    Every customer order today is touched by sales, customer service, operations, finance, and operations again. Each handover is a manual email or shared inbox forward.

    PropTypeNotes
    severity"low" | "medium" | "high"Optional. Default: medium. Visual emphasis. High = red ring, medium = amber, low = neutral.Values: low, medium, highExample: high
    titlestringOptional. Optional title shown above the body.Example: Order intake passes through five inboxes
    childrenReactNodeRequired. The finding's body prose.

    Examples

    <KeyFinding severity="high" title="Order intake passes through five inboxes before reaching ERP">
        Every customer order today is touched by sales, customer service,
        operations, finance, and operations again. Each handover is a manual
        email or shared inbox forward.
    </KeyFinding>
  • <Recommendation>

    workshopAgendaContentNarrative

    Actionable next step with priority, owner, and effort.

    When to use: Whenever the report tells the client to do something. Always answer 'what should they do, and by when'. Use sparingly: a 12-page report typically has 5–8.

    Preview

    RecommendationDo nowEffort: MOwner: COO + IT Director

    Commit to a 12-week first wave covering invoice intake, order acks, and HR triage

    The three use cases share a deployment pattern (Lunnoa agent + ERP API + ticket fan-out) and together cover the largest concentration of manual hours we observed.

    PropTypeNotes
    priority"now" | "next" | "later"Optional. Default: next. Wave assignment. now = wave 1, next = wave 2, later = backlog.Values: now, next, laterExample: now
    titlestringRequired. The recommendation as an imperative sentence.Example: Commit to a 12-week first wave covering invoice intake, order acks, and HR triage
    ownerstringOptional. Named role or person responsible.Example: COO + IT Director
    effort"S" | "M" | "L" | "XL"Optional. T-shirt size estimate.Values: S, M, L, XLExample: M
    childrenReactNodeRequired. Rationale and next-step detail.

    Examples

    <Recommendation priority="now" title="Commit to a 12-week first wave covering invoice intake, order acks, and HR triage" effort="M" owner="COO + IT Director">
        The three use cases share a deployment pattern (Lunnoa agent + ERP
        API + ticket fan-out) and together cover the largest concentration of
        manual hours we observed.
    </Recommendation>
  • <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>
  • <PersonCard>

    workshopListQuoteNarrative

    Contributor attribution card.

    When to use: Use in contributor or participant sections to credit interviewees, sponsors, and reviewers.

    Preview

    Maria Fernandes
    Head of Finance · Finance
    Interviewee
    PropTypeNotes
    namestringRequired. Person's full name.Example: Maria Fernandes
    rolestringOptional. Job title.Example: Head of Finance
    departmentstringOptional. Team or department.Example: Finance
    contributedAs"Interviewee" | "Participant" | "Sponsor" | "Reviewer"Optional. How the person contributed to the report.Values: Interviewee, Participant, Sponsor, ReviewerExample: Interviewee

    Examples

    <PersonCard name="Maria Fernandes" role="Head of Finance" department="Finance" contributedAs="Interviewee" />
  • <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>
  • <Testimonial>

    testimonialTestimonialNarrative

    Customer or stakeholder quote card with avatar, name, role, and company.

    When to use: Always inside `<TestimonialGrid>`. Use for social proof in case studies and workshop reports. One centred card or three across.

    Often with: TestimonialGridQuotePersonCard

    Preview

    The first automation went live in two weeks. Finance finally trusts the numbers.

    Maria Fernandes
    Head of Finance, Acme Corp
    PropTypeNotes
    childrenReactNodeRequired. Quote body (no surrounding quotation marks).
    namestringRequired. Speaker name.Example: Maria Fernandes
    rolestringOptional. Job title.Example: Head of Finance
    companystringOptional. Organisation.Example: Acme Corp
    avatarUrlstringOptional. Optional headshot URL.
    initialsstringOptional. Avatar fallback.Example: MF

    Examples

    <Testimonial name="Maria Fernandes" role="Head of Finance" company="Acme Corp" initials="MF">
      The first automation went live in two weeks. Finance finally trusts the numbers.
    </Testimonial>
  • <TestimonialGrid>

    testimonialTestimonialNarrative

    Grid of one or three Testimonial cards with optional heading.

    When to use: Social proof section in document reports. One testimonial for a hero quote; three for a balanced row.

    Often with: TestimonialQuote

    Preview

    What teams say after Wave 1

    The first automation went live in two weeks.

    Maria Fernandes
    Head of Finance, Acme Corp
    PropTypeNotes
    titlestringOptional. Section heading above the grid.
    descriptionstringOptional. Subtitle or context line.
    childrenReactNodeRequired. One or three <Testimonial> elements.

    Examples

    <TestimonialGrid title="What teams say after Wave 1">
      <Testimonial name="Maria Fernandes" role="Head of Finance" company="Acme Corp" initials="MF">
        The first automation went live in two weeks.
      </Testimonial>
    </TestimonialGrid>
  • <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>
  • <BeforeAfter>

    workshopCompareNarrative

    Explicit current-state vs proposed-state comparison block.

    When to use: Use to contrast the as-is process with the to-be process for one specific flow. Most reports have 1–2 of these maximum.

    Preview

    Today (manual)
    • Invoice arrives by email, forwarded to AP shared inbox.
    • AP coordinator opens PDF, retypes header data into ERP.
    After Wave 1 (Lunnoa-led)
    • Lunnoa agent receives the invoice, extracts header and lines.
    • Three-way match runs automatically; clean invoices post.
    PropTypeNotes
    beforeTitlestringOptional. Default: "Today". Label above the left card.
    beforeReactNodeRequired. Current-state content (often a <ul>).
    afterTitlestringOptional. Default: "After". Label above the right card.
    afterReactNodeRequired. Proposed-state content.

    Examples

    <BeforeAfter
        beforeTitle="Today (manual)"
        afterTitle="After Wave 1 (Lunnoa-led)"
        before={
            <ul>
                <li>Invoice arrives by email, forwarded to AP shared inbox.</li>
                <li>AP coordinator opens PDF, retypes header data into ERP.</li>
            </ul>
        }
        after={
            <ul>
                <li>Lunnoa agent receives the invoice, extracts header and lines.</li>
                <li>Three-way match runs automatically; clean invoices post.</li>
            </ul>
        }
    />
  • <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"] }
        ]}
    />
  • <UseCaseCard>

    workshopContentListDashboard

    One use case tile with priority, area, owner, impact, effort, and time-to-value.

    When to use: Use to surface recommended automation use cases. Always inside <UseCaseGrid>.

    Often with: UseCaseGrid

    Preview

    Wave 1 Finance

    Supplier invoice intake

    Automate three-way matching between supplier invoices, purchase orders, and goods receipts.
    Impact
    ≈ 220 hrs / month
    Effort
    M
    TTV
    6 weeks
    Owner: Maria Fernandes
    PropTypeNotes
    titlestringRequired. Short use case name.Example: Supplier invoice intake
    descriptionReactNodeRequired. One-paragraph description of what gets automated.
    areastringOptional. Functional area.Example: Finance
    ownerstringOptional. Process owner.Example: Maria Fernandes
    priority"now" | "next" | "later"Optional. Default: next. Wave assignment.Values: now, next, laterExample: now
    impactstringOptional. Estimated impact.Example: ≈ 220 hrs / month
    effortstringOptional. Estimated effort, T-shirt size or duration.Example: M
    timeToValuestringOptional. Estimated time to first value.Example: 6 weeks

    Examples

    <UseCaseCard
        title="Supplier invoice intake"
        priority="now"
        area="Finance"
        owner="Maria Fernandes"
        impact="≈ 220 hrs / month"
        effort="M"
        timeToValue="6 weeks"
        description="Automate three-way matching between supplier invoices, purchase orders, and goods receipts."
    />
  • <UseCaseGrid>

    workshopContentListDashboard

    Grid of UseCaseCards.

    When to use: Wrap recommended use cases together. Use 2 columns by default; 3 only if you have many small cards.

    Often with: UseCaseCard

    Preview

    Wave 1 Finance

    Supplier invoice intake

    Automate three-way matching between supplier invoices, purchase orders, and goods receipts.
    Impact
    ≈ 220 hrs / month
    Effort
    M
    TTV
    6 weeks
    Owner: Maria Fernandes
    Wave 1 Operations

    Order acknowledgements

    Auto-generate acknowledgement emails within five minutes.
    Impact
    ≈ 90 hrs / month
    Effort
    S
    TTV
    4 weeks
    Owner: James Okafor
    PropTypeNotes
    columns2 | 3Optional. Default: 2. Desktop column count.Values: 2, 3Example: 2
    childrenReactNodeRequired. One or more <UseCaseCard> elements.

    Examples

    <UseCaseGrid columns={2}>
        <UseCaseCard
            title="Supplier invoice intake"
            priority="now"
            area="Finance"
            owner="Maria Fernandes"
            impact="≈ 220 hrs / month"
            effort="M"
            timeToValue="6 weeks"
            description="Automate three-way matching between supplier invoices, purchase orders, and goods receipts."
        />
        <UseCaseCard
            title="Order acknowledgements"
            priority="now"
            area="Operations"
            owner="James Okafor"
            impact="≈ 90 hrs / month"
            effort="S"
            timeToValue="4 weeks"
            description="Auto-generate acknowledgement emails within five minutes."
        />
    </UseCaseGrid>
  • <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="#"
    />
  • <FinanceOverviewCard>

    financeStatsChartDashboard

    Two-column financial headline: left block (title, date range, total revenue with optional delta and footnote), right block (product condition headline, optional trend pill, year selector, multi-series monotone area chart), plus a three-column footer row for supporting metrics.

    When to use: Use for executive-style revenue or adoption snapshots in report readouts. Pass `chartMonths` and aligned `chartSeries[].values`; include at least one series (empty shows a muted placeholder). Keep series count to 2–4 for clarity. Footer `stats` should contain exactly three items.

    Preview

    Financial income

    1 Aug 2025 – 1 Nov 2025

    Total revenue+9.78%

    £8,240.00

    Increased 15% from last month

    Product condition

    75%2.8%

    Selling product

    £335,000

    Followers

    1,500

    Campaign

    560

    PropTypeNotes
    dateRangestringRequired. Human-readable range under the income title.Example: 1 Aug 2025 – 1 Nov 2025
    totalRevenueValuestringRequired. Large headline currency string.Example: £8,240.00
    incomeTitlestringOptional. Default: "Financial income". Left column title.
    totalRevenueLabelstringOptional. Default: "Total revenue". Label above the headline figure.
    totalRevenueDeltastringOptional. Optional percentage chip; leading + uses success colour, leading − uses error.
    totalRevenueFootnotestringOptional. Muted line under the revenue figure.
    conditionValuestringRequired. Large headline next to the chart header.Example: 75%
    conditionTitlestringOptional. Default: "Product condition". Muted label above the condition value.
    conditionBadgeTextstringOptional. Pill text beside the condition value.Example: 2.8%
    conditionBadgeTrend"up" | "down"Optional. Default: down. Arrow direction inside the pill.Values: up, down
    yearsstring[]Optional. Default: ["2025"]. Year `<select>` options.
    chartMonthsstring[]Required. X-axis labels in order (e.g. Jan … Jul).
    chartSeries{ name: string; values: number[]; colorKey: "brand"|"sky"|"slate" }[]Required. One or more series; each `values` array must match `chartMonths` length. If empty, a muted placeholder is shown instead of the chart.
    stats{ label: string; value: string; labelTone?: "brand"|"sky"|"emerald" }[]Required. Exactly three footer metrics.

    Examples

    <FinanceOverviewCard
        dateRange="1 Aug 2025 – 1 Nov 2025"
        totalRevenueDelta="+9.78%"
        totalRevenueValue="£8,240.00"
        totalRevenueFootnote="Increased 15% from last month"
        conditionValue="75%"
        conditionBadgeText="2.8%"
        years={["2024", "2025", "2026"]}
        chartMonths={["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]}
        chartSeries={[
            { name: "Series A", values: [1200, 1800, 2200, 2600, 3000, 3400, 3800], colorKey: "sky" },
            { name: "Series B", values: [800, 1100, 1400, 1700, 2100, 2500, 2900], colorKey: "brand" },
            { name: "Series C", values: [400, 500, 600, 700, 800, 900, 1000], colorKey: "slate" }
        ]}
        stats={[
            { label: "Selling product", value: "£335,000", labelTone: "sky" },
            { label: "Followers", value: "1,500", labelTone: "sky" },
            { label: "Campaign", value: "560", labelTone: "emerald" }
        ]}
    />
  • <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>
  • <AllocationDriftChart>

    financeChartDataDashboard

    Current vs target allocation table with drift, tolerance band status (in band / overweight / underweight), and inline weight bars.

    When to use: Use in portfolio monitoring or rebalancing reports to show how each sleeve drifts from strategic targets. Pass weights as numbers (typically percent). Set `tolerance` per row when bands differ by asset class.

    Often with: ComparisonBarUsageDonutCardSimpleDataTable

    Preview

    Strategic allocation drift

    Tolerance band ±1% unless noted

    Asset classCurrentTargetDriftStatusWeight
    Global equities42.3%40.0%+2.3%Overweight
    Investment grade credit24.1%25.0%-0.9%In band
    Private markets18.6%20.0%-1.4%In band
    Cash8.2%10.0%-1.8%Underweight
    Alternatives6.8%5.0%+1.8%Overweight
    Current weight Target weight
    PropTypeNotes
    titlestringOptional. Chart heading.
    descriptionstringOptional. Muted subheading.
    rows{ label: string; current: number; target: number; tolerance?: number }[]Required. One row per asset class or sleeve. `tolerance` defaults to 1 (same unit as weights).
    unitstringOptional. Default: "%". Suffix for weight and drift columns.

    Examples

    <AllocationDriftChart
      title="Strategic allocation drift"
      description="Tolerance band ±1% unless noted"
      rows={[
        { label: "Global equities", current: 42.3, target: 40, tolerance: 1 },
        { label: "Investment grade credit", current: 24.1, target: 25, tolerance: 1 },
        { label: "Private markets", current: 18.6, target: 20, tolerance: 2 },
        { label: "Cash", current: 8.2, target: 10, tolerance: 1 },
        { label: "Alternatives", current: 6.8, target: 5, tolerance: 1 }
      ]}
    />
  • <RebalanceTradeList>

    financeDataListDashboard

    Ordered list of proposed rebalance trades with buy/sell/hold chips, formatted amounts, optional weight before/after, and optional turnover footer.

    When to use: Use after an allocation drift review to show the concrete trades required to return to target. Keep amounts pre-formatted strings (currency or % of NAV).

    Often with: AllocationDriftChartSimpleDataTableDataTable

    Preview

    Proposed rebalance trades

    To restore policy weights as of 31 May 2026

    • Sell

      MSCI World ETF

      Trim overweight global equity sleeve

      £420,000

      22.1%20.0%

    • Buy

      Sterling IG credit fund

      £280,000

      23.4%25.0%

    • Hold

      Private equity co-invest

      Within tolerance; no trade required

    Estimated turnover3.2% of NAV
    PropTypeNotes
    titlestringOptional. Section heading.
    descriptionstringOptional. Muted subheading.
    trades{ asset: string; action: "buy" | "sell" | "hold"; amount: string; weightBefore?: string; weightAfter?: string; note?: string }[]Required. Proposed trades in execution order.
    turnoverLabelstringOptional. Footer label, e.g. "Estimated turnover".
    turnoverValuestringOptional. Footer value; shown only when both turnover props are set.

    Examples

    <RebalanceTradeList
      title="Proposed rebalance trades"
      description="To restore policy weights as of 31 May 2026"
      trades={[
        { asset: "MSCI World ETF", action: "sell", amount: "£420,000", weightBefore: "22.1%", weightAfter: "20.0%", note: "Trim overweight global equity sleeve" },
        { asset: "Sterling IG credit fund", action: "buy", amount: "£280,000", weightBefore: "23.4%", weightAfter: "25.0%" },
        { asset: "Private equity co-invest", action: "hold", amount: "—", note: "Within tolerance; no trade required" }
      ]}
      turnoverLabel="Estimated turnover"
      turnoverValue="3.2% of NAV"
    />
  • <PortfolioSummaryCard>

    financeStatsDataDashboard

    Headline portfolio card: NAV or AUM with optional delta pill and a grid of supporting metrics (returns, volatility, tracking error, cash, etc.).

    When to use: Use at the top of a portfolio or rebalancing report for the headline snapshot. Pair with `<AllocationDriftChart>` and `<RebalanceTradeList>` in the sections below.

    Often with: KPIKPIGridFinanceOverviewCardAnalyticsCard

    Preview

    Balanced growth portfolio

    As of 31 May 2026
    NAV

    £24.6m

    +1.8% MTD
    YTD return

    +4.2%

    Volatility (1y)

    9.1%

    Tracking error

    42 bps

    Cash

    8.2%

    PropTypeNotes
    titlestringOptional. Default: "Portfolio summary". Card heading.
    asOfstringOptional. Valuation date label.
    navLabelstringOptional. Default: "NAV". Label above the headline figure.
    navValuestringRequired. Headline NAV or AUM (include currency).
    navDeltastringOptional. Optional change pill, e.g. "+1.8% MTD".
    navDeltaTone"up" | "down" | "flat"Optional. Default: up. Colours the delta pill.Values: up, down, flat
    metrics{ label: string; value: string; tone?: "up" | "down" | "flat" }[]Optional. Supporting metrics in a responsive grid (typically 3–4 items).

    Examples

    <PortfolioSummaryCard
      title="Balanced growth portfolio"
      asOf="31 May 2026"
      navValue="£24.6m"
      navDelta="+1.8% MTD"
      navDeltaTone="up"
      metrics={[
        { label: "YTD return", value: "+4.2%", tone: "up" },
        { label: "Volatility (1y)", value: "9.1%" },
        { label: "Tracking error", value: "42 bps" },
        { label: "Cash", value: "8.2%" }
      ]}
    />
  • <PillarScoreCard>

    seoStatsChartDashboard

    Lighthouse-style score gauge: an overall 0–100 ring (green ≥ 80, amber ≥ 50, red below) plus horizontal sub-score bars per pillar. Domain-neutral — use for SEO, GEO/AEO, maturity, or any multi-pillar assessment.

    When to use: Use once at the top of an executive summary as the headline verdict. Works for SEO analysis (Technical, Content, Authority), combined SEO/GEO/AEO scores, organisational maturity, or any scored assessment with 2–5 pillars. Scores must come from a real audit or documented method; never invent them. `<SeoScoreCard>` is a deprecated alias with the same props.

    Often with: SeoAuditChecklistCoreWebVitalsCardKPIGridMaturityRadar

    Preview

    Combined visibility score

    Weighted score from the 7 Jul 2026 site crawl and AI citation audit

    Assessed 7 Jul 2026

    SEO / GEO / AEO

    • SEO80

      Strong technical base; one broken footer link

    • GEO80
    • AEO60

      Schema present but thin entity coverage

    SEO health score

    Weighted score across four pillars, from the June 2026 crawl of 1,240 pages

    Crawled 14 Jun 2026

    Overall score

    • Technical48

      Crawl budget wasted on 3,100 parameter URLs

    • Content71
    • Authority66
    • Experience58

      INP fails on mobile product pages

    PropTypeNotes
    scorenumberRequired. Overall score 0–100 (clamped).Example: 62
    scoreLabelstringOptional. Default: "Overall score". Label beside the pillar bars.Example: Combined SEO / GEO / AEO
    titlestringOptional. Card heading.Example: Site visibility score
    descriptionstringOptional. Muted subheading, e.g. the scoring method.
    asOfstringOptional. Assessment date label shown top-right.Example: Crawled 14 Jun 2026
    pillars{ label: string; score: number; note?: string }[]Optional. Category sub-scores (0–100), typically 2–5 pillars (e.g. SEO, GEO, AEO or Technical, Content, Authority).

    Examples

    <PillarScoreCard
        title="Combined visibility score"
        scoreLabel="SEO / GEO / AEO"
        description="Weighted score from the 7 Jul 2026 site crawl and AI citation audit"
        asOf="Assessed 7 Jul 2026"
        score={73}
        pillars={[
            { label: "SEO", score: 80, note: "Strong technical base; one broken footer link" },
            { label: "GEO", score: 80 },
            { label: "AEO", score: 60, note: "Schema present but thin entity coverage" }
        ]}
    />
    <PillarScoreCard
        title="SEO health score"
        description="Weighted score across four pillars, from the June 2026 crawl of 1,240 pages"
        asOf="Crawled 14 Jun 2026"
        score={62}
        pillars={[
            { label: "Technical", score: 48, note: "Crawl budget wasted on 3,100 parameter URLs" },
            { label: "Content", score: 71 },
            { label: "Authority", score: 66 },
            { label: "Experience", score: 58, note: "INP fails on mobile product pages" }
        ]}
    />
  • <KeywordRankingTable>

    seoDataListDashboard

    Keyword ranking table with SERP position, position change arrows (green up / red down), monthly volume, colour-coded difficulty, search-intent chips, and the ranking URL.

    When to use: Use for the core keyword evidence in an SEO report: current rankings, movers since the last crawl, or a target keyword list. Keep to 8–15 rows per table for print; split by topic cluster if longer. Omit `position` (or set 0) for keywords not ranking in the top 100.

    Often with: SerpPositionDistributionOpportunityMatrixDataTable

    Preview

    Priority keywords — current rankings

    KeywordPositionChangeVolume / moDifficultyIntentURL
    workflow automation software862,40068Commercial/platform
    ai agent platform1211,90072Commercial/platform/agents
    invoice automation>1003,60055Transactional/solutions/finance
    Source: Ahrefs, 14 Jun 2026, UK desktop
    PropTypeNotes
    titlestringOptional. Table heading.Example: Priority keywords — current rankings
    descriptionstringOptional. Muted subheading.
    rows{ keyword: string; position?: number; previousPosition?: number; volume?: string; difficulty?: number; intent?: "informational" | "navigational" | "commercial" | "transactional"; url?: string }[]Required. One row per keyword. Change is derived from previousPosition − position (positive = improved). difficulty is 0–100.
    sourceNotestringOptional. Caption below the table.Example: Source: Ahrefs, 14 Jun 2026, UK desktop

    Examples

    <KeywordRankingTable
        title="Priority keywords — current rankings"
        sourceNote="Source: Ahrefs, 14 Jun 2026, UK desktop"
        rows={[
            { keyword: "workflow automation software", position: 8, previousPosition: 14, volume: "2,400", difficulty: 68, intent: "commercial", url: "/platform" },
            { keyword: "ai agent platform", position: 12, previousPosition: 11, volume: "1,900", difficulty: 72, intent: "commercial", url: "/platform/agents" },
            { keyword: "invoice automation", volume: "3,600", difficulty: 55, intent: "transactional", url: "/solutions/finance" }
        ]}
    />
  • <SerpPositionDistribution>

    seoChartStatsDashboard

    Bar chart of keyword counts per SERP position bucket (e.g. Top 3, 4–10, 11–20, 21–50, 51–100), with an optional muted comparison series for the previous crawl.

    When to use: Use to show the shape of the ranking profile and how it shifted, typically right after the headline KPIs in an SEO report. Buckets are free-form labels, so it also works for striking-distance analyses (positions 11–20).

    Often with: KeywordRankingTableTrafficTrendChartKPIGrid

    Preview

    Keyword distribution by SERP position

    Tracked keyword set, June vs March 2026

    Mar 2026 Jun 2026
    PropTypeNotes
    titlestringOptional. Chart heading.Example: Keyword distribution by SERP position
    descriptionstringOptional. Muted subheading.
    buckets{ label: string; count: number; previousCount?: number }[]Required. Buckets in display order. previousCount adds a grey comparison bar.
    currentLabelstringOptional. Default: "Current". Legend label for the current series.
    previousLabelstringOptional. Default: "Previous". Legend label for the comparison series.

    Examples

    <SerpPositionDistribution
        title="Keyword distribution by SERP position"
        description="Tracked keyword set, June vs March 2026"
        currentLabel="Jun 2026"
        previousLabel="Mar 2026"
        buckets={[
            { label: "Top 3", count: 12, previousCount: 8 },
            { label: "4–10", count: 34, previousCount: 29 },
            { label: "11–20", count: 58, previousCount: 61 },
            { label: "21–50", count: 71, previousCount: 78 },
            { label: "51–100", count: 44, previousCount: 52 }
        ]}
    />
  • <CoreWebVitalsCard>

    seoStatsDataDashboard

    Core Web Vitals tiles (LCP, INP, CLS or any measured metrics) with good / needs-improvement / poor rating chips, threshold hints, and a source footer.

    When to use: Use in the technical section of an SEO report to report field or lab performance data. State the device class and data source in `sourceNote` (e.g. mobile CrUX field data). Ratings must reflect the tool's thresholds; do not restate lab data as field data.

    Often with: SeoAuditChecklistPillarScoreCard

    Preview

    Core Web Vitals — mobile

    LCPNeeds improvement

    Largest Contentful Paint

    3.4 s

    Good ≤ 2.5 s

    INPPoor

    Interaction to Next Paint

    420 ms

    Good ≤ 200 ms

    CLSGood

    Cumulative Layout Shift

    0.06

    Good ≤ 0.1

    Mobile, CrUX field data, 28 days to 14 Jun 2026
    PropTypeNotes
    titlestringOptional. Card heading.Example: Core Web Vitals — mobile
    descriptionstringOptional. Muted subheading.
    metrics{ id: string; name?: string; value: string; rating: "good" | "needs-improvement" | "poor"; threshold?: string }[]Required. Typically the three vitals; the grid adapts to 2 or 3 columns.
    sourceNotestringOptional. Footer line naming device class, data source, and window.Example: Mobile, CrUX field data, 28 days to 14 Jun 2026

    Examples

    <CoreWebVitalsCard
        title="Core Web Vitals — mobile"
        sourceNote="Mobile, CrUX field data, 28 days to 14 Jun 2026"
        metrics={[
            { id: "LCP", name: "Largest Contentful Paint", value: "3.4 s", rating: "needs-improvement", threshold: "Good ≤ 2.5 s" },
            { id: "INP", name: "Interaction to Next Paint", value: "420 ms", rating: "poor", threshold: "Good ≤ 200 ms" },
            { id: "CLS", name: "Cumulative Layout Shift", value: "0.06", rating: "good", threshold: "Good ≤ 0.1" }
        ]}
    />
  • <SeoAuditChecklist>

    seoDataListDashboard

    Technical audit checklist: one row per check with a pass / warn / fail / info status chip, affected count (pages or URLs), and a one-line remediation hint. The header summarises fail/warn/pass counts.

    When to use: Use to report crawl findings (indexability, meta data, canonicals, structured data, hreflang) in the technical section of an SEO report. Group long audits into several checklists with `groupLabel` rather than one very long list. Every fail should map to a `<Recommendation>` later in the report.

    Often with: CoreWebVitalsCardPillarScoreCardRecommendation

    Preview

    Crawl audit — indexability

    Screaming Frog crawl of 1,240 HTML pages, 14 Jun 2026

    1 fail1 warning1 pass
    • Parameter URLs consuming crawl budget

      Faceted navigation generates indexable parameter combinations; add canonical + robots rules.

      3,100 URLsFail
    • Missing meta descriptions

      Concentrated in the blog archive.

      34 pagesWarning
    • XML sitemap valid and referenced in robots.txt

      Pass
    • Structured data on product pages

      No Product schema yet; opportunity rather than defect.

      0 pagesInfo
    PropTypeNotes
    titlestringOptional. Card heading.Example: Crawl audit — indexability
    descriptionstringOptional. Muted subheading, e.g. crawler and scope.
    groupLabelstringOptional. Small uppercase band above the list for grouping.Example: Indexability
    items{ check: string; status: "pass" | "warn" | "fail" | "info"; affected?: string; detail?: string }[]Required. One row per audit check, most severe first.

    Examples

    <SeoAuditChecklist
        title="Crawl audit — indexability"
        description="Screaming Frog crawl of 1,240 HTML pages, 14 Jun 2026"
        items={[
            { check: "Parameter URLs consuming crawl budget", status: "fail", affected: "3,100 URLs", detail: "Faceted navigation generates indexable parameter combinations; add canonical + robots rules." },
            { check: "Missing meta descriptions", status: "warn", affected: "34 pages", detail: "Concentrated in the blog archive." },
            { check: "XML sitemap valid and referenced in robots.txt", status: "pass" },
            { check: "Structured data on product pages", status: "info", affected: "0 pages", detail: "No Product schema yet; opportunity rather than defect." }
        ]}
    />
  • <BacklinkSnapshotCard>

    seoStatsDataDashboard

    Backlink profile snapshot: a strip of headline metrics (referring domains, backlinks, authority, toxic share) with optional deltas, plus a list of notable referring domains with authority pills and link counts.

    When to use: Use in the authority section of an SEO report. Keep `topDomains` to the 5–8 most notable wins or risks; the full export belongs in an appendix `<DataTable>`. Name the tool and date in `sourceNote` since authority scales differ per vendor.

    Often with: KPIGridDataTableTrafficTrendChart

    Preview

    Backlink profile

    Referring domains

    412

    +18 in 90 days

    Backlinks

    6,840

    Domain rating

    54

    Toxic share

    2.1%

    −0.4 pts

    • techcrunch.com

      Product launch coverage, Feb 2026

      DR 931 link
    • computerwoche.de

      Automation market round-up

      DR 783 links
    Source: Ahrefs, 14 Jun 2026
    PropTypeNotes
    titlestringOptional. Card heading.Example: Backlink profile
    descriptionstringOptional. Muted subheading.
    metrics{ label: string; value: string; delta?: string }[]Optional. 3–4 headline metrics. delta with leading + renders green, leading − renders red.
    topDomains{ domain: string; authority?: string; links?: string; note?: string }[]Optional. Notable referring domains. authority is pre-formatted (e.g. "DR 91").
    sourceNotestringOptional. Footer naming the tool and date.Example: Source: Ahrefs, 14 Jun 2026

    Examples

    <BacklinkSnapshotCard
        title="Backlink profile"
        sourceNote="Source: Ahrefs, 14 Jun 2026"
        metrics={[
            { label: "Referring domains", value: "412", delta: "+18 in 90 days" },
            { label: "Backlinks", value: "6,840" },
            { label: "Domain rating", value: "54" },
            { label: "Toxic share", value: "2.1%", delta: "−0.4 pts" }
        ]}
        topDomains={[
            { domain: "techcrunch.com", authority: "DR 93", links: "1 link", note: "Product launch coverage, Feb 2026" },
            { domain: "computerwoche.de", authority: "DR 78", links: "3 links", note: "Automation market round-up" }
        ]}
    />
  • <TrafficTrendChart>

    seoChartDashboard

    Multi-series area chart over time for traffic-style metrics: organic sessions, clicks, impressions, or conversions, with automatic k/M axis formatting and a legend built from series names.

    When to use: Use to show organic performance over the last 6–12 periods, e.g. GSC clicks and impressions or GA4 sessions. Keep to 1–3 series so the story stays readable; name the data source and window in `sourceNote`. For financial revenue snapshots prefer `<FinanceOverviewCard>`.

    Often with: SerpPositionDistributionKPIGridFinanceOverviewCard

    Preview

    Organic clicks and impressions, 12 months

    Impressions ClicksSource: Google Search Console, 12 months to Jun 2026
    PropTypeNotes
    titlestringOptional. Chart heading.Example: Organic clicks and impressions, 12 months
    descriptionstringOptional. Muted subheading.
    labelsstring[]Required. X-axis labels in order (months, weeks).Example: ["Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
    series{ name: string; values: number[]; color?: "brand" | "sky" | "emerald" | "purple" | "slate" }[]Required. Each values array must match labels length. Colours default to the palette order when omitted.
    unitstringOptional. Default: "". Suffix appended to axis ticks and tooltip values.
    sourceNotestringOptional. Caption under the legend.Example: Source: Google Search Console, 12 months to Jun 2026

    Examples

    <TrafficTrendChart
        title="Organic clicks and impressions, 12 months"
        sourceNote="Source: Google Search Console, 12 months to Jun 2026"
        labels={["Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun"]}
        series={[
            { name: "Impressions", values: [82000, 85000, 91000, 96000, 90000, 84000, 98000, 104000, 112000, 118000, 121000, 127000], color: "sky" },
            { name: "Clicks", values: [2400, 2600, 2900, 3100, 2800, 2500, 3200, 3500, 3900, 4200, 4400, 4700], color: "brand" }
        ]}
    />
  • <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"
    />