Use shared form controls and enforce UI patterns (Refs #110)
CI / checks (push) Successful in 4m51s

This commit is contained in:
2026-10-10 00:19:48 -04:00
parent 8d1bb69591
commit cc9c10226e
54 changed files with 1431 additions and 1449 deletions
+38 -54
View File
@@ -1,4 +1,7 @@
<script lang="ts">
import Select from '$ui/components/primitives/Select.svelte';
import Field from '$ui/components/primitives/Field.svelte';
import Input from '$ui/components/primitives/Input.svelte';
import { onMount } from 'svelte';
import { timeFormat, timeZone, timeZonePersistenceError } from '$ui/stores/time-zone.js';
import { availableTimeZones, DEFAULT_TIME_ZONE } from '$lib/time-zone.js';
@@ -105,53 +108,57 @@
</p>{/if}
<form onsubmit={changePassword} aria-busy={busy}>
<fieldset disabled={busy || signingOut}>
<input
type="text"
class="password-username"
value={user.username}
autocomplete="username"
aria-label="Username"
readonly
tabindex="-1"
/>
<label for="current-password"
>{user.mustChangePassword ? 'Temporary password' : 'Current password'}<input
<Field label="Username" hiddenLabel class="password-username"
><Input
type="text"
value={user.username}
autocomplete="username"
aria-label="Username"
readonly
tabindex={-1}
/></Field
>
<Field
forId="current-password"
label={user.mustChangePassword ? 'Temporary password' : 'Current password'}
>
<Input
id="current-password"
name="currentPassword"
type="password"
autocomplete="current-password"
required
maxlength="128"
maxlength={128}
bind:value={currentPassword}
/></label
/></Field
>
<label for="new-password"
>New password<input
<Field forId="new-password" label="New password">
<Input
id="new-password"
name="newPassword"
type="password"
autocomplete="new-password"
required
minlength="8"
maxlength="128"
minlength={8}
maxlength={128}
bind:value={newPassword}
aria-describedby="new-password-help"
/></label
/></Field
>
<p id="new-password-help" class="hint">
Use 8–128 characters and a password you do not use elsewhere.
</p>
<label for="confirm-password"
>Confirm new password<input
<Field forId="confirm-password" label="Confirm new password">
<Input
id="confirm-password"
name="confirmPassword"
type="password"
autocomplete="new-password"
required
minlength="8"
maxlength="128"
minlength={8}
maxlength={128}
bind:value={confirmPassword}
/></label
/></Field
>
<div>
<Button
@@ -169,16 +176,15 @@
</section>
<section class="panel" aria-labelledby="display-heading">
<h2 id="display-heading">Display preferences</h2>
<label class="timezone"
>Timezone
<select bind:value={$timeZone} aria-describedby="timezone-help">
<Field class="timezone" label="Timezone">
<Select bind:value={$timeZone} aria-describedby="timezone-help">
{#each zones as zone}<option value={zone}
>{zone.replaceAll('_', ' ')}{zone === DEFAULT_TIME_ZONE
? ' (default)'
: ''}</option
>{/each}
</select>
</label>
</Select>
</Field>
<p id="timezone-help">
Dates and times throughout the app use this timezone, including daylight saving
changes. Saved automatically in this browser.
@@ -193,23 +199,14 @@
</section>
<style>
.timezone {
.panel :global(.timezone) {
display: grid;
gap: 8px;
margin-top: 16px;
font-size: var(--text-sm);
max-width: 420px;
}
.timezone select {
width: 100%;
padding: 10px 12px;
font: inherit;
color: var(--text-primary);
background: var(--surface-sunken);
border: 1px solid var(--border-control);
border-radius: var(--radius-sm);
}
.timezone ~ p {
.panel :global(.timezone ~ p) {
font-size: var(--text-sm);
color: var(--text-secondary);
}
@@ -263,21 +260,8 @@
gap: 18px;
min-width: 0;
}
label {
display: grid;
gap: 8px;
font-size: var(--text-sm);
}
input {
width: 100%;
padding: 12px;
color: var(--text-primary);
background: var(--surface-sunken);
border: 1px solid var(--border-control);
border-radius: var(--radius-md);
font: inherit;
}
.password-username {
.panel :global(.password-username) {
position: absolute;
width: 1px;
height: 1px;