Skip to content

fix(wcag): Legend panel issues#3349

Closed
kenchase wants to merge 1 commit intoCanadian-Geospatial-Platform:developfrom
kenchase:3306-legend-panel-issues
Closed

fix(wcag): Legend panel issues#3349
kenchase wants to merge 1 commit intoCanadian-Geospatial-Platform:developfrom
kenchase:3306-legend-panel-issues

Conversation

@kenchase
Copy link
Contributor

@kenchase kenchase commented Mar 13, 2026

  • documentation: update accessibility best practices md file (work in progress)
  • geolocator: add aria-disabled to clear filters button when no filters selected
  • global: updates to use unique ids
  • global styles: add css rule for aria-disabled icon buttons (buttonOutline)
  • global styles: add reusable style utility for visuallyHidden
  • legend layer: remove truncation from legend title
  • legend layer: remove tooltips from non interactive elements (legend title)
  • legend layer: add accessible code for loading layer
  • legend panel: fix fullscreen icon button focus management
  • legend panel: fix to allow both zoom icon buttons to retain focus after being pressed
  • legend panel: improve aria and semantic HTML implementation
  • legend panel: fix images that open a lightbox to use an interactive element (button)
  • legend panel: fix focus management on lightbox close (returns focus to triggering item)
  • legend panel fullscreen: update panel title to be more descriptive (read only)
  • panel and other modals: improve aria and semantic HTML implementation
  • panel and appBar and related: updated to use consistent ID naming conventions (mapId-containerType...)
  • switch: make label required for accessibility
  • switch: make focus indicators more noticeable
  • switch: add unique id to associate the label to the switch

Description

A11Y fixes related to WCAG review of the legend panel.

Fixes #3327

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Tested manually using keyboard navigation, Chrome dev tools, W3C HTML validator.

Add the URL for your deploy!

Checklist:

  • I have build (rush build) and deploy (rush host) my PR
  • I have connected the issues(s) to this PR
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have created new issue(s) related to the outcome of this PR is needed
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

This change is Reviewable

