ArcGIS Blog

Developers

ArcGIS Experience Builder

Building Accessible Custom Widgets in ArcGIS Experience Builder with Jimu UI, Calcite, and JavaScript Maps SDK Components

By Kevin Gonzago and Kitty Hurley

Custom widgets in ArcGIS Experience Builder provide flexibility over layout, interaction, and data workflows. The flexibility also comes with responsibility. When you build a widget, you define how users move through it, how they discover information, and how they complete tasks.

Accessibility directly shapes that experience. Individuals who rely on keyboard and assistive technologies, or who have low vision, depend on clear structure, predictable focus behavior, and operable controls. Experience Builder provides accessibility support at the framework level, but custom widgets require deliberate design decisions to maintain those standards.

Explore practical techniques for building accessible custom widgets using Jimu UI, Calcite components, and ArcGIS Maps SDK for JavaScript components, which include practical patterns, developer responsibilities, and implementation techniques ensuring accessibility is built into your widget from the start.

Why accessibility matters

Accessible widgets support individuals who rely on keyboards, assistive technologies, or alternative input devices. Accessibility also improves the overall usability, consistency, and maintainability of the solutions you build, including:

  • Alignment with the Web Content Accessibility Guidelines (WCAG)
  • Avoiding fragile, input-specific interactions
  • Reducing late-stage rework and costly last-minute fixes
  • Building widgets that integrate with Experience Builder layouts

Experience Builder applications often combine multiple widgets on a single page, where accessibility constraints in one widget could affect an entire experience. Accessibility must be integrated throughout widget design and development, not treated as a final review step.

Accessibility in custom widgets

Custom widgets are built using React and integrate with the Experience Builder framework. Accessibility in custom widgets is supported by three component systems:

The Experience Builder framework supports custom development with accessibility features. Developers must continue building accessibility best practices into their widgets to support more individuals, including:

  • Starting with semantic HTML prior to using ARIA attributes
  • Ensuring interactive functionality is available for keyboard navigation
  • Providing visible focus indicators on interactive elements
  • Managing focus and providing context when content changes dynamically

Understanding the roles of each system

When building accessible custom widgets in Experience Builder, accessibility responsibilities are distributed across three systems, where each library solves a different problem. The table below outlines offerings and developer responsibilities.

Library Primary role Offerings Developer responsibility examples
Jimu UI React components for Experience Builder
  • Keyboard behavior aligned with Experience Builder layouts and windows
  • Accessible roles and states in provided components
  • Consistent integration with theme and layout
  • Consider the appropriate component
  • Avoid breaking heading hierarchy
  • Manage focus when widget state changes
Calcite components Part of Esri’s design system built as standards-based web components, which provide robust accessibility support
  • Built-in keyboard interaction patterns
  • Focus management inside components
  • Accessible roles and WCAG-compliant contrast
  • Provide visible labels
  • Maintain logical DOM order
  • Handle focus transitions between components
ArcGIS Maps SDK for JavaScript components Web components focused on mapping solutions, including controls and interaction
  • Provide accessible alternatives for map data, such as lists or tables
  • Expose feature information outside of the map canvas
  • Avoid hover-only interactions

The key takeaways between these component libraries, include:

  • Jimu UI manages framework integration
  • Calcite provides accessible UI building blocks
  • JavaScript Maps SDK ensures map interaction is operable
  • The developer composes them into an accessible workflow

Common accessibility themes

Jimu UI, Calcite components, and JavaScript Maps SDK components serve different purposes, but share foundational accessibility principles, which are applied differently within Experience Builder, including:

  • Keyboard operability
  • Visible and predictable focus behavior
  • Accessible roles and states
  • Logical interaction patterns
  • Alignment with WCAG guidance

Accessibility and Jimu UI components

When using Jimu UI components in custom widgets:

  • Avoid introducing heading elements unless required, since Experience Builder manages page-level hierarchy
  • Manage focus when widget state changes dynamically
  • Prioritize framework components over custom HTML where possible

Design accessible interfaces with Calcite components

When composing interfaces with Calcite components:

  • Provide visible labels or accessible names
  • Ensure DOM order matches visual order
  • Define required properties explicitly
  • Return focus to the initiating control when panels or dialogs close

Build accessible mapping experiences with JavaScript Maps SDK components

