Spatial finding
The finding a spatial model output makes, complete without the image: what, where in the units the reader navigates in, how big, from what. The marks are pixels; this is the claim.
Overview
Attribution overlays has one test for a spatial finding: cover the image, and the finding is still complete. That page also has the sharpest rule in the imaging half of this system — an overlay shows where the model’s response concentrated, and calling it an explanation makes it read as evidence for a finding it is not evidence for. This system ships no image viewer, on purpose; what it can ship is the words beside the image, and the words are where every rule on that page that is not about canvas rendering actually lives.
The control that shows the marks cannot be labelled anything that sounds like a reason. Its label is a constant — Show where the response concentrated — and the source contains none of the words explain, reasoning, why or because, which an assertion checks.
An address, in the reader’s units
address is required and is series, slice and the size of the series. A
coordinate nobody can type into their own viewer is not an address, and it is not
expressible here.
Off by default, one action to remove
Marks start hidden. The reader shows them with a switch, and hides them with the same switch. There is no prop to start them shown.
Navigating is not assessing
The only callback is onNavigate, called with the address. There is no
onSeen, onAssessed or onReviewed, so the
component cannot be wired to record an evaluation nobody made.
Anatomy
| Element | Rule |
|---|---|
| What | The finding in words — what was marked and where in the anatomy. Quotable, dictatable, readable under magnification. |
| Address | Series and slice, with the size of the series, so “slice 47” is “slice 47 of 300”. A button when the caller can navigate there; text otherwise. |
| Extent | “14 mm across the mark” — the size of the mark, which is not the size of anything in the patient. See Derived measurement before a contour becomes a number. |
| Provenance | Detector and version, with a fixed suffix: detection only, not a diagnosis. |
| Marks state | One of four: available (a switch, off), none (scoped words), partial (what was and was not analysed), outside scope (a reason). Each renders differently, because they are different facts. |
Four states, none of them “normal”
marks.kind | Renders as |
|---|---|
available |
A switch, off: “Show where the response concentrated (3 marks)”. Showing
and hiding call onShownChange, so a dismissal can be recorded. |
none |
“No regions marked for pulmonary nodules. This says nothing about anything
else.” Scoped to scope, which is required. The source contains
neither normal nor no abnormality, which an assertion checks. |
partial |
“Analysed: 3 of 4 series. Not analysed: the non-contrast series. The unanalysed part has not been assessed.” |
outside-scope |
“Not analysed — outside the validated scope: paediatric study.” Withhold rather than extrapolate, per Confidence disclosure. |
Do's and don'ts
Show where the response concentrated (1 mark)
The control names what the overlay is. A reader who turns it on knows they are looking at where the model attended, not at why it concluded.
Explain this finding
The same pixels, now offered as a reason. This label is not expressible: the control’s text is a constant, and the source is asserted to contain no word of explanation.
series 4, slice 47 of 300
The reader can type this into their own viewer. The denominator says how far into the study it is.
ROI #a91f · z=0.41
An internal identifier and a normalised coordinate. Nobody can navigate
there, so the finding has no address, so the reader cannot find it. Not expressible:
address is series, slice and count.
No regions marked for pulmonary nodules. This says nothing about anything else.
Scoped to what the model detects. Absence of marks over 299 slices means the model produced no output there, and the sentence says exactly that.
Normal — no abnormality detected
A single-condition detector read as a clearance of the whole study. Not expressible: the none state has one wording, and it is scoped.
What the component enforces, and what it only asks for
The words are held here. The pixels — outline rather than fill, no ramp, no animation, never an alarm hue — are drawn by a viewer this package does not ship, and the rules about them stay with whoever draws them.
| Rule | Held by | How |
|---|---|---|
| The address is in the units the reader navigates in | The component | address is required and is { series, slice, of }; it
renders as “series 4, slice 47 of 300”. What, extent and provenance
render beside it, so the finding is complete with the image covered.Asserted: SpatialFinding: the address is in the reader's units, and complete without the image |
| Marks are off by default and removable in one action; the control never says “explain” | The component | The switch starts off and there is no prop to start it on. Its label is a constant,
and the component’s code contains none of explain, explanation,
reasoning, why or because. Asserted: SpatialFinding: marks start hidden, and the control never says explain |
| Unhighlighted is not “assessed and clear” | The component | The none state requires a scope and renders “No regions marked
for … This says nothing about anything else.” The code contains neither
normal nor no abnormality.Asserted: SpatialFinding: nothing-marked is scoped, and never 'normal' |
| Not-analysed is distinct from nothing-marked | The component | Four kinds — available, none, partial, outside-scope — each with its own
wording. A partial analysis cannot be rendered as a negative one. Asserted: SpatialFinding: partial and outside-scope are distinct from none |
| Navigating there is not assessing it | The component | onNavigate is the only callback and receives the address. There is no
way to wire a “seen” or “assessed” event to anything here.Asserted: SpatialFinding: navigating is the only callback; nothing records assessment |
| Showing and hiding the marks reaches the caller, each time | The component | Driven in a browser: the switch reads off to the accessibility tree before anything moves, onShownChange has not fired, and each move after that reports its own value — so a dismissal can be recorded as one.Asserted: SpatialFinding: the marks switch starts off, and each move reaches the caller |
| The address button navigates with the address, unaltered | The component | Driven in a browser: a button named by the address calls onNavigate with exactly { series, slice, of }.Asserted: SpatialFinding: the address navigates, with the address and nothing else |
| The address button meets the touch floor | The component | It was 16 px tall — a standalone control, not a link inside a sentence — found the first time the components were measured with a stylesheet. Asserted: Layout: every control a finger can reach is at least 44 px tall |
| The address shows the system’s focus ring | The component | The governed --ring outline, at 3:1 against its surround; it had none of its own. Measured with the stylesheet a consumer has.Asserted: Focus: every focusable control shows a ring that meets 3:1 against its surround |
| The words come first in the reading order | You | This is the words. Whether it precedes the image in your layout is document order on the review screen. |
| Outline, not fill; no ramp; no animation; never an alarm hue | You | Canvas rendering in your viewer. Nothing here draws a pixel. |
| Attribution is not evidence in the record | You | What you write to the Event log when the switch moves
is your choice; onShownChange gives you the moment, not the words. |
| The extent is the size of the mark, not of the lesion | You | extent is a string you write. “14 mm across the mark” is
honest; “14 mm lesion” is a measurement with a reproducibility nobody
stated — see Derived
measurement. |
A rule stated on a component page reads as a rule the component keeps. 7 of these 11 are; 4 are obligations this page is handing to the reader. The four are about pixels and layout, which is the boundary this package drew when it declined to ship a viewer.
Accessibility
- The finding is text before it is anything else, so it can be read by a screen reader, magnified, and dictated down a telephone.
- The marks control is a real switch — the Switch component, announced as on or off, with its effect immediate.
- The address is a button when it navigates, named by the address itself, and plain text when it does not.
- Counts carry their nouns — “3 marks”, “slice 47 of 300” — never a bare digit.
- Nothing here is a live region. A finding does not announce itself; the reader comes to it.
Outcomes of use
- Effectiveness — a reader can find the finding in their own viewer and knows what the model did and did not look at.
- Efficiency — the address is typed, not searched for.
- Satisfaction — not being handed a heatmap as an answer. Attributes believed to contribute to an outcome; the outcome is settled by observing real use, not by this page — see Usability & context of use.
Clinical safety notes
Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1). Which of these the component keeps and which are yours is stated in the guarantees table; a control the component does not keep is one your risk file must show you keep.
- The marks control cannot be labelled as an explanation. (the component) Mitigates: reliance on a reason the output does not contain.
- The finding is statable in words with an address. (the component) Mitigates: a finding that cannot be quoted, escalated, printed or read non-visually.
- Nothing-marked is scoped to what the model detects. (the component) Mitigates: a single-condition model read as a clearance of the whole study.
- Marks are off by default. (the component) Mitigates: anchoring — the reader’s impression formed by the model’s mark rather than the image.
- Navigation is recorded, assessment is not inferred. (the component) Mitigates: “the reader was shown it” mistaken for “the reader evaluated it”.
- The overlay is outline, not fill, and never an alarm hue. (you) Mitigates: the overlay obscuring the evidence it comments on; dilution of the alarm palette.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/spatial-finding.json
<SpatialFinding
label="Finding 1"
what="Region marked, left upper quadrant"
address={{ series: 4, slice: 47, of: 300 }} // required: the reader's units
extent="14 mm across the mark"
detector={{ name: "ForeCall", version: "2.4" }}
marks={{ kind: "available", count: 1, onShownChange: (shown) => log(shown) }}
onNavigate={(a) => viewer.goTo(a)} // the only callback; there is no onAssessed
/>
// marks.kind is available | none | partial | outside-scope. "none" needs a scope,
// and renders as "No regions marked for …" — never as normal.
Related
- Attribution overlays — what a mark may claim, and the rules this component holds.
- Derived measurement — when the extent of a mark becomes a number with a reproducibility.
- Reader paradigm — when marks may appear at all, relative to the reader’s first impression.
- Switch — the control that shows and hides the marks.
- Key–value pair — the same distinction between not-measured and not-applicable, for values rather than marks.