Skip to content

fix(canvas): the edit-lock control shows locked and unlocked at a glance, without colour (v0.21.3) #335

Description

@MerciHanrim

The problem

The Controls rail's lock button looks almost the same in both states. Measured on production v0.21.2 (c5e7d8f):

  • The two icons differ only in the left end of the shackle: under 1 % of the button's pixels at 4× (92 of 10,816 in light), a few pixels at the real 14 px size. Seen at actual size, both read as a closed padlock.
  • The locked state's only other tell is a background change to surface-sunken: 1.07 : 1 against the unlocked background in light, 1.47 : 1 in dark, and none at all in forced colours (the system colours drop the token, and the lock is not in the rail's forced-colours pressed rule).
  • The other rail toggles (Focus, Filters, Group frame, Activity) mark ON with an inset 2 px ring and a tint (5.4 : 1 in light and dark) and with Highlight / HighlightText in forced colours (15 : 1).
  • The accessible name and the tooltip both name the next action ("Lock editing…" when unlocked, "Unlock editing…" when locked) while aria-pressed also flips, so the name changes with the state it is meant to report.

The control never contradicts the real state (in every measured case pressed, the closed-lock icon and the blocked edits agreed); the problem is that a locked canvas looks unlocked.

The contract (decided)

  • Unlocked: an open padlock. The shackle is lifted clearly and swung outward on one side, with a visible gap above the body.
  • Locked: a closed padlock, the shackle centred and fully seated in the body, plus the same pressed tell as Focus: background, inset ring and colour together.
  • Never colour alone: the two silhouettes must tell the states apart in greyscale and at the real display size.
  • Forced colours: the locked state uses the Highlight pair, like the other rail toggles.
  • The accessible name is fixed and does not change with the state, for example "Edit lock"; aria-pressed carries the state.
  • The tooltip names the next action: "Lock editing" / "Unlock editing" (with the existing explanation after it).
  • Light, dark and forced colours and keyboard focus are checked automatically.
  • The phone stays view-only and gets no lock button.

Before implementation

Two or three open-padlock mockups at the real size (14 px icon in the rail button, 1× and 2× device pixels), next to the closed one, in light, dark and forced colours, are compared and one is chosen. Judged at actual size, not only from enlarged crops.

Tests

  • The open icon, rendered at the real 1× size: a measurable gap of background pixels between the lifted end of the shackle and the body.
  • The closed icon at 1×: no such gap; the shackle meets the body on both sides.
  • Each state matches its committed baseline in light, dark and forced colours, stable across runs.
  • The pressed tell: the locked button's ring and background against the unlocked one meet 3 : 1 in light and dark; in forced colours the locked button is Highlight.
  • Keyboard focus stays visible on a locked button (the pressed tell must not hide the focus ring).
  • The name stays the same across a toggle, aria-pressed follows the state, and the tooltip names the next action, in every shipped language.

Out of scope

Released together with #334 as v0.21.3, in one pull request: #334's behaviour first, then the mockups, then this. Related: #330 starts (v0.22.0) after this release and the v0.21.4 silhouette work.

Activity

  1. changed the title [-]fix(canvas): the edit-lock control shows locked and unlocked at a glance, without colour (v0.21.4)[/-] [+]fix(canvas): the edit-lock control shows locked and unlocked at a glance, without colour (v0.21.3)[/+] on Oct 7, 2026
  2. MerciHanrim commented on Oct 8, 2026

    @MerciHanrim
    OwnerAuthor

    Shipped in v0.21.3 by #336, together with #334, squash-merged as 70a9c37 on 2026-10-08 at 10:41 Seoul time (01:41 UTC). Its tree is identical to the approved pull request head fb7ad93.

    • Scope delivered: unlocked is an open padlock with its shackle swung to the side, the free end 2 px clear of the body at 1×; locked is the closed padlock with the rail's pressed tell (the same tint, inset ring and colour as Focus; Highlight / HighlightText in forced colours). The accessible name is fixed, "Edit lock", in 18 languages; aria-pressed carries the state and the tooltip names the next action. The phone stays view-only, with no lock button. Six new baselines pin the button in light, dark and forced colours, unlocked and locked.
    • Pull request CI (run 37712395950 at fb7ad93, attempt 1): every job passed, the aggregate e2e job included; the five shards ran exactly the 2,030 listed tests, each once (2,023 passed, 7 skipped by design, 0 failed, 0 retried); the production bundle 16 of 16, the PWA 19 of 19.
    • Main CI (run 37714159715 at 70a9c37, attempt 1): every job passed, the aggregate e2e job included; the 2,030 listed tests each ran: 2,022 passed, 7 skipped by design, 0 failed, 1 flaky / 1 retry. The retried test, frame-label-locale-switch.spec.ts:142, is outside this change and passed without a retry on the same tree in the pull request run; it is recorded in test(e2e): the zh-Hant descriptive-copy wrapping test slows down on CI and ended a browser session #305.
    • Production: https://cozy-loop-studio.pages.dev serves v0.21.3 · build 70a9c37. The same automated check passed 41 of 41, including, in light, dark and forced colours: a 2 px gap on the open icon at 1× and none on the closed one; the fixed name with aria-pressed and the tooltip following the state; the pressed tell equal to Focus in light and dark and the Highlight pair in forced colours; a visible keyboard focus ring on the locked button; and no lock button on the phone. No manual screen-reader check was made; the accessibility claims are the automated name, state, focus and keyboard checks.

    Out of scope, as decided in this issue: making the other rail toggles' names follow the same rule, a separate memo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions