Let the lock screen wear the theme instead of its own copy of it
ci / build and test (push) Successful in 1m13s
ci / android head (push) Failing after 5s
ci / api image (push) Successful in 23s

Four controls on this screen carried a full set of metrics each, and three of
them were a shared class rewritten by hand — UNLOCK is Button.primary down to
the font weight, the fingerprint button is Button.secondary, the passphrase box
is TextBox.field. Copies drift, and every difference between these and the
originals was drift rather than intent: corner radii of 6 from the first design
where v2's ladder says 9 for a button and 11 for a field, a label half a point
small, and the missing content alignment that put UNLOCK's word against the top
of its box and started this.

Two things fall out that are worth naming, because neither was the reported
fault. UNLOCK now has the pressed and disabled states the class carries, and
before it had neither — pressing it ran Fluent's default, which replaces the
accent fill with a theme grey, so the one button this application most wants to
look deliberate flashed the wrong colour under a thumb. And the passphrase box
is 44 rather than 48, which is what every other box on the phone is; the four
pixels bought it nothing that the button under it does not already give the
thumb.

RESET keeps two properties of its own against Button.danger, and they are the
two that carry meaning. That class is a DELETE beside a KEEP, where the red
outline says which of two offered actions costs something. Here there is no
pair, and a bordered red button under UNLOCK reads as the next thing to try
rather than as the last resort the screen means it to be.

The live-sessions disclosure is not a class anywhere — warn cards are plain
borders on all three screens that draw one — but its radius was 6 against the
12 the other two use, so it joins the ladder without joining a class.
This commit is contained in:
2026-08-03 14:08:30 +02:00
parent 6fb82a111e
commit 416f233657
@@ -49,45 +49,31 @@
the nearest thing to hand, and reaching past it to a button is the sort of friction that gets a
phone client called slow.
-->
<TextBox Classes="secret" Text="{Binding Passphrase}" PlaceholderText="vault passphrase"
Height="48" Padding="14,0" VerticalContentAlignment="Center"
Background="{StaticResource Field}" BorderBrush="{StaticResource BorderMid}"
BorderThickness="1" CornerRadius="6" Foreground="{StaticResource Text}"
FontFamily="{StaticResource MonoFont}" FontSize="12"
<TextBox Classes="field secret" Text="{Binding Passphrase}" PlaceholderText="vault passphrase"
IsEnabled="{Binding !IsBusy}">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding UnlockCommand}" />
</TextBox.KeyBindings>
</TextBox>
<!--
VerticalContentAlignment is not decoration and not a duplicate of the line above it. A button's
content alignment defaults to Stretch and the Fluent template binds it straight through, so the
text block filling a 48-pixel button draws its line at the top of it. Every button class in
Phone.axaml sets this; the three on this screen are the only ones on the phone that carry their
own metrics instead, which is how this one came to sit high in its box.
-->
<Button Content="UNLOCK" Command="{Binding UnlockCommand}" IsEnabled="{Binding !IsBusy}"
Height="48" Margin="0,10,0,0" HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Background="{StaticResource Accent}" Foreground="{StaticResource AccentInk}"
CornerRadius="6" FontFamily="{StaticResource MonoFont}" FontSize="12" FontWeight="SemiBold" />
<Button Classes="primary" Content="UNLOCK" Margin="0,10,0,0"
Command="{Binding UnlockCommand}" IsEnabled="{Binding !IsBusy}" />
<!--
Present only when this phone actually holds a device key. The design draws it unconditionally,
but offering a fingerprint that cannot open anything is worse than not offering one — see
AndroidDeviceKeyStore for the three ordinary ways it stops being available.
The label sets no font of its own: inside a Button.secondary it inherits the class's, which is
what stops this one reading half a point smaller than every other outlined button on the phone.
-->
<Button Command="{Binding UnlockWithDeviceCommand}" IsVisible="{Binding CanUnlockWithDevice}"
IsEnabled="{Binding !IsBusy}"
Height="48" Margin="0,8,0,0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Background="Transparent" BorderBrush="{StaticResource BorderMid}" BorderThickness="1"
CornerRadius="6" Foreground="{StaticResource Text}">
<Button Classes="secondary" Margin="0,8,0,0"
Command="{Binding UnlockWithDeviceCommand}" IsVisible="{Binding CanUnlockWithDevice}"
IsEnabled="{Binding !IsBusy}">
<StackPanel Orientation="Horizontal" Spacing="9">
<Ellipse Width="16" Height="16" Stroke="{StaticResource Accent}" StrokeThickness="1.5"
VerticalAlignment="Center" />
<TextBlock Text="UNLOCK WITH FINGERPRINT" FontFamily="{StaticResource MonoFont}"
FontSize="11" FontWeight="SemiBold" VerticalAlignment="Center" />
<TextBlock Text="UNLOCK WITH FINGERPRINT" VerticalAlignment="Center" />
</StackPanel>
</Button>
@@ -101,10 +87,15 @@
<!--
◆ The disclosure. Absent when there is nothing to disclose; never a card reading zero.
Not Border.card, and neither are its two counterparts on HOSTS and FILES: that class is a panel,
and this is a warning, so the background and the border are the warn pair rather than the chrome
one. What it does take is the radius the v2 ladder gives anything card-sized, which is what the
other two already draw.
-->
<Border IsVisible="{Binding HasLiveSessions}" Margin="0,22,0,0"
Background="{StaticResource WarnWash}" BorderBrush="{StaticResource WarnSoft}"
BorderThickness="1" CornerRadius="6" Padding="14,12">
BorderThickness="1" CornerRadius="12" Padding="14,12">
<StackPanel Spacing="6">
<StackPanel Orientation="Horizontal" Spacing="8">
<!-- Live, not Accent: this marks shells that are still running, which is a fact and not a
@@ -124,12 +115,17 @@
<!--
The only answer to a forgotten passphrase, and it is deliberately the last thing on the screen and
the only red one. Nothing can recover a passphrase; this empties the phone and starts again.
Two of Button.danger's properties are overridden rather than taken, and they are the two that would
change what this control says. Elsewhere that class is a DELETE sitting beside a KEEP, where a red
outline marks which of two offered actions costs something; here there is no pair, and a bordered
red button under UNLOCK would read as the second thing to try rather than as the last. Borderless
and Medium is what keeps it a way out rather than an offer. Everything else — the height, the
centring, the colour, the type — is the class's.
-->
<Button Grid.Row="2" Command="{Binding SignOutCommand}"
Margin="0,28,0,20" Padding="0,14" HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center" Background="Transparent" BorderThickness="0"
Foreground="{StaticResource Danger}" FontFamily="{StaticResource MonoFont}"
FontSize="10.5" FontWeight="Medium"
<Button Grid.Row="2" Classes="danger" Margin="0,28,0,20"
BorderThickness="0" FontWeight="Medium"
Command="{Binding SignOutCommand}"
Content="RESET THIS PHONE — forgot passphrase" />
</Grid>