Access control — 375 px, English · עברית
Frames 3a–3b at a phone width. One card per grant with the role select full-width, the 409 last-owner refusal inline on that card, the role matrix as a per-role list, and “Add person” as a BottomSheet.
en · ltr · 375 · access panel
en · ltr · 375 · 409 last owner
Access
409). Promote another person to list-owner first. The role was put back.
What each role can do
he · rtl · 375 · access panel
en · ltr · 375 · add person
Add person to Countries
Members of Northwind only.
Role — roles don't stack
Change values, translations and the list itself.
Translate labels; can't add or remove values.
Read only.
Owner and contributor are further down the list.
States at 375 px
Same states as frames 3a–3b.
loading
empty
error
Couldn't load access
No grants have been changed.
403 — read-only
Changing access needs manage_access (list-owner or org admin).
Rules the implementation must keep
- At ≤ 40em each grant is a card: name, email (an LTR island in RTL), granted-by/when, a full-width role select and a ≥ 44 px Remove button with an accessible name that includes the person.
- The 409 last-owner refusal lands on that card — select snapped back to
list-owner, the reason linked witharia-describedbyand announced (role="alert"). Never a toast that scrolls away; never pre-emptively hidden controls (the server decides who the last owner is). - The role matrix becomes one row per role listing what it can do — no sideways-scrolling table.
- Add person is a BottomSheet: org-scoped search, already-granted members shown but not selectable, roles as a radio list with their plain-language descriptions, and the confirm label names the person and role.
- RTL: role names are localized with the role id kept as an LTR island; names and emails are bidi-isolated.
Test hooks: [data-frame='17-mobile-access-control'] · [data-device='375'] · [data-panel='access-mobile'] · [data-locale='he'][data-dir='rtl'] · [data-grant='usr_01h455vb4pex5vsknk084sn02q'] · [data-sole-owner='true'] · [data-role-select] · [data-action='revoke-access'] · [data-action='add-access'] · [data-case='last-owner'] · [data-error='CONFLICT'] · [data-panel='role-matrix'] · [data-dialog='add-access'][data-presentation='bottom-sheet'] · [data-already-granted='true'] · [data-control='role'] · [data-state='loading'] · [data-state='empty'] · [data-state='error'] · [data-error='FORBIDDEN'] · [data-rules='mobile-access-control']
Playwright drives the real panel at { viewport: 375×812, locale: en | he-IL }: no horizontal scroll, demoting the sole owner shows the 409 inside
that grant's card with the select reverted, and every Remove button is ≥ 44 px.
Tokens only — no raw hex, spacing or type outside tokens.css / frame.css / mobile.css.