Finish the keyboards, and say a state once
Shift+M and Shift+Y reach the month and year dropdowns, which is M3's date picker keyboard table; Home and End go to the ends of an open combobox list, and stay the field's own caret keys when it is closed. The password reveal drops aria-pressed and keeps only its flipping label, so a screen reader hears the state once rather than twice and inverted. The radio's `inline` now says that M3 cautions against a row and wants an option chosen on load. Plan step 20, findings IN-22, IN-23, IN-28 and IN-29. 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
856b6f476b
commit
0074f9bd5c
@@ -7,8 +7,10 @@
|
||||
you are free" is seven filter chips, not a dropdown opened seven times. `single` makes them
|
||||
choice chips, one on at a time.
|
||||
- **`searchable`**, for a list too long to lay out — four hundred time zones. A text field
|
||||
that filters as you type, with the matches in M3's menu under it: the arrow keys move,
|
||||
Enter chooses, Escape puts the field back. One value only. The list is a popover in the top
|
||||
that filters as you type, with the matches in M3's menu under it: the arrow keys move, Home
|
||||
and End go to the ends of the open list (with it closed they are the field's own and move the
|
||||
caret), Enter chooses, Escape puts the field back — WAI-ARIA's combobox keyboard, since M3
|
||||
has no combobox. One value only. The list is a popover in the top
|
||||
layer, placed by CSS anchor positioning, so a card's clipping never cuts it off. `icon`,
|
||||
`variant` and `placeholder` are the field's.
|
||||
|
||||
@@ -104,6 +106,14 @@
|
||||
this.active = (this.active + step + this.filtered.length) % this.filtered.length;
|
||||
this.$nextTick(() => this.reveal());
|
||||
},
|
||||
// The APG combobox puts Home and End at the ends of the open popup. With the popup
|
||||
// closed they are the text field's own, and move the caret.
|
||||
jump(event, last) {
|
||||
if (! this.open || this.filtered.length === 0) return;
|
||||
event.preventDefault();
|
||||
this.active = last ? this.filtered.length - 1 : 0;
|
||||
this.$nextTick(() => this.reveal());
|
||||
},
|
||||
choose(option) {
|
||||
if (! option || option.disabled) return;
|
||||
this.value = option.value;
|
||||
@@ -139,6 +149,8 @@
|
||||
x-on:input="open = true; active = 0"
|
||||
x-on:keydown.arrow-down.prevent="move(1)"
|
||||
x-on:keydown.arrow-up.prevent="move(-1)"
|
||||
x-on:keydown.home="jump($event, false)"
|
||||
x-on:keydown.end="jump($event, true)"
|
||||
x-on:keydown.enter.prevent="choose(filtered[active])"
|
||||
x-on:keydown.escape="close()"
|
||||
x-on:keydown.tab="close()"
|
||||
|
||||
@@ -286,7 +286,7 @@
|
||||
<div data-datepicker-nav x-show="presentation === 'modal'">
|
||||
<button
|
||||
type="button"
|
||||
data-datepicker-menu-button
|
||||
data-datepicker-menu-button="years"
|
||||
x-on:click="toggleView('years')"
|
||||
x-bind:aria-expanded="(view === 'years').toString()"
|
||||
x-bind:aria-label="monthYear + ', ' + @js(__('Switch to selecting a year'))"
|
||||
@@ -308,7 +308,7 @@
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
data-datepicker-menu-button
|
||||
data-datepicker-menu-button="months"
|
||||
x-on:click="toggleView('months')"
|
||||
x-bind:aria-expanded="(view === 'months').toString()"
|
||||
x-bind:aria-label="monthLabel + ', ' + @js(__('Switch to selecting a month'))"
|
||||
@@ -327,7 +327,7 @@
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
data-datepicker-menu-button
|
||||
data-datepicker-menu-button="years"
|
||||
x-on:click="toggleView('years')"
|
||||
x-bind:aria-expanded="(view === 'years').toString()"
|
||||
x-bind:aria-label="yearLabel + ', ' + @js(__('Switch to selecting a year'))"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
{{-- A password field, with M3's trailing eye to show what was typed.
|
||||
|
||||
The eye flips the control's `type` and nothing else, so the value never leaves the field and a
|
||||
password manager still sees a password input on load. Its label says what pressing it will do.
|
||||
password manager still sees a password input on load. Its label says what pressing it will do —
|
||||
M3's own pattern for an interactive trailing icon ("Show password" / "Hide password") — and it
|
||||
carries no `aria-pressed`, which would say the state a second time and the other way round.
|
||||
`label`, `hint`, `icon`, `variant`, `size`; every other attribute reaches the `<input>`
|
||||
(`autocomplete="current-password"`, `wire:model`). --}}
|
||||
|
||||
@@ -39,9 +41,7 @@
|
||||
type="button"
|
||||
x-on:click="shown = ! shown"
|
||||
x-bind:aria-label="shown ? @js(__('Hide password')) : @js(__('Show password'))"
|
||||
x-bind:aria-pressed="shown"
|
||||
aria-label="{{ __('Show password') }}"
|
||||
aria-pressed="false"
|
||||
class="field-trailing field-button"
|
||||
data-field-reveal
|
||||
>
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
(600px, M3's compact/medium boundary — compact always stacks), and
|
||||
`value` for the option chosen when the page loads (with `wire:model`, Livewire sets it).
|
||||
Errors are read from the bag under the `wire:model` name
|
||||
(resources/css/components/selection.css). --}}
|
||||
(resources/css/components/selection.css).
|
||||
|
||||
M3 stacks radio buttons vertically and cautions against a row at any width, so `inline` is for
|
||||
a group of two or three short labels that would waste a column, not the shape to reach for.
|
||||
It also asks for five options or fewer and for one of them to be chosen when the page loads:
|
||||
give the group a `value` (or a bound property that already holds one). --}}
|
||||
|
||||
@props([
|
||||
'label' => null,
|
||||
|
||||
Reference in New Issue
Block a user