refactor(a11y): add scope=col to the agent costs table headers (#1789)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agent detail page has a Costs section. That section renders a
data table of per-run spend.
> - The table has a header row, but its `<th>` elements carry no `scope`
attribute.
> - A screen reader uses `scope="col"` to bind each data cell to its
column header. Without it, the reader announces a number without telling
the user which column it belongs to.
> - A table of costs is exactly the case where that hurts. Every cell is
a bare figure.
> - This pull request adds `scope="col"` to the five header cells in
that table.
> - The benefit is that assistive technology announces the cost table
correctly. The change is markup only, so sighted users see no
difference.

## Linked Issues or Issue Description

No public GitHub issue covers this. The problem is described in-PR,
following the enhancement template.

**What existing behavior does this improve?**

The Costs table rendered by `CostsSection` in
`ui/src/pages/AgentDetail.tsx`.

**Subsystem affected**

ui/ — React + Vite board UI

**Current behavior**

The table renders five header cells: Date, Run, Input, Output, and Cost.
None of them set `scope`. A screen reader must guess the header-to-cell
relationship, so a user hears a value with no column name attached to
it.

**Proposed behavior**

Each header cell sets `scope="col"`. A screen reader then announces the
column name together with each cell, so a cost figure is read as part of
the Cost column.

**Reason and benefit**

`scope` is the standard way to associate header cells with data cells in
an HTML table. The attribute has no visual effect, so the fix carries no
design cost and makes the table usable with a screen reader.

**Breaking changes**

None. `scope` is a presentational-neutral HTML attribute. No component
API, no styling, and no test changes.

**Related pull requests**

- #2215 proposed the same attribute for the Routines table. It is
closed, because that table no longer exists on master.
- #1524 and #1522 applied `scope="col"` to other tables. Both are
closed.

## What Changed

- Added `scope="col"` to the five `<th>` elements in the `CostsSection`
table in `ui/src/pages/AgentDetail.tsx`.
- Rebased the branch onto current master.
- Dropped the original `ui/src/pages/Routines.tsx` hunks. Master rebuilt
the Routines page around folder-grouped rows, so the table those hunks
targeted no longer exists.
- Dropped the original `HintIcon` opacity change. It altered a visible
colour, which is out of scope for a markup-only accessibility fix.

## Verification

- Run `pnpm --filter @paperclipai/ui typecheck` and `pnpm --filter
@paperclipai/ui build`. This is a markup-only change, so a clean
type-check and build is the relevant automated signal.
- Open an agent detail page and go to the Costs section. Inspect the
header row. Each `<th>` now carries `scope="col"`.
- Navigate the same table with a screen reader, cell by cell. Each cell
is announced with its column name.
- Compare the rendered page before and after. It is unchanged, because
`scope` has no styling effect.

## Risks

Low risk. The change adds one standard HTML attribute to five header
cells in a single table. It introduces no code path, changes no
component API, and has no visual effect. The worst case is that the
attribute is redundant for a reader that already infers the column,
which is harmless.

## Model Used

Anthropic Claude Opus 5, exact model ID `claude-opus-5`. It ran with
extended thinking and repository read/write tools, inside a
maintainer-operated triage agent. The model rebased the branch, dropped
the two out-of-scope hunks, and wrote this description. The original
change was authored by @bluzername, and the model used for that work is
not recorded here.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` references)
- [x] I have considered and documented any risks above
- [x] My branch name describes the change and contains no internal
ticket id
- [ ] I have run tests locally and they pass — not run. This is a
markup-only change and the package has no test covering this table.
- [ ] I have added or updated tests where applicable — no test added,
which is why this PR is titled `refactor:`.
- [ ] I have updated relevant documentation to reflect my changes — no
documentation describes this markup.
- [ ] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work — not checked by the maintainer who rebased this.
- [ ] All Paperclip CI gates are green — CI re-runs on this push.
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
Greptile re-reviews on this push.

---------

Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
This commit is contained in:
Evyatar Bluzer 2026-08-08 08:46:47 +07:00 committed by GitHub
parent cc35c3c395
commit 19be4cf927
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 5 additions and 5 deletions

View File

@ -1743,11 +1743,11 @@ function CostsSection({
<table className="w-full text-xs">
<thead>
<tr className="border-b border-border bg-accent/20">
<th className="text-left px-3 py-2 font-medium text-muted-foreground">Date</th>
<th className="text-left px-3 py-2 font-medium text-muted-foreground">Run</th>
<th className="text-right px-3 py-2 font-medium text-muted-foreground">Input</th>
<th className="text-right px-3 py-2 font-medium text-muted-foreground">Output</th>
<th className="text-right px-3 py-2 font-medium text-muted-foreground">Cost</th>
<th scope="col" className="text-left px-3 py-2 font-medium text-muted-foreground">Date</th>
<th scope="col" className="text-left px-3 py-2 font-medium text-muted-foreground">Run</th>
<th scope="col" className="text-right px-3 py-2 font-medium text-muted-foreground">Input</th>
<th scope="col" className="text-right px-3 py-2 font-medium text-muted-foreground">Output</th>
<th scope="col" className="text-right px-3 py-2 font-medium text-muted-foreground">Cost</th>
</tr>
</thead>
<tbody>