Skip to content

Commit 20b616c

Browse files
authored
Merge pull request #3 from geospatialem/geospatialem/high-contrast-sample
feat: add high contrast sample
2 parents 0f7e83a + 318f76e commit 20b616c

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,22 @@ Presented at the 2025 Esri Developer Summit by Kelly Hutchins and Kitty Hurley o
55
- [Demo Site](https://geospatialem.github.io/dts-2025-build-a11y-web-maps-sdk-js-calcite)
66
- [Code](https://github.com/geospatialem/dts-2025-build-a11y-web-maps-sdk-js-calcite)
77
- Slides, opens in a new window 🚧 _Coming soon_ 🚧
8+
9+
## Demos
10+
11+
1. Map Description and Live Regions 🚧
12+
- Provide context to when the map has loaded and include a description when the map is in focus to further WCAG's [1.3.1: Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) Success Criterion.
13+
2. [Expand Component Focus Trap Disabled](demos/expand-component/index.html)
14+
- Provide users the ability to not be trapped when interacting with the Expand component to further WCAG's [2.1.2: No Keyboard Trap](https://www.w3.org/WAI/WCAG21/Understanding/no-keyboard-trap.html) Success Criterion.
15+
3. Consistent Focus 🚧
16+
- Provide a consistent focus order throughout your UI supporting WCAG's [2.4.3: Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) and [3.2.3: Consistent Navigation](https://www.w3.org/WAI/WCAG22/Understanding/consistent-navigation.html) Success Criterion.
17+
4. [High Contrast](demos/high-contrast/index.html)
18+
- Explore contrast with your data, altering the basemap and layer effects to support the map's purpose when a user has enabled high contrast on their operating system, also supporting WCAG's [1.4.3: Contrast Minimum](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum) Success Criterion.
19+
5. Reduced Motion 🚧
20+
- Support reduced motion when a user has enabled reduced motion, or disabled animations on their operating system, also supporting WCAG's [2.3.3: Animation from Interactions](https://www.w3.org/WAI/WCAG22/Understanding/animation-from-interactions.html) Success Criterion.
21+
22+
## Resources
23+
24+
- [Calcite Accessibility](https://developers.arcgis.com/calcite-design-system/foundations/accessibility)
25+
- [ArcGIS Maps SDK for JavaScript accessibility](https://developers.arcgis.com/javascript/latest/accessibility)
26+
- [Subscribe to the Esri accessibility community](https://community.esri.com/t5/accessibility/ct-p/accessibility)

demos/high-contrast/app.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
require(["esri/Basemap"], (Basemap) => {
2+
3+
const mapEl = document.getElementById("mapEl");
4+
5+
// High contrast basemap (dark)
6+
const highContrastDarkBasemap = new Basemap({
7+
portalItem: {
8+
id: "3e23478909194c54992eaaee78b5f754" // Dark
9+
},
10+
title: "High contrast dark theme",
11+
id: "high-contrast-dark"
12+
});
13+
14+
// High contrast basemap (light)
15+
const highContrastLightBasemap = new Basemap({
16+
portalItem: {
17+
id: "084291b0ecad4588b8c8853898d72445" // Light
18+
},
19+
title: "High contrast (light theme)",
20+
id: "high-contrast-light"
21+
});
22+
23+
// Mode
24+
let mode = "light";
25+
const toggleModeEl = document.getElementById("toggle-mode");
26+
const darkModeCss = document.getElementById("jsapi-mode-dark");
27+
const lightModeCss = document.getElementById("jsapi-mode-light");
28+
29+
toggleModeEl.addEventListener("click", handleModeChange);
30+
31+
function handleModeChange() {
32+
mode = mode === "dark" ? "light" : "dark";
33+
const isDarkMode = mode === "dark";
34+
darkModeCss.disabled = !isDarkMode;
35+
lightModeCss.disabled = isDarkMode;
36+
toggleModeEl.icon = isDarkMode ? "moon" : "brightness";
37+
document.body.className = isDarkMode ? "calcite-mode-dark" : "";
38+
39+
// If high contrast is enabled, display a high contrast basemap
40+
// Else display a gray basemap
41+
if (contrastMedia.matches) {
42+
mapEl.basemap = isDarkMode ? highContrastDarkBasemap : highContrastLightBasemap;
43+
} else {
44+
mapEl.basemap = isDarkMode ? "dark-gray-vector" : "gray-vector";
45+
}
46+
47+
document.querySelectorAll(`.calcite-mode-${isDarkMode ? "light" : "dark"}`).forEach(node =>
48+
node.classList.replace(`calcite-mode-${isDarkMode ? "light" : "dark"}`, `calcite-mode-${mode}`)
49+
);
50+
}
51+
52+
// High contrast support with basemap and layer effects
53+
const contrastMedia = matchMedia("(forced-colors: active)");
54+
function checkContrastMedia() {
55+
try {
56+
if (mode == "dark") {
57+
mapEl.basemap = contrastMedia.matches ? highContrastDarkBasemap : "dark-gray-vector";
58+
//mapEl.basemap = "dark-gray-vector";
59+
contrastMedia.matches ? mapEl.map.layers._items[2].effect = "bloom(1.5, 0.5px, 0.1)" : mapEl.map.layers._items[2].effect = "bloom(0, 0px, 0)";
60+
} else {
61+
mapEl.basemap = contrastMedia.matches ? highContrastLightBasemap : "gray-vector";
62+
//mapEl.basemap = "gray-vector";
63+
contrastMedia.matches ? mapEl.map.layers._items[2].effect = "drop-shadow(3px, 1px, 3px)" : mapEl.map.layers._items[2].effect = "drop-shadow(0px, 0px, 0px)";
64+
}
65+
} catch(err) { }
66+
}
67+
68+
// Event listeners on map load and high contrast media query
69+
mapEl.addEventListener("arcgisViewChange", checkContrastMedia);
70+
contrastMedia.addListener(checkContrastMedia);
71+
72+
});

demos/high-contrast/index.html

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<html lang="en">
2+
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" />
6+
<title>2025 Esri Developer & Technology Summit - High contrast</title>
7+
8+
<!-- Calcite imports -->
9+
<script type="module" src="https://js.arcgis.com/calcite-components/3.0.3/calcite.esm.js"></script>
10+
11+
12+
<!-- ArcGIS Maps SDK for JavaScript imports -->
13+
<script src="https://js.arcgis.com/4.32/"></script>
14+
<link id="jsapi-mode-light" rel="stylesheet" href="https://js.arcgis.com/4.32/esri/themes/light/main.css" />
15+
<link disabled id="jsapi-mode-dark" rel="stylesheet" href="https://js.arcgis.com/4.32/esri/themes/dark/main.css" />
16+
<script type="module" src="https://js.arcgis.com/map-components/4.32/arcgis-map-components.esm.js"></script>
17+
18+
<!-- Demo imports -->
19+
<script src="./app.js" defer></script>
20+
<link rel="stylesheet" href="./style.css" />
21+
</head>
22+
23+
<body>
24+
<calcite-shell content-behind>
25+
<calcite-navigation slot="header" id="nav">
26+
<calcite-navigation-logo href="#" icon="accessibility" alt="Application logo" slot="logo"
27+
heading="High contrast"
28+
description="Esri Developer & Technology Summit 2025"></calcite-navigation-logo>
29+
<calcite-action-pad layout="horizontal" expand-disabled slot="content-end">
30+
<calcite-action id="toggle-mode" text="Toggle mode" icon="brightness"></calcite-action>
31+
</calcite-action-pad>
32+
<calcite-tooltip placement="bottom" reference-element="toggle-mode" slot="content-end">Toggle
33+
mode</calcite-tooltip>
34+
</calcite-navigation>
35+
<arcgis-map item-id="5255b3b101594f259ce4326671e8d5c6" id="mapEl" zoom="2">
36+
<arcgis-legend legend-style="classic" position="bottom-right"></arcgis-legend>
37+
</arcgis-map>
38+
</calcite-shell>
39+
40+
</body>
41+
42+
</html>

demos/high-contrast/style.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* Esri Developer & Technology Summit Demo Template */
2+
/* Demo template theming example */
3+
4+
body calcite-navigation-logo{
5+
--calcite-color-text-2:#737373;
6+
}
7+
body.calcite-mode-dark calcite-navigation-logo{
8+
--calcite-icon-color:#8FD0FF;
9+
--calcite-color-text-2:#C7C7C7;
10+
}
11+
12+
13+
html,
14+
body,
15+
arcgis-map {
16+
padding: 0;
17+
margin: 0;
18+
height: 100%;
19+
width: 100%;
20+
}
21+
22+
calcite-shell-panel[slot="panel-start"] calcite-panel {
23+
border-top: 0;
24+
}

0 commit comments

Comments
 (0)