ui(properties): answer the visual review — quiet actions, real hint tone, dividers, legible disabled

Review findings from a screenshot of the Stream Alerts panel, each addressed:

- "Reset to built-in" and "Browse…" were YtButton-derived (white, semi-bold, 13px), so a reset
  action outranked the section header. Property-panel actions now use PropertyButton: bordered,
  11px, normal weight, OnSurface text.
- Explanation text and field labels were the same muted blue-gray. PropertyHint is now
  OnSurfaceDisabled, one clear step below a label.
- Groups inside a category ran together. PropertyDivider (1px OutlineVariant) separates Video,
  Sound byte and Message ticker.
- The disabled loop switch was near-invisible. Disabled now keeps the label at OnSurfaceVariant
  and dims the whole control to 0.55 — readable, obviously inactive.
- Switch "on" was full-strength accent, shouting about static state. The track now sits at 0.6
  opacity when on, which also stops it competing with the slider's accent fill.

No new palette entries: every change uses an existing token, so DESIGN.md stays the one colour
source. Release 0 warnings; token and typography tests pass.
This commit is contained in:
2026-11-01 15:53:40 -08:00
parent 8221bd6478
commit c297b3fb0f
2 changed files with 84 additions and 39 deletions
+37 -36
View File
@@ -291,7 +291,7 @@
<TextBlock Text="{Binding WebcamLockAlert}" Foreground="{StaticResource Brush.OnError}" FontSize="11"
TextWrapping="Wrap"/>
<Button Content="Retry" Command="{Binding RetryWebcamLockCommand}"
Style="{StaticResource YtButtonSecondary}" Padding="10,2"
Style="{StaticResource PropertyButton}" Padding="10,2"
HorizontalAlignment="Right" Margin="0,6,0,0"/>
</StackPanel>
</Border>
@@ -333,7 +333,7 @@
<TextBlock Text="Shape" Style="{StaticResource PropertyHeader}"/>
<StackPanel Orientation="Horizontal" Margin="10,0,0,4">
<Button Content="{Binding SelectedElement.MirrorButtonText}"
Style="{StaticResource YtButtonSecondary}" Padding="10,4"
Style="{StaticResource PropertyButton}" Padding="10,4"
Click="MirrorButton_Click"/>
<ToggleButton Style="{StaticResource SegmentToggle}" Padding="10,4" Margin="8,0,0,0"
IsChecked="{Binding SelectedElement.IsRectShape, Mode=TwoWay}">
@@ -369,8 +369,7 @@
Value="{Binding SelectedElement.BlurAmount, Mode=TwoWay}"
VerticalAlignment="Center"/>
</DockPanel>
<TextBlock Text="Strongest blur wins where they overlap — layers cannot add up."
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" TextWrapping="Wrap" Margin="10,2,0,0"/>
<TextBlock Text="Strongest blur wins where they overlap — layers cannot add up." Style="{StaticResource PropertyHint}"/>
</StackPanel>
<!-- The drag hint sits under the shape row — indented, so the
@@ -758,45 +757,24 @@
<!-- Stream Alerts config -->
<StackPanel Visibility="{Binding SelectedElement.IsAlertBox, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Stream Alerts" Style="{StaticResource PropertyHeader}"/>
<TextBlock Text="The built-in clip plays on every alert (Super Chat, membership…) unless a custom video is picked."
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" TextWrapping="Wrap" Margin="0,0,0,6"/>
<TextBlock Text="The built-in clip plays on every alert (Super Chat, membership…) unless a custom video is picked." Style="{StaticResource PropertyHint}"/>
<DockPanel Margin="0,0,0,4">
<TextBlock Text="Video" Style="{StaticResource PropertySubHeader}"/>
<DockPanel Margin="10,0,0,4">
<!-- Test alert (creator ruling 2026-11-01): plays the clip this box
would play — custom if one is set, built-in otherwise. -->
<Button DockPanel.Dock="Right" Content="▶ Test alert"
Command="{Binding TestAlertCommand}"
Style="{StaticResource YtButtonSecondary}"
Style="{StaticResource PropertyButton}"
Padding="8,2" FontSize="11" VerticalAlignment="Center"
ToolTip="Play the alert here, exactly as a viewer would trigger it"/>
<TextBlock DockPanel.Dock="Left" Text="Built-in video" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Built-in" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.AlertUseDefaultVideo, Mode=TwoWay}"
HorizontalAlignment="Left"/>
</DockPanel>
<!-- Sound byte (creator ruling 2026-11-01): plays WITH the alert clip, on
the default device, so it is audible and shows in the desktop meter.
The video itself always plays once to completion. -->
<TextBlock Text="Sound byte" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
FontWeight="SemiBold" Margin="0,6,0,2"/>
<DockPanel Margin="0,0,0,2">
<Button DockPanel.Dock="Right" Content="Browse…" Command="{Binding BrowseAlertAudioCommand}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
Padding="8,2" Margin="6,0,0,0" FontSize="11"/>
<Button DockPanel.Dock="Right" Content="✕" Command="{Binding ClearAlertAudioCommand}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
Padding="7,2" FontSize="11" ToolTip="Remove the sound byte"
Visibility="{Binding SelectedElement.HasAlertAudio, Converter={StaticResource BoolToVis}}"/>
<TextBox Text="{Binding SelectedElement.AlertAudioFileDisplay, Mode=OneWay}"
Style="{StaticResource YtTextBoxCompact}" IsReadOnly="True"
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
</DockPanel>
<CheckBox Content="Loop the sound until the video ends" Style="{StaticResource PropertyPillCheck}" Margin="0,2,0,6"
IsChecked="{Binding SelectedElement.AlertAudioLoop, Mode=TwoWay}"
IsEnabled="{Binding SelectedElement.HasAlertAudio}"/>
<Grid Margin="0,0,0,4"
<Grid Margin="10,0,0,4"
Visibility="{Binding SelectedElement.AlertUseDefaultVideo, Converter={StaticResource InverseBoolToVis}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -806,16 +784,39 @@
Style="{StaticResource YtTextBoxCompact}"
IsReadOnly="True" Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Browse…" Command="{Binding BrowseAlertVideoCommand}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource PropertyButton}"
Margin="4,0,0,0" Padding="8,2"/>
</Grid>
<Button Content="Reset to built-in" Command="{Binding ResetAlertVideoCommand}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
HorizontalAlignment="Left" Padding="8,2" Margin="0,0,4,0"/>
CommandParameter="{Binding SelectedElement}" Style="{StaticResource PropertyButton}"
HorizontalAlignment="Left" Padding="8,2" Margin="10,0,0,0"/>
<TextBlock Text="Custom clips are referenced from disk — never stored in the layout."
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" TextWrapping="Wrap" Margin="0,4,0,6"/>
Style="{StaticResource PropertyHint}"/>
<!-- Sound byte: plays WITH the clip, on the default device, so it is both
audible and visible in the desktop meter. -->
<Border Style="{StaticResource PropertyDivider}"/>
<TextBlock Text="Sound byte" Style="{StaticResource PropertySubHeader}"/>
<DockPanel Margin="10,0,0,2">
<Button DockPanel.Dock="Right" Content="Browse…" Command="{Binding BrowseAlertAudioCommand}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource PropertyButton}"
Padding="8,2" Margin="6,0,0,0" FontSize="11"/>
<Button DockPanel.Dock="Right" Content="✕" Command="{Binding ClearAlertAudioCommand}"
CommandParameter="{Binding SelectedElement}" Style="{StaticResource PropertyButton}"
Padding="7,2" FontSize="11" ToolTip="Remove the sound byte"
Visibility="{Binding SelectedElement.HasAlertAudio, Converter={StaticResource BoolToVis}}"/>
<TextBox Text="{Binding SelectedElement.AlertAudioFileDisplay, Mode=OneWay}"
Style="{StaticResource YtTextBoxCompact}" IsReadOnly="True"
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
</DockPanel>
<CheckBox Content="Loop until the video ends" Style="{StaticResource PropertyPillCheck}" Margin="10,4,0,2"
IsChecked="{Binding SelectedElement.AlertAudioLoop, Mode=TwoWay}"
IsEnabled="{Binding SelectedElement.HasAlertAudio}"/>
<TextBlock Text="The video always plays once, to completion. The sound may loop under it."
Style="{StaticResource PropertyHint}"/>
<Border Style="{StaticResource PropertyDivider}"/>
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Message ticker" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton Style="{StaticResource PillToggle}"
@@ -868,7 +869,7 @@
Style="{StaticResource YtTextBoxCompact}"
IsReadOnly="True" Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Browse…" Command="{Binding BrowseBackgroundCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="4,0,0,0" Padding="8,2"/>
Style="{StaticResource PropertyButton}" Margin="4,0,0,0" Padding="8,2"/>
</Grid>
<TextBlock Text="{Binding BackgroundSizeMismatchText}"
Foreground="{StaticResource Brush.Error}" FontSize="11" TextWrapping="Wrap"
+47 -3
View File
@@ -177,12 +177,52 @@
</Style>
<Style x:Key="PropertyHint" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceVariant}"/>
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceDisabled}"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Margin" Value="10,0,0,6"/>
</Style>
<!-- A property-panel action: bordered, quiet, 11px normal weight. The accent-filled YtButton
(white semi-bold 13px) made secondary actions like "Reset to built-in" the loudest text in
the panel, which is backwards. -->
<Style x:Key="PropertyButton" TargetType="Button">
<Setter Property="Background" Value="{StaticResource Brush.SurfaceContainerHigh}"/>
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurface}"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="Padding" Value="8,3"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{StaticResource Brush.Outline}" BorderThickness="1"
CornerRadius="{StaticResource Radius.Sm}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{StaticResource Brush.SurfaceContainerHighest}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceDisabled}"/>
<Setter TargetName="Bd" Property="Opacity" Value="0.6"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Hairline between groups inside one properties category. -->
<Style x:Key="PropertyDivider" TargetType="Border">
<Setter Property="Height" Value="1"/>
<Setter Property="Background" Value="{StaticResource Brush.OutlineVariant}"/>
<Setter Property="Margin" Value="0,10,0,8"/>
</Style>
<!-- Checkboxes are pill switches (creator ruling 2026-11-01): a 14px box in a properties panel
reads as a form, and a switch reads as a setting. IsChecked semantics are untouched, so
every existing binding still works. -->
@@ -216,6 +256,8 @@
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Track" Property="Background" Value="{StaticResource Brush.Tertiary}"/>
<Setter TargetName="Track" Property="BorderBrush" Value="{StaticResource Brush.Tertiary}"/>
<!-- 0.6 keeps the state readable without the neon fill the review flagged. -->
<Setter TargetName="Track" Property="Opacity" Value="0.6"/>
<Setter TargetName="Thumb" Property="Fill" Value="{StaticResource Brush.OnAccent}"/>
<Trigger.EnterActions>
<BeginStoryboard>
@@ -235,8 +277,10 @@
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceDisabled}"/>
<Setter TargetName="Track" Property="Opacity" Value="0.5"/>
<!-- Legible-but-dim, not invisible: the label stays readable and the
track drops back so "disabled" reads as a state, not as a bug. -->
<Setter Property="Foreground" Value="{StaticResource Brush.OnSurfaceVariant}"/>
<Setter Property="Opacity" Value="0.55"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>