| index e3264b0..d84859c 100644 |
| --- a/internal/slides/templates/signin.html |
| +++ b/internal/slides/templates/signin.html |
| @@ -18,29 +18,30 @@ |
| Bare rst-* attribute grammar throughout, never class="rst-*", and |
| no JavaScript: a form and a submit button are the whole screen. */}} |
| {{define "title"}}{{.Title}}{{end}} |
| +{{/* The app's name, not a link. Everything the shell bar can navigate |
| + to on this screen is behind the session gate, so the default |
| + brand — an <a href="/"> — would bounce straight back to the page |
| + it was clicked from. The account disclosure removes itself |
| + (tokens.css hides [rst-shell-account] when its menu is empty, and |
| + this page defines no "account" block), which leaves only the |
| + narrow-viewport hamburger, hidden below. */}} |
| +{{define "brand"}}<span rst-shell-brand>Slides</span>{{end}} |
| {{define "head"}}<style> |
| - /* No shell bar on the front door. The topbar shell writes |
| - [rst-shell-bar] on every page, outside any block a page could |
| - override, so this screen hides it in CSS — the only lever it has. |
| + /* The hamburger opens the bar's tail — nav, account, locale — and on |
| + this page every one of those is empty. A control that visibly does |
| + nothing is worse than no control, so it goes. |
| |
| - It hides it because there is nothing true to put in it: the |
| - default brand is a link to "/", which the session gate bounces |
| - straight back here, and below 800px the hamburger would open an |
| - empty tail. An empty strip of chrome that looks like it should do |
| - something is worse than none. |
| - |
| - display: none, not visibility or opacity: this must leave the |
| - accessibility tree too, or a screen-reader user tabs into an |
| - "Account" disclosure nobody can see. The shell's skip link is a |
| - sibling of the bar rather than inside it, so this page keeps a |
| - working skip-to-content. |
| + display: none, not visibility or opacity: it must leave the |
| + accessibility tree too, or a screen-reader user tabs into a |
| + disclosure nobody can see. The bar itself stays: naming the app on |
| + its own front door is worth a strip of chrome. |
| |
| Written as a descendant of [rst-shell-topbar] to match the |
| framework's own selector, and that is load-bearing rather than |
| - stylistic: tokens.css lays the bar out with |
| - `[rst-shell-topbar] [rst-shell-bar]`, specificity (0,2,0), which a |
| - bare `[rst-shell-bar]` at (0,1,0) would lose to. */ |
| - [rst-shell-topbar] [rst-shell-bar] { |
| + stylistic: tokens.css lays the control out with |
| + `[rst-shell-topbar] [rst-shell-menu]`, specificity (0,2,0), which a |
| + bare `[rst-shell-menu]` at (0,1,0) would lose to. */ |
| + [rst-shell-topbar] [rst-shell-menu] { |
| display: none; |
| } |
| /* One measure for the whole screen, shared by the callouts and the |
| @@ -55,21 +56,16 @@ |
| [slides-signin] { |
| max-inline-size: 32rem; |
| } |
| - /* The field and its button share a line, with real space between |
| - them. flex-basis 12rem with room to shrink, and min-inline-size: 0 |
| - because an <input>'s automatic minimum size is its content width — |
| - without the explicit 0 it refuses to shrink and the row overflows |
| - instead of reflowing at the 320px width WCAG 2.2 AA commits to. |
| - Below that the button wraps under the field rather than either one |
| - being squeezed unusable. */ |
| - [slides-signin-form] { |
| - display: flex; |
| - flex-wrap: wrap; |
| - align-items: center; |
| - gap: var(--rst-sp-3); |
| - } |
| + /* The field and the button stack, rather than sharing a line. One |
| + labelled field over its own submit is the shape that survives a |
| + 320px viewport without either control being squeezed, and it keeps |
| + the visible label directly above the input it names. |
| + |
| + min-inline-size: 0 on the input because an <input>'s automatic |
| + minimum size is its content width: without the explicit 0 it |
| + refuses to shrink and the box overflows instead of reflowing. */ |
| [slides-signin-form] [rst-input] { |
| - flex: 1 1 12rem; |
| + inline-size: 100%; |
| min-inline-size: 0; |
| } |
| </style>{{end}} |
| @@ -91,13 +87,24 @@ |
| {{template "callout" dict "Body" .ErrorMessage "Tone" "negative" "Alert" true}} |
| {{end}} |
| <div rst-box> |
| - <div rst-box-head> |
| - <p>{{.Body}}</p> |
| - </div> |
| - <form slides-signin-form method="post" action="/signin"> |
| + <p>{{.Body}}</p> |
| + {{/* field-text and form-foot rather than a hand-rolled label and |
| + button: AGENTS.md marks fields and form actions as vendored |
| + components to use, not to reimplement, and what they carry is |
| + the wiring that is easy to get subtly wrong — a real <label> |
| + bound to the input's id, aria-describedby listing exactly the |
| + ids that render, aria-invalid on an error. A placeholder is |
| + not a label. */}} |
| + <form slides-signin-form rst-form method="post" action="/signin"> |
| <input type="hidden" name="force" value="1"> |
| - <input rst-input type="email" name="address" placeholder="{{.AddressLabel}}" aria-label="{{.AddressLabel}}" autocomplete="email" required> |
| - <button rst-btn="primary" type="submit">{{.SubmitLabel}}</button> |
| + {{template "field-text" dict |
| + "Name" "address" |
| + "Label" .AddressLabel |
| + "Type" "email" |
| + "Autocomplete" "email" |
| + "Required" true |
| + "Autofocus" true}} |
| + {{template "form-foot" dict "Submit" .SubmitLabel}} |
| </form> |
| </div> |
| </div> |