Merge branch 'fix/menu-item-danger' into main
Merge fix/menu-item-danger: a destructive tone for menu rows menu.Item.Danger draws estilo-menu-item="danger", so destructive actions like Calls' "End meeting for everyone" read as dangerous. AA contrast in light and dark. Reviewed by GPT-6-Astra via Codex (#20): approve.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 files changed,
+39
−2
menu/menu.css+4 −0menu/menu.go+6 −0menu/menu_test.go+26 −0menu/templates/menu.html+3 −2
CI passed — run details
CI log
=== modcache === all modules verified === vet === CGO_ENABLED=0 go vet ./... === fmt === === build === CGO_ENABLED=0 go build -buildvcs=false ./... === go-test === CGO_ENABLED=0 go test -count=1 ./... ok amadan.net/oficina/estilo.git/base 0.003s ok amadan.net/oficina/estilo.git/brand 0.003s ok amadan.net/oficina/estilo.git/busy 0.003s ok amadan.net/oficina/estilo.git/callui 0.003s ok amadan.net/oficina/estilo.git/chrome 0.209s ? amadan.net/oficina/estilo.git/cmd/oficina-notices [no test files] ok amadan.net/oficina/estilo.git/credits 0.007s ok amadan.net/oficina/estilo.git/credits/catalog 0.006s ok amadan.net/oficina/estilo.git/homeconnect 0.006s ok amadan.net/oficina/estilo.git/internal/lint 0.007s ? amadan.net/oficina/estilo.git/internal/tokencheck [no test files] ok amadan.net/oficina/estilo.git/menu 0.009s ok amadan.net/oficina/estilo.git/savestatus 0.072s ok amadan.net/oficina/estilo.git/sky 0.021s ok amadan.net/oficina/estilo.git/theme 0.008s
diff --git a/menu/menu.css b/menu/menu.css| index 2ddf029..9070652 100644 |
| --- a/menu/menu.css |
| +++ b/menu/menu.css |
| @@ -63,6 +63,10 @@ |
| [estilo-menu-item]:hover, |
| [estilo-menu-item]:focus-visible { background: var(--rst-accent-soft); } |
| [estilo-menu-item]:focus-visible { outline: 2px solid var(--rst-accent); outline-offset: -2px; } |
| +/* A destructive row: the negative tone, and its own tint on hover. The |
| + label says what it does; the tone says it is the one to read twice. */ |
| +[estilo-menu-item~="danger"] { color: var(--rst-tone-negative-fg); } |
| +[estilo-menu-item~="danger"]:hover, [estilo-menu-item~="danger"]:focus-visible { background: var(--rst-tone-negative-bg); } |
| [estilo-menu-item][aria-disabled="true"], |
| [estilo-menu-item]:disabled { |
| color: var(--rst-text-faint); |
diff --git a/menu/menu.go b/menu/menu.go| index f143c47..9c21d78 100644 |
| --- a/menu/menu.go |
| +++ b/menu/menu.go |
| @@ -51,6 +51,12 @@ type Item struct { |
| Action string |
| // Dialog is the id of the <dialog> a button row opens. |
| Dialog string |
| + // Danger draws a destructive row in the negative tone |
| + // (estilo-menu-item="danger"). Calls, the fifth adopter, was the |
| + // first with one in a menu: "End meeting for everyone". The row still |
| + // goes to its own confirm page; the tone only says it is the one to |
| + // read twice. |
| + Danger bool |
| } |
| // Apple reports whether the request comes from an Apple platform, the |
diff --git a/menu/menu_test.go b/menu/menu_test.go| index 60c5158..40b50c2 100644 |
| --- a/menu/menu_test.go |
| +++ b/menu/menu_test.go |
| @@ -161,3 +161,29 @@ func TestApplePlatformDetection(t *testing.T) { |
| } |
| } |
| } |
| + |
| +// A destructive row ("End meeting for everyone", Calls' first need) is |
| +// drawn in the negative tone, as a link, a POST or a dialog alike, and |
| +// its hover is the negative tint, not the accent: colour is not its only |
| +// cue — the label says what it does — but a red row that lights up blue |
| +// says two things at once. |
| +func TestADangerItemIsDrawnInTheNegativeTone(t *testing.T) { |
| + for _, it := range []Item{{Href: "/m/x/end", Label: "End"}, {Action: "/m/x/end", Label: "End"}, {Dialog: "end", Label: "End"}} { |
| + it.Danger = true |
| + if html := render(t, "estilo-menu-item", it); !strings.Contains(html, `estilo-menu-item="danger"`) { |
| + t.Fatalf("a danger row is not marked: %s", html) |
| + } |
| + it.Danger = false |
| + if html := render(t, "estilo-menu-item", it); strings.Contains(html, "danger") { |
| + t.Fatalf("a plain row is marked danger: %s", html) |
| + } |
| + } |
| + for _, want := range []string{ |
| + `[estilo-menu-item~="danger"] { color: var(--rst-tone-negative-fg); }`, |
| + `[estilo-menu-item~="danger"]:hover, [estilo-menu-item~="danger"]:focus-visible { background: var(--rst-tone-negative-bg); }`, |
| + } { |
| + if !strings.Contains(string(CSS()), want) { |
| + t.Errorf("menu.css lacks %s", want) |
| + } |
| + } |
| +} |
diff --git a/menu/templates/menu.html b/menu/templates/menu.html| index f0982b8..dd668ae 100644 |
| --- a/menu/templates/menu.html |
| +++ b/menu/templates/menu.html |
| @@ -4,8 +4,9 @@ |
| platform's invoker command). All three work with scripts off; chrome.js |
| only adds the dialog for engines without invoker commands. Callers |
| supply the label in their own locale; Key is an already-formatted |
| - shortcut (menu.Shortcut). */}} |
| -{{define "estilo-menu-item"}}{{if .Action}}<form method="post" action="{{.Action}}"><button estilo-menu-item type="submit">{{template "estilo-menu-item-body" .}}</button></form>{{else if .Dialog}}<button estilo-menu-item type="button" commandfor="{{.Dialog}}" command="show-modal">{{template "estilo-menu-item-body" .}}</button>{{else}}<a estilo-menu-item href="{{.Href}}">{{template "estilo-menu-item-body" .}}</a>{{end}}{{end}} |
| + shortcut (menu.Shortcut). Danger marks a destructive row |
| + (estilo-menu-item="danger"), drawn in the negative tone. */}} |
| +{{define "estilo-menu-item"}}{{if .Action}}<form method="post" action="{{.Action}}"><button estilo-menu-item{{if .Danger}}="danger"{{end}} type="submit">{{template "estilo-menu-item-body" .}}</button></form>{{else if .Dialog}}<button estilo-menu-item{{if .Danger}}="danger"{{end}} type="button" commandfor="{{.Dialog}}" command="show-modal">{{template "estilo-menu-item-body" .}}</button>{{else}}<a estilo-menu-item{{if .Danger}}="danger"{{end}} href="{{.Href}}">{{template "estilo-menu-item-body" .}}</a>{{end}}{{end}} |
| {{define "estilo-menu-item-body"}}<span estilo-menu-label>{{.Label}}</span>{{with .Key}}<kbd estilo-menu-key>{{.}}</kbd>{{end}}{{end}} |
| {{/* estilo-icon-check: the glyph inside an estilo-menu-check column. |
| Lucide "check" (ISC, brand/LICENSE.lucide). */}} |