@its-thepoe/alt-text 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/SKILL.md +159 -0
  2. package/package.json +13 -0
  3. package/reference.md +235 -0
package/SKILL.md ADDED
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: alt-text
3
+ description: >-
4
+ Scans images in a codebase for missing, empty, or low-quality alt text and
5
+ generates context-aware alt text drafts. Presents findings in a review table
6
+ for user approval before applying changes. Use when the user says "add alt
7
+ text", "fix alt text", "accessibility audit images", "a11y images", or
8
+ "missing alt". Supports Next.js (next/image), React, Astro, and Vue. Not for
9
+ full WCAG audits beyond images — use accessibility-review if present.
10
+ argument-hint: "[scope: repo | folder path | cms]"
11
+ ---
12
+
13
+ # Alt text
14
+
15
+ **Orchestrator skill:** scan → classify → draft → **review table (blocking)** → apply → verify.
16
+
17
+ Deep rules, grep patterns, decision tree, and examples: [reference.md](reference.md).
18
+
19
+ ---
20
+
21
+ ## Scope
22
+
23
+ If the user did not specify, ask once:
24
+
25
+ | Scope | Meaning |
26
+ |------|---------|
27
+ | **repo** | Entire project (default if they say “whole repo” or give no path) |
28
+ | **folder** | Limit to one directory (e.g. `src/components/`) |
29
+ | **cms** | Content / data only — MDX, Markdown, JSON, YAML, CMS-shaped fields |
30
+
31
+ Respect `node_modules`, build output, `.next`, `dist`, and large binary dirs — do not treat assets inside them as “source” unless the user insists.
32
+
33
+ ---
34
+
35
+ ## Step 1 — Detect stack
36
+
37
+ Read `package.json` (and if present `astro.config.*`, `nuxt.config.*`, `vite.config.*`) to infer which image APIs apply. If multiple stacks exist, scan with **all** relevant patterns from [reference.md](reference.md#framework-image-patterns-and-greps).
38
+
39
+ ---
40
+
41
+ ## Step 2 — Scan for image usages
42
+
43
+ Framework-aware search (see [reference.md](reference.md#framework-image-patterns-and-greps)):
44
+
45
+ - **Next.js:** `next/image`, `next/legacy/image`, `<Image`, `fill`, `sizes` patterns with `alt`
46
+ - **React:** `<img`, team `<Image>` wrappers
47
+ - **Astro:** `astro:assets`, `<Image`, `<Picture`
48
+ - **Vue / Nuxt:** `<img`, `:alt`, `v-bind:alt`
49
+ - **MDX / MD:** `![](...)`, `<img`
50
+ - **CMS-shaped content:** image URLs plus `alt`, `caption`, `asset`, `fields` (JSON/YAML)
51
+
52
+ For each hit, classify:
53
+
54
+ - **missing** — no `alt` attribute / no markdown alt / no CMS alt field when required
55
+ - **empty** — `alt=""` or equivalent — only OK if [decorative per decision tree](reference.md#w3c-inspired-decision-tree)
56
+ - **present** — has non-empty alt; may still be **low quality** (filename, “image”, redundant, too long)
57
+
58
+ ---
59
+
60
+ ## Step 3 — Decision tree (per image)
61
+
62
+ Apply [W3C-inspired decision tree](reference.md#w3c-inspired-decision-tree) **before** drafting:
63
+
64
+ - Decorative / redundant with adjacent text → propose **`alt=""`** (or keep empty) and explain
65
+ - Functional (inside link, acts as control) → alt describes **destination or action**
66
+ - Informative → short, contextual description
67
+ - Complex (chart, diagram) → short **summary** in alt; flag need for extended text elsewhere
68
+ - Text in image → transcribe when that text is unique; if duplicated nearby → may be `alt=""`
69
+
70
+ ---
71
+
72
+ ## Step 4 — Draft alt text
73
+
74
+ For every row that needs a new or better alt:
75
+
76
+ - Use **surrounding component / page / heading context** and filename as weak hints only
77
+ - **~125 characters** or less unless complex-image exception; front-load important words
78
+ - **Do not** start with “image of”, “photo of”, “picture of”, or “graphic of”
79
+ - Assign **confidence:** `HIGH` (clear role in context) or `LOW` (decorative vs informative unclear, brand tone unknown, unclear subject)
80
+
81
+ ---
82
+
83
+ ## Step 5 — Review table (mandatory stop)
84
+
85
+ Output **one** markdown table with **all** proposed changes (including “keep `alt=""`” rows you want the user to confirm):
86
+
87
+ | # | Location | Framework / kind | Current | Proposed | Confidence | Type |
88
+ |---|----------|------------------|---------|----------|------------|------|
89
+ | 1 | `path:line` | Next `<Image>` | (missing) | … | HIGH | informative |
90
+
91
+ **STOP.** Wait for explicit user approval: all, per-row edits, or skip list. Do **not** edit files until they respond.
92
+
93
+ ---
94
+
95
+ ## Step 6 — Apply (after approval)
96
+
97
+ Apply only approved rows using correct syntax:
98
+
99
+ - Next `<Image alt="…" />`
100
+ - React `<img alt="…" />` (or wrapper’s `alt` prop)
101
+ - Astro `<Image alt="…" />` / `<Picture>` slots per Astro docs
102
+ - Vue static images: plain `alt="…"`; use `:alt` only when value is **dynamic** from CMS/props
103
+ - Markdown: `![alt](url)`
104
+ - CMS JSON/YAML: set the project’s canonical alt field (see [reference.md](reference.md#cms-content-patterns))
105
+
106
+ Preserve formatting and conventions of each file.
107
+
108
+ ---
109
+
110
+ ## Step 7 — Verify
111
+
112
+ Re-scan changed files only:
113
+
114
+ - No `<img` / `<Image` without `alt` where one is required
115
+ - No accidental removal of intentional empty alts the user wanted to keep
116
+ - Markdown / MDX image syntax still valid
117
+
118
+ ---
119
+
120
+ ## Invariants
121
+
122
+ - Never remove or overwrite **valid** alt text without explicit user approval in the review step
123
+ - **Never** apply code or content edits before the review table and user confirmation
124
+ - Never set `alt=""` on an image that is **informative or functional** in context
125
+ - Never add “image of” / “photo of” / “picture of” filler to drafts
126
+ - Decorative images with confirmed `alt=""` are **not** defects
127
+
128
+ ---
129
+
130
+ ## Handoffs / out of scope
131
+
132
+ - Full-site WCAG audits, focus order, ARIA beyond images → **accessibility-review** (or project a11y skill)
133
+ - Adding new CMS schema fields, migrations, or Studio config so `alt` exists → **manual / separate task**; note in table as blocked
134
+ - No images in scope → report “no images found” and stop
135
+
136
+ ---
137
+
138
+ ## Install this skill (copy or symlink the folder)
139
+
140
+ | Product | Personal (all projects) | Project-only |
141
+ |--------|-------------------------|--------------|
142
+ | **Cursor** | `~/.cursor/skills/alt-text/` | `<repo>/.cursor/skills/alt-text/` |
143
+ | **Claude Code** | `~/.claude/skills/alt-text/` | `<repo>/.claude/skills/alt-text/` |
144
+ | **OpenCode** | `~/.config/opencode/skills/alt-text/` | `<repo>/.opencode/skills/alt-text/` |
145
+ | **Windsurf** | `~/.codeium/windsurf/skills/alt-text/` | `<repo>/.windsurf/skills/alt-text/` |
146
+ | **Antigravity / Gemini** | Confirm in product docs (often `~/.gemini/skills/`) | `<repo>/.agent/skills/alt-text/` (common) |
147
+
148
+ ```bash
149
+ SKILL_SRC="/path/to/skills/alt-text"
150
+ mkdir -p ~/.cursor/skills ~/.claude/skills ~/.config/opencode/skills ~/.codeium/windsurf/skills
151
+ ln -sf "$SKILL_SRC" ~/.cursor/skills/alt-text
152
+ ln -sf "$SKILL_SRC" ~/.claude/skills/alt-text
153
+ ln -sf "$SKILL_SRC" ~/.config/opencode/skills/alt-text
154
+ ln -sf "$SKILL_SRC" ~/.codeium/windsurf/skills/alt-text
155
+ ```
156
+
157
+ Reload or restart the agent after install.
158
+
159
+ **Standards:** [WCAG 2 — Non-text content](https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html), [W3C Images Tutorial](https://www.w3.org/WAI/tutorials/images/), [WebAIM Alternative Text](https://webaim.org/techniques/alttext/).
package/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@its-thepoe/alt-text",
3
+ "version": "1.0.0",
4
+ "description": "Agent Skill: scan and draft alt text for images (Next.js, React, Astro, Vue, CMS) with review table before apply.",
5
+ "license": "MIT",
6
+ "keywords": ["agent-skills", "accessibility", "alt-text", "a11y", "cursor"],
7
+ "files": ["SKILL.md", "reference.md", "package.json"],
8
+ "exports": {
9
+ ".": "./SKILL.md",
10
+ "./SKILL.md": "./SKILL.md",
11
+ "./reference.md": "./reference.md"
12
+ }
13
+ }
package/reference.md ADDED
@@ -0,0 +1,235 @@
1
+ # Alt text — reference
2
+
3
+ Supporting detail for [SKILL.md](SKILL.md). Keep `SKILL.md` as the entry; load this file when scanning, classifying, or drafting.
4
+
5
+ ---
6
+
7
+ ## Core quality rules (evidence-aligned)
8
+
9
+ Great alt text is:
10
+
11
+ | Principle | Meaning |
12
+ |-----------|---------|
13
+ | **Accurate & equivalent** | Same meaning and **function** as the image in *this* context |
14
+ | **Succinct** | Usually a few words; aim **&lt; ~125 characters** unless complex-image exception |
15
+ | **Contextual** | Depends on role on the page — never describe in isolation |
16
+ | **Non-redundant** | No repeating adjacent headings/captions; no “image of …” |
17
+ | **Purpose-driven** | Why it’s there — not literal pixels unless appearance *is* the point |
18
+ | **Always present** | Every `<img>` needs an `alt` attribute — **even if empty** (`alt=""`) for decorative |
19
+
20
+ **Pitfalls:** filenames as alt, keyword stuffing, subjective words (“beautiful”), leaving alt off when content exists, decorative images with verbose alt.
21
+
22
+ ---
23
+
24
+ ## W3C-inspired decision tree
25
+
26
+ Before writing alt, answer in order (adapted from [W3C Images Tutorial](https://www.w3.org/WAI/tutorials/images/decisiontree/)):
27
+
28
+ 1. **Does the image contain text?**
29
+ - Text is already nearby as real text → often **`alt=""`** (decorative/redundant)
30
+ - Text only for visual treatment → **`alt=""`**
31
+ - Text has a **function** (e.g. icon) → describe **function**
32
+ - Text is **unique** to the image → **transcribe** the text (concisely)
33
+
34
+ 2. **Is the image a link or button and would purpose be unclear without it?**
35
+ - Yes → describe **destination or action** (e.g. “View full report”, not “picture of document”)
36
+
37
+ 3. **Does the image add meaning beyond decoration?**
38
+ - Yes, simple photo/graphic → brief **meaning**
39
+ - Yes, complex (chart, diagram, map) → **short summary** in `alt`; put details in body, `<figcaption>`, or linked description
40
+ - Yes, but **redundant** with adjacent text → **`alt=""`**
41
+
42
+ 4. **Purely decorative or no added meaning?**
43
+ - Yes → **`alt=""`** (or prefer CSS `background-image` for purely decorative)
44
+
45
+ **Unclear?** Prefer a short purpose statement in context, or ask the user (mark **LOW** confidence).
46
+
47
+ ---
48
+
49
+ ## Framework image patterns and greps
50
+
51
+ Use `rg` (ripgrep) or project search. Adjust globs to the repo’s `src/`, `app/`, `content/` layouts.
52
+
53
+ **Exclude:** `node_modules/`, `dist/`, `.next/`, `build/`, `coverage/`, lockfiles unless user wants them.
54
+
55
+ ### Next.js (`next/image`)
56
+
57
+ - Import: `from "next/image"` or `from "next/legacy/image"`
58
+ - JSX: `<Image` with props `src`, `alt`, often `width`/`height` or `fill`
59
+
60
+ **Example greps:**
61
+
62
+ ```bash
63
+ rg '<Image\b' --glob '*.{tsx,jsx,js}'
64
+ rg 'next/image' --glob '*.{tsx,jsx,js}' # import path
65
+ rg 'next/legacy/image' --glob '*.{tsx,jsx,js}'
66
+ ```
67
+
68
+ **Rules:**
69
+
70
+ - `alt` is **required** for meaningful images; use `alt=""` when [decorative](#w3c-inspired-decision-tree).
71
+ - For `fill` layouts, `alt` rules are the same — size props don’t change a11y.
72
+
73
+ ### React (non-Next)
74
+
75
+ - `<img` lowercase in JSX
76
+ - Design-system `Image` components: same as whatever prop they map to `alt` on the DOM
77
+
78
+ ```bash
79
+ rg '<img\b' --glob '*.{tsx,jsx,js}'
80
+ rg '<Image\b' --glob '*.{tsx,jsx,js}'
81
+ ```
82
+
83
+ If a wrapper passes `alt` through, set the wrapper’s `alt` prop per its docs.
84
+
85
+ ### Astro
86
+
87
+ - `@astrojs/image` / `astro:assets`: `<Image`, `<Picture`
88
+
89
+ ```bash
90
+ rg '<Image\b|<Picture\b' --glob '**/*.{astro,tsx,jsx}'
91
+ rg 'astro:assets|astrojs/image' --glob '**/astro.config.*'
92
+ ```
93
+
94
+ **Rules:**
95
+
96
+ - Set `alt` on Astro `<Image>`; for `<Picture>`, set alt on the primary image per [Astro image docs](https://docs.astro.build/en/guides/images/).
97
+
98
+ ### Vue / Nuxt
99
+
100
+ - `<img alt="static">` vs `:alt="dynamicFromCms"` — static marketing copy should be a plain string; bind only when data-driven.
101
+
102
+ ```bash
103
+ rg '<img\b' --glob '*.{vue,tsx,jsx}'
104
+ rg '(:alt|v-bind:alt)\s*=' --glob '*.vue'
105
+ ```
106
+
107
+ ### Markdown / MDX
108
+
109
+ - `![alt text](url "optional title")`
110
+ - Raw HTML `<img>` inside MDX
111
+
112
+ ```bash
113
+ rg '!\[[^\]]*\]\([^)]+\)' --glob '*.{md,mdx}'
114
+ rg '<img\b' --glob '*.{md,mdx}'
115
+ ```
116
+
117
+ **Rules:**
118
+
119
+ - **Empty** markdown alt `![](url)` is usually an a11y gap — propose a real alt or move to decorative CSS.
120
+
121
+ ### Plain HTML templates
122
+
123
+ ```bash
124
+ rg '<img\b' --glob '*.{html,htm,vue,astro,svelte}'
125
+ ```
126
+
127
+ ---
128
+
129
+ ## CMS content patterns
130
+
131
+ **Scope `cms`:** focus on `content/`, `data/`, `posts/`, `sanity-export/`, `*.json` frontmatter blocks, etc.
132
+
133
+ ### Common shapes
134
+
135
+ | Shape | Where alt lives |
136
+ |-------|-----------------|
137
+ | **Frontmatter** | `image: { src, alt }` or `coverAlt:` |
138
+ | **Portable Text / rich text** | Image blocks with `asset` + `alt` field — **field name varies** |
139
+ | **Headless JSON** | `fields.file.url` + sibling `fields.title` or `description` — confirm which field is a11y-safe |
140
+ | **YAML collections** | Same as JSON; watch indentation |
141
+
142
+ ### Sanity (illustrative)
143
+
144
+ - Image type often has `asset` + optional `alt`. If the schema has no alt, **flag as blocked** — schema/workflow change needed, not a one-line content fix.
145
+
146
+ ### Contentful / similar
147
+
148
+ - Asset **description** vs **title** — prefer whatever your frontend maps to `alt`. If unclear, mark **LOW** and ask.
149
+
150
+ **Rule:** Never invent new CMS keys; match the repo’s existing content contract. If missing, add a table row: “Blocked — no alt field in schema/content model.”
151
+
152
+ ---
153
+
154
+ ## Framework-specific implementation notes
155
+
156
+ ### Next.js `<Image>`
157
+
158
+ ```tsx
159
+ import Image from "next/image";
160
+
161
+ <Image src={hero} alt="Concise description of the photo in context." width={800} height={600} priority />;
162
+ ```
163
+
164
+ Decorative:
165
+
166
+ ```tsx
167
+ <Image src={texture} alt="" width={32} height={32} aria-hidden />;
168
+ ```
169
+
170
+ Use `aria-hidden` only when the image is **purely** decorative; pair with `alt=""` per team policy — don’t double-announce.
171
+
172
+ ### React `<img>`
173
+
174
+ ```tsx
175
+ <img src={url} alt="Meaningful short description" loading="lazy" decoding="async" />
176
+ ```
177
+
178
+ ### Astro `<Image>` (astro:assets)
179
+
180
+ Follow project imports; always pass `alt`:
181
+
182
+ ```astro
183
+ <Image src={import('../assets/photo.png')} alt="Summary appropriate to the page." />
184
+ ```
185
+
186
+ ### Vue
187
+
188
+ ```vue
189
+ <img :src="url" :alt="imageAltFromCms" />
190
+ ```
191
+
192
+ Static marketing:
193
+
194
+ ```vue
195
+ <img src="/hero.jpg" alt="Customers collaborating on a dashboard in an office." />
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Good vs bad examples
201
+
202
+ | Type | Bad | Why | Good |
203
+ |------|-----|-----|------|
204
+ | Informative (article) | `Image of a smiling woman in a spacesuit` | “Image of”, too generic | `Astronaut Ellen Ochoa` |
205
+ | Functional (logo link) | `Shield with longhorn` | Appearance, not destination | `University of Texas at Austin homepage` |
206
+ | Decorative | `Decorative swirl separator` | Noise for SRs | `alt=""` |
207
+ | Complex | `Sales chart` (vague) | No insight | `Line chart: revenue rising from $1.2M to $2.8M, Q1–Q4 2025` + data table in page |
208
+ | Text in image | `Blue infographic` | Ignores text | `Infographic: 40% faster loading; 25% more engagement; accessible to all users` |
209
+
210
+ ---
211
+
212
+ ## Quick checklist (every image)
213
+
214
+ - [ ] Has an `alt` attribute (or markdown equivalent)?
215
+ - [ ] Accurate to **context and purpose**?
216
+ - [ ] Succinct (~125 chars or less unless complex)?
217
+ - [ ] No redundancy with adjacent text?
218
+ - [ ] No “image of …” / filename-as-alt?
219
+ - [ ] Makes sense when read aloud with surrounding content?
220
+
221
+ ---
222
+
223
+ ## Verification aids (optional)
224
+
225
+ - **axe DevTools**, **WAVE**, **Lighthouse** “image alt” audits
226
+ - Screen readers: VoiceOver (macOS), NVDA (Windows)
227
+ - Re-grep after edits for `<Image` / `<img` without `alt` in touched files
228
+
229
+ ---
230
+
231
+ ## Further reading
232
+
233
+ - [W3C WAI: An alt Decision Tree](https://www.w3.org/WAI/tutorials/images/decisiontree/)
234
+ - [WebAIM: Alternative Text](https://webaim.org/techniques/alttext/)
235
+ - [WCAG 2.2 Understanding 1.1.1 Non-text Content](https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html)