Skip to content

Commit 161785f

Browse files
Lay the wait palette out instead of picking it
Twenty-three wait categories had colours chosen one at a time, and it showed. Three failed the 3:1 contrast floor for a graphical object against the page - Buffer IO sat at 2.00, a blue so dark it was effectively a hole in the chart. Buffer IO and Parallelism shared an identical hue. Three oranges sat within 9 degrees of each other, three pinks within 28, and six near-grey categories were separated by nothing but lightness, two of them closer than the eye reliably splits. Now hue carries the family - greens for compute, blues for storage and network, warm for contention, grey for the buckets that mean "no information" - and within a family the categories climb a lightness ladder. That second part is the one that matters for a red-green colour deficiency, where the hues inside a family collapse and lightness is all that is left. Every colour clears the contrast floor, nearest neighbours are 12 apart in CIELAB with normal vision and 6 under simulated deuteranopia, with no outliers below that. Twenty-three categories cannot all be distinct by colour alone under that condition, which is what the legend is for; the ladder keeps neighbours in one chart apart. The plan viewer had a second, smaller scheme of its own drawn from the status triad: Lock in the error brush, Network in the success brush, I/O in the warning brush. That judged - a lock wait is not an error - and it coupled a wait category to a token meaning something else, so repainting "error" would have repainted "Lock". It also put red and green on two categories in a tool for DBAs. Those six now use the identity palette, which is what that palette asked for: one wait type, one colour, everywhere. Verified on screen against SQL2022 as well as by measurement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DEJ8FiSKJg44Ew2GG2nQ7
1 parent 9746891 commit 161785f

5 files changed

Lines changed: 65 additions & 42 deletions

File tree

‎src/PlanViewer.App/Controls/PlanViewerControl.WaitStats.cs‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,24 +180,30 @@ private static string GetWaitCategory(string waitType)
180180
}
181181

182182
/// <summary>
183-
/// The theme token each wait category is drawn in.
184-
///
185-
/// <para>I/O, Lock and Network map onto the semantic tokens that already mean the same thing
186-
/// elsewhere (warning, error, ok). CPU takes the Wait Stats panel's own accent. Memory has no
187-
/// semantic token of its own, so it borrows the one token left that stays distinct from the
188-
/// other five. "Other" is the unclassified bucket and gets the neutral muted foreground, which
189-
/// is what it always meant.</para>
183+
/// The theme token each wait category is drawn in — the same identity palette the Query Store
184+
/// charts use, so a wait type is one colour everywhere in the app.
190185
/// </summary>
186+
/// <remarks>
187+
/// <para>These used to be the status triad: Lock drawn in the error brush, Network in the
188+
/// success brush, I/O in the warning brush. Two things wrong with that. It judged — a lock wait
189+
/// is not an error and a network wait is not a success, and the wait palette's own rule is that
190+
/// these classify rather than judge. And it coupled a wait category to a token that means
191+
/// something else entirely, so repainting "error" would have quietly repainted "Lock".</para>
192+
///
193+
/// <para>It also put red and green on two categories in a tool for DBAs, which is the one pair
194+
/// a red-green colour deficiency cannot separate. The identity palette spaces its categories by
195+
/// lightness within each hue family for exactly that reason.</para>
196+
/// </remarks>
191197
private static string GetWaitCategoryBrushKey(string category)
192198
{
193199
return category switch
194200
{
195-
"CPU" => "InsightWaitsBrush",
196-
"I/O" => "WarningBrush",
197-
"Lock" => "ErrorBrush",
198-
"Memory" => "InsightServerBrush",
199-
"Network" => "SuccessBrush",
200-
_ => "ForegroundMutedBrush"
201+
"CPU" => "WaitCategory.CPU",
202+
"I/O" => "WaitCategory.Buffer IO",
203+
"Lock" => "WaitCategory.Lock",
204+
"Memory" => "WaitCategory.Memory",
205+
"Network" => "WaitCategory.Network IO",
206+
_ => "WaitCategory.Others"
201207
};
202208
}
203209
}

