Skip to content

A11y: Improve page navigation#3264

Merged
samuelwei merged 12 commits into
developfrom
a11y-aria-roles-landmarks
Jul 3, 2026
Merged

A11y: Improve page navigation#3264
samuelwei merged 12 commits into
developfrom
a11y-aria-roles-landmarks

Conversation

@samuelwei

@samuelwei samuelwei commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Type

  • Bugfix
  • Feature
  • Documentation
  • Refactoring (e.g. Style updates, Test implementation, etc.)
  • Other (please describe): Accessibility

Checklist

  • Code updated to current develop branch head
  • Passes CI checks
  • Is a part of an issue
  • Tests added for the bugfix or newly implemented feature, describe below why if not
  • Changelog is updated
  • Documentation of code and features exists

Changes

  • Added landmark role to all search fields
  • Added skip links
  • Changed focus on page change back to page start
  • Added announce for screen reader about page change

Other information

Summary by CodeRabbit

  • New Features
    • Added skip links, an on-page route change announcer, and a reusable Page Title heading across multiple screens.
  • Accessibility
    • Expanded localized ARIA/navigation strings and improved focus management on navigation (including focus styling and keyboard behavior).
    • Enhanced search accessibility by using native search semantics consistently (e.g., type="search" and <search> wrappers) across key views and controls.
  • UI Updates
    • Updated navigation/header and footer layout for clearer structure and consistent separators.

@samuelwei samuelwei marked this pull request as ready for review June 23, 2026 15:53
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@samuelwei, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 849f4ee4-e9f1-49de-b8da-2c37b46ae142

📥 Commits

Reviewing files that changed from the base of the PR and between c189c83 and bd06033.

📒 Files selected for processing (1)
  • CHANGELOG.md

Walkthrough

Adds accessibility navigation primitives, route-driven page titles, page-title components, semantic search wrappers, and changelog notes.

Changes

Accessibility and Page Title Infrastructure

