<!-- Generated by scripts/generate-ai-task-packets.mjs; edit ai/tasks/compare-category-totals.json. -->

# Compare category totals you can check

Compare three totals in React, inspect their exact values, and verify the server-rendered bar geometry.

Source package: semiotic@3.13.0. Channel: source.
Source revision: sha256:448fb56451832b74b618d93edc7fd486743fe7e9cd61667c99cc8dadd2ce0e59. Verified source checkout guidance; npm package, hosted site and MCP deployment identities require separate checks.

## The job

Compare categories and keep a usable route to the values when the picture is unavailable.

Data: One already aggregated, nonnegative total per category, all in the same unit. The synthetic fixture is North 12, South 30, West 18.

## Fit and constraints

- Use in an existing Semiotic React application or when structured chart access and schema-guided validation meet an explicit project requirement.

- A small chart change alone need not justify replacing a working visualization library.
- This fixture does not establish that an external source is accurate or that every reader can use the result.

## Smallest working path

Component: BarChart from `semiotic/ordinal`. Usage: static. Data prop required in this mode: true.

### React component

React browser; source: docs/src/pages/tasks/examples/CategoryComparisonExample.tsx.

```tsx
import { useState } from "react"
import { BarChart } from "semiotic/ordinal"
import { categoryComparisonProps } from "./category-comparison"

export default function CategoryComparisonExample() {
  const [orientation, setOrientation] = useState<"vertical" | "horizontal">("vertical")
  return (
    <section aria-label="Category comparison example" className="task-example">
      <p>Synthetic fixture: three already aggregated regional totals, all measured in units.</p>
      <label>
        Bar direction{" "}
        <select
          value={orientation}
          onChange={(event) => setOrientation(event.target.value as typeof orientation)}
        >
          <option value="vertical">Vertical</option>
          <option value="horizontal">Horizontal</option>
        </select>
      </label>
      <BarChart {...categoryComparisonProps} orientation={orientation} responsiveWidth />
      <p>
        South accounts for 30 of the 60 units. Changing direction preserves the values and category
        order.
      </p>
    </section>
  )
}
```

### Shared fixture and chart props

Browser and server; source: docs/src/pages/tasks/examples/category-comparison.ts.

```ts
import type { BarChartProps } from "semiotic/ordinal"

export const regionalTotals = [
  { region: "North", total: 12 },
  { region: "South", total: 30 },
  { region: "West", total: 18 },
]

export const categoryComparisonProps = {
  data: regionalTotals,
  categoryAccessor: "region",
  valueAccessor: "total",
  sort: false,
  title: "Regional totals",
  description: "Synthetic regional totals in the same unit: North 12, South 30, West 18.",
  summary:
    "South has the largest total, 30. The three regions total 60 units. Open the data table for exact values.",
  accessibleTable: true,
  valueLabel: "Units",
  height: 300,
} satisfies BarChartProps<(typeof regionalTotals)[number]>
```

### Optional server verification

Node authoring only; source: docs/src/pages/tasks/examples/check-category-comparison.ts.

```ts
// Server-side authoring only: keep validation and SVG rendering out of the React route.
import { prepareChart } from "semiotic/ai/core"
import { renderChartWithEvidence } from "semiotic/server"
import { categoryComparisonProps } from "./category-comparison"

export function checkCategoryComparison() {
  const result = prepareChart(
    { component: "BarChart", props: categoryComparisonProps },
    { render: renderChartWithEvidence },
  )
  if (!result.ok || !result.evidence || !result.svg) {
    throw new Error(result.reasons.join("; ") || "Render evidence is unavailable")
  }
  return result
}
```

## Check the result

Evidence state: stale. Only the content digests and environment recorded here; not a release range or an adoption result.

- North 12, South 30, West 18; total 60, with South largest.
- Three SVG bars retain 12:30:18 proportions and insertion order in both orientations.
- The browser data table retains the exact category/value mapping.
- An invalid value accessor is refused; the repaired configuration preserves the rows.