Copilot AI review requested due to automatic review settings March 13, 2026 20:07
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements WCAG-driven accessibility improvements centered on the Legend panel (Issue #3327), while also standardizing IDs, improving focus management, and adding reusable a11y styling utilities across geoview-core (and one plugin touchpoint).

Changes:

  • Improves Legend/Legend fullscreen accessibility (ARIA semantics, focus restoration, loading announcements, remove non-interactive tooltips/truncation).
  • Standardizes/uniquifies DOM IDs and strengthens focus-trap + dialog semantics across panels/modals/app-bar controls.
  • Updates shared UI primitives for a11y (Switch now requires a label + uses unique ids; reusable visuallyHidden; aria-disabled styling for icon buttons; ProgressBar supports aria-label).

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
packages/geoview-custom-legend/src/components/layer-item.tsx Passes mapId/containerType into core LegendLayer for consistent IDs/ARIA.
packages/geoview-core/src/ui/switch/switch.tsx Makes label required and links label↔control via unique ids.
packages/geoview-core/src/ui/switch/switch-style.ts Improves focus indication styling for switch + minor palette tweak.
packages/geoview-core/src/ui/style/themeOptionsGenerator.ts Adds global styling for [aria-disabled="true"] icon buttons (buttonOutline).
packages/geoview-core/src/ui/style/default.ts Introduces shared visuallyHidden sr-only style utility.
packages/geoview-core/src/ui/panel/panel.tsx Updates dialog semantics/labeling and changes panel container id format.
packages/geoview-core/src/ui/linear-progress/linear-progress.tsx Allows passing an aria-label to ProgressBar for SRs.
packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx Requires containerType, adds unique ids, improves tooltip semantics.
packages/geoview-core/src/core/components/notifications/notifications.tsx Updates app-bar notification button id to new naming scheme.
packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx Updates Switch usage to satisfy required label typing.
packages/geoview-core/src/core/components/legend/legend.tsx Wires mapId/containerType into LegendLayer, adds fullscreen button ref for focus restore, updates ToggleAll usage.
packages/geoview-core/src/core/components/legend/legend-styles.ts Removes title truncation styling and adopts shared visuallyHidden.
packages/geoview-core/src/core/components/legend/legend-layer.tsx Adds unique ids + ARIA relationships, removes non-interactive tooltip, adds SR live announcements for load/error, passes props into collapsible content.
packages/geoview-core/src/core/components/legend/legend-layer-items.tsx Improves visibility toggle labeling/pressed state + stabilizes item ids for focus restoration.
packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx Adjusts tooltips/ARIA labels and introduces aria-disabled/pressed patterns for icon buttons.
packages/geoview-core/src/core/components/legend/legend-layer-container.tsx Makes WMS legend image interactive (ButtonBase), adds region semantics + links collapse to header, adds focus restoration hooks.
packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx Improves fullscreen dialog semantics/ids/title, switches to IconButton, restores focus on close, marks background inert.
packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx Updates Switch usage to satisfy required label typing.
packages/geoview-core/src/core/components/layers/layers-toolbar.tsx Adds containerType + unique ids; passes containerType into ToggleAll.
packages/geoview-core/src/core/components/layers/layers-panel.tsx Propagates containerType into LayersToolbar.
packages/geoview-core/src/core/components/geolocator/geolocator.tsx Adjusts aria-modal semantics for WCAG mode; minor focus-trap handling change.
packages/geoview-core/src/core/components/geolocator/geolocator-style.ts Replaces local sr-only pattern with shared visuallyHidden.
packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx Adds aria-disabled behavior to clear-filters button based on active filters/results.
packages/geoview-core/src/core/components/export/export-modal.tsx Updates ids to be map-scoped and dialog-consistent.
packages/geoview-core/src/core/components/export/export-modal-button.tsx Simplifies Export button ARIA (dialog semantics) + trims unused props.
packages/geoview-core/src/core/components/details/coordinate-info.tsx Updates Switch usage to satisfy required label typing.
packages/geoview-core/src/core/components/data-table/filter-map.tsx Updates Switch usage to satisfy required label typing.
packages/geoview-core/src/core/components/common/layer-list.tsx Removes redundant aria-disabled when disabled is already used.
packages/geoview-core/src/core/components/app-bar/buttons/version.tsx Adopts shared visuallyHidden and standardizes ids for focus trap + dialog.
packages/geoview-core/src/core/components/app-bar/app-bar.tsx Improves app-bar button ARIA based on WCAG mode and avoids ESC conflicts with lightbox.
packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts Minor import ordering / type import cleanup.
packages/geoview-core/public/locales/fr/translation.json Adds/updates strings for new labels and SR announcements.
packages/geoview-core/public/locales/en/translation.json Adds/updates strings for new labels and SR announcements.
docs/app/accessibility.md Expands internal a11y best-practices documentation (WIP).

You can also share your feedback on Copilot code review. Take the survey.

setActiveAppBarTab(DEFAULT_APPBAR_CORE.GEOLOCATOR, false, false);
setTimeout(() => {
disableFocusTrap(`${mapId}-${CONTAINER_TYPE.APP_BAR}-${DEFAULT_APPBAR_CORE.GEOLOCATOR}-panel-btn`);
disableFocusTrap(`${mapId}-${CONTAINER_TYPE.APP_BAR}-${DEFAULT_APPBAR_CORE.GEOLOCATOR}-panel-btn`);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

<ButtonBase
id={buttonId}
sx={styles.imageButton}
onClick={() => initLightBox(imgSrc, buttonId, 0, 2)}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Now uses separate props for altText and returnFocusId

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Now uses separate props for altText and returnFocusId

Comment on lines +185 to +189
aria-label={`${t('layers.zoomVisibleScale')} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips
aria-disabled={!isZoomToVisibleScaleCapable}
aria-pressed={!isZoomToVisibleScaleCapable}
className={`buttonOutline`}
onClick={controls.handleZoomToLayerVisibleScale}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +223 to +224
aria-disabled={!isInVisibleRange || parentHidden || !isVisible || layerStatus === 'error'} // WCAG - used instead of disabled to allow button to retain focus after keyboard press
aria-pressed={!isInVisibleRange}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

*
* @param item - The legend item to generate an ID for
* @returns A stable, unique ID in format: "legend-item-{randomId}-{mapId}"
* @returns A stable, unique ID in format: "{mapId}-legend-item-{randomId}-"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +43 to +50
${mapId}-${containerType}-${panel.panelId)-[element]

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
${mapId}-${containerType}-[element]-${uniqueId}
${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documentation is WIP. Will be reviewed later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documentation is WIP. Will be reviewed later.

}}
ref={panelContainerRef}
id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-${panel.panelId || ''}-panel`}
id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-panel-${panel.panelId || ''}`}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -150,7 +154,7 @@ export function GeolocatorResult({ geoLocationData, searchValue, error }: Geoloc
className="buttonOutline"
aria-label={t('geolocator.clearFilters')}
onClick={handleClearFilters}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 20 to 24
interface CollapsibleContentProps {
layerPath: string;
initLightBox: (imgSrc: string, title: string, index: number, total: number) => void;
LegendLayerComponent: typeof LegendLayer;
showControls: boolean;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +43 to +50
${mapId}-${containerType}-${panel.panelId)-[element]

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
${mapId}-${containerType}-[element]-${uniqueId}
${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documentation is WIP. Will be reviewed later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documentation is WIP. Will be reviewed later.

@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from f3da7f2 to 656d2dd Compare March 16, 2026 18:31
Copilot AI review requested due to automatic review settings March 16, 2026 18:54
@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from 656d2dd to 8091658 Compare March 16, 2026 18:54
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR focuses on WCAG-driven accessibility fixes across GeoView’s legend panel and related UI controls, improving ARIA semantics, focus management, and DOM ID uniqueness across core and plugin surfaces.

Changes:

  • Improve legend panel accessibility (ARIA semantics, live region announcements, focus restoration, tooltip/labeling adjustments).
  • Standardize/uniquify DOM IDs and add shared visuallyHidden utility for sr-only content.
  • Strengthen switch and icon-button accessibility patterns (required labels, focus styling, aria-disabled usage).

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/geoview-custom-legend/src/components/layer-item.tsx Pass mapId/containerType into core LegendLayer for unique IDs and ARIA relationships.
packages/geoview-core/src/ui/switch/switch.tsx Make label required and generate a unique id to associate label and control.
packages/geoview-core/src/ui/switch/switch-style.ts Add stronger focus indication styling for switch labels/controls.
packages/geoview-core/src/ui/style/themeOptionsGenerator.ts Add styling for aria-disabled="true" icon buttons (buttonOutline).
packages/geoview-core/src/ui/style/default.ts Introduce shared visuallyHidden (sr-only) style utility.
packages/geoview-core/src/ui/panel/panel.tsx Update panel ARIA semantics for focus-trapped (dialog) mode and labels.
packages/geoview-core/src/ui/linear-progress/linear-progress.tsx Allow passing aria-label to progress bar for screen readers.
packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx Require source/containerType and make toggle-all IDs unique per map/container.
packages/geoview-core/src/core/components/notifications/notifications.tsx Update notification button IDs to match naming conventions / unique IDs.
packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx Update Switch usage to align with required label typing.
packages/geoview-core/src/core/components/legend/legend.tsx Pass mapId/containerType to LegendLayer; wire fullscreen focus restoration refs; update ToggleAll usage.
packages/geoview-core/src/core/components/legend/legend-styles.ts Remove title truncation; reuse shared visuallyHidden; adjust toggle bar padding.
packages/geoview-core/src/core/components/legend/legend-layer.tsx Add stable IDs for ARIA relationships and ARIA live region announcements for loading states.
packages/geoview-core/src/core/components/legend/legend-layer-items.tsx Improve ARIA labeling (aria-pressed, descriptive labels) and update item ID format.
packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx Improve tooltips/ARIA labels; add aria-disabled patterns for zoom controls.
packages/geoview-core/src/core/components/legend/legend-layer-container.tsx Use interactive element (ButtonBase) for WMS legend images; link collapsibles via aria-labelledby; pass through IDs.
packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx Improve fullscreen dialog semantics/IDs; restore focus to triggering button; update title copy.
packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx Update Switch labels to match new required typing.
packages/geoview-core/src/core/components/layers/layers-toolbar.tsx Add unique IDs; thread containerType; use ToggleAll with required props.
packages/geoview-core/src/core/components/layers/layers-panel.tsx Pass containerType into LayersToolbar.
packages/geoview-core/src/core/components/geolocator/geolocator.tsx Add ARIA live region for loading announcements; progress bar labeling; adjust aria-modal usage.
packages/geoview-core/src/core/components/geolocator/geolocator-style.ts Replace duplicated sr-only CSS with shared visuallyHidden.
packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx Use aria-disabled for clear-filters button and prevent action when disabled.
packages/geoview-core/src/core/components/export/export-modal.tsx Make dialog/menu/input IDs unique per map instance.
packages/geoview-core/src/core/components/export/export-modal-button.tsx Simplify export button ARIA attributes; mark as dialog trigger.
packages/geoview-core/src/core/components/details/feature-info-table.tsx Update lightbox API usage to support focus restoration + alt text parameterization.
packages/geoview-core/src/core/components/details/coordinate-info.tsx Update Switch labels to match new required typing.
packages/geoview-core/src/core/components/data-table/filter-map.tsx Update Switch labels to match new required typing.
packages/geoview-core/src/core/components/data-table/data-table.tsx Update lightbox API usage for new signature (altText + returnFocusId).
packages/geoview-core/src/core/components/common/layer-list.tsx Remove redundant aria-disabled when using disabled.
packages/geoview-core/src/core/components/common/hooks/use-light-box.tsx Add focus restoration by element id; update init signature to include alt text + return focus id.
packages/geoview-core/src/core/components/app-bar/buttons/version.tsx Update IDs to match ${mapId}-${CONTAINER_TYPE...} conventions; reuse shared visuallyHidden.
packages/geoview-core/src/core/components/app-bar/app-bar.tsx Adjust ARIA semantics for WCAG (dialog vs region); avoid handling ESC when lightbox is open.
packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts Import ordering cleanup.
packages/geoview-core/public/locales/en/translation.json Add new keys for panel labeling and legend/geolocator loading announcements; adjust legend fullscreen title.
packages/geoview-core/public/locales/fr/translation.json Same as EN: add new keys and update legend fullscreen title.
docs/app/accessibility.md Expand accessibility best practices guidance, especially around IDs, aria-disabled, and live regions.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +43 to +50
${mapId}-${containerType}-${panel.panelId)-[element]

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
${mapId}-${containerType}-[element]-${uniqueId}
${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

When a button has keyboard focus and becomes disabled on press, focus is lost and jumps unpredictably to another element, disorienting keyboard users who lose track of their position in the interface.

- Use aria-disabled instead of disabled
- Style the the aria-disabled element to look like it would if disabled
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

tooltip={t('layers.zoomVisibleScale')}
aria-label={`${t('layers.zoomVisibleScale')} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips
aria-disabled={!isZoomToVisibleScaleCapable}
aria-pressed={!isZoomToVisibleScaleCapable}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +229 to 233
tooltip={t('legend.zoomTo')}
aria-label={`${t('legend.zoomTo')} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips
aria-disabled={isZoomToLayerDisabled} // WCAG - used instead of disabled to allow button to retain focus after keyboard press
aria-pressed={!isInVisibleRange}
className="buttonOutline"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +36 to +41
'&[aria-disabled="true"]': {
color: `${geoViewColors.bgColor.dark[450]}`,
backgroundColor: 'transparent',
cursor: 'not-allowed',
pointerEvents: 'none',
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 101 to 106
component="section"
role={open ? 'dialog' : undefined}
aria-modal={open ? 'true' : undefined}
role={activeTrapGeoView ? 'dialog' : undefined}
aria-label={t('general.panelLabel', { title: t(panel.title) })!}
aria-hidden={!open}
aria-label={`${t(panel.title)} panel`}
aria-modal={activeTrapGeoView || undefined}
sx={{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

component="section"
role={isPanelOpen ? 'dialog' : undefined}
aria-modal={isPanelOpen ? 'true' : undefined}
aria-modal={activeTrapGeoView || undefined}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from 8091658 to 3b1e284 Compare March 16, 2026 19:14
Copilot AI review requested due to automatic review settings March 16, 2026 19:38
@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from 3b1e284 to a4daa30 Compare March 16, 2026 19:38
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

WCAG/accessibility fixes centered on the Legend panel (and related UI primitives) within GeoView’s React+TypeScript viewer, improving labeling, focus management, unique IDs, and screen-reader announcements.

Changes:

  • Improve Legend (and fullscreen Legend) semantics, focus restoration, and screen reader support (live regions, better ARIA labels, more interactive affordances).
  • Standardize/uniquify element IDs across panels, toolbars, modals, and buttons to avoid duplicate IDs and strengthen ARIA relationships.
  • Update shared UI primitives/styles for accessibility (Switch requires a label + unique id association, aria-disabled styling, shared visuallyHidden utility, ProgressBar aria-label support).

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/geoview-custom-legend/src/components/layer-item.tsx Passes mapId/containerType into core LegendLayer from the custom legend plugin.
packages/geoview-core/src/ui/switch/switch.tsx Makes label required and associates label↔switch via unique id.
packages/geoview-core/src/ui/switch/switch-style.ts Improves focus indication styling for Switch.
packages/geoview-core/src/ui/style/themeOptionsGenerator.ts Adds global styling for [aria-disabled="true"] icon buttons (buttonOutline).
packages/geoview-core/src/ui/style/default.ts Adds shared visuallyHidden sr-only style utility.
packages/geoview-core/src/ui/panel/panel.tsx Improves dialog semantics/labeling and standardizes panel IDs.
packages/geoview-core/src/ui/linear-progress/linear-progress.tsx Allows aria-label passthrough for ProgressBar accessibility.
packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx Requires container context, unique IDs, and improves tooltip semantics.
packages/geoview-core/src/core/components/notifications/notifications.tsx Updates button IDs and dialog semantics for notifications popper.
packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx Adjusts Switch labels to align with required-label change.
packages/geoview-core/src/core/components/legend/legend.tsx Passes mapId/containerType into LegendLayer; improves fullscreen button focus management and ToggleAll usage.
packages/geoview-core/src/core/components/legend/legend-styles.ts Removes truncation rules for legend titles and reuses shared visuallyHidden.
packages/geoview-core/src/core/components/legend/legend-layer.tsx Adds ARIA live region announcements, improves collapse button ARIA, and threads mapId/containerType.
packages/geoview-core/src/core/components/legend/legend-layer-items.tsx Improves toggle button ARIA, adjusts tooltip text, and stabilizes unique IDs with mapId prefix.
packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx Improves icon button labels/state with aria-pressed/aria-disabled and click-guarding for focus retention.
packages/geoview-core/src/core/components/legend/legend-layer-container.tsx Makes legend images open lightbox via an interactive element (ButtonBase) and adds region semantics for collapsible content.
packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx Improves fullscreen dialog IDs/title, uses IconButton, restores focus on close, and sets inert content behind dialog.
packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx Updates Switch labels to match required-label API.
packages/geoview-core/src/core/components/layers/layers-toolbar.tsx Adds containerType + unique IDs and passes containerType into ToggleAll.
packages/geoview-core/src/core/components/layers/layers-panel.tsx Threads containerType into LayersToolbar.
packages/geoview-core/src/core/components/geolocator/geolocator.tsx Adds live region announcements for loading status and improves aria-modal behavior in WCAG mode.
packages/geoview-core/src/core/components/geolocator/geolocator-style.ts Reuses shared visuallyHidden utility.
packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx Uses aria-disabled (with early return) for clear-filters button based on active filters.
packages/geoview-core/src/core/components/export/export-modal.tsx Standardizes dialog/input/menu IDs with mapId prefix.
packages/geoview-core/src/core/components/export/export-modal-button.tsx Simplifies Export button API and sets aria-haspopup="dialog".
packages/geoview-core/src/core/components/details/feature-info-table.tsx Updates lightbox API usage to include alt text + focus restoration id.
packages/geoview-core/src/core/components/details/coordinate-info.tsx Updates Switch label usage to match required-label API.
packages/geoview-core/src/core/components/data-table/filter-map.tsx Updates Switch label usage to match required-label API.
packages/geoview-core/src/core/components/data-table/data-table.tsx Updates lightbox init signature to include alt text + focus restoration id.
packages/geoview-core/src/core/components/common/layer-list.tsx Removes redundant aria-disabled where disabled already applies.
packages/geoview-core/src/core/components/common/hooks/use-light-box.tsx Enhances lightbox API to support alt text and focus restoration to triggering element.
packages/geoview-core/src/core/components/app-bar/buttons/version.tsx Standardizes IDs and focus trap ids; updates popper labeling.
packages/geoview-core/src/core/components/app-bar/app-bar.tsx Improves ARIA semantics for buttons/panels in WCAG mode and avoids ESC conflicts when lightbox is open.
packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts Minor import organization.
packages/geoview-core/public/locales/fr/translation.json Adds new WCAG-related strings (panel label, legend fullscreen title, live announcements, etc.).
packages/geoview-core/public/locales/en/translation.json Adds new WCAG-related strings (panel label, legend fullscreen title, live announcements, etc.).
docs/app/accessibility.md Expands accessibility best-practices documentation (WIP).
Comments suppressed due to low confidence (2)

packages/geoview-core/src/core/components/notifications/notifications.tsx:257

  • The notifications IconButton no longer exposes its expanded state or controlled element relationship. Since it toggles a role="dialog" popper (id="notification-dialog-${mapId}"), consider restoring aria-expanded={open} and aria-controls pointing at that dialog id.
        <IconButton
          id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-notifications-btn`}
          aria-label={t('appbar.notifications')}
          aria-haspopup="dialog"
          tooltipPlacement="right"
          onClick={handleOpenPopover}

packages/geoview-core/src/core/components/app-bar/buttons/version.tsx:129

  • The version IconButton toggles a role="dialog" popper (id="${mapId}-${CONTAINER_TYPE.APP_BAR}-version-dialog") but no longer sets aria-expanded / aria-controls. Re-adding these would preserve the button→dialog relationship and state for assistive tech.
        <IconButton
          id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-version-btn`}
          aria-haspopup="dialog"
          aria-label={t('appbar.version')}
          tooltipPlacement="right"
          onClick={handleOpenPopover}

You can also share your feedback on Copilot code review. Take the survey.


_Work in progress_

The viewer needs to be accessible for keyboard and screen reader. It's should follow WCAG 2.1 requirements: https://www.w3.org/TR/WCAG21
Copy link
Contributor Author

Choose a reason for hiding this comment

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

WIP

Copy link
Contributor Author

Choose a reason for hiding this comment

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

WIP

edge="end"
size="small"
tooltip={tooltip}
aria-label={`${tooltip} - ${layerName}`} // WCAG - Provide descriptive aria-label for icon button tooltips
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 170 to 173
<IconButton
aria-label={t('legend.selectLayerAndScroll')}
tooltip={t('legend.selectLayerAndScroll')}
aria-label={`${t('legend.selectLayerAndScroll')} - ${layerName}`}
className="buttonOutline"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +49 to +52
'&:focus-visible': {
outline: '2px solid',
outlineColor: 'primary.main',
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ignoring

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ignoring

@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from a4daa30 to 1c45047 Compare March 16, 2026 19:52
Copilot AI review requested due to automatic review settings March 16, 2026 20:26
@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from 1c45047 to f0f73e6 Compare March 16, 2026 20:26
Copy link
Contributor Author

@kenchase kenchase left a comment

Choose a reason for hiding this comment

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

@kenchase made 22 comments.
Reviewable status: 0 of 37 files reviewed, 22 unresolved discussions.

Comment on lines +43 to +50
${mapId}-${containerType}-${panel.panelId)-[element]

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
${mapId}-${containerType}-[element]-${uniqueId}
${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documentation is WIP. Will be reviewed later.

Comment on lines +43 to +50
${mapId}-${containerType}-${panel.panelId)-[element]

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
${mapId}-${containerType}-[element]-${uniqueId}
${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Documentation is WIP. Will be reviewed later.

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +43 to +50
${mapId}-${containerType}-${panel.panelId)-[element]

// If a unique ID is required
// uniqueId could be generated from:
// const id = useId();
// const id generateId(8);
${mapId}-${containerType}-[element]-${uniqueId}
${mapId}-${containerType}-${panel.panelId)-[element]-${uniqueId}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

When a button has keyboard focus and becomes disabled on press, focus is lost and jumps unpredictably to another element, disorienting keyboard users who lose track of their position in the interface.

- Use aria-disabled instead of disabled
- Style the the aria-disabled element to look like it would if disabled
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 170 to 173
<IconButton
aria-label={t('legend.selectLayerAndScroll')}
tooltip={t('legend.selectLayerAndScroll')}
aria-label={`${t('legend.selectLayerAndScroll')} - ${layerName}`}
className="buttonOutline"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

*
* @param item - The legend item to generate an ID for
* @returns A stable, unique ID in format: "legend-item-{randomId}-{mapId}"
* @returns A stable, unique ID in format: "{mapId}-legend-item-{randomId}-"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

}}
ref={panelContainerRef}
id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-${panel.panelId || ''}-panel`}
id={`${mapId}-${CONTAINER_TYPE.APP_BAR}-panel-${panel.panelId || ''}`}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 101 to 106
component="section"
role={open ? 'dialog' : undefined}
aria-modal={open ? 'true' : undefined}
role={activeTrapGeoView ? 'dialog' : undefined}
aria-label={t('general.panelLabel', { title: t(panel.title) })!}
aria-hidden={!open}
aria-label={`${t(panel.title)} panel`}
aria-modal={activeTrapGeoView || undefined}
sx={{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +36 to +41
'&[aria-disabled="true"]': {
color: `${geoViewColors.bgColor.dark[450]}`,
backgroundColor: 'transparent',
cursor: 'not-allowed',
pointerEvents: 'none',
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses WCAG-related accessibility issues in GeoView’s legend panel and related UI primitives (switches, panels, dialogs/lightbox), improving keyboard navigation, ARIA semantics, focus management, and ID uniqueness across the app.

Changes:

  • Improve legend accessibility: semantic markup, descriptive ARIA labels, live-region announcements for loading, focus restoration for fullscreen/lightbox, and removal of non-interactive tooltips/truncation.
  • Standardize unique ID generation/usage and update components to pass mapId/containerType where needed.
  • Strengthen shared UI primitives for accessibility (required Switch labels + label association, aria-disabled styling, progress bar labeling, panel/dialog ARIA updates).

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/geoview-custom-legend/src/components/layer-item.tsx Passes mapId/containerType into core LegendLayer for consistent IDs/ARIA relationships.
packages/geoview-core/src/ui/switch/switch.tsx Makes label required and associates label↔switch via generated unique IDs.
packages/geoview-core/src/ui/switch/switch-style.ts Improves focus indication styling for the switch wrapper and adjusts focus-visible styling.
packages/geoview-core/src/ui/style/themeOptionsGenerator.ts Adds visual styling for aria-disabled="true" icon buttons (cursor + muted colors).
packages/geoview-core/src/ui/style/default.ts Adds shared visuallyHidden sr-only utility style.
packages/geoview-core/src/ui/panel/panel.tsx Updates panel ARIA semantics for focus-trapped (dialog) vs non-trapped modes; standardizes IDs.
packages/geoview-core/src/ui/linear-progress/linear-progress.tsx Allows aria-label to be passed through to progress bar for SR labeling.
packages/geoview-core/src/core/components/toggle-all/toggle-all.tsx Requires explicit source and containerType and updates IDs/tooltips/labels for accessibility.
packages/geoview-core/src/core/components/notifications/notifications.tsx Updates notification button ID to match global conventions; dialog semantics remain.
packages/geoview-core/src/core/components/nav-bar/buttons/measurement.tsx Updates Switch usage to align with required label typing.
packages/geoview-core/src/core/components/legend/legend.tsx Passes mapId/containerType to LegendLayer; adds fullscreen button ref for focus restoration; updates ToggleAll usage.
packages/geoview-core/src/core/components/legend/legend-styles.ts Removes truncation/out-of-range hiding, tweaks layout padding, and reuses shared visuallyHidden.
packages/geoview-core/src/core/components/legend/legend-layer.tsx Adds IDs for ARIA relationships, live-region announcements for layer status changes, and propagates mapId/containerType.
packages/geoview-core/src/core/components/legend/legend-layer-items.tsx Makes visibility toggle buttons more descriptive with aria-label/aria-pressed and ID convention updates.
packages/geoview-core/src/core/components/legend/legend-layer-ctrl.tsx Refines legend control button ARIA/tooltip behavior and uses aria-disabled patterns for focus retention.
packages/geoview-core/src/core/components/legend/legend-layer-container.tsx Makes WMS legend images open lightbox via an interactive element (ButtonBase) and links collapsibles via ARIA attributes.
packages/geoview-core/src/core/components/legend/legend-fullscreen.tsx Improves fullscreen dialog ID/title semantics and restores focus to triggering button on exit.
packages/geoview-core/src/core/components/layers/right-panel/layer-details.tsx Updates Switch label usage to match required label typing.
packages/geoview-core/src/core/components/layers/layers-toolbar.tsx Adds containerType/map-scoped IDs and updates ToggleAll usage.
packages/geoview-core/src/core/components/layers/layers-panel.tsx Wires containerType down into LayersToolbar.
packages/geoview-core/src/core/components/geolocator/geolocator.tsx Adds live-region loading announcements and improves aria-modal handling when focus-trapped.
packages/geoview-core/src/core/components/geolocator/geolocator-style.ts Reuses shared visuallyHidden style instead of duplicating sr-only CSS.
packages/geoview-core/src/core/components/geolocator/geolocator-result.tsx Uses aria-disabled + early return for “clear filters” to preserve focus behavior.
packages/geoview-core/src/core/components/export/export-modal.tsx Makes dialog/menu/input IDs map-scoped and more consistent.
packages/geoview-core/src/core/components/export/export-modal-button.tsx Simplifies export button ARIA props and indicates dialog semantics via aria-haspopup.
packages/geoview-core/src/core/components/details/feature-info-table.tsx Updates lightbox init signature to support alt text and focus restoration ID.
packages/geoview-core/src/core/components/details/coordinate-info.tsx Updates Switch label usage to match required label typing.
packages/geoview-core/src/core/components/data-table/filter-map.tsx Updates Switch label usage to match required label typing.
packages/geoview-core/src/core/components/data-table/data-table.tsx Updates lightbox init signature to include alt text + focus restoration ID.
packages/geoview-core/src/core/components/common/layer-list.tsx Removes redundant aria-disabled where native disabled is already set.
packages/geoview-core/src/core/components/common/hooks/use-light-box.tsx Updates lightbox API to accept alt text and return-focus element ID; restores focus on exit.
packages/geoview-core/src/core/components/app-bar/buttons/version.tsx Updates IDs to match conventions and reuses shared visuallyHidden.
packages/geoview-core/src/core/components/app-bar/app-bar.tsx Adjusts ARIA attributes based on focus-trap mode and avoids ESC conflicts when lightbox is open.
packages/geoview-core/src/core/components/app-bar/app-bar-helper.ts Import cleanup/reordering.
packages/geoview-core/public/locales/fr/translation.json Adds new accessibility strings (panel label, legend fullscreen title, loading announcements, etc.).
packages/geoview-core/public/locales/en/translation.json Adds new accessibility strings (panel label, legend fullscreen title, loading announcements, etc.).
docs/app/accessibility.md Expands accessibility best-practices documentation (unique IDs, ARIA labels, focus management, live regions).

You can also share your feedback on Copilot code review. Take the survey.

const parentHidden = useMapSelectorLayerParentHidden(layerPath);
const highlightedLayer = useLayerHighlightedLayer();
const isFocusTrap = useUIActiveTrapGeoView();
st layerName = useLayerSelectorName(layerPath) ?? layerPath;

_Work in progress_

The viewer needs to be accessible for keyboard and screen reader. It's should follow WCAG 2.1 requirements: https://www.w3.org/TR/WCAG21
* documentation: update accessibility best practices md file (work in progress)
* geolocator: add aria-disabled to clear filters button when no filters selected
* geolocator: updated loading status region for A11Y (aria-live) (ProgressBar)
* global: updates to use unique ids
* global styles: add css rule for aria-disabled icon buttons (buttonOutline)
* global styles: add reusable style utility for visuallyHidden
* legend layer: remove truncation from legend title
* legend layer: remove tooltips from non interactive elements (legend title)
* legend layer: add accessible code for loading layer
* legend panel: fix fullscreen icon button focus management
* legend panel: fix to allow both zoom icon buttons to retain focus after being pressed
* legend panel: improve aria and semantic HTML implementation
* legend panel: fix images that open a lightbox to use an interactive element (button)
* legend panel: fix focus management on lightbox close (returns focus to triggering item)
* legend panel fullscreen: update panel title to be more descriptive (read only)
* panel and other modals: improve aria and semantic HTML implementation
* panel and appBar and related: updated to use consistent ID naming conventions (mapId-containerType...)
* switch: make label required for accessibility
* switch: make focus indicators more noticeable
* switch: add unique id to associate the label to the switch
* use-light-box: update to use separate props for image alt test and focus management element ID
* use-light-box (global): update to set alt text to "" where descriptive alt text is unavailable
@kenchase kenchase force-pushed the 3306-legend-panel-issues branch from f0f73e6 to 426b432 Compare March 16, 2026 20:37
@kenchase
Copy link
Contributor Author

Replaced with PR #3353

@kenchase kenchase closed this Mar 17, 2026
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.

[WCAG] Legend Panel WCAG Review

2 participants