Layer / File(s) Summary
Shared primitives and config
resources/js/composables/useMenu.js, resources/js/stores/route.js, resources/js/PageTitle.vue, lang/en/app.php, resources/css/app/_navigation.css, vite.config.common.js
Introduces menu breakpoint helpers, the route title store, the page-title component, accessibility translation keys, navigation CSS additions, and Vue compiler support for the search tag.
Skip links and shell navigation
resources/js/components/SkipLinks.vue, resources/js/components/App.vue, resources/js/components/MainNav.vue, resources/js/components/MainNavLogo.vue, resources/js/components/AppFooter.vue, resources/js/views/RouteAnnouncer.vue
Adds skip links and route announcements, updates shell focus handling, emits route-change events from main navigation actions, and restructures footer navigation markup.
Router, admin layout, and room title flow
resources/js/router.js, resources/js/views/AdminLayout.vue, resources/js/views/RoomsView.vue, resources/js/views/RoomsIndex.vue
Adds route metadata titles and after-each title updates, composes admin titles from breadcrumbs, and routes room title and focus updates through the shared store and page-title element.
PageTitle adoption in views
resources/js/views/Login.vue, resources/js/views/Logout.vue, resources/js/views/Profile.vue, resources/js/views/MeetingsIndex.vue, resources/js/views/NotFound.vue, resources/js/components/RoomHeader.vue
Replaces inline heading markup with PageTitle across login, logout, profile, meetings, not-found, and room header views.
Semantic search wrappers
resources/js/components/RoomTab*.vue, resources/js/views/Admin*Index.vue, resources/js/views/RoomsIndex.vue, resources/js/views/MeetingsIndex.vue
Changes search controls to use search wrappers and type="search" inputs across room, admin, rooms index, and meetings views.
Changelog accessibility notes
CHANGELOG.md
Updates the unreleased changelog accessibility notes and adds the [#3264] reference definition.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: frontend, UI

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the accessibility-focused page navigation changes.
Description check ✅ Passed The description mostly matches the template and clearly lists the main accessibility changes, checklist, and type.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch a11y-aria-roles-landmarks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.40559% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.11%. Comparing base (1b716b9) to head (bd06033).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
resources/js/components/SkipLinks.vue 80.00% 5 Missing ⚠️
resources/js/stores/route.js 81.81% 2 Missing ⚠️
resources/js/components/MainNav.vue 94.11% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #3264      +/-   ##
=============================================
- Coverage      97.15%   97.11%   -0.04%     
  Complexity      1948     1948              
=============================================
  Files            477      483       +6     
  Lines          16546    16646     +100     
  Branches        2407     2407              
=============================================
+ Hits           16075    16166      +91     
- Misses           471      480       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cypress

cypress Bot commented Jun 23, 2026

Copy link
Copy Markdown

PILOS    Run #3144

Run Properties:  status check passed Passed #3144  •  git commit bd060335c5: A11y: Improve page navigation
Project PILOS
Branch Review a11y-aria-roles-landmarks
Run status status check passed Passed #3144
Run duration 08m 01s
Commit git commit bd060335c5: A11y: Improve page navigation
Committer Samuel Weirich
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 635
View all changes introduced in this branch ↗︎

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
resources/js/PageTitle.vue (1)

1-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move PageTitle.vue into resources/js/components to match repository conventions.

This is a reusable component and should live under resources/js/components (for example, resources/js/components/PageTitle.vue) with updated imports.

As per coding guidelines, "Reusable components should be stored in resources/js/components".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/PageTitle.vue` around lines 1 - 14, Move the reusable PageTitle
component into resources/js/components to match the project convention, and
update any imports/usages that reference PageTitle.vue so they point to the new
location. Keep the component definition intact (the PageTitle.vue script setup
and template) and ensure all consumers now import it from
resources/js/components/PageTitle.vue.

Source: Coding guidelines

resources/js/views/RouteAnnouncer.vue (1)

10-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clarify the initial-load suppression logic.

initialLoad.value = initialLoad.value === null is correct (it suppresses the very first announcement triggered by the initial navigation) but obscure. A named flag reads more clearly and avoids accidental breakage on future edits.

♻️ Suggested refactor
-const title = ref(null);
-const initialLoad = ref(null);
+const title = ref(null);
+const hasNavigatedOnce = ref(false);

 watch(
   () => routeStore.pageTitle,
   function (newPageTitle) {
-    initialLoad.value = initialLoad.value === null;
-    title.value = newPageTitle;
+    title.value = newPageTitle;
+    if (!hasNavigatedOnce.value) {
+      hasNavigatedOnce.value = true;
+      return; // skip announcing the initial page load
+    }
   },
 );

Then guard the span with v-if="title && hasNavigatedOnce".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/views/RouteAnnouncer.vue` around lines 10 - 16, In
RouteAnnouncer.vue, the watcher on routeStore.pageTitle uses an obscure
self-referential toggle for initial-load suppression, which should be replaced
with a clearly named flag. Introduce a dedicated state such as hasNavigatedOnce
in the RouteAnnouncer component, update it inside the pageTitle watcher, and use
it to control whether the announcement span renders. Keep the watcher and
template logic aligned so the first route change is suppressed intentionally and
future edits remain clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@resources/js/components/MainNav.vue`:
- Around line 308-311: The dark mode and locale menu actions in MainNav.vue
should not emit routeChanged, since that event is reserved for real navigation
and triggers unwanted focus changes via App.vue’s moveFocus(). Update the
command handlers for changeDarkMode and changeLocale so they only perform their
own action and remove the routeChanged emit from those non-navigation items.
Keep routeChanged emission only in actual route-changing commands.

In `@resources/js/components/SkipLinks.vue`:
- Around line 6-21: The SkipLinks component currently duplicates styling in the
template while the shared `.skip-links` and `.skip-link` rules are left unused
in the navigation stylesheet. Update `SkipLinks.vue` to either restore use of
the shared skip-link classes on the `<ul>` and link items, or remove the stale
CSS definitions from `app/_navigation.css` so there is only one skip-link
styling pattern. Keep the markup and the existing `SkipLinks`/skip-link symbols
aligned with whichever approach you choose.

In `@resources/js/router.js`:
- Around line 697-713: The afterEach hook in router.js is returning too early on
the initial navigation because from.path === to.path also matches START_LOCATION
for a direct visit, so the home page never calls routeStore.setPageTitle().
Update the guard in router.afterEach to special-case the initial navigation
using START_LOCATION (or equivalent) and only skip updates for real
hash/query-only route changes, while still allowing the first render of "/" to
set the title via to.meta.title.

In `@resources/js/stores/route.js`:
- Around line 19-22: The title-building logic in route.js should guard against
an undefined app name from settings.getSetting("general.name"), which can
produce titles like "Rooms - undefined" before settings load. Update the
document.title assignment in the route store so it falls back to a safe default
or omits the postfix when the app name is missing, keeping the pageTitle/postfix
handling in the same location.

---

Nitpick comments:
In `@resources/js/PageTitle.vue`:
- Around line 1-14: Move the reusable PageTitle component into
resources/js/components to match the project convention, and update any
imports/usages that reference PageTitle.vue so they point to the new location.
Keep the component definition intact (the PageTitle.vue script setup and
template) and ensure all consumers now import it from
resources/js/components/PageTitle.vue.

In `@resources/js/views/RouteAnnouncer.vue`:
- Around line 10-16: In RouteAnnouncer.vue, the watcher on routeStore.pageTitle
uses an obscure self-referential toggle for initial-load suppression, which
should be replaced with a clearly named flag. Introduce a dedicated state such
as hasNavigatedOnce in the RouteAnnouncer component, update it inside the
pageTitle watcher, and use it to control whether the announcement span renders.
Keep the watcher and template logic aligned so the first route change is
suppressed intentionally and future edits remain clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: abb4cfe5-e432-4519-9cb4-63eb00de2228

📥 Commits

Reviewing files that changed from the base of the PR and between 1ed5ee1 and 24e5716.

📒 Files selected for processing (32)
  • CHANGELOG.md
  • lang/en/app.php
  • resources/css/app/_navigation.css
  • resources/js/PageTitle.vue
  • resources/js/components/App.vue
  • resources/js/components/AppFooter.vue
  • resources/js/components/MainNav.vue
  • resources/js/components/RoomHeader.vue
  • resources/js/components/RoomTabFiles.vue
  • resources/js/components/RoomTabHistoryAttendanceButton.vue
  • resources/js/components/RoomTabMembers.vue
  • resources/js/components/RoomTabPersonalizedLinks.vue
  • resources/js/components/RoomTabRecordings.vue
  • resources/js/components/SkipLinks.vue
  • resources/js/composables/useMenu.js
  • resources/js/router.js
  • resources/js/stores/route.js
  • resources/js/views/AdminLayout.vue
  • resources/js/views/AdminRolesIndex.vue
  • resources/js/views/AdminRoomTypesIndex.vue
  • resources/js/views/AdminServerPoolsIndex.vue
  • resources/js/views/AdminServersIndex.vue
  • resources/js/views/AdminUsersIndex.vue
  • resources/js/views/Login.vue
  • resources/js/views/Logout.vue
  • resources/js/views/MeetingsIndex.vue
  • resources/js/views/NotFound.vue
  • resources/js/views/Profile.vue
  • resources/js/views/RoomsIndex.vue
  • resources/js/views/RoomsView.vue
  • resources/js/views/RouteAnnouncer.vue
  • vite.config.common.js

Comment thread resources/js/components/MainNav.vue
Comment thread resources/js/components/SkipLinks.vue Outdated
Comment thread resources/js/router.js
Comment thread resources/js/stores/route.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@resources/js/components/SkipLinks.vue`:
- Line 11: The skip-link scrolling in SkipLinks.vue always uses smooth
animation, which ignores users’ reduced-motion preference. Update the scroll
behavior in the skip-link handler that calls scrollIntoView so it checks the
user’s prefers-reduced-motion setting and uses instant scrolling when reduced
motion is requested, while keeping smooth scrolling only for users who allow it.
- Around line 49-55: The skip link item in SkipLinks.vue is using Button with
link-like behavior instead of native anchor semantics, so update the rendered
element to use PrimeVue’s anchor mode. Replace the current Button usage in the
skip link loop with an anchor-rendered Button via as="a" and an href bound to
the target id, and remove the click-only link behavior while preserving the
existing scrollAndMoveFocus focus handling through the anchor navigation flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: eedd4311-97ef-40be-b78f-18399a66a94b

📥 Commits

Reviewing files that changed from the base of the PR and between 24e5716 and 5781395.

📒 Files selected for processing (5)
  • lang/en/app.php
  • resources/css/app/_navigation.css
  • resources/js/components/AppFooter.vue
  • resources/js/components/MainNav.vue
  • resources/js/components/SkipLinks.vue
✅ Files skipped from review due to trivial changes (1)
  • resources/css/app/_navigation.css
🚧 Files skipped from review as they are similar to previous changes (3)
  • lang/en/app.php
  • resources/js/components/AppFooter.vue
  • resources/js/components/MainNav.vue

Comment thread resources/js/components/SkipLinks.vue Outdated
Comment thread resources/js/components/SkipLinks.vue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@resources/js/components/SkipLinks.vue`:
- Around line 55-56: The SkipLinks anchor currently blocks native hash
navigation because `@click.stop.prevent` cancels the real `href` fallback in the
`SkipLinks.vue` template. Update the click handling on the skip link so
`scrollAndMoveFocus(link.id)` still runs, but do not prevent the anchor’s
default navigation; keep the helper in place while allowing normal hash behavior
to remain as a fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d4bfd4c7-55b5-4760-babe-aea24aec10dd

📥 Commits

Reviewing files that changed from the base of the PR and between 0dc3854 and 5d0cb57.

📒 Files selected for processing (1)
  • resources/js/components/SkipLinks.vue

Comment thread resources/js/components/SkipLinks.vue Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@resources/js/components/SkipLinks.vue`:
- Line 46: The SkipLinks reveal container currently uses transition-based motion
on focus, so update the SkipLinks.vue wrapper class handling to respect
reduced-motion preferences. In the skip-link container markup, keep the current
focus-within behavior but add a reduced-motion-safe variant so the reveal does
not animate when users request reduced motion. Use the existing skip-links
container element/class as the target for the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6b8f3b0b-6598-4f78-b513-a9277998475f

📥 Commits

Reviewing files that changed from the base of the PR and between 5d0cb57 and 6491e27.

📒 Files selected for processing (1)
  • resources/js/components/SkipLinks.vue

Comment thread resources/js/components/SkipLinks.vue Outdated
samuelwei added 3 commits July 1, 2026 17:06
It's not necessary for all links to be contained in a <nav> element. <nav> is intended only for a major block of navigation links; typically the <footer> element often has a list of links that don't need to be in a <nav> element.
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/nav
We cannot show an empty menu, as this throws an error in primvue when navigating with the keyboard
@samuelwei

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@samuelwei samuelwei merged commit 2b1500c into develop Jul 3, 2026
7 of 10 checks passed
@samuelwei samuelwei deleted the a11y-aria-roles-landmarks branch July 3, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant