Repository navigation
The desktop surface has no automation properties at all, so the rollup tiles fixed for the web have no screen-reader equivalent #3057
Description
This is parked, not queued. It needs a decision before it needs an implementation, and it is deliberately not workable until that decision exists.
#3045 tied every rollup figure on both web pages to the text that says what it counts, using aria-describedby. The desktop surface has the identical construct and no equivalent.
The fact that makes it concrete
AutomationProperties appears in zero files across all three desktop trees — measured on origin/dev:
| tree | files referencing AutomationProperties |
|---|---|
Darling/PerformanceMonitor.Darling.Viewer |
0 |
PerformanceMonitor.Ui |
0 |
Lite |
0 |
So this is not "seven tiles lack descriptions." The desktop surface has no automation properties at all, anywhere, in either SKU. That is a larger and more accurate statement, and it is the one that belongs in the record.
MainWindow.xaml's FleetRollupContainer (line ~778) is the same number-over-label construct the web fix was about — fourteen TextBlocks inside it, seven number/label pairs.
Why it is a decision rather than a task
Three things make it unlike #3045, and each cuts against just doing it:
- UIA, not ARIA. The mechanism is
AutomationProperties.Name/HelpText/LabeledBy, notaria-describedby. The web fix's reasoning does not port: the ARIA choice turned on.numbeing a plain div mapping torole=generic, a name-prohibited role. WPF has no analogous prohibition, so the right carrier has to be chosen fresh rather than inherited. - Seven hand-written tiles, not one closure. The web fix was cheap and safe precisely because both pages build every tile through one builder, which is what let a pin assert
tiles == 1 && figures == 1per page and catch a hand-rolled eighth. The XAML has seven independent tiles, so the equivalent fix is either seven hand edits with no guard against the eighth, or a restructuring into a templated item — and the second is a real design change to the fleet rollup. - Nothing establishes the intended baseline. With zero occurrences repo-wide there is no house convention to follow and no partial adoption to complete. Whether this product intends to support screen readers on the desktop at all is a product question, not an implementation detail, and answering it by adding properties to one container would answer it by accident.
What a decision looks like
Any of these closes it, and the first two are seconds of work:
- Won't fix — the desktop surface is not a screen-reader target. Legitimate, and worth recording so the question stops recurring.
- Park until someone asks — keep it open as a known gap with no owner.
- Restructure the fleet rollup into a templated item and wire UIA once, then guard it the way
RollupTileDescriptionTestsguards the web pages. This is the real work and it is not small.
Why this is an issue rather than a chip
It was chipped first and that was the wrong call. Three reasons it needed an artifact:
- A chip is not durable. It lives until someone dismisses it and leaves nothing behind.
- The scoping above IS the content. An issue whose scope is "this needs a decision before an implementation" is scoped, not unscoped — the parked bucket exists for exactly that.
- A reviewer already claimed this gap does not exist. The automated review on Fixes #3045 #3054 asserted the desktop side has "no equivalent rollup-tile UI," which is flatly false, and it arrived as an issue comment rather than a review. Without something durable contradicting it, that comment is the record.
Provenance
Found by the #3045 lane while hoisting the shared helper, argued from chip to issue by fable-loop during #3054's review, and the zero-occurrence measurement re-run here across all three trees — it is broader than the Viewer alone, which is how it was first described.
Activity
erikdarlingdata commentedon Sep 6, 2026
Claude posting for Erik Darling
Adding the site detail and answering the
FinOpsTabquestion, since it changes the scope.FinOpsTab.xamlis clean, andViewerServerTab.xamlis notCounting
<TextBlock … FontSize="16"or larger — the "number" half of a number-over-label tile — across every XAML in the Viewer andPerformanceMonitor.Ui:ViewerServerTab.xamlMainWindow.xamlFinOpsTab.xamlSo
FinOpsTabdoes not have this shape — worth recording, because it was the file most likely to. The one-each files are almost certainly headers rather than tiles and should be confirmed as such rather than assumed, butViewerServerTab.xamlat 12 is the larger site and needs a census before any fix is scoped. A fix that coversMainWindowand misses that file would leave the bigger half undone.I checked the strict
Border > StackPanel > TextBlock(big) + TextBlock(small)pattern first and it returned zero forFinOpsTaband zero forMainWindow— which is wrong forMainWindow, whose tiles plainly are that shape. The regex was over-tight on whitespace. Recording that because a census done that way would report a clean sweep on a file that has five tiles.The confirmed shape in
FleetRollupContainerEach of five tiles:
Border(with a band-colouredBorderBrush) >StackPanel>TextBlock Text="{Binding TotalServers}" FontSize="22" FontWeight="Bold"overTextBlock Text="Servers" FontSize="10". Repeated forHealthyCount/Healthy,WarningCount/Warning,CriticalCount/Critical,OfflineCount/Offline.The mechanism question, stated as candidates rather than a choice
UIA, not ARIA, and the web fix's reasoning does not transfer — it turned on
.numbeing a plain div mapping torole=generic, a name-prohibited role. WPF has no analogous prohibition, so the carrier has to be chosen on its own merits:AutomationProperties.HelpTexton the number — closest analogue toaria-describedbyAutomationProperties.LabeledBypointing at the labelTextBlockAutomationProperties.Nameon the tileBorderWhich of these a screen reader actually announces for a non-focusable
TextBlockinside aBorderneeds deciding, not assuming. That is the part no amount of source reading settles.Why the restructuring is the decision and not a detail
The web tiles are built from one closure per page, which is why #3038 and PR #3054 were each a single edit no call site could opt out of — and why a pin could assert
tiles == 1 && figures == 1per page and catch a hand-rolled extra one. These are hand-written XAML, oneBorderper tile, so the same fix is N copies unless it goes into aStyle/ControlTemplateor a smallUserControlfirst. Doing that is most of the work, and without it there is no guard against the next hand-written tile.Prior art
#105 ([A11Y] Accessibility compliance gaps) was closed as completed on 2026-02-22. Worth reading before deciding won't-fix here — whatever was concluded then is the closest thing to a stated position on whether this surface is a screen-reader target, and
AutomationPropertiesbeing at zero across all three trees today is evidence about what that closure actually delivered.Unchanged
No screen reader has been run on any of this, on either surface. PR #3054's "Not verified" section says so for the web pages and it applies here with more force, since the whole question is what UIA announces.
erikdarlingdata commentedon Sep 6, 2026
Claude posting for Erik Darling
No PR. The gating decision this issue asks for already exists, in the negative — so the census and both design decisions are done and recorded here, and nothing was shipped.
#105 is the decision, and it declined this
Its body carries a Screen Reader Support section led by "Add
AutomationProperties.Nameto all buttons, icons, and status indicators". It was closed COMPLETED on 2026-02-22 with:Keyboard shortcuts were carved out as worth doing. Screen readers were not. So
AutomationPropertiessitting at zero is not an oversight — it is what that closure delivered, deliberately. A repo-wide search for accessibility / a11y / AutomationProperties / screen-reader returns #105, this issue, and the two web ones; nothing has revisited it.Shipping desktop UIA now would reverse a stated owner decision on a product question, by agent — which is the "answering it by accident" this issue was filed to avoid.
Counter-pressure, on the record: the web surface was wired in September 2026 for the same audience without revisiting #105. That weakens "not a target" as a blanket position but does not overturn it. The cost asymmetry is why the two were treated differently — one closure per web page versus 25 hand-written sites here.
Census: 25 real pairs in 3 files, across BOTH SKUs, zero wired
127 XAML files parsed with
System.Xml.Linq, floored so a sweep reading nothing fails loudly.Darling/PerformanceMonitor.Darling.Viewer/ViewerServerTab.xamlL2016–2052Lite/Controls/ServerTab.xamlL1476–1512Darling/PerformanceMonitor.Darling.Viewer/MainWindow.xamlL823–859Four corrections to what this issue and I previously said:
MainWindowhas 5 pairs, not seven. The earlier figure counted header and fleet-totals TextBlocks that are not tiles.ViewerServerTab's 10 pairs are INVERTED — label above value ("Physical Memory" FontSize=11thenPhysicalMemoryText FontSize=18). A value-first matcher returns zero tiles for that file. That is the same class of miss as the whitespace-tight regex recorded above, in the ordering dimension rather than the whitespace one. My "12" was a large-TextBlock count, not a pair count: 10 are strip values and 2 are the "No Plan Loaded" empty state.Litewas never censused and holds 10 of the 25. The earlier table covered the Viewer andPerformanceMonitor.Uionly, so a Viewer-only fix leaves an entire second SKU undone.FinOpsTabis clean in both SKUs, confirmed at FontSize floors 16 and 12. The one-each files are confirmed headers — literal static headings or singlex:Named window titles — rather than assumed. Sensitivity check: dropping the floor 16→12 takes large TextBlocks 107→555 but pairs only 31→36, all chrome.Correction to this issue's own framing
"The desktop surface has no automation properties at all" is wrong.
GetHelpTextCore()falls back toToolTip, andMainWindow'sTotalDeadlocksfigure already has one — so it already exposes UIA HelpText with zeroAutomationPropertiesin the file. The surface sets none explicitly. The real gap is exactly #3031's: two sibling text elements, correct reading order, no programmatic association.Decision 1 —
AutomationProperties.HelpTexton the figure. The other two are eliminated by source, not preference.FrameworkElementAutomationPeer.GetNameCore()is one chain:AutomationProperties.Name→LabeledBy→((FrameworkElement)Owner).GetPlainText().TextBlockAutomationPeerdoes not override it, andTextBlock.GetPlainText()returns its own text. So every figure already has a UIA Name equal to its number, for free — and bothNameandLabeledBysit upstream of it.LabeledByon the figure is actively destructive. It pre-emptsGetPlainText(), so the number announces as "Servers" and "12" is gone — worse than the status quo. Pointing it at theBorderinstead is a silent no-op, sinceGetLabeledByCore()iselement.GetAutomationPeer()andBorderhas none.Nameon the figure evicts the same way, and only works as a composed bound string that duplicates the figure.Nameon the tileBorderis inert.UIElement.OnCreateAutomationPeer()returnsnulland neitherBordernorDecoratoroverrides it, so no UIA element exists to carry it.HelpTextis the only additive carrier — a separate property, so Name stays "12" and the description arrives alongside it.Same carrier as the web fix, independently derived and for a different reason. Web:
role=genericis name-prohibited. WPF: no prohibition, butNameis already occupied by the figure and everything else is upstream of it.Decision 2 —
ItemsControl+DataTemplate, or aUserControl. Never aControlTemplate.TextBlockAutomationPeer.IsControlElementCore()returns a hardfalsewhenTemplatedParentis a templated control and not aContentPresenter. So aControlTemplatedrops the figures out of the UIA control view entirely — the restructuring this issue calls "most of the work" would, in that form, delete the very figures it was meant to make announceable.DataTemplate(ContentPresenter parent) andUserControl(null parent) are safe. A setters-onlyStyleis safe but cannot carry per-tile labels.Proposed split, if #105 is reversed: (1)
MainWindow's 5 tiles →ItemsControl+DataTemplatewithHelpTextwired once, plus the guard — theFleetRollupVM already computes the counts; (2)ViewerServerTab's 10, which need a new item VM since those are code-behind-assigned; (3)Lite's identical 10, plus parity between the SKUs. Any guard must be derived from shipped XAML, floored, and ordering-blind — an ordering-blind pin is precisely what would have stopped the first census readingViewerServerTabas clean.Not verified
No screen reader was run. Same state PR #3054 recorded for the web pages, and it binds harder here because the whole question is what UIA announces. Nothing was built, run or rendered; no UIA tree was inspected; no BAML produced. Every announcement mechanic above is read from
dotnet/wpfsource, not observed. No code was written, so there is no mutation testing to report.The one question
Does #105's "not relevant for this audience" still hold for the desktop SKUs now that the web is wired?