Initial skills documentation — 25 categories, all SKILL.md + references + scripts
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# Web Standards & Accessibility — Ops Portal Checklist
|
||||
|
||||
Applied during the July 2026 audit to all 10 ops portal HTML files. Covers HTML5 validity, WCAG 2.1 AA, and ARIA compliance.
|
||||
|
||||
## Structural Requirements
|
||||
|
||||
### Document-Level
|
||||
- `<!DOCTYPE html>` (first line)
|
||||
- `<html lang="en">`
|
||||
- `<meta charset="UTF-8">` (first element in `<head>`)
|
||||
- `<meta name="viewport" content="width=device-width, initial-scale=1.0">`
|
||||
|
||||
### Semantic Elements
|
||||
| Element | When to Use |
|
||||
|---------|-------------|
|
||||
| `<main id="main-content" role="main">` | One per page, wraps primary content |
|
||||
| `<header>` | Page header (h1 + actions) |
|
||||
| `<footer>` | Page footer (last-updated timestamp) |
|
||||
| `<nav>` / `<nav role="navigation" aria-label="Main navigation">` | Navigation block |
|
||||
| `<section class="card" aria-labelledby="sectionId">` | Every card/section, paired with `<h2 id="sectionId">` |
|
||||
| `<h1>` → `<h2>` → `<h3>` | Strict hierarchy, no skipping |
|
||||
|
||||
### Section Card Pattern (before → after)
|
||||
|
||||
**Before (invalid):**
|
||||
```html
|
||||
<div class="card">
|
||||
<div class="card-header">Section Title</div>
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
**After (valid):**
|
||||
```html
|
||||
<section class="card" aria-labelledby="myHeading">
|
||||
<h2 class="card-header" id="myHeading">Section Title</h2>
|
||||
...
|
||||
</section>
|
||||
```
|
||||
|
||||
## Accessibility Requirements
|
||||
|
||||
### Interactive Elements
|
||||
- **SVG icons** — all decorative/informational SVGs need:
|
||||
- `role="img"`
|
||||
- `aria-label="Refresh"` (or equivalent descriptive text)
|
||||
- **Error banners** — must have `role="alert"` and `aria-live="polite"`
|
||||
- **Tables** — every `<th>` must have `scope="col"` (or `scope="row"`)
|
||||
- **Buttons** — `<button>` elements need accessible text (icon-only buttons need `aria-label`)
|
||||
- **Navigation toggle** — hamburger button needs `aria-label="Toggle navigation"` and `aria-expanded`
|
||||
|
||||
### Color & Contrast
|
||||
- The dark theme palette passes WCAG AA:
|
||||
- `--bg-body: #0f172a` / `--text-primary: #f1f5f9` → ratio 15.1:1
|
||||
- `--accent: #f59e0b` (amber) on `--bg-card: #1e293b` → ratio 7.2:1
|
||||
- Status colors (green `#22c55e`, red `#ef4444`) used only as supplements, not sole indicators
|
||||
- Status dots (`.status-dot`) are always paired with text labels
|
||||
|
||||
### Focus & Keyboard
|
||||
- `.refresh-btn:hover` has `border-color: var(--accent)` — also applies `:focus-visible` (browser default)
|
||||
- `.filter-btn.active` — selection state is conveyed through text (class change + visible count)
|
||||
- Sortable table headers use `cursor: pointer` and hover highlight — no `:focus` outline removed
|
||||
|
||||
### Landmarks
|
||||
- `<div id="nav">` → nav injected via `fetch('/nav.html')`, already has `role="navigation" aria-label="Main navigation"`
|
||||
- `<main id="main-content" role="main">` — primary content landmark
|
||||
- `<header>` — implicit banner landmark
|
||||
- `<footer>` — implicit contentinfo landmark
|
||||
|
||||
## Nav Injection Pattern (Critical)
|
||||
|
||||
Inline `<script>` in `nav.html` **does not execute** when the file is loaded via `innerHTML`. Always:
|
||||
|
||||
1. **Place nav JS in `/js/utils.js`** as `window.initNav()`
|
||||
2. **Call `initNav()` after every nav injection:**
|
||||
|
||||
```html
|
||||
fetch('/nav.html')
|
||||
.then(r => r.text())
|
||||
.then(html => {
|
||||
document.getElementById('nav').innerHTML = html;
|
||||
if (typeof window.initNav === 'function') window.initNav();
|
||||
})
|
||||
```
|
||||
|
||||
`initNav()` handles:
|
||||
- Active link highlighting (via `data-path` attribute)
|
||||
- Hamburger toggle (`aria-expanded` management)
|
||||
- Click-outside-to-close
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
# Structural balance check
|
||||
for section balance:
|
||||
grep -Pc '<section\b' file.html
|
||||
grep -c '</section>' file.html
|
||||
|
||||
for main balance:
|
||||
grep -c '<main' file.html
|
||||
grep -c '</main>' file.html
|
||||
|
||||
for h2 balance:
|
||||
grep -Pc '<h2\b' file.html
|
||||
grep -c '</h2>' file.html
|
||||
```
|
||||
|
||||
## Common Fixes
|
||||
|
||||
| Issue | Fix |
|
||||
|-------|-----|
|
||||
| `<div class="card">` → `<section>` | Change opening to `<section class="card" aria-labelledby="id">`, closing to `</section>` |
|
||||
| `.card-header` → `<h2>` | Change `<div class="card-header">` to `<h2 class="card-header" id="id">`, closing to `</h2>` |
|
||||
| `</div>` should be `</section>` | Convert all section-closing `</div>` to `</section>` |
|
||||
| SVG missing `role="img"` | Add `role="img"` and `aria-label="..."` |
|
||||
| Error banner missing ARIA | Add `role="alert" aria-live="polite"` |
|
||||
| `<th>` missing `scope` | Add `scope="col"` to every `<th>` |
|
||||
| No `<main>` wrapper | Wrap primary content in `<main id="main-content" role="main">` |
|
||||
| Inline nav script | Move to `/js/utils.js` as `window.initNav()`, call after injection |
|
||||
Reference in New Issue
Block a user