ui(properties): no raw typography left in the panel, and one heading rank

The earlier pass checked wiring only; this is the typography sweep. Raw Foreground/FontSize
labels are gone from the elements panel:

- Opacity, Timer (minutes) and URI are headings above their control (PropertyHeader), with the
  control row on the panel's 10px indent — the same shape as Border Thickness/Opacity.
- Scroll speed, scoreboard Opacity/Text size and their value readouts are PropertyFieldLabel.
- The drag hint and the scoreboard sign-in note are PropertyHint.
- Simulate (dev) keeps its orange, gains the field-label typography.
- Edge, Text colour and Background were PropertySubHeader/SubHeader: ONE heading rank now, so
  PropertySubHeader and the legacy SubHeader are deleted from Themes/Controls.xaml.
- DeveloperPanel — the last panel with raw typography and an unstyled CheckBox — now uses the
  same styles, and PropertiesStyleTests accepts PropertyPillCheck (the guard went stale when
  every check box became a pill switch). 18 tests pass; Release 0 warnings.
This commit is contained in:
2026-11-01 16:26:20 -08:00
parent 790cff45c2
commit 54902cb838
4 changed files with 47 additions and 48 deletions
+5 -5
View File
@@ -7,15 +7,15 @@
to a window or reference it from code that compiles in Release; the only host is
OverlayHost.xaml.cs inside #if DEBUG. -->
<StackPanel>
<TextBlock Text="Developer" Foreground="{StaticResource Brush.Attention}" FontSize="11"
FontWeight="SemiBold" Margin="0,10,0,2"/>
<TextBlock Text="Developer" Style="{StaticResource PropertyHeader}"
Foreground="{StaticResource Brush.Attention}"/>
<CheckBox Margin="10,2,0,0" IsChecked="{Binding DeveloperMode, Mode=TwoWay}"
Foreground="{StaticResource Brush.OnSurface}"
<CheckBox Style="{StaticResource PropertyPillCheck}" Margin="10,2,0,0"
IsChecked="{Binding DeveloperMode, Mode=TwoWay}"
Content="Developer mode — all resolutions, output budget ignored"/>
<TextBlock Text="Debug builds only. Overrides the free output profile (921,600 px / 30 fps). Never present in a release build."
Foreground="{StaticResource Brush.Attention}" FontSize="10" TextWrapping="Wrap"
Style="{StaticResource PropertyHint}" Foreground="{StaticResource Brush.Attention}"
Margin="10,4,0,8"/>
</StackPanel>
</UserControl>
+24 -26
View File
@@ -315,10 +315,10 @@
<StackPanel Visibility="{Binding SelectedElement.IsDraggable, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<StackPanel Visibility="{Binding SelectedElement.IsChatBox, Converter={StaticResource InverseBoolToVis}}">
<StackPanel Visibility="{Binding SelectedElement.IsBlur, Converter={StaticResource InverseBoolToVis}}">
<TextBlock Text="Opacity" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,0,0,2"/>
<DockPanel Margin="0,0,0,6">
<TextBlock x:Name="OpacityValueText" Text="100%" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
DockPanel.Dock="Right" VerticalAlignment="Center" Margin="8,0,0,0" Width="32"/>
<TextBlock Text="Opacity" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,6">
<TextBlock x:Name="OpacityValueText" Text="100%" Style="{StaticResource PropertyFieldLabel}"
DockPanel.Dock="Right" Margin="8,0,0,0" Width="32"/>
<Slider x:Name="OpacitySlider" Width="120" Minimum="0" Maximum="1"
Value="{Binding SelectedElement.Opacity, Mode=TwoWay}"
ValueChanged="OpacitySlider_ValueChanged" VerticalAlignment="Center"/>
@@ -375,7 +375,7 @@
<!-- The drag hint sits under the shape row — indented, so the
parentage shows. -->
<TextBlock Text="Drag to move · drag the corner dot to resize"
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" Margin="10,2,0,0"/>
Style="{StaticResource PropertyHint}" Margin="10,2,0,0"/>
</StackPanel>
<!-- Webcam geometry (TASK 57): under the vertical tier the camera
@@ -490,7 +490,7 @@
<StackPanel Visibility="{Binding SelectedElement.IsScoreboard, Converter={StaticResource BoolToVis}}"
IsEnabled="{Binding CanUseScoreboard}" Margin="0,0,0,8">
<TextBlock Text="Sign in to YouTube to use the scoreboard."
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,0,0,4"
Style="{StaticResource PropertyHint}" Margin="0,0,0,4"
Visibility="{Binding CanUseScoreboard, Converter={StaticResource InverseBoolToVis}}"/>
<!-- DEBUG builds only: prove the strip in the preview before a real
stream. Absent from Release, not merely hidden by a flag. -->
@@ -500,21 +500,21 @@
Visibility="{Binding IsDevelopmentBuild, Converter={StaticResource BoolToVis}}">
<!-- Dev-only affordances wear orange so a development control can never
be mistaken for product. -->
<TextBlock Text="Simulate (dev)" Foreground="{StaticResource Brush.Attention}" FontSize="12"/>
<TextBlock Text="Simulate (dev)" Style="{StaticResource PropertyFieldLabel}"
Foreground="{StaticResource Brush.Attention}"/>
</CheckBox>
<CheckBox Style="{StaticResource PropertyPillCheck}" Margin="0,0,0,4"
Content="Show the scoreboard" IsChecked="{Binding ScoreboardEnabled, Mode=TwoWay}"
ToolTip="Off hides the strip in every scene AND stops the channel polling."/>
<TextBlock Text="Edge (socials take the other)" Style="{StaticResource PropertySubHeader}"/>
<TextBlock Text="Edge (socials take the other)" Style="{StaticResource PropertyHeader}"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11" Margin="10,0,0,6"
ItemsSource="{Binding ScoreboardEdgeOptions}"
SelectedItem="{Binding ScoreboardEdge, Mode=TwoWay}"/>
<DockPanel Margin="10,0,0,6">
<TextBlock DockPanel.Dock="Left" Text="Scroll speed" Foreground="{StaticResource Brush.OnSurfaceVariant}"
FontSize="11" Width="76" VerticalAlignment="Center"/>
<TextBlock DockPanel.Dock="Right" Width="56" TextAlignment="Right" FontSize="11"
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"
<TextBlock DockPanel.Dock="Left" Text="Scroll speed" Style="{StaticResource PropertyFieldLabel}" Width="76"/>
<TextBlock DockPanel.Dock="Right" Width="56" TextAlignment="Right"
Style="{StaticResource PropertyFieldLabel}"
Text="{Binding ScoreboardScrollSpeed, StringFormat={}{0:0} px/s}"/>
<Slider Minimum="20" Maximum="200" SmallChange="10" LargeChange="40"
Value="{Binding ScoreboardScrollSpeed, Mode=TwoWay}" VerticalAlignment="Center"/>
@@ -540,7 +540,7 @@
IsChecked="{Binding ScoreboardShowLastGift, Mode=TwoWay}"/>
</WrapPanel>
<TextBlock Text="Text colour" Style="{StaticResource PropertySubHeader}"/>
<TextBlock Text="Text colour" Style="{StaticResource PropertyHeader}"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11" Margin="10,0,0,6"
ItemsSource="{Binding ScoreboardColorOptions}" DisplayMemberPath="Name"
SelectedValuePath="Hex" SelectedValue="{Binding ScoreboardTextColor, Mode=TwoWay}"/>
@@ -551,20 +551,18 @@
ItemsSource="{Binding ScoreboardColorOptions}" DisplayMemberPath="Name"
SelectedValuePath="Hex" SelectedValue="{Binding ScoreboardBackgroundColor, Mode=TwoWay}"/>
<DockPanel Margin="10,0,0,6">
<TextBlock DockPanel.Dock="Left" Text="Opacity" Foreground="{StaticResource Brush.OnSurfaceVariant}"
FontSize="11" Width="60" VerticalAlignment="Center"/>
<TextBlock DockPanel.Dock="Right" Width="42" TextAlignment="Right" FontSize="11"
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"
<TextBlock DockPanel.Dock="Left" Text="Opacity" Style="{StaticResource PropertyFieldLabel}" Width="60"/>
<TextBlock DockPanel.Dock="Right" Width="42" TextAlignment="Right"
Style="{StaticResource PropertyFieldLabel}"
Text="{Binding ScoreboardBackgroundOpacity, StringFormat={}{0:P0}}"/>
<Slider Minimum="0" Maximum="1" SmallChange="0.05" LargeChange="0.1"
Value="{Binding ScoreboardBackgroundOpacity, Mode=TwoWay}" VerticalAlignment="Center"/>
</DockPanel>
<DockPanel Margin="10,0,0,0">
<TextBlock DockPanel.Dock="Left" Text="Text size" Foreground="{StaticResource Brush.OnSurfaceVariant}"
FontSize="11" Width="60" VerticalAlignment="Center"/>
<TextBlock DockPanel.Dock="Right" Width="56" TextAlignment="Right" FontSize="11"
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"
<TextBlock DockPanel.Dock="Left" Text="Text size" Style="{StaticResource PropertyFieldLabel}" Width="60"/>
<TextBlock DockPanel.Dock="Right" Width="56" TextAlignment="Right"
Style="{StaticResource PropertyFieldLabel}"
Text="{Binding ScoreboardFontSizeLabel}"/>
<Slider Minimum="14" Maximum="24" SmallChange="1" LargeChange="2" TickFrequency="1" IsSnapToTickEnabled="True"
Value="{Binding ScoreboardFontSize, Mode=TwoWay}" VerticalAlignment="Center"/>
@@ -573,8 +571,8 @@
<!-- Countdown config -->
<StackPanel Visibility="{Binding SelectedElement.IsCountdown, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Timer (minutes)" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,0,0,2"/>
<DockPanel Margin="0,0,0,6">
<TextBlock Text="Timer (minutes)" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,6">
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
Width="22" Height="22" Margin="4,0,0,0"
Click="CountdownAccept_Click">
@@ -613,8 +611,8 @@
<TextBox Text="{Binding SelectedElement.Height, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}"/>
</DockPanel>
<TextBlock Text="URI" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="0,0,0,2"/>
<DockPanel>
<TextBlock Text="URI" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,0">
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
Width="22" Height="22" Margin="4,0,0,0" IsTabStop="False"
Click="WebAccept_Click">
@@ -852,7 +850,7 @@
<!-- Background properties -->
<StackPanel Margin="0,8,0,0"
Visibility="{Binding SelectedElement.IsBackground, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Background" Style="{StaticResource SubHeader}"/>
<TextBlock Text="Background" Style="{StaticResource PropertyHeader}"/>
<StackPanel Margin="4,0,0,0">
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="Use default background:" Style="{StaticResource PropertyFieldLabel}"/>
+8 -15
View File
@@ -147,9 +147,11 @@
(the guard test in ytLive.Tests/PropertiesStyleTests.cs enforces it). -->
<!-- ═══ Property-panel typography (2026-11-01) ═══
One hierarchy for every properties section, so no panel invents its own:
PropertyHeader — the category (bright, 11px, semibold)
PropertySubHeader — a field label inside it (dim, 10px, indented)
PropertyHint — the explanatory sentence under a control (dim, 10px, wrapped)
PropertyHeader — the heading of a group (bright, 11px, semibold)
PropertyFieldLabel — the label in a row with its control (dim, 10px, semibold)
PropertyHint — the explanatory sentence under a control (dim, 10px, wrapped)
ONE heading rank: a group is never sometimes a header and sometimes a sub-header, which
is what put Ticker out of step with Geometry (creator ruling, 2026-11-01).
Colour does the grouping: bright for the thing you are editing, dim for its labels and
help. No new palette entries; these are the existing OnSurface / OnSurfaceVariant tokens. -->
<Style x:Key="PropertyHeader" TargetType="TextBlock">
@@ -159,18 +161,9 @@
<Setter Property="Margin" Value="0,0,0,6"/>
</Style>
<!-- Sub-headings sit FLUSH LEFT, level with the category header; it is their smaller size and
dimmer colour that make them subordinate, not an indent. Only the controls indent. -->
<Style x:Key="PropertySubHeader" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceVariant}"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Margin" Value="0,6,0,3"/>
</Style>
<!-- A field label that sits in a row with its control (DockPanel.Dock="Left", fixed Width).
Same typography as PropertySubHeader, but vertically centred and marginless so the rows
stay on a grid. -->
<!-- A field label that sits in a row with its control (DockPanel.Dock="Left", fixed Width),
or a value readout beside a slider. Vertically centred and marginless so the rows stay on
a grid. -->
<Style x:Key="PropertyFieldLabel" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceVariant}"/>
<Setter Property="FontSize" Value="10"/>
+10 -2
View File
@@ -43,9 +43,17 @@ public class PropertiesStyleTests
AssertStyled(combo, "YtComboBox",
$"{where}: a ComboBox is not styled with YtComboBox — it renders with the light system template");
// Every check box is a pill switch (creator ruling, 2026-11-01: "all checkboxes as
// pill switches"), so PropertyPillCheck is the theme style that matters here; YtCheckBox
// is still accepted where a plain box is deliberate.
foreach (var check in doc.Descendants().Where(e => e.Name.LocalName == "CheckBox"))
AssertStyled(check, "YtCheckBox",
$"{where}: a CheckBox is not styled with YtCheckBox — it renders with the light system template");
{
var style = (string?)check.Attribute("Style") ?? string.Empty;
Assert.True(
style.Contains("PropertyPillCheck", StringComparison.Ordinal)
|| style.Contains("YtCheckBox", StringComparison.Ordinal),
$"{where}: a CheckBox carries no theme style — it renders with the light system template");
}
}
// 3. Every field in the properties panel carries the compact theme style and paints