Skip to content

Accessibility: name every button, restore focus, title every screen - #48

Merged
yt314 merged 1 commit into
mainfrom
a11y-keyboard-and-names
Sep 18, 2026
Merged

yt314 merged 1 commit into
mainfrom
a11y-keyboard-and-names

Conversation

@yt314

@yt314 yt314 commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Three things a mouse user never notices, and a keyboard or screen-reader user runs into every single time.

1. Icon-only buttons with no accessible name

I measured the accessible name of every control on the real screens. Before:

Student card | unnamed buttons: 11  (whatsapp, envelope, times, pencil×4, trash×4)
Teacher profile | unnamed buttons: 5   | unnamed fields: 10 (all availability hours)
Payments     | unnamed buttons: 2   (delete payment)
Students     | unnamed buttons: 4   (edit parent)

A screen reader announced only "button" — eleven times in a row on a student card, four of them note deletions and one an unlink-parent. The pTooltip some of them carry does not help: it adds aria-describedby only while it is shown, and it is not a name.

After:

Student card | unnamed: 0
    ✓ pi-whatsapp → שליחת וואטסאפ אל דוד כהן
    ✓ pi-times    → ניתוק הקישור אל דוד כהן
    ✓ pi-trash    → מחיקת ההערה מ־08/09/2026 19:22
Payments | unnamed: 0
    ✓ pi-trash    → מחיקת התשלום של דוד כהן מ־08/09/2026
Profile  | unnamed: 0
    ✓ time        → שעת התחלה, חלון 1 ביום ראשון
    ✓ pi-times    → הסרת חלון 1 ביום שני

Each name identifies its row, because in a list of four notes "delete" on its own says nothing. The pattern already exists in the app — the notifications bell, the messages screen, the dark-mode toggle — it just was not applied here.

2. Focus lost after every dialog close

A. before open: BUTTON:סמן את הבא          → after Escape: BODY (focus lost)
B. before open: DIV:📌 חתונה במשפחה        → after Escape: BODY (focus lost)
C. before open: BUTTON:עריכת ההערה…        → after Escape: BODY (focus lost)

This happened on all 20 dialogs. PrimeNG's p-dialog moves focus inside on open (focusOnShow) and has no setting at all to put it back on close — I checked the library source. The result: after Escape you have to tab from the top of the page again.

The fix is a directive on the p-dialog selector, so every dialog in a component that imports it gets the behaviour without touching a template. It is deliberately cautious — it restores only if the trigger still exists in the document (after a delete it does not), and only if nothing else has taken focus meanwhile.

After:

A. after Escape: BUTTON:סמן את הבא
B. after Escape: DIV:📌 חתונה במשפחה — לא מלמדת
C. after Escape: BUTTON:עריכת ההערה מ־08/09/2026 19:22

3. The document title was "תלמידון" on every screen

/app/dashboard → תלמידון      /app/lessons  → תלמידון
/app/payments  → תלמידון      /app/reports  → תלמידון

In a single-page app there is no page load to announce a screen change, and the title is the only thing that says "you have moved". It is also what shows in the tab, in history and in bookmarks. After:

/app/dashboard → ראשי · תלמידון          /app/lessons → יומן שיעורים · תלמידון
/student/games → משחקים · תלמידון        /login       → התחברות · תלמידון
/             → תלמידון   (the home page stays as it was)

Each route's title matches the heading shown on the screen, so the tab and the page say the same thing.

Checked and found sound — left alone

  • <main> and an h1 are present on every teacher, parent and student screen.
  • lang="he" dir="rtl" on the document.
  • Focus rings are defined globally (:focus-visible).
  • Calendar lessons are keyboard reachable: Tab gets to them, Enter opens, focus enters the dialog.
  • The parent portal and the student portal — zero unnamed buttons or fields.

Testing

  • npx ng build — clean.
  • npx vitest run — 30 passing (app.spec.ts fails on main too, unrelated). The directive itself was verified in the browser rather than in a unit test: it acts on real focus and a real DOM, and the test environment here is Node without one.
  • Mouse regression: open, close via the X, quick search with Ctrl+K, and navigation between screens — all as before, with no JS errors.

Demo data untouched — 4 teachers, 3 students, 2 parents.

שלושה דברים שמי שעובדת בעכבר לא מרגישה בהם, ומי שעובדת במקלדת או בקורא
מסך נתקעת בהם.

1. כפתורי אייקון בלי שם. קורא מסך הקריא "לחצן" בלבד — בכרטיס תלמידה
   אחת עשרה פעמים ברצף, כולל מחיקת הערה וניתוק הורה. בפרופיל המורה גם
   עשרת שדות השעה של חלונות הזמינות היו בלי שם, כך שאי אפשר היה לדעת
   איזה יום ואיזה קצה. התוספת היא ariaLabel שמזהה את השורה: "מחיקת
   התשלום של דוד כהן מ־08/09/2026". הדפוס כבר קיים במערכת (פעמון
   ההתראות, מסך ההודעות) — רק לא הופעל כאן.

2. המיקוד אבד אחרי כל סגירת דיאלוג. p-dialog מעביר מיקוד פנימה בפתיחה
   ואין לו בכלל הגדרה להחזיר אותו בסגירה, כך שאחרי Escape המיקוד חזר
   ל-body וצריך היה לטאב מראש הדף. הנחיה על הבורר p-dialog זוכרת מה
   היה ממוקד ומחזירה — רק אם הכפתור עדיין קיים ואם אף אחד לא לקח את
   המיקוד בינתיים.

3. כותרת הדף הייתה "תלמידון" בכל מסך. באפליקציית עמוד יחיד אין טעינה
   שתכריז על מעבר מסך, והכותרת היא ההודעה היחידה שאומרת "עברת". נוספה
   TitleStrategy וכותרת לכל מסלול, זהה לכותרת שעל המסך.
@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
talmidon Ready Ready Preview Sep 18, 2026 7:53am UTC

@yt314
yt314 merged commit 1cd310b into main Sep 18, 2026
7 checks passed
@yt314 yt314 changed the title נגישות: שם לכל כפתור, החזרת מיקוד אחרי דיאלוג, וכותרת לכל מסך Accessibility: name every button, restore focus, title every screen Sep 18, 2026

This branch was successfully deployed

1 active deployment
Preview — 6bc8187c Deployed Sep 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant