Accessibility 學習筆記(1)
===

###### tags: `accessibility`, `React`, `ally`, `無障礙設計`
# 使用工具來偵測網頁無障礙以及確保每一個網頁都具備 h1 標籤
這篇主要紀錄自己在學習無障礙設計相關知識,此篇分享的方法來自於 Marcy Sutton 的 [Testing Accessibility](https://testingaccessibility.com/) 課程。
## 幫助網頁進行無障礙設計檢測的擴充工具
Chrome 擴充裡面有幾個好用的工具,可以協助開發者進行無障礙相關的檢測,以下分享幾個常用的:
1. [Accessibility Insights for Web](https://chrome.google.com/webstore/detail/accessibility-insights-fo/pbjjkligggfmakdaogkfomddhfmpjeni)
2. [Web developer](https://chrispederick.com/work/web-developer/)
3. [WAVE Evaluation tool](https://chrome.google.com/webstore/detail/wave-evaluation-tool/jbbplnpkjmmeebjpijfedlgcdilocofh)
## heading 標籤的使用
一個網頁的 heading 要怎麼規劃,一般來說每一個網頁都須具備一個 `h1` 標籤,接著按照網頁的規劃從 `h2`、`h3` 到 `h6`,以下我們將透過 **Web developer** 工具來查看網頁的標題標籤的結構。
課程內提供的網頁如下:

### 使用 Web DEveloper 工具:
接著我們按照這個步驟來檢視網頁的結構:
1. 到 Chrome 安裝 **Web developer** 擴充。
2. 安裝完畢後會出現在網頁的右上角,圖似一個小齒輪,左鍵點擊。
3. 選擇 **View Document Outline**。

檢視結果如下:發現我們缺少一個 `h1` 標籤。

> `h1` 主要在做什麼的?
> 對於螢幕閱讀器使用者來說,進入一個網頁內最需要瞭解的莫過於該網頁的主要目的,`h1` 主要提供給他們一個快速理解所在網頁的目的或者相關資訊。
### 其他視覺上的小缺失
另外我們也可以觀察到網頁中有一些副標題類的,似乎太小,視覺上並沒有區分出層級以及距離。

## 該怎麼把 `h1` 放進去?
假設網頁由你規劃,在了解無障礙設計的原則之後,相信都會把標題標籤的使用規劃進去,但萬一是你要維護的舊網頁呢?幾個面向可以思考:
1. 把網頁再重新規劃。
2. 直接把 `h1` 加在導航欄下。
以上都可以做到,但是都會需要動用已經規劃好的結構,以及可能會需要和相關部門如 UI/UX 等溝通協調。
## 解決辦法
透過 `React` 提供的 `Portal` 功能,[官網](https://reactjs.org/docs/portals.html#gatsby-focus-wrapper)是這樣描述這個功能的
`Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.`
我們先在 `index.html` 內建立一個 `<div id="portal-root"></div>` 並置於最上層,這樣當我們使用 `HeaderPortal` 組件時,所呈現的任何内容都將高於網頁中其他所有内容的層次結構。
### portal-root(naming is not mandatory)
```htmlembedded!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<title>CampSpots</title>
</head>
<body>
<div id="portal-root"></div>
<div id="app-root"></div>
<script type="module" src="index.js"></script>
</body>
</html>
```
### creatPortal
透過建立一個 `HeaderPortal` 的組件,然後引用 `createPortal` 模組。
這個組件將會在網頁渲染後,抓取到 `id` 為 `portal-root` 的 `<div>`,然後建立另一個 `<div>` 並加入到 `<div id="portal-root"></div>` 之下。
```javascript!
import React, { useEffect } from "react"
import { createPortal } from "react-dom"
const HeaderPortal = ({children}) => {
const mount = document.getElementById("portal-root")
const el = document.createElement("div")
useEffect(() => {
mount.appendChild(el)
return () => mount.removeChild(el)
}, [el, mount])
return createPortal(children, el)
}
export default HeaderPortal
```
```javascript!
// 網頁組件
// 此省略其餘引用的組件和模組等
import HeaderPortal from './header-portal'
const Listing = props => {
const data = ListingsData.listings[props.id]
const headerImageUrl = LoadedImageUrl(imageURLs, data.detailHeaderImageSrc)
return (
<BodyClassName className="header-overlap page-listing-detail">
<>
<div
className="page-header"
style={{ backgroundImage: `url(${headerImageUrl}` }}
>
<div className="page-header-content wide-layout">
<div className="listing-name">{data.listingName}</div>
<p className="location">{data.location}</p>
</div>
</div>
<div className="wide-layout two-parts-70-30">
<div>
<div>Description</div>
<div className="description-text" dangerouslySetInnerHTML={{ __html: sanitizeHtml(data.description) }} />
<div>
Amenities
</div>
<div className="amenity-icons grid">
{data.amenities.map((amenity, index) => {
return <div key={index}>
<Icon name={amenity} showText={true} />
</div>
})}
</div>
</div>
<div>
<div className="h4-style">
Calendar
<DatePicker />
</div>
</div>
</div>
<div className="wide-layout">
<div className="detail-images">
{data.detailImages.map((image, index) => {
let detailImageUrl = LoadedImageUrl(imageURLs, image.imageSrc)
return <img
key={index}
src={detailImageUrl}
alt={image.imageAlt}
/>
})}
</div>
</div>
</div>
</>
</BodyClassName>
)
}
export default Listing
```
上面是尚未將 `HeaderPortal` 組件加入到該網頁的組件中的 DOM tree 結構,可以看到它只是單純的一個 `<div>`,如下圖:

```javascript!
import HeaderPortal from './header-portal'
const Listing = props => {
const data = ListingsData.listings[props.id]
const headerImageUrl = LoadedImageUrl(imageURLs, data.detailHeaderImageSrc)
return (
<BodyClassName className="header-overlap page-listing-detail">
<>
// 這邊加入 <HeaderPortal> 組件
<HeaderPortal>
<h1>Camp Spots - {data.listingName}</h1>
</HeaderPortal>
...
</>
)
}
// 以下省略
```
下圖是把 `<HeaderPortal>` 加入到組件的頂層後,在 Devtools 內的 DOM tree 的結構,會看到我們順利把新的元素嵌入進去。

但是問題來了,把標題加進去後,毀了我們的版面,此時我們可以使用 `CSS` 來隱藏它,且也可以與行銷部門討論該怎麼描述這個 `h1` 標題,這樣即使隱藏起來,螢幕閱讀器使用者也依舊可以知道這個網頁的用途和相關資訊了,下圖是把 `h1` 標籤內文再寫得更詳細,透過螢幕閱讀器的樣子。

下圖是使用螢幕閱讀器的顯示,這樣視障人士就會知道說這個頁面是要預定旅程的。

## 整理其他標籤
經過後續的整理,就有結構比較完整的網頁了。