‎src/PlanViewer.App/Controls/WaitProfileBarControl.axaml.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ private void Redraw()
132132
private IBrush ResolveBrush(string category, bool isNamed)
133133
{
134134
if (!isNamed)
135-
return TryFindBrush("WaitCategory.Others", new SolidColorBrush(Color.Parse("#555D66")));
136-
return TryFindBrush($"WaitCategory.{category}", new SolidColorBrush(Color.Parse("#555D66")));
135+
return TryFindBrush("WaitCategory.Others", new SolidColorBrush(Color.Parse("#676E73")));
136+
return TryFindBrush($"WaitCategory.{category}", new SolidColorBrush(Color.Parse("#676E73")));
137137
}
138138

139139
private IBrush TryFindBrush(string key, IBrush fallback)

‎src/PlanViewer.App/Controls/WaitStatsProfileControl.axaml.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ private void Legend_Click(object? sender, RoutedEventArgs e)
117117
for (int i = 0; i < AllWaitCategories.Length; i++)
118118
{
119119
var cat = AllWaitCategories[i];
120-
var brush = TryFindBrush($"WaitCategory.{cat}", new SolidColorBrush(Color.Parse("#555D66")));
120+
var brush = TryFindBrush($"WaitCategory.{cat}", new SolidColorBrush(Color.Parse("#676E73")));
121121

122122
var swatch = new Border
123123
{

‎src/PlanViewer.App/Controls/WaitStatsRibbonControl.axaml.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,8 @@ private void Redraw()
293293
private IBrush ResolveBrush(string category, bool isNamed)
294294
{
295295
if (!isNamed)
296-
return TryFindBrush("WaitCategory.Others", new SolidColorBrush(Color.Parse("#555D66")));
297-
return TryFindBrush($"WaitCategory.{category}", new SolidColorBrush(Color.Parse("#555D66")));
296+
return TryFindBrush("WaitCategory.Others", new SolidColorBrush(Color.Parse("#676E73")));
297+
return TryFindBrush($"WaitCategory.{category}", new SolidColorBrush(Color.Parse("#676E73")));
298298
}
299299

300300
private IBrush TryFindBrush(string key, IBrush fallback)

‎src/PlanViewer.App/Themes/DarkTheme.axaml‎

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -229,30 +229,47 @@
229229
<!-- ===== 7. Wait Stats categories ======================================================== -->
230230

231231
<!-- Fixed per category: the same wait type must keep the same colour across every chart and
232-
legend in the app, so these are identity, not palette. Do not reuse them elsewhere. -->
233-
<SolidColorBrush x:Key="WaitCategory.Unknown" Color="#8E8E93"/>
234-
<SolidColorBrush x:Key="WaitCategory.CPU" Color="#00bd23"/>
235-
<SolidColorBrush x:Key="WaitCategory.Worker Thread" Color="#52E3B5"/>
236-
<SolidColorBrush x:Key="WaitCategory.Lock" Color="#EE5A24"/>
237-
<SolidColorBrush x:Key="WaitCategory.Latch" Color="#F368E0"/>
238-
<SolidColorBrush x:Key="WaitCategory.Buffer Latch" Color="#f5069e"/>
239-
<SolidColorBrush x:Key="WaitCategory.Buffer IO" Color="#2d13f2"/>
240-
<SolidColorBrush x:Key="WaitCategory.Compilation" Color="#A29BFE"/>
241-
<SolidColorBrush x:Key="WaitCategory.SQL CLR" Color="#b8dcdc"/>
242-
<SolidColorBrush x:Key="WaitCategory.Mirroring" Color="#54625f"/>
243-
<SolidColorBrush x:Key="WaitCategory.Transaction" Color="#c77416"/>
244-
<SolidColorBrush x:Key="WaitCategory.Preemptive" Color="#FD79A8"/>
245-
<SolidColorBrush x:Key="WaitCategory.Service Broker" Color="#E17055"/>
246-
<SolidColorBrush x:Key="WaitCategory.Tran Log IO" Color="#0984E3"/>
247-
<SolidColorBrush x:Key="WaitCategory.Network IO" Color="#75bbf8"/>
248-
<SolidColorBrush x:Key="WaitCategory.Parallelism" Color="#7B4FFF"/>
249-
<SolidColorBrush x:Key="WaitCategory.Memory" Color="#FDCB6E"/>
250-
<SolidColorBrush x:Key="WaitCategory.Tracing" Color="#B2BEC3"/>
251-
<SolidColorBrush x:Key="WaitCategory.Full Text Search" Color="#DFE6E9"/>
252-
<SolidColorBrush x:Key="WaitCategory.Other Disk IO" Color="#636E72"/>
253-
<SolidColorBrush x:Key="WaitCategory.Replication" Color="#81ECEC"/>
254-
<SolidColorBrush x:Key="WaitCategory.Log Rate Governor" Color="#FAB1A0"/>
255-
<SolidColorBrush x:Key="WaitCategory.Others" Color="#555D66"/>
232+
legend in the app, so these are identity, not palette. Do not reuse them elsewhere.
233+
234+
Laid out rather than picked. Hue carries the family — greens for compute, blues for storage
235+
and network, warm for contention, grey for the buckets that mean "no information" — and
236+
WITHIN a family the categories are spaced up a lightness ladder. That second part is what
237+
makes the chart readable to a red-green colour deficiency, where the hues inside a family
238+
collapse together and lightness is all that survives. Twenty-three categories cannot all be
239+
told apart by colour alone under that condition; the ladder is what keeps neighbours in the
240+
same chart from merging, and the legend is what answers the rest.
241+
242+
Every colour clears 3:1 against the page background, which is the floor for a graphical
243+
object you are expected to read. Three of them used to fail it — Buffer IO sat at 2.00,
244+
a saturated blue so dark it was effectively a hole in the chart — and two pairs shared a
245+
hue outright. Nearest neighbours are now 12 apart in CIELAB with normal vision and 6 under
246+
simulated deuteranopia, with no outliers below that.
247+
248+
If you repaint one, check it against its family's ladder and re-measure both numbers. -->
249+
<!-- Highlight is the selection stroke, not a category: white, and it stays that way. -->
250+
<SolidColorBrush x:Key="WaitCategory.Unknown" Color="#9CA1A5"/>
251+
<SolidColorBrush x:Key="WaitCategory.CPU" Color="#3EBD50"/>
252+
<SolidColorBrush x:Key="WaitCategory.Worker Thread" Color="#72E1A9"/>
253+
<SolidColorBrush x:Key="WaitCategory.Lock" Color="#FB6640"/>
254+
<SolidColorBrush x:Key="WaitCategory.Latch" Color="#FEB5ED"/>
255+
<SolidColorBrush x:Key="WaitCategory.Buffer Latch" Color="#E8469D"/>
256+
<SolidColorBrush x:Key="WaitCategory.Buffer IO" Color="#238CFA"/>
257+
<SolidColorBrush x:Key="WaitCategory.Compilation" Color="#A1A3FB"/>
258+
<SolidColorBrush x:Key="WaitCategory.SQL CLR" Color="#63C5C6"/>
259+
<SolidColorBrush x:Key="WaitCategory.Mirroring" Color="#30907E"/>
260+
<SolidColorBrush x:Key="WaitCategory.Transaction" Color="#DB9027"/>
261+
<SolidColorBrush x:Key="WaitCategory.Preemptive" Color="#FFA8C0"/>
262+
<SolidColorBrush x:Key="WaitCategory.Service Broker" Color="#FF9289"/>
263+
<SolidColorBrush x:Key="WaitCategory.Tran Log IO" Color="#8AC6FF"/>
264+
<SolidColorBrush x:Key="WaitCategory.Network IO" Color="#BBE2FE"/>
265+
<SolidColorBrush x:Key="WaitCategory.Parallelism" Color="#9A84FE"/>
266+
<SolidColorBrush x:Key="WaitCategory.Memory" Color="#FFE6B1"/>
267+
<SolidColorBrush x:Key="WaitCategory.Tracing" Color="#BDC8CE"/>
268+
<SolidColorBrush x:Key="WaitCategory.Full Text Search" Color="#D8F2FC"/>
269+
<SolidColorBrush x:Key="WaitCategory.Other Disk IO" Color="#0FB3E2"/>
270+
<SolidColorBrush x:Key="WaitCategory.Replication" Color="#5DF9ED"/>
271+
<SolidColorBrush x:Key="WaitCategory.Log Rate Governor" Color="#FFCACA"/>
272+
<SolidColorBrush x:Key="WaitCategory.Others" Color="#676E73"/>
256273
<SolidColorBrush x:Key="WaitCategory.Highlight" Color="#FFFFFF"/>
257274

258275
</ResourceDictionary>

0 commit comments

Comments
 (0)