When using JavaScript Maps SDK components:

  • Prioritize built-in map controls such as Zoom, Search, Legend, and LayerList
  • Provide alternative formats for map data, such as lists or tables
  • Synchronize map selection with accessible UI outside the canvas
  • Avoid hover-only interactions without keyboard equivalents

Applying accessibility in a custom widget

In this widget, the user interaction workflow of an individual filtering a layer to reveal a map component and obtain the feature details is handled outside the map, so that assistive technologies do not need to interact with the map canvas. This example demonstrates:

  • Using Jimu UI and Calcite components when possible and
  • Managing keyboard navigation and a visible focus, including restoring focus after action

Set an accessible name on the map

Once the map view is ready, add an accessible label via the aria property, setting the aria-label to the webmap title. The accessible label provides context for assistive technologies:

useEffect(() => {
  const mapEl = mapElRef.current;
  if (!mapEl) return;

  (async () => {
    try {
      await mapEl.viewOnReady();
      const { portalItem } = mapEl.map;
      mapEl.aria = {
        label: portalItem.title,
      };
      setStatus("Map loaded.");
    } catch {
      setStatus("Map failed to load.");
    }
  })();
}, []);

Provide context to dynamic changes

Next, create a single-line status message and update it for asynchronous actions, such as “Applying…” or “Applied…”. Use the aria-describedby attribute to connect the controls to the status text, and use aria-live to announce dynamic changes on demand to assistive technologies.

<p
  id="widget-status"
  role="status"
  aria-live="polite"
  aria-atomic="true"
>
  {status}
</p>

Add a button that updates the status text:

<Button
  id="apply-filters-btn"
  onClick={applyFilter}
  aria-describedby="widget-status"
>
  Apply filters
</Button>
...
setStatus(`Applying "${layer}" filter…`);

Note that the Jimu UI <Button> already has an accessible name from its visible text Apply filters, and the focus order will follow the DOM order.

Label form controls with Calcite components

Use visible labels by wrapping inputs with CalciteLabel. The component provides a consistent UI and accessible labeling without the need for aria-named attributes:

<CalciteLabel>
  Select layer
  <CalciteSelect
    label="Selected layer"
    value={layer}
    onCalciteSelectChange={(event) => {
      const target = event.target as HTMLSelectElement;
      setLayer(target.value as LayerKey);
      setStatus(`Layer set to "${target.value}". Ready to apply filter.`);
    }}
  >
    <CalciteOption value="parcels">Parcels</CalciteOption>
    <CalciteOption value="buildings">Buildings</CalciteOption>
    <CalciteOption value="roads">Roads</CalciteOption>
  </CalciteSelect>
</CalciteLabel>

Return focus after applying changes

After asynchronous updates, return focus to the initiating control to prevent users from losing their place. Returning focus to the triggering element is especially useful when applied filters change content that may not be visible in the current UI, or which affects content that isn’t in close proximity to the current control.

const restoreApplyFocus = () => {
  if (applyBtnRef.current && "focus" in applyBtnRef.current) {
    (applyBtnRef.current as HTMLElement).focus();
    return;
  }
  document.getElementById("apply-filters-btn")?.focus();
};
...
const applyFilter = async () => {
  setStatus(`Applying "${layer}" filter…`);
  await new Promise((r) => setTimeout(r, 250));

  setSelectedObjectId(101);
  setStatus(`Filter applied to "${layer}". Showing selected feature details.`);

  restoreApplyFocus();
};

To view the full code sample, explore the GitHub gist.

Accessibility testing for custom widgets

Automated accessibility tools help identify common issues, however testing cannot guarantee compliance. Some testing techniques include:

  • Move focus to the widget and traverse interactive components using only keyboard navigation
  • Using a screen reader, such as JAWS, NVDA , or VoiceOver
  • Verifying focus order and focus visibility
  • Confirming all interactive elements contain accessible names

Explore recommended testing tools including a developer checklist to further boost custom widget development.

Summary

Accessible widgets are not automatic. They result from thoughtful design, semantic markup, keyboard operability, and careful consideration of Jimu UI, Calcite components, and JavaScript Maps SDK components. When accessibility is integrated from the start, custom widgets become more reliable, usable, and inclusive by design. To learn more about accessibility support and how to adapt accessibility into your solution, visit:

Stay in the loop with the latest in developer technology by subscribing to the Esri Developer Newsletter.

Share this article

Leave a Reply