Skip to content

Commit 6be130d

Browse files
authored
Merge pull request #91 from fulll/feat/accessibility-homepage
Make VitePress docs fully WCAG 2.1 AA accessible
2 parents 851e4d8 + 8971570 commit 6be130d

33 files changed

+1618
-118
lines changed

.github/instructions/bug-fixing.instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ excludeAgent: "code-review"
55

66
# Bug fixing — instructions for Copilot coding agent
77

8+
> **Skill reference:** for the extended symptom → module diagnostic table, test-first patterns and minimal fix principles read `.github/skills/bug-fixing.md` first.
9+
810
Follow these steps when fixing a bug in this repository.
911

1012
## 1. Reproduce the bug before writing any fix

.github/instructions/documentation.instructions.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ applyTo: "docs/**"
44

55
# Documentation — instructions for Copilot coding agent
66

7+
> **Skill reference:** for deep domain knowledge (VitePress theme architecture, CSS variables, accessibility patterns, responsive patterns, full validation commands) read `.github/skills/documentation.md` first.
8+
79
Follow these conventions when writing or editing pages in the `docs/` directory.
810

911
## 1. Tool & rendering pipeline
@@ -93,9 +95,20 @@ docs/
9395
Before opening a PR for any docs change:
9496

9597
```bash
96-
bun run docs:build # must complete without errors
97-
bun run format:check # oxfmt — no formatting diff
98+
bun run docs:build # must complete without errors or dead-link warnings
99+
bun run format:check # oxfmt — no formatting diff
98100
```
99101

100102
- All internal links must resolve (VitePress reports dead links on build).
101103
- No new `bun run knip` violations (docs/\*\* is excluded but `package.json` changes are not).
104+
105+
If the PR modifies any Vue component, CSS, or page layout, also run the accessibility and responsive suites:
106+
107+
```bash
108+
bun run docs:build:a11y
109+
bun run docs:preview -- --port 4173 &
110+
bun run docs:a11y # pa11y-ci WCAG 2.1 AA — must report 0 errors
111+
bun run docs:test:responsive # Playwright — 20/20 tests green (4 viewports × 5 pages)
112+
```
113+
114+
See `.github/skills/documentation.md` for pa11y configuration details, WCAG contrast rules, accessible component patterns, and responsive breakpoint guidance.

.github/instructions/implement-feature.instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ excludeAgent: "code-review"
55

66
# Implement feature — instructions for Copilot coding agent
77

8+
> **Skill reference:** for the full architectural layer map, type-first design patterns, CLI option conventions, render sub-module extension playbook and test strategies read `.github/skills/feature.md` first.
9+
810
Follow these steps when implementing a new feature in this repository.
911

1012
## 1. Understand the task scope before writing code

.github/instructions/refactoring.instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ excludeAgent: "code-review"
55

66
# Refactoring — instructions for Copilot coding agent
77

8+
> **Skill reference:** for architectural invariants, safe rename playbook, module extraction patterns, characterisation test strategy and `knip` output interpretation read `.github/skills/refactoring.md` first.
9+
810
Follow these steps when refactoring existing code in this repository.
911

1012
## 1. Define the goal and scope

.github/instructions/release.instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ excludeAgent: "code-review"
55

66
# Release — instructions for Copilot coding agent
77

8+
> **Skill reference:** for the semver decision guide, CD pipeline mechanics, binary targets, blog post format reference and versioned docs snapshot details read `.github/skills/release.md` first.
9+
810
Follow these steps when cutting a new release of `github-code-search`.
911

1012
## 1. Determine the version bump

.github/skills/bug-fixing.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Bug fixing skill — github-code-search
2+
3+
Deep reference for diagnosing and fixing bugs in this codebase.
4+
This skill complements `.github/instructions/bug-fixing.instructions.md`.
5+
6+
---
7+
8+
## Symptom → module diagnostic table
9+
10+
| Symptom | Primary suspect | Secondary suspect |
11+
| ----------------------------------------------------------- | ----------------------------------------------- | --------------------------------- |
12+
| Results missing or duplicated | `src/aggregate.ts` | `src/api.ts` (pagination) |
13+
| Wrong repository grouping | `src/group.ts` | `src/aggregate.ts` |
14+
| `--exclude-repositories` / `--exclude-extracts` not working | `src/aggregate.ts` | `github-code-search.ts` (parsing) |
15+
| Markdown output malformed | `src/output.ts` ||
16+
| JSON output missing fields or wrong shape | `src/output.ts` | `src/types.ts` (interface) |
17+
| Syntax highlighting wrong colour / wrong language | `src/render/highlight.ts` ||
18+
| Row navigation skips or wraps incorrectly | `src/render/rows.ts` | `src/tui.ts` (key handler) |
19+
| Select-all / select-none inconsistent | `src/render/selection.ts` | `src/tui.ts` |
20+
| Filter count / stats incorrect | `src/render/filter.ts`, `src/render/summary.ts` ||
21+
| Path filter (`/regex/`) doesn't match expected | `src/render/filter-match.ts` | `src/tui.ts` (filter state) |
22+
| API returns 0 results or stops paginating | `src/api.ts` | `src/api-utils.ts` |
23+
| Rate limit hit / 429 not retried | `src/api-utils.ts` (`fetchWithRetry`) ||
24+
| TUI shows blank screen or wrong row | `src/tui.ts` | `src/render/rows.ts` |
25+
| Help overlay doesn't appear / has wrong keys | `src/render.ts` (`renderHelpOverlay`) | `src/tui.ts` |
26+
| Upgrade fails or replaces wrong binary | `src/upgrade.ts` ||
27+
| Completion script wrong content | `src/completions.ts` ||
28+
| Completion file written to wrong path | `src/completions.ts` (`getCompletionFilePath`) | env vars (`XDG_*`, `ZDOTDIR`) |
29+
| Completion not refreshed after upgrade | `src/upgrade.ts` (`refreshCompletions`) ||
30+
| `--version` shows wrong info | `build.ts` (SHA injection) ||
31+
| CLI option ignored or parsed wrong | `github-code-search.ts` | `src/types.ts` (`OutputType`) |
32+
33+
---
34+
35+
## Reproducing a bug
36+
37+
A complete bug report must have:
38+
39+
1. **Exact command** (redact `GITHUB_TOKEN` with `***`):
40+
```
41+
GITHUB_TOKEN=*** github-code-search query "pattern" --org acme
42+
```
43+
2. **Observed output** vs **expected output**.
44+
3. **Version string**: `github-code-search --version` → e.g. `1.8.0 (a1b2c3d · darwin/arm64)`.
45+
4. **Bun version** (when running from source): `bun --version`.
46+
47+
If the report is missing items 1 or 3, read the relevant module(s) to hypothesise the root cause before asking for more info.
48+
49+
---
50+
51+
## Test-first patterns for bugs
52+
53+
### Pure function bug (aggregate, group, output, render/\*)
54+
55+
```typescript
56+
// src/aggregate.test.ts
57+
describe("applyFiltersAndExclusions — bug #N", () => {
58+
it("excludes org-prefixed repo names correctly", () => {
59+
const result = applyFiltersAndExclusions(matches, {
60+
excludeRepositories: ["acme/my-repo"], // the previously broken form
61+
});
62+
expect(result).not.toContainEqual(expect.objectContaining({ repo: "acme/my-repo" }));
63+
});
64+
});
65+
```
66+
67+
The test must **fail** with the current code before the fix. Commit the test, then fix.
68+
69+
### api-utils bug (retry / pagination)
70+
71+
```typescript
72+
// src/api-utils.test.ts
73+
it("retries on 429 with Retry-After header", async () => {
74+
let callCount = 0;
75+
globalThis.fetch = async () => {
76+
callCount++;
77+
if (callCount === 1) {
78+
return new Response(null, {
79+
status: 429,
80+
headers: { "Retry-After": "0" },
81+
});
82+
}
83+
return new Response(JSON.stringify({ ok: true }), { status: 200 });
84+
};
85+
await fetchWithRetry("https://api.github.com/test");
86+
expect(callCount).toBe(2);
87+
});
88+
```
89+
90+
### Side-effectful bug (tui, api) — no unit test possible
91+
92+
Document manual repro steps in the PR description:
93+
94+
```markdown
95+
## Steps to reproduce (before fix)
96+
97+
1. `GITHUB_TOKEN=... github-code-search query "foo" --org acme`
98+
2. Press `` past the last result
99+
3. Expected: cursor stays on last row / Expected: wraps to first row
100+
4. Observed: cursor jumps to blank row
101+
```
102+
103+
---
104+
105+
## Minimal fix principles
106+
107+
- **Touch only the root cause.** Do not opportunistically refactor neighbouring code in the same PR — it makes the fix harder to review and risks introducing new bugs.
108+
- **Respect pure/IO layering**: a fix in `aggregate.ts` must not add a `console.log` call.
109+
- **Type changes cascade**: if `src/types.ts` must change, run `bun run knip` to find all affected consumers and update them all in the same PR.
110+
- **Regression comment**: if the root cause is non-obvious, add one line above the fix:
111+
```typescript
112+
// Fix: short names ("repo") and qualified names ("org/repo") must both match — see issue #N
113+
```
114+
115+
---
116+
117+
## Validation after fix
118+
119+
```bash
120+
bun test # the previously failing test now passes; full suite still green
121+
bun run lint # oxlint — zero errors
122+
bun run format:check # oxfmt — no formatting diff
123+
bun run knip # no unused exports or imports
124+
bun run build.ts # binary compiles without errors
125+
```

.github/skills/documentation.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Documentation skill — github-code-search
2+
3+
Deep reference for writing and maintaining VitePress documentation in this project.
4+
This skill complements `.github/instructions/documentation.instructions.md` (the step-by-step workflow).
5+
6+
---
7+
8+
## VitePress theme architecture
9+
10+
Custom components live in `docs/.vitepress/theme/`:
11+
12+
| File / folder | Role |
13+
| ------------------------- | ------------------------------------------------------------------ |
14+
| `index.ts` | Theme entry point — imports components and global CSS |
15+
| `custom.css` | All CSS overrides — brand colours, typography, responsive, a11y |
16+
| `Layout.vue` | Root layout wrapper (adds `RichFooter`, manages slot injection) |
17+
| `TerminalDemo.vue` | Animated terminal on the hero — `aria-hidden="true"` (decorative) |
18+
| `ComparisonTable.vue` | Feature comparison table with responsive 3-column layout |
19+
| `UseCaseTabs.vue` | WAI-ARIA Tabs pattern (roving tabindex, ArrowLeft/Right/Home/End) |
20+
| `InstallSection.vue` | Install command snippets with OS tabs |
21+
| `HowItWorks.vue` | 3-step explainer with responsive card layout |
22+
| `TestimonialsSection.vue` | Community testimonials carousel |
23+
| `ProductionCta.vue` | "Used in production?" CTA banner (`<section aria-labelledby="…">`) |
24+
| `VersionBadge.vue` | Release badge — reads `version` from `package.json` at build time |
25+
| `RichFooter.vue` | Custom footer replacing VitePress default |
26+
27+
**Extending a component:**
28+
29+
1. Locate the `.vue` file above.
30+
2. Follow the existing scoped `<style>` conventions (no global selectors inside `<style scoped>`).
31+
3. Add responsive styles inside the component's `<style>` or in `custom.css` if the rule is global.
32+
4. Never import additional NPM packages for styling — only `picocolors` (CLI) and VitePress built-ins.
33+
34+
---
35+
36+
## CSS variable system
37+
38+
Always use VitePress CSS variables — never hard-code colours in component `<style>` blocks:
39+
40+
| Variable | Meaning |
41+
| ----------------------- | -------------------------------------------------------- |
42+
| `--vp-c-brand-1` | Violet `#9933FF` / `#cc88ff` (dark) |
43+
| `--vp-c-brand-2` | Hover darkening |
44+
| `--vp-c-brand-soft` | Soft tint for backgrounds |
45+
| `--vp-c-text-1` | Primary text (≥ WCAG AA on bg) |
46+
| `--vp-c-text-2` | Secondary text (≥ WCAG AA on bg) |
47+
| `--vp-c-text-3` | **Do not use for text** — 2.87:1 contrast, fails WCAG AA |
48+
| `--vp-c-divider` | Border / separator |
49+
| `--vp-c-bg-soft` | Card / inset background |
50+
| `--vp-font-family-mono` | Monospace (code blocks) |
51+
52+
Dark mode: VitePress applies a `.dark` class on `<html>`. Use `.dark .selector { … }` — never `@media (prefers-color-scheme: dark)`.
53+
54+
---
55+
56+
## Accessibility (WCAG 2.1 AA)
57+
58+
This project maintains **zero pa11y-ci violations** at WCAG 2.1 AA level.
59+
60+
### Tool
61+
62+
```bash
63+
bun run docs:build:a11y # build with VITEPRESS_HOSTNAME=http://localhost:4173
64+
bun run docs:preview -- --port 4173 &
65+
bun run docs:a11y # pa11y-ci via sitemap — must report 0 errors
66+
```
67+
68+
Config: `.pa11yci.json`. F77 (Mermaid duplicate SVG IDs) is ignored — not blocking for AT users.
69+
70+
### Common patterns
71+
72+
| Pattern | Correct implementation |
73+
| -------------------------------- | ------------------------------------------------------------------------------------------------------ |
74+
| Landmark regions | `<section aria-labelledby="id">` + matching `id` on heading |
75+
| Icon-only buttons / links | `aria-label="Descriptive text"` |
76+
| Decorative images / SVG | `aria-hidden="true"` and no `alt` (or `alt=""`) |
77+
| External links (open in new tab) | `aria-label="Label (opens in a new tab)"` or `.sr-only` suffix |
78+
| Check / cross icons in tables | `aria-label="Yes"` / `aria-label="No"` |
79+
| Table column headers | `<th scope="col">` + `<caption class="sr-only">` on `<table>` |
80+
| Interactive tabs | Full WAI-ARIA Tabs: `role="tablist"`, `role="tab"`, `role="tabpanel"`, roving `tabindex`, keyboard nav |
81+
| Screen-reader-only text | `.sr-only` utility class defined in `custom.css` |
82+
| Focus visibility | `:focus-visible` ring defined globally in `custom.css` |
83+
84+
### Contrast minimums (WCAG AA)
85+
86+
- Normal text (< 18pt): **4.5:1**
87+
- Large text (≥ 18pt bold or ≥ 24pt): **3:1**
88+
- UI components and icons: **3:1**
89+
90+
Avoid `var(--vp-c-text-3)` for any visible text — it is ~2.87:1 against default VitePress backgrounds.
91+
92+
---
93+
94+
## Responsive (mobile-first)
95+
96+
This project maintains **zero horizontal overflow** across 4 tested viewports via Playwright.
97+
98+
### Tested viewports
99+
100+
| Label | Width | Height |
101+
| --------------- | ----- | ------ |
102+
| Galaxy S21 | 360px | 800px |
103+
| iPhone SE | 375px | 667px |
104+
| iPhone 14 | 390px | 844px |
105+
| Tablet portrait | 768px | 1024px |
106+
107+
### Tool
108+
109+
```bash
110+
bun run docs:build
111+
bun run docs:preview -- --port 4173 &
112+
bun run docs:test:responsive # 20 tests (4 viewports × 5 pages) — must all pass
113+
```
114+
115+
Config: `playwright.config.ts`. Spec: `scripts/responsive.pw.ts`. Screenshots on failure: `test-results/screenshots/` (gitignored).
116+
117+
### VitePress quirks at 768px
118+
119+
VitePress hides its hamburger at `≥768px` and shows desktop nav links — which overflow the viewport at exactly 768px on this project's config. The fix in `custom.css`:
120+
121+
```css
122+
@media (max-width: 960px) {
123+
.VPNavBarMenu,
124+
.VPNavBarExtra {
125+
display: none !important;
126+
}
127+
.VPNavBarHamburger {
128+
display: flex !important;
129+
}
130+
}
131+
@media (min-width: 768px) and (max-width: 960px) {
132+
.VPNavScreen {
133+
display: block !important;
134+
}
135+
}
136+
```
137+
138+
### Responsive patterns for components
139+
140+
| Problem | Solution |
141+
| ----------------------------------------- | ---------------------------------------------------------------------------------------- |
142+
| Table columns too wide on mobile | `table-layout: fixed`, abbreviated headers via `.ct-name-short` / `.ct-name-long` toggle |
143+
| Long feature descriptions push rows wider | `display: none` on `.ct-feature-desc` at `≤640px` |
144+
| Terminal/code blocks cause page scroll | `overflow-x: auto` on the scroll container, `max-width: 100%` on the block |
145+
| Long strings in `<pre>` overflow | `pre { max-width: 100%; overflow-x: auto; }` in `custom.css` |
146+
147+
Never use `overflow-x: hidden` on the page root — it silently clips content. Apply it only to specific containers where clipping is intentional.
148+
149+
---
150+
151+
## Validation checklist
152+
153+
Before opening a PR for any docs change:
154+
155+
```bash
156+
bun run docs:build # must complete without errors or dead-link warnings
157+
bun run docs:build:a11y # build for a11y audit
158+
bun run docs:preview -- --port 4173 &
159+
bun run docs:a11y # 0 pa11y violations
160+
bun run docs:test:responsive # 20/20 Playwright tests green
161+
bun run format:check # oxfmt — no formatting diff
162+
```

0 commit comments

Comments
 (0)