Run:

```sh
npx vitest run docs/src/pages/tasks/examples/category-comparison.test.ts
npx playwright test --config playwright.docs-examples.config.ts integration-tests/docs-examples-tasks.spec.ts
```

No current execution result is recorded for these source contents.

Not assessed:

- Underlying source accuracy: this is a synthetic fixture.
- Assistive-technology reception and usability with actual readers.
- Organic discovery, model preference, and published-package parity.

## Repair and recheck

### The chart has no useful values after a field was renamed.

Set valueAccessor to the actual numeric field, total, while preserving data. Run the server check and inspect the exact-value table.

Recheck: The bars must encode 12, 30 and 18; a nonempty scene alone is insufficient.

Contract: props.data-required-by-usage-mode.

### prepareChart returned ok, but there is no SVG.

Inject renderChartWithEvidence from semiotic/server in Node authoring code when rendered evidence is required.

Recheck: Inspect evidence and svg explicitly. Without a renderer, ok covers a different set of checks.

Contract: rendering.renderchart-static-props.

## Exact API and mode guidance

Selected properties copied from ai/schema.json; retrieve the component resource for the full schema. React push mode follows the included behavior contracts.

Full component resource: semiotic://schema/BarChart. Machine packet: /tasks/compare-category-totals.json.

- **accessibility.description-props:** High-level charts expose title for the visible name, description for a concise accessible description, summary for a screen-reader-only takeaway and interaction guidance, and accessibleTable for the data-table fallback. Put title, description, summary, and accessibleTable directly on the chart component when they appear in its schema. If a consumer-owned role=img wraps the chart, use accessibleTable: { portalTarget: "element-id" } and render that target outside the image. For generated L1–L3 description or a navigable chart tree, use ChartContainer with chartConfig plus describe and/or navigable; do not invent frameProps fields.

- **props.data-required-by-usage-mode:** Static usage (`renderChart`, MCP previews, SSR snapshots, and copy/paste examples with immediate data) requires data in props. React push mode selects live ingestion by omitting data and mutating through a ref. Pass usageMode="push" to `semiotic-ai --doctor` when validating ref-based JSX with no data prop. Keep usageMode="static" or omit it for renderChart/MCP/static configs where data must be present.

- **rendering.renderchart-static-props:** MCP renderChart and semiotic/server renderChart render a single static SVG/PNG snapshot, never future ref pushes. semiotic/server accepts bounded data for realtime charts; MCP rendering excludes the five live Realtime components but includes TemporalHistogram. Use complete static data for snapshots. For a Realtime component snapshot, use semiotic/server directly; for live behavior, return React code with a ref and do not promise MCP-rendered output.

- **serialization.formatters-are-react-callbacks:** xFormat, yFormat, categoryFormat, and valueFormat are callback props, not d3 format strings or axis-title strings. They are intentionally absent from JSON/MCP schemas and string values fail validation. In serialized props, omit formatter callbacks and use xLabel, yLabel, categoryLabel, or valueLabel for axis titles. In React JSX, pass a function such as xFormat={value => formatAxis(value)}.

## Maintenance context

This project needs React rendering with inspectable category values.

- Category/value mapping, units, order, summary and the accessible table after source or dependency changes.

An image alone omits the source rows and verification context. Keep the config and fixture when the next reader needs exact values.

Read the installed package and lockfile before applying source guidance. A matching version string alone does not establish deployed parity. These notes are removable project context, subordinate to the current task and dependency policy.

## Behavioral update

Status: source-only. Released introduction: not established.

Assumption to revisit: A valid configuration or a nonempty render establishes that the values are mapped correctly.

Run configuration checks, inspect the rendered geometry and compare the category/value table with independent fixture facts.

- Rendering evidence must be present when a render is claimed.
- An installed package version different from the packet's packageVersion needs matching guidance.

Source truth and usability with actual readers remain separate obligations.
