Hold the time picker's two selectors apart
The 24-hour clock has no period selector beside the hour and minute boxes, so M3 widens them to 114px; AM/PM takes its own tertiary-container rather than borrowing the boxes' primary-container; a disabled period button uses M3's 12% disabled container, not 10%; and the pair is a radio group, which is what M3's labelling table asks for, so the arrows move between AM and PM. Plan step 20, findings IN-10, IN-11, IN-26 and IN-27. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
f30fd575cf
commit
9624d4bfd4
@@ -11,7 +11,9 @@
|
||||
* [data-timepicker-display] the time selector: hour and minute boxes 96×80 (display-large,
|
||||
* small corner; primary-container when selected, otherwise
|
||||
* surface-container-highest), a 24px separator, and the period
|
||||
* selector 52×80 beside them, 4px away
|
||||
* selector 52×80 beside them, 4px away. On a 24-hour clock
|
||||
* (data-cycle="24") there is no period selector and the boxes are
|
||||
* 114px, TimeSelector24HVerticalContainerWidth.
|
||||
* [data-timepicker-dial] 256px, surface-container-highest; numbers in body-large at radius
|
||||
* 101 (the 24-hour inner ring at 69), a primary selector — a 2px line,
|
||||
* an 8px centre and a 48px handle — with the number under the handle
|
||||
@@ -20,9 +22,11 @@
|
||||
* "Minute" (or the error) below in body-small, the period 52×72
|
||||
* [data-timepicker-actions] a 48px row: the mode toggle, then Cancel and OK
|
||||
*
|
||||
* The period selector is Compose's current one (ComposeMaterial3Flags.isUpdatedTimepickerToggleEnabled,
|
||||
* on by default): two toggle buttons 4px apart, round and surface-container-lowest when off, a 12px
|
||||
* corner and primary-container with a bold label when on, not the outlined pair of the tokens.
|
||||
* The period selector keeps Compose's current shape (ComposeMaterial3Flags.isUpdatedTimepickerToggleEnabled,
|
||||
* on by default): two buttons 4px apart, round and surface-container-lowest when off, a 12px corner
|
||||
* when on, not the outlined pair of the tokens. The selected colour is M3's own tertiary-container
|
||||
* with a bold label (PeriodSelectorSelectedContainerColor), which keeps AM/PM apart from the
|
||||
* primary-container hour and minute boxes. They are radios, so the state is `aria-checked`.
|
||||
*
|
||||
* In a landscape window with no room for the upright dial the dial variant lies on its side, as
|
||||
* Compose's HorizontalTimePicker does: the display with the period selector (216×38, 16px under it)
|
||||
@@ -130,6 +134,13 @@
|
||||
color: var(--md-sys-color-on-primary-container);
|
||||
}
|
||||
|
||||
/* A 24-hour clock has no period selector beside the boxes, and M3 widens them into the room
|
||||
(TimePickerTokens.TimeSelector24HVerticalContainerWidth = 114dp). Landscape keeps 96px: the
|
||||
display is a 216px column there. */
|
||||
[data-timepicker-display][data-cycle="24"] [data-timepicker-box] {
|
||||
width: 7.125rem;
|
||||
}
|
||||
|
||||
[data-timepicker-separator] {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -165,20 +176,22 @@
|
||||
}
|
||||
|
||||
[data-timepicker-period] > button:active,
|
||||
[data-timepicker-period] > button[aria-pressed="true"] {
|
||||
[data-timepicker-period] > button[aria-checked="true"] {
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
[data-timepicker-period] > button[aria-pressed="true"] {
|
||||
background-color: var(--md-sys-color-primary-container);
|
||||
color: var(--md-sys-color-on-primary-container);
|
||||
/* Tertiary, not the primary the hour and minute boxes take: M3 gives the two selectors different
|
||||
emphases (PeriodSelectorSelectedContainerColor = TertiaryContainer). */
|
||||
[data-timepicker-period] > button[aria-checked="true"] {
|
||||
background-color: var(--md-sys-color-tertiary-container);
|
||||
color: var(--md-sys-color-on-tertiary-container);
|
||||
font-weight: var(--md-ref-typeface-weight-bold);
|
||||
}
|
||||
|
||||
[data-timepicker-period] > button:disabled {
|
||||
cursor: default;
|
||||
color: color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);
|
||||
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent);
|
||||
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent);
|
||||
}
|
||||
|
||||
/* ---- The dial ----------------------------------------------------------------------------- */
|
||||
@@ -436,6 +449,10 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[data-timepicker-surface][data-mode="dial"] [data-timepicker-display][data-cycle="24"] [data-timepicker-box] {
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
[data-timepicker-surface][data-mode="dial"] [data-timepicker-display] [data-timepicker-period] {
|
||||
flex-direction: row;
|
||||
width: 13.5rem;
|
||||
|
||||
@@ -380,6 +380,21 @@ document.addEventListener('alpine:init', () => {
|
||||
this.aim()
|
||||
},
|
||||
|
||||
/**
|
||||
* AM and PM are a radio group, which M3's labelling table asks for: an arrow key moves to
|
||||
* the other option and selects it, and focus follows, as WAI-ARIA's radio group does.
|
||||
*/
|
||||
movePeriod(event) {
|
||||
const pm = !this.isPm
|
||||
|
||||
if (!this.periodAllowed(pm)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setPeriod(pm)
|
||||
event.currentTarget.querySelector(`[data-timepicker-period-option="${pm ? 'pm' : 'am'}"]`)?.focus()
|
||||
},
|
||||
|
||||
/** The allowed time in a period nearest to `time` (periodAllowed has said there is one). */
|
||||
nearestInPeriod(pm, time) {
|
||||
let best = null
|
||||
|
||||
@@ -31,14 +31,16 @@
|
||||
TimePickerTokens and TimeInputTokens — a surface-container-high dialog with an extra-large
|
||||
corner and elevation 3, a 256dp surface-container-highest dial with body-large numbers, a primary
|
||||
selector with a 48dp handle, a 2dp line and an 8dp centre, 96×80 time selector boxes in
|
||||
display-large (primary-container when selected), 96×72 time fields in display-medium — and
|
||||
TimePicker.kt and TimePickerDialog.kt for the layout, the 24-hour inner ring (12–23, at 69dp;
|
||||
00–11 outside at 101dp, as Material Components for Android labels them too), the gestures, the
|
||||
move on to minutes and the error texts. The period selector is Compose's current default
|
||||
(`isUpdatedTimepickerToggleEnabled`): two separate shape-morphing toggle buttons in
|
||||
primary-container, not the outlined pair its tokens still describe. The dial's numbers are
|
||||
aria-hidden; the dial is a `slider` whose value text names the hour or minute. The dialog is
|
||||
`wire:ignore`, so a Livewire render never closes an open picker or resets its draft. --}}
|
||||
display-large (primary-container when selected; 114dp wide on a 24-hour clock, where there is no
|
||||
period selector beside them, per TimeSelector24HVerticalContainerWidth), 96×72 time fields in
|
||||
display-medium — and TimePicker.kt and TimePickerDialog.kt for the layout, the 24-hour inner
|
||||
ring (12–23, at 69dp; 00–11 outside at 101dp, as Material Components for Android labels them
|
||||
too), the gestures, the move on to minutes and the error texts. The period selector keeps
|
||||
Compose's current default shape morph (`isUpdatedTimepickerToggleEnabled`) but M3's own
|
||||
tertiary-container, which is what holds hour/minute (primary-container) and AM/PM apart; it is a
|
||||
radio group, as M3's labelling table says, so the arrows move between AM and PM. The dial's
|
||||
numbers are aria-hidden; the dial is a `slider` whose value text names the hour or minute. The
|
||||
dialog is `wire:ignore`, so a Livewire render never closes an open picker or resets its draft. --}}
|
||||
|
||||
@props([
|
||||
'label' => null,
|
||||
@@ -211,7 +213,7 @@
|
||||
<h2 id="{{ $id }}-title" data-timepicker-title x-text="mode === 'dial' ? @js(__('Select time')) : @js(__('Enter time'))">{{ __('Select time') }}</h2>
|
||||
|
||||
<div data-timepicker-picker>
|
||||
<div data-timepicker-display>
|
||||
<div data-timepicker-display x-bind:data-cycle="is24 ? '24' : '12'">
|
||||
<div data-timepicker-numbers>
|
||||
<button
|
||||
type="button"
|
||||
@@ -236,13 +238,25 @@
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<div data-timepicker-period role="group" aria-label="{{ __('Select AM or PM') }}" x-show="! is24">
|
||||
<div
|
||||
data-timepicker-period
|
||||
role="radiogroup"
|
||||
aria-label="{{ __('Select AM or PM') }}"
|
||||
x-show="! is24"
|
||||
x-on:keydown.arrow-right.prevent="movePeriod($event)"
|
||||
x-on:keydown.arrow-left.prevent="movePeriod($event)"
|
||||
x-on:keydown.arrow-down.prevent="movePeriod($event)"
|
||||
x-on:keydown.arrow-up.prevent="movePeriod($event)"
|
||||
>
|
||||
@foreach ([false => 'am', true => 'pm'] as $pm => $period)
|
||||
<button
|
||||
type="button"
|
||||
data-timepicker-period-option="{{ $period }}"
|
||||
class="state-layer focus-ring"
|
||||
x-bind:aria-pressed="({{ $pm ? '' : '! ' }}isPm).toString()"
|
||||
role="radio"
|
||||
aria-checked="false"
|
||||
x-bind:aria-checked="({{ $pm ? '' : '! ' }}isPm).toString()"
|
||||
x-bind:tabindex="({{ $pm ? '' : '! ' }}isPm) ? 0 : -1"
|
||||
x-bind:disabled="! periodAllowed({{ $pm ? 'true' : 'false' }})"
|
||||
x-on:click="setPeriod({{ $pm ? 'true' : 'false' }})"
|
||||
x-text="periods[{{ (int) $pm }}]"
|
||||
@@ -330,13 +344,25 @@
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<div data-timepicker-period role="group" aria-label="{{ __('Select AM or PM') }}" x-show="! is24">
|
||||
<div
|
||||
data-timepicker-period
|
||||
role="radiogroup"
|
||||
aria-label="{{ __('Select AM or PM') }}"
|
||||
x-show="! is24"
|
||||
x-on:keydown.arrow-right.prevent="movePeriod($event)"
|
||||
x-on:keydown.arrow-left.prevent="movePeriod($event)"
|
||||
x-on:keydown.arrow-down.prevent="movePeriod($event)"
|
||||
x-on:keydown.arrow-up.prevent="movePeriod($event)"
|
||||
>
|
||||
@foreach ([false => 'am', true => 'pm'] as $pm => $period)
|
||||
<button
|
||||
type="button"
|
||||
data-timepicker-period-option="{{ $period }}"
|
||||
class="state-layer focus-ring"
|
||||
x-bind:aria-pressed="({{ $pm ? '' : '! ' }}isPm).toString()"
|
||||
role="radio"
|
||||
aria-checked="false"
|
||||
x-bind:aria-checked="({{ $pm ? '' : '! ' }}isPm).toString()"
|
||||
x-bind:tabindex="({{ $pm ? '' : '! ' }}isPm) ? 0 : -1"
|
||||
x-bind:disabled="! periodAllowed({{ $pm ? 'true' : 'false' }})"
|
||||
x-on:click="setPeriod({{ $pm ? 'true' : 'false' }})"
|
||||
x-text="periods[{{ (int) $pm }}]"
|
||||
|
||||
Reference in New Issue
Block a user