Having decided a scrollable code block needs a role and a name, the obvious
move was to match its siblings. scroll-area/root and table/container both
use role="region". So: region.
That was wrong, and the way to find out was to count.
What the pages actually contain
/components/code 13 code blocks, 1 with a title, 2 with a language
/index 4 code blocks, 0 with a title, 0 with a languageA region is a landmark. It appears in the landmark list that screen-reader
users navigate by. Ten of the thirteen blocks on one page would have been
landmarks named “Code” — the same name, ten times, in the list meant to help
you skip around the page.
That is not a smaller version of a good outcome. It is a worse outcome than having no landmark at all.
The axis that actually matters
The siblings are regions for a reason that does not generalise: a data table or a scroll panel is one prominent thing, deliberately placed, and its name is a required prop the consumer must supply. A code block names itself and a documentation page renders one per fence.
Same runtime, same tab stop, opposite frequency — and frequency is the axis
that picks region from group. group names the tab stop and stays out of
the landmark list.
“The other two are regions” is consistency of implementation, not consistency of situation.
Two passes, one answer
A second reviewer reached group independently, before seeing the count, on
the required-prop-versus-self-naming argument. Two routes to the same call is
a better signal than either one alone — and it is the kind of check worth
setting up deliberately when a decision has no test that can fail.