Skip to content

Commit

Permalink
Fixes from accessibility audit. (#344)
Browse files Browse the repository at this point in the history
* Improve accessibility of app.

* remove user-scalable=no from html head
* add aria-label to builds page elements
* hide build table columns to prevent horizontal scrolling
* display error when map URL can't be loaded in viewer
* add accessible labels to theme and language select elements
* stateful text display on show JSON button
* bump maplibre-gl-inspect to 4.7.1 for accessible toggle button
  • Loading branch information
bdon authored Jan 6, 2025
1 parent a65c962 commit cee4351
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 59 deletions.
2 changes: 1 addition & 1 deletion app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Protomaps Basemaps</title>
<link rel="preconnect" href="https://demo-bucket.protomaps.com"/>
</head>
Expand Down
8 changes: 4 additions & 4 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"format": "biome format --write src test --javascript-formatter-indent-style=space --json-formatter-indent-style=space"
},
"dependencies": {
"@maplibre/maplibre-gl-inspect": "^1.7.0",
"@maplibre/maplibre-gl-inspect": "^1.7.1",
"maplibre-gl": "5.0.0",
"pixelmatch": "^5.3.0",
"pmtiles": "^4.1.0",
Expand Down
61 changes: 29 additions & 32 deletions app/src/Builds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ interface Build {
version: string;
}

function isMonday(dateStr: string): boolean {
function toDate(dateStr: string): Date {
const year = Number.parseInt(dateStr.substring(0, 4), 10);
const month = Number.parseInt(dateStr.substring(4, 6), 10) - 1; // Subtract 1 because months are 0-indexed in JavaScript dates
const day = Number.parseInt(dateStr.substring(6, 8), 10);
const date = new Date(year, month, day);
return date.getDay() === 1;
return new Date(year, month, day);
}

function formatBytes(bytes: number, decimals = 2) {
Expand Down Expand Up @@ -48,9 +47,10 @@ function BuildComponent(props: {
setCmpB: (i: number) => void;
}) {
const build = props.build;
const dateStr = build.key.substr(0, 8);
const date = toDate(dateStr);
const link = `https://build.protomaps.com/${build.key}`;
const date = build.key.substr(0, 8);
const statsLink = `https://build.protomaps.com/${date}.layerstats.parquet`;
const statsLink = `https://build.protomaps.com/${dateStr}.layerstats.parquet`;
const idx = props.idx;

const onChangeA = () => {
Expand All @@ -62,31 +62,28 @@ function BuildComponent(props: {
};

return (
<tr style={{ color: isMonday(date) ? "black" : "#aaa" }}>
<tr style={{ color: date.getDay() === 1 ? "black" : "#aaa" }}>
<td>
<span style={{ display: "inline-block", width: "20px" }}>
{idx > props.cmpB && (
<input
type="radio"
onChange={onChangeA}
checked={idx === props.cmpA}
/>
)}
</span>
<span style={{ display: "inline-block", width: "20px" }}>
{idx < props.cmpA && (
<input
type="radio"
onChange={onChangeB}
checked={idx === props.cmpB}
/>
)}
</span>
<input
disabled={idx <= props.cmpB}
type="radio"
onChange={onChangeA}
checked={idx === props.cmpA}
aria-label={`compare earlier build ${date.toDateString()}`}
/>
<input
class="ml-2"
disabled={idx >= props.cmpA}
type="radio"
onChange={onChangeB}
checked={idx === props.cmpB}
aria-label={`compare later build ${date.toDateString()}`}
/>
</td>
<td>{build.key}</td>
<td>{build.version}</td>
<td>{formatBytes(build.size)}</td>
<td>{build.uploaded}</td>
<td class="hidden lg:table-cell">{formatBytes(build.size)}</td>
<td class="hidden lg:table-cell">{build.uploaded}</td>
<td>
<a class="underline" href={`/#tiles=${link}`}>
map
Expand All @@ -100,13 +97,13 @@ function BuildComponent(props: {
xray
</a>
</td>
<td>
<td class="hidden lg:table-cell">
<a class="underline" href={link}>
download
</a>
</td>
<td>
{date >= "20231228" ? (
<td class="hidden lg:table-cell">
{dateStr >= "20231228" ? (
<a class="underline" href={statsLink}>
stats
</a>
Expand Down Expand Up @@ -193,15 +190,15 @@ function Builds() {
<p>Only Monday builds (black) are kept indefinitely.</p>
<div class="space-x-2 my-2">
<button class="btn-primary" type="button" onClick={openVisualTests}>
Compare visual tests
Compare selected versions
</button>
{latestStyle() ? (
<button class="btn-primary" type="button" onClick={openMaperture}>
Compare in Maperture (style {latestStyle()})
Compare in Maperture
</button>
) : null}
</div>
<table class="table-auto border-separate border-spacing-4 font-mono">
<table class="table-auto border-separate text-xs lg:text-base border-spacing-2 lg:border-spacing-4 font-mono">
<tbody>
<For each={builds()}>
{(build, idx) => (
Expand Down
74 changes: 55 additions & 19 deletions app/src/MapView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ function MapLibreView(props: {
let protocolRef: Protocol | undefined;
let hiddenRef: HTMLDivElement | undefined;

const [error, setError] = createSignal<string | undefined>();

onMount(() => {
if (getRTLTextPluginStatus() === "unavailable") {
setRTLTextPlugin(
Expand Down Expand Up @@ -241,6 +243,14 @@ function MapLibreView(props: {
maxWidth: "none",
});

map.on("error", (e) => {
setError(e.error.message);
});

map.on("idle", () => {
setError(undefined);
});

map.on("contextmenu", (e) => {
const features = map.queryRenderedFeatures(e.point);
if (hiddenRef && features.length) {
Expand Down Expand Up @@ -318,6 +328,11 @@ function MapLibreView(props: {
<>
<div class="hidden" ref={hiddenRef} />
<div ref={mapContainer} class="h-100 w-full flex" />
<Show when={error()}>
<div class="absolute h-20 w-full flex justify-center items-center bg-white bg-opacity-50 font-mono text-red">
{error()}
</div>
</Show>
</>
);
}
Expand Down Expand Up @@ -421,33 +436,54 @@ function MapView() {
<div class="max-w-[1500px] mx-auto">
<form onSubmit={loadTiles} class="flex">
<input
class="border-2 border-gray p-1 flex-1 mr-2"
class="border-2 border-gray p-1 flex-1 mr-2 text-xs lg:text-base"
type="text"
name="tiles"
value={tiles()}
style={{ width: "50%" }}
autocomplete="off"
/>
<button class="btn-primary" type="submit">
load
</button>
</form>
<div class="my-2 space-x-2">
<select onChange={(e) => setTheme(e.target.value)} value={theme()}>
<option value="light">light</option>
<option value="dark">dark</option>
<option value="white">data viz (white)</option>
<option value="grayscale">data viz (grayscale)</option>
<option value="black">data viz (black)</option>
</select>
<select onChange={(e) => setLang(e.target.value)} value={lang()}>
<For each={language_script_pairs}>
{(pair) => (
<option value={pair.lang}>
{pair.lang} ({pair.full_name})
</option>
)}
</For>
</select>
<div class="flex my-2 space-y-2 lg:space-y-0 space-x-2 flex-col lg:flex-row items-center">
<div class="flex items-center">
<label for="theme" class="text-xs mr-1">
theme
</label>
<select
id="theme"
onChange={(e) => setTheme(e.target.value)}
value={theme()}
autocomplete="on"
>
<option value="light">light</option>
<option value="dark">dark</option>
<option value="white">data viz (white)</option>
<option value="grayscale">data viz (grayscale)</option>
<option value="black">data viz (black)</option>
</select>
</div>
<div class="flex items-center">
<label for="lang" class="text-xs mr-1">
language
</label>
<select
id="lang"
onChange={(e) => setLang(e.target.value)}
value={lang()}
autocomplete="on"
>
<For each={language_script_pairs}>
{(pair) => (
<option value={pair.lang}>
{pair.lang} ({pair.full_name})
</option>
)}
</For>
</select>
</div>
<div class="hidden lg:inline">
<input
id="localSprites"
Expand Down Expand Up @@ -497,7 +533,7 @@ function MapView() {
class="btn-primary hidden lg:inline"
onClick={() => setShowStyleJson(!showStyleJson())}
>
get style JSON
{showStyleJson() ? "Close style JSON" : "Get style JSON"}
</button>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/src/VisualTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ function VisualTests() {
return (
<div class="flex flex-col h-screen w-full">
<Nav page={2} />
<div class="w-[1500px] mx-auto">
<div class="w-[1500px] mx-auto p-2">
<h1 class="my-8 text-4xl">Visual Tests</h1>
<div class="inline-block w-[500px] font-mono text-xs">
leftTiles={displayInfo().leftTiles}
Expand Down
3 changes: 2 additions & 1 deletion app/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export default {
purple: "#3131DC",
white: "#FFFFFF",
black: "#000000",
gray: "#E7E7F9"
gray: "#E7E7F9",
red: "#FF0000"
},
},
plugins: [],
Expand Down

0 comments on commit cee4351

Please sign in to comment.