मार्कडाउन एक्सेसिबिलिटी चेकर स्किल
Wikiprompt से, मुफ्त प्रॉम्प्ट विश्वकोश
मार्कडाउन एक्सेसिबिलिटी चेकर स्किल मार्कडाउन दस्तावेज़ों की पहुंच के लिए समीक्षा करने, alt text लिखने, और सुधार सूची या सही की गई फ़ाइल लौटाने वाले AI एजेंट के लिए एक पूर्ण कौशल परिभाषा। इसमें कार्यप्रवाह, संदर्भ गाइड, टेम्पलेट, एक उदाहरण समीक्षा, और एक Python चेकर स्क्रिप्ट शामिल है।
प्रॉम्प्ट सामग्रीसहेजें
🌐
---
name: markdown-accessibility-checker
description: Markdown दस्तावेज़ों (README, docs साइटें, wikis, blog पोस्ट, changelogs) की accessibility की समीक्षा करता है - गुम या खराब image alt text, heading structure, अस्पष्ट या डुप्लिकेट link text, bare URLs, खाली table headers, color-only निर्देश, ALL CAPS, और emoji bullets - फिर context से हर image के लिए अच्छा alt text लिखता है और एक fix list या सही की गई file लौटाता है। तब उपयोग करें जब कोई user "check my README for accessibility", "write alt text for these images", "make our docs screen reader friendly" पूछे, या CI में Markdown के लिए accessibility check चाहता हो।
---
# Markdown Accessibility Checker
आप Markdown दस्तावेज़ों को सभी के लिए काम करने योग्य बनाते हैं, जिनमें वे लोग भी शामिल हैं जो screen readers, keyboard navigation, magnification, या reading aids का उपयोग करते हैं। आप उन समस्याओं को ढूंढते हैं जिन्हें एक script पहचान सकती है, फिर वह हिस्सा करते हैं जो script नहीं कर सकती: सार्थक alt text और link text लिखना जो दस्तावेज़ के उद्देश्य के अनुकूल हों।
## इस skill में फ़ाइलें
- `scripts/check_md_a11y.py` - Markdown फ़ाइलों या फ़ोल्डरों को स्कैन करता है और line numbers और severities के साथ समस्याओं की रिपोर्ट करता है (केवल Python 3 standard library)
- `references/alt-text-guide.md` - image प्रकार के अनुसार alt text कैसे लिखें (screenshots, diagrams, charts, photos, logos, badges, decorative images)
- `references/structure-and-links.md` - headings, link text, tables, lists, emphasis, और script में प्रत्येक नियम का क्या अर्थ है
- `templates/a11y-review.md` - समीक्षा रिपोर्ट और alt text worksheet
- `examples/example-readme-review.md` - एक project README की की गई समीक्षा
## कार्यप्रवाह
### 1. समीक्षा का दायरा तय करें
पूछें कि कौन सी फ़ाइलें या फ़ोल्डर जांचने हैं, उन्हें कौन पढ़ता है (developers, end users, students), और क्या user एक रिपोर्ट, एक सही की गई फ़ाइल, या दोनों चाहता है। यदि दस्तावेज़ किसी site generator द्वारा प्रस्तुत किए जाते हैं, तो पूछें कि क्या यह अपना स्वयं का page title जोड़ता है (तब Markdown को H1 की आवश्यकता नहीं हो सकती है)।
### 2. Checker चलाएँ
```bash
python3 scripts/check_md_a11y.py README.md docs/
python3 scripts/check_md_a11y.py docs/ --json
python3 scripts/check_md_a11y.py docs/ --fail-on warn # CI के लिए
```
यह fenced code blocks और inline code को अनदेखा करता है। HIGH का मतलब है कुछ पाठकों के लिए सामग्री खो गई है (missing alt, empty link, empty heading), WARN को ठीक किया जाना चाहिए, INFO को मानवीय जांच की आवश्यकता है। यदि आप script नहीं चला सकते हैं, तो फ़ाइल पढ़कर समान नियमों की जांच करें और ऐसा कहें।
### 3. Alt text लिखें
प्रत्येक flagged image के लिए, और प्रत्येक image के लिए जिसमें मौजूदा alt text है, `references/alt-text-guide.md` का उपयोग करके इसका उद्देश्य तय करें:
- इसके आसपास का पाठ पढ़ें: दस्तावेज़ में इस बिंदु पर image किस लिए है?
- यदि आप image देख सकते हैं, तो वर्णन करें कि उस उद्देश्य के लिए क्या मायने रखता है। यदि आप नहीं कर सकते हैं, तो user से पूछें, या फ़ाइल नाम और संदर्भ से स्पष्ट रूप से चिह्नित ड्राफ्ट लिखें (उदाहरण के लिए "DRAFT: check against the image")।
- Decorative images को खाली alt text मिलता है; Markdown में उन्हें हटाना पसंद करें, या HTML `<img alt="">` का उपयोग करें।
`templates/a11y-review.md` में alt text worksheet भरें।
### 4. Structure और links ठीक करें
`references/structure-and-links.md` लागू करें: एक H1, कोई skipped levels नहीं, bold lines के बजाय वास्तविक headings, वर्णनात्मक अद्वितीय link text, headers के साथ tables, ऐसे निर्देश जो केवल रंग पर निर्भर न हों।
### 5. वितरण
- रिपोर्ट: `templates/a11y-review.md` भरें, जैसा कि `examples/example-readme-review.md` में है, सबसे महत्वपूर्ण fixes पहले।
- सही की गई फ़ाइल: fixes लागू करके पूरा Markdown लौटाएं और एक छोटी change list दें। अर्थ, code samples, या links के गंतव्य न बदलें।
- सही की गई फ़ाइल पर checker दोबारा चलाएं और पहले और बाद की संख्याएँ शामिल करें।
## नियम
- कभी आविष्कार न करें कि image क्या दिखाती है। यदि आपने इसे नहीं देखा है, तो alt text को ड्राफ्ट के रूप में चिह्नित करें।
- Alt text छोटा रखें (आमतौर पर 150 वर्णों से कम); लंबी व्याख्याएँ (charts, diagrams) पाठ या caption में रखें और alt में ऐसा कहें।
- Alt text को "image of" या "picture of" से शुरू न करें।
- याद रखें कि स्वचालित जाँचें समस्याओं का केवल एक हिस्सा ढूंढती हैं; हमेशा उल्लेख करें कि अभी भी किस चीज़ की मैन्युअल जांच की आवश्यकता है (images में color contrast, reading order, video captions)।
- लेखक की आवाज़ का सम्मान करें; accessibility ठीक करें, शैली नहीं।
FILE:references/alt-text-guide.md
# Alt text गाइड
Alt text उन लोगों के लिए image को बदल देता है जो इसे नहीं देख सकते। पूछें: "यदि मैं इस दस्तावेज़ को ज़ोर से पढ़ूं, तो image दिखाने के बजाय मैं इस बिंदु पर क्या कहूंगा?" उत्तर इस बात पर निर्भर करता है कि image वहाँ क्यों है, न कि केवल उसमें क्या है।
## मूल बातें
- विशिष्ट और छोटा हो: आमतौर पर 5 से 20 शब्द, शायद ही कभी 150 वर्णों से अधिक।
- महत्वपूर्ण भाग से शुरू करें। "image of", "picture of", या "screenshot of" से शुरू न करें जब तक कि प्रकार स्वयं मायने न रखता हो ("Screenshot" मदद कर सकता है जब पाठक को वही स्क्रीन ढूंढनी हो)।
- अंत में period लगाएं ताकि screen readers रुकें।
- Caption या image के बगल वाले वाक्य को शब्द-दर-शब्द न दोहराएं।
- Image में दिखाई देने वाला पाठ शामिल करें यदि पाठक को इसकी आवश्यकता है (button labels, error messages)।
## Image प्रकार के अनुसार
### किसी app या website के Screenshots
स्क्रीन और एक चीज़ बताएं जो पाठक को नोटिस करनी चाहिए।
- कमजोर: "screenshot"
- बेहतर: "Settings screen with the Units toggle set to Metric."
### Hero images और banners
यदि यह केवल मूड सेट करता है, तो यह decorative है। यदि यह उत्पाद दिखाता है, तो एक वाक्य में वर्णन करें।
- "Trailhead Weather on a phone, showing a three-day forecast for a mountain pass."
### Diagrams और flowcharts
मुख्य विचार alt में दें, और पूरी संरचना image के ठीक बाद पाठ या सूची में दें।
- Alt: "Architecture diagram: the mobile app calls the API, which reads from a forecast cache and a weather provider. Details in the list below."
### Charts और graphs
प्रकार, विषय और मुख्य निष्कर्ष दें; यदि पाठकों को संख्याओं की आवश्यकता है तो डेटा को table में रखें।
- "Line chart of daily active users, January to June 2026, rising from 2,000 to 9,500 with a dip in April."
### Photos
वर्णन करें कि दस्तावेज़ के उद्देश्य के लिए क्या मायने रखता है: subject, action, setting।
- "Snow-covered ridge trail with orange marker poles leading to a hut."
### Logos
संगठन या उत्पाद का नाम उपयोग करें, साथ ही "logo" केवल तभी जोड़ें यदि यह मदद करता है: "Trailhead Weather logo."
जब logo एक link हो, तो गंतव्य का वर्णन करें: "Trailhead Weather home."
### Badges (build status, version, license)
वह जानकारी लिखें जो badge दिखाता है: "Build: passing", "License: MIT", "Version 2.4.1".
### पाठ या links के अंदर Icons
यदि icon का कोई कार्य है, तो कार्य का वर्णन करें ("Download"); यदि यह पास के पाठ को दोहराता है, तो यह decorative है।
### Animated GIFs और छोटे videos
वर्णन करें कि क्या होता है: "Animation: dragging a pin onto the map adds a new trailhead to the list." Videos को captions और, महत्वपूर्ण सामग्री के लिए, एक पाठ विकल्प की भी आवश्यकता होती है।
### Decorative images
Dividers, background textures, और विशुद्ध रूप से दृश्य अलंकरण। HTML में `alt=""` का उपयोग करें ताकि screen readers उन्हें छोड़ दें। Markdown में, `` अस्पष्ट है (अक्सर एक गलती), इसलिए image को हटाना या `<img src="divider.png" alt="">` का उपयोग करना पसंद करें।
## सामान्य गलतियाँ
- फ़ाइल नाम alt text के रूप में: "IMG_2041.jpg", "hero-final-v3"।
- एक शब्द: "image", "logo", "chart"।
- खोज इंजन के लिए keyword stuffing।
- कई स्थानों पर उपयोग की गई एक ही image के लिए अलग-अलग alt text।
- रंगों या लेआउट का वर्णन करना जब पाठक को जानकारी चाहिए, रूप नहीं।
FILE:references/structure-and-links.md
# Structure, links, और प्रत्येक नियम का क्या अर्थ है
Screen reader उपयोगकर्ता अक्सर headings या links द्वारा दस्तावेज़ में कूदते हैं। अच्छी संरचना ही इसे संभव बनाती है।
## Headings
| नियम | गंभीरता | क्यों | समाधान |
|---|---|---|---|
| empty-heading | HIGH | एक खाली heading को "heading level 2" घोषित किया जाता है जिसके बाद कुछ नहीं होता। | पाठ जोड़ें या # हटाएं। |
| no-h1 | WARN | पृष्ठ में heading outline में कोई शीर्षक नहीं है। | एक `# Title` से शुरू करें। इसे छोड़ दें यदि site generator front matter से H1 जोड़ता है। |
| multiple-h1 | WARN | कई शीर्षक outline को भ्रमित करते हैं। | एक H1 रखें; अनुभागों के लिए `##` का उपयोग करें। |
| heading-skip | WARN | H2 से H4 पर कूदना एक लापता अनुभाग का सुझाव देता है। | अगले स्तर (H3) का उपयोग करें। संरचना के लिए स्तर चुनें, फ़ॉन्ट आकार के लिए नहीं। |
| fake-heading | INFO | एक bold line heading जैसी दिखती है लेकिन outline में नहीं है। | यदि यह एक अनुभाग शुरू करती है तो वास्तविक heading का उपयोग करें; यदि यह केवल जोर है तो bold रखें। |
| no-headings | INFO | बिना headings के लंबा पाठ स्कैन करना कठिन है। | हर कुछ पैराग्राफ में अनुभाग headings जोड़ें। |
## Links
| नियम | गंभीरता | क्यों | समाधान |
|---|---|---|---|
| empty-link | HIGH | Link के पास कोई सुलभ नाम नहीं है। | वर्णनात्मक पाठ जोड़ें। |
| vague-link | WARN | "Click here" या "read more" links की सूची में कुछ भी नहीं दर्शाता। | गंतव्य का नाम बताएं: "download the latest release", "setup guide". |
| same-text-different-url | WARN | दो links जिन्हें "docs" कहा जाता है जो अलग-अलग स्थानों पर जाते हैं, अप्रभेद्य हैं। | प्रत्येक पाठ को अद्वितीय बनाएं: "API reference", "setup guide". |
| url-as-link-text | INFO | लंबे URLs को वर्ण-दर-वर्ण पढ़ा जाता है। | एक छोटा विवरण उपयोग करें; URL केवल तभी रखें यदि पाठकों को इसे कॉपी करना हो। |
| bare-url | INFO | उपरोक्त के समान, और हमेशा क्लिक करने योग्य नहीं। | एक वर्णनात्मक link में लपेटें। |
Link text युक्तियाँ: महत्वपूर्ण शब्दों को पहले रखें, इसे छोटा रखें, कहें कि link कब एक फ़ाइल खोलता है ("pricing sheet (PDF, 2 MB)"), और पाठ में "link" का उपयोग न करें।
## Images
| नियम | गंभीरता | क्यों |
|---|---|---|
| img-missing-alt | HIGH | `` कोई विकल्प नहीं देता; कुछ पाठक फ़ाइल नाम की घोषणा करते हैं। |
| img-no-alt | HIGH | `<img>` बिना alt attribute के; screen readers फ़ाइल नाम पर वापस आ जाते हैं। |
| img-generic-alt | WARN | "image", "screenshot", "logo" अकेले कोई जानकारी नहीं देते। |
| img-filename-alt | WARN | "IMG_2041.jpg" एक विवरण नहीं है। |
| img-long-alt | WARN | बहुत लंबा alt text नेविगेट नहीं किया जा सकता; विवरण पाठ में ले जाएं। |
| img-redundant-alt | INFO | "Image of" अनावश्यक है; screen readers पहले से ही "image" कहते हैं। |
| img-decorative | INFO | `alt=""` image छुपाता है; पुष्टि करें कि यह वास्तव में decorative है। |
पाठ कैसे लिखें इसके लिए `alt-text-guide.md` देखें।
## Tables
- table-empty-header (WARN): प्रत्येक कॉलम को एक header की आवश्यकता होती है ताकि screen readers "Pro, Forecast days, 10" घोषित कर सकें। पहले कॉलम में "Feature" जैसा header जोड़ें।
- डेटा के लिए tables का उपयोग करें, लेआउट के लिए नहीं। उन्हें छोटा रखें; बहुत चौड़ी tables को विभाजित करें।
## अन्य नियम
- color-only (WARN): "Press the green button" color-blind पाठकों और screen reader उपयोगकर्ताओं के लिए विफल रहता है। बटन का नाम बताएं: "Select Save (the green button)"।
- all-caps (INFO): लंबा ALL CAPS पाठ पढ़ना कठिन है, और कुछ screen readers छोटे कैप्स शब्दों को अक्षर-दर-अक्षर वर्तनी करते हैं। सामान्य केस और जोर के लिए bold का उपयोग करें।
- emoji-bullet (INFO): emoji को नाम से ज़ोर से पढ़ा जाता है ("white heavy check mark Hourly forecasts")। सामान्य list markers का उपयोग करें, और emoji को दुर्लभ और सार्थक रखें।
## Script द्वारा जाँच नहीं की गई (हाथ से जाँचें)
- क्या alt text सटीक और उपयोगी है।
- Color contrast और images के अंदर का पाठ (छोटे पाठ वाले screenshots, कम contrast वाले charts)।
- जटिल लेआउट का reading order, और Markdown में embedded HTML blocks।
- Videos और audio के लिए captions और transcripts।
- सरल भाषा: छोटे वाक्य, परिभाषित शब्दजाल, और वर्णनात्मक अनुभाग नाम।
FILE:templates/a11y-review.md
# Accessibility Review: <document or folder>
दिनांक: <YYYY-MM-DD>
जाँची गई फ़ाइलें: <n> (<list or folder>)
दर्शक: <who reads these docs>
Checker run: `python3 scripts/check_md_a11y.py <paths>` -> HIGH <n>, WARN <n>, INFO <n>
## शीर्ष fixes (सबसे अधिक प्रभाव पहले)
1. <fix> (<file:line>)
2. <fix>
3. <fix>
## Alt text worksheet
| File:line | Image | दस्तावेज़ में उद्देश्य | वर्तमान alt | नया alt | छवि देखी? |
|---|---|---|---|---|---|
| | | informative / functional / decorative / complex | | | yes / draft |
## Headings
- वर्तमान outline: <H1 ... / H2 ... / H4 ...>
- प्रस्तावित outline: <H1 ... / H2 ... / H3 ...>
## Links
| File:line | वर्तमान पाठ | नया पाठ | गंतव्य |
|---|---|---|---|
| | | | |
## अन्य निष्कर्ष
| File:line | नियम | परिवर्तन |
|---|---|---|
| | | |
## Fixes के बाद
Checker rerun: HIGH <n>, WARN <n>, INFO <n> (शेष INFO आइटम नीचे समझाए गए हैं)।
- <INFO item: reason it is acceptable>
## अभी भी आवश्यक मैन्युअल जाँचें
- <उदाहरण के लिए: screenshots के अंदर पाठ का contrast, video captions>
FILE:examples/example-readme-review.md
# उदाहरण: एक project README की accessibility समीक्षा
## अनुरोध
"हमारे open source app के README को बहुत ट्रैफ़िक मिलता है। एक user जो screen reader पर निर्भर है, ने कहा कि इसे समझना कठिन है। क्या आप इसे जांच कर ठीक कर सकते हैं? Hero image phone पर app है जिसमें 3-day forecast है; app-main.png मुख्य स्क्रीन है जिसमें hourly temperature, wind, और snow line है; badge हमारी build status है।"
## Checker run (पहले)
```
$ python3 scripts/check_md_a11y.py README.md
Markdown accessibility check: 1 file(s), 3 image(s)
HIGH 2 WARN 8 INFO 5
README.md (3 images, 4 headings): 15 finding(s)
3 HIGH img-missing-alt Image hero.png has empty alt text; describe it, or confirm it is purely decorative.
5 INFO all-caps Long ALL CAPS text is hard to read and some screen readers spell it letter by letter.
9 WARN vague-link Link text "here" does not say where it goes; describe the destination.
9 WARN vague-link Link text "click here" does not say where it goes; describe the destination.
11 WARN img-generic-alt Alt text "screenshot" for app-main.png is generic; say what the image shows or why it is there.
13 HIGH img-no-alt <img src="docs/img/badge.svg"> has no alt attribute.
15 WARN heading-skip Heading level jumps from H2 to H4 ("Configuration"); use H3.
17 WARN color-only Instruction relies on color; add the label, shape, or position too.
19 INFO fake-heading Bold line "Features" looks like a heading; use a real # heading if it starts a section.
21 INFO emoji-bullet Emoji used as a bullet is read aloud by name; use a list marker and put meaning in words.
22 INFO emoji-bullet Emoji used as a bullet is read aloud by name; use a list marker and put meaning in words.
24 WARN table-empty-header Table header has 1 empty cell(s); every column needs a header.
28 WARN same-text-different-url Link text "docs" points to 2 different URLs; make each text unique.
28 INFO bare-url Bare URL https://example.org/docs; screen readers spell it out. Use [descriptive text](https://example.org/docs).
35 WARN multiple-h1 2 H1 headings (lines 1, 35); keep one title and use ## for sections.
```
bash code block के अंदर `` और अंतिम पंक्ति पर inline code example को सही ढंग से अनदेखा किया गया।
## समीक्षा (भरा गया template, संक्षिप्त)
# Accessibility Review: README.md
दर्शक: hikers और contributors, कई search से आ रहे हैं।
Checker run: HIGH 2, WARN 8, INFO 5।
### शीर्ष fixes
1. Hero image और build badge को वास्तविक alt text दें (पंक्तियाँ 3 और 13)।
2. "here" और "click here" को ऐसे link text से बदलें जो गंतव्य का नाम बताता है (पंक्ति 9)।
3. Outline ठीक करें: "Configuration" H3 बन जाता है, "Features" एक वास्तविक H3 बन जाता है, और दूसरा H1 "Contributing" H2 बन जाता है।
4. Save बटन का नाम बताएं केवल उसके रंग के बजाय (पंक्ति 17)।
5. पहले तालिका कॉलम को एक header दें (पंक्ति 24)।
### Alt text worksheet
| File:line | Image | उद्देश्य | वर्तमान alt | नया alt | देखा? |
|---|---|---|---|---|---|
| README.md:3 | hero.png | informative: उत्पाद दिखाता है | (खाली) | Trailhead Weather on a phone, showing a three-day forecast for a mountain pass. | user द्वारा वर्णित |
| README.md:11 | app-main.png | informative: मुख्य स्क्रीन क्या दिखाती है | screenshot | Main screen with hourly temperature, wind, and snow line for the selected trailhead. | user द्वारा वर्णित |
| README.md:13 | badge.svg | informative: build status | (कोई alt नहीं) | Build: passing | user द्वारा वर्णित |
### Headings
- पहले: H1 Trailhead Weather / H2 Install / H4 Configuration / H1 Contributing (साथ ही एक bold "Features" पंक्ति)।
- बाद में: H1 Trailhead Weather / H2 Install / H3 Configuration / H3 Features / H2 Contributing।
### Links
| पंक्ति | वर्तमान पाठ | नया पाठ | गंतव्य |
|---|---|---|---|
| 9 | here | Download the latest release | https://example.org/releases |
| 9 | click here | setup guide | docs/guides/setup.md |
| 28 | docs (bare URL + "docs") | API reference | https://example.org/docs |
| 28 | docs | setup guide | docs/guides/setup.md |
### अन्य निष्कर्ष
| पंक्ति | नियम | परिवर्तन |
|---|---|---|
| 5 | all-caps | bold नोट के अंदर सामान्य केस। |
| 17 | color-only | "Select Save (the green button at the bottom of Settings)". |
| 21-22 | emoji-bullet | सामान्य "-" list items। |
| 24 | table-empty-header | पहले कॉलम का header "Feature"। |
### सही किया गया README (अंश)
```markdown
# Trailhead Weather

Trailhead Weather shows mountain forecasts for hikers. **Note: this project is still in beta and things will break often.**
## Install
[Download the latest release](https://example.org/releases), or follow the [setup guide](docs/guides/setup.md).
<img src="docs/img/badge.svg" width="90" alt="Build: passing">
### Configuration
Select Save (the green button at the bottom of Settings) to save your settings.
### Features
- Hourly forecasts
- Avalanche bulletins
| Feature | Free | Pro |
|---|---|---|
| Forecast days | 3 | 10 |
```
### Fixes के बाद
```
$ python3 scripts/check_md_a11y.py README-fixed.md
Markdown accessibility check: 1 file(s), 3 image(s)
HIGH 0 WARN 0 INFO 0
```
### अभी भी आवश्यक मैन्युअल जाँचें
- Hero image में phone स्क्रीन पर छोटा पाठ है; alt text मुख्य संदेश देता है, इसलिए यहाँ यह ठीक है।
- यदि README कभी docs site पृष्ठ में बदल जाता है, तो पुष्टि करें कि site दूसरा H1 नहीं जोड़ता है।
## यह एक अच्छा उत्तर क्यों है
- Alt text प्रत्येक image के उद्देश्य का वर्णन करता है, यह अनुमान लगाने के बजाय user ने जो कहा कि images दिखाती हैं, उसका उपयोग करके।
- Links और headings को screen reader उपयोगकर्ताओं के नेविगेट करने के तरीके के लिए ठीक किया गया है: link list और heading outline द्वारा।
- Fix को checker को दोबारा चलाकर सत्यापित किया जाता है, और शेष मैन्युअल जाँचें ईमानदारी से सूचीबद्ध हैं।
FILE:scripts/check_md_a11y.py
#!/usr/bin/env python3
"""Check Markdown files for common accessibility problems.
Usage:
python3 check_md_a11y.py README.md docs/ # files and folders (*.md, *.markdown)
python3 check_md_a11y.py docs/ --json
cat page.md | python3 check_md_a11y.py -
python3 check_md_a11y.py docs/ --fail-on warn # exit 1 on WARN too (for CI)
Checks (code blocks and inline code are ignored):
images missing alt text, file-name or generic alt ("image", "screenshot"),
redundant "image of ..." openers, very long alt, HTML <img> without alt,
empty alt (fine only for decorative images)
headings no H1 or more than one H1, skipped levels (H2 -> H4), empty headings,
bold lines used as fake headings
links empty link text, vague text ("click here", "read more", "here"),
bare URLs, the same text pointing to different URLs
tables empty header cells
other ALL CAPS sentences, emoji used as bullets, color-only instructions
Severities: HIGH (blocks understanding), WARN (should fix), INFO (check by hand).
Exit code: 0 no HIGH, 1 HIGH found (or WARN with --fail-on warn), 2 usage error.
Standard library only.
"""
import argparse
import json
import os
import re
import sys
GENERIC_ALT = {"image", "img", "picture", "pic", "photo", "screenshot", "screen shot", "graphic", "icon",
"logo", "diagram", "chart", "figure", "untitled", "alt", "alt text", "banner", "placeholder"}
VAGUE_LINK = {"click here", "here", "click", "read more", "more", "learn more", "link", "this", "this link",
"this page", "go", "details", "more info", "info", "see here", "check it out"}
REDUNDANT_START = re.compile(r"^(an? )?(image|picture|photo|screenshot|graphic) (of|showing)\b", re.I)
FILE_ALT = re.compile(r"^[\w\-. ]+\.(png|jpe?g|gif|svg|webp|bmp|tiff?)$|^(img|image|dsc|screenshot)[_\- ]?\d+", re.I)
IMG_MD = re.compile(r"!\[([^\]]*)\]\(\s*<?([^)\s>]*)>?(?:\s+\"[^\"]*\")?\s*\)")
IMG_REF = re.compile(r"!\[([^\]]*)\]\[[^\]]*\]")
LINK_MD = re.compile(r"(?<!!)\[([^\]]*)\]\(\s*<?([^)\s>]*)>?(?:\s+\"[^\"]*\")?\s*\)")
HTML_IMG = re.compile(r"<img\b[^>]*>", re.I)
BARE_URL = re.compile(r"(?<![(<\"'=\[])\bhttps?://[^\s)>\]]+")
COLOR_ONLY = re.compile(r"\b(click|press|tap|select|choose|use)\b[^.]{0,30}\b(red|green|blue|yellow|orange|purple)\b(?! *\()", re.I)
EMOJI_BULLET = re.compile(r"^\s*[\u2705\u274c\u2b50\u27a1\u2714\u2716\U0001F300-\U0001FAFF]\ufe0f?\s+\S")
def strip_inline_code(line):
return re.sub(r"`+[^`]*`+", lambda m: " " * len(m.group(0)), line)
def check_text(text, name):
findings = []
def add(sev, line, rule, msg):
findings.append({"file": name, "line": line, "severity": sev, "rule": rule, "message": msg})
lines = text.replace("\r\n", "\n").split("\n")
in_fence, fence = False, ""
headings, link_targets = [], {}
images = 0
prev_blank = True
for no, raw in enumerate(lines, 1):
stripped = raw.strip()
m = re.match(r"^\s{0,3}(`{3,}|~{3,})", raw)
if m:
if not in_fence:
in_fence, fence = True, m.group(1)[0] * 3
elif stripped.startswith(fence):
in_fence = False
continue
if in_fence or raw.startswith(" ") and prev_blank and not stripped.startswith(("-", "*", "+")):
prev_blank = not stripped if not in_fence else prev_blank
continue
line = strip_inline_code(raw)
hm = re.match(r"^\s{0,3}(#{1,6})(\s+|$)(.*?)\s*#*\s*$", line)
if hm:
level, title = len(hm.group(1)), hm.group(3).strip()
headings.append((no, level, title))
if not title:
add("HIGH", no, "empty-heading", f"Empty H{level} heading; screen reader users navigate by headings.")
elif re.fullmatch(r"\s*(\*\*|__)[^*_]{2,80}(\*\*|__):?\s*", line) and prev_blank:
add("INFO", no, "fake-heading", f"Bold line \"{stripped.strip('*_:')}\" looks like a heading; use a real # heading if it starts a section.")
images += len(IMG_MD.findall(line)) + len(IMG_REF.findall(line)) + len(HTML_IMG.findall(line))
for alt, src in IMG_MD.findall(line):
check_alt(add, no, alt, src)
for alt in IMG_REF.findall(line):
check_alt(add, no, alt, "(reference)")
for tag in HTML_IMG.findall(line):
am = re.search(r"\balt\s*=\s*(\"([^\"]*)\"|'([^']*)'|(\S+))", tag, re.I)
src = re.search(r"\bsrc\s*=\s*[\"']?([^\"'\s>]+)", tag, re.I)
src = src.group(1) if src else "?"
if not am:
add("HIGH", no, "img-no-alt", f"<img src=\"{src}\"> has no alt attribute.")
else:
check_alt(add, no, am.group(2) if am.group(2) is not None else (am.group(3) if am.group(3) is not None else am.group(4)), src, html=True)
no_images = IMG_MD.sub(" ", line)
for text_, url in LINK_MD.findall(no_images):
label = re.sub(r"[*_`]", "", text_).strip()
if not label:
add("HIGH", no, "empty-link", f"Link to {url or '(no url)'} has no text; screen readers will read the URL or nothing.")
elif label.lower().rstrip(".!:") in VAGUE_LINK:
add("WARN", no, "vague-link", f"Link text \"{label}\" does not say where it goes; describe the destination.")
elif re.fullmatch(r"https?://\S+", label):
add("INFO", no, "url-as-link-text", f"Link text is a raw URL ({label[:60]}); use a short description.")
if label:
link_targets.setdefault(label.lower(), {}).setdefault(url, no)
for url in BARE_URL.findall(LINK_MD.sub(" ", no_images)):
add("INFO", no, "bare-url", f"Bare URL {url[:60]}; screen readers spell it out. Use [descriptive text]({url[:40]}).")
if "|" in line and re.match(r"^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$", line) and no > 1:
header = lines[no - 2].strip().strip("|")
cells = [c.strip() for c in header.split("|")]
if any(not c for c in cells):
add("WARN", no - 1, "table-empty-header", f"Table header has {sum(1 for c in cells if not c)} empty cell(s); every column needs a header.")
run = longest = 0
for w in re.findall(r"[A-Za-z]+", line):
run = run + 1 if w.isupper() and (len(w) > 1 or w in "AI") else 0
longest = max(longest, run)
if longest >= 5 and not hm:
add("INFO", no, "all-caps", "Long ALL CAPS text is hard to read and some screen readers spell it letter by letter.")
if EMOJI_BULLET.match(line):
add("INFO", no, "emoji-bullet", "Emoji used as a bullet is read aloud by name; use a list marker and keep emoji rare and meaningful.")
cm = COLOR_ONLY.search(line)
if cm:
between = line[cm.end(1):cm.start(2)]
inside_parens = line[:cm.start(2)].count("(") > line[:cm.start(2)].count(")")
names_label = re.match(r"\s+(\*\*|\"|')?[A-Z]", between) # "Select Save ..." or "Press **Save**"
if inside_parens or names_label:
cm = None
if cm:
add("WARN", no, "color-only", "Instruction relies on color; add the label, shape, or position too.")
prev_blank = not stripped
h1 = [h for h in headings if h[1] == 1]
if headings and not h1:
add("WARN", headings[0][0], "no-h1", "Document has no H1; start with one # title.")
if len(h1) > 1:
add("WARN", h1[1][0], "multiple-h1", f"{len(h1)} H1 headings (lines {', '.join(str(h[0]) for h in h1)}); keep one title and use ## for sections.")
for (pno, plev, _), (no, lev, title) in zip(headings, headings[1:]):
if lev > plev + 1:
add("WARN", no, "heading-skip", f"Heading level jumps from H{plev} to H{lev} (\"{title[:50]}\"); use H{plev + 1}.")
if not headings and len([l for l in lines if l.strip()]) > 15:
add("INFO", 1, "no-headings", "Long document with no headings; add headings so readers can navigate.")
for label, targets in link_targets.items():
if len(targets) > 1 and label not in VAGUE_LINK:
first = min(targets.values())
add("WARN", first, "same-text-different-url", f"Link text \"{label}\" points to {len(targets)} different URLs; make each text unique.")
order = {"HIGH": 0, "WARN": 1, "INFO": 2}
findings.sort(key=lambda f: (f["line"], order[f["severity"]]))
return findings, len(headings), images
def check_alt(add, no, alt, src, html=False):
a = alt.strip()
base = os.path.basename(src) if src else ""
if a == "" and html:
add("INFO", no, "img-decorative", f"<img src=\"{base}\"> has alt=\"\" (marked decorative); confirm it carries no information.")
return
if a == "" and src:
add("HIGH", no, "img-missing-alt", f"Image {base or src} has empty alt text; describe it, or confirm it is purely decorative.")
return
low = a.lower().rstrip(".")
if low in GENERIC_ALT:
add("WARN", no, "img-generic-alt", f"Alt text \"{a}\" for {base} is generic; say what the image shows or why it is there.")
elif FILE_ALT.search(a) or (base and low == os.path.splitext(base)[0].lower()):
add("WARN", no, "img-filename-alt", f"Alt text \"{a}\" looks like a file name; describe the image instead.")
elif REDUNDANT_START.match(a):
add("INFO", no, "img-redundant-alt", f"Alt text starts with \"{' '.join(a.split()[:3])}\"; screen readers already say 'image', so start with the content.")
if len(a) > 150:
add("WARN", no, "img-long-alt", f"Alt text is {len(a)} characters; keep it under about 150 and move details to the surrounding text or a caption.")
def collect(paths):
files = []
for p in paths:
if p == "-":
files.append("-")
elif os.path.isdir(p):
for root, dirs, names in os.walk(p):
dirs[:] = sorted(d for d in dirs if not d.startswith(".") and d != "node_modules")
files += [os.path.join(root, n) for n in sorted(names) if n.lower().endswith((".md", ".markdown"))]
elif os.path.isfile(p):
files.append(p)
else:
raise FileNotFoundError(p)
return files
def main():
ap = argparse.ArgumentParser(description="Markdown accessibility checker")
ap.add_argument("paths", nargs="+", help="Markdown files, folders, or - for stdin")
ap.add_argument("--json", action="store_true")
ap.add_argument("--fail-on", choices=["high", "warn"], default="high")
args = ap.parse_args()
try:
files = collect(args.paths)
except FileNotFoundError as e:
print(f"error: path not found: {e}", file=sys.stderr)
return 2
if not files:
print("error: no Markdown files (*.md, *.markdown) found", file=sys.stderr)
return 2
all_findings, stats = [], []
for f in files:
text = sys.stdin.read() if f == "-" else open(f, encoding="utf-8", errors="replace").read()
name = "<stdin>" if f == "-" else f
found, nhead, images = check_text(text, name)
stats.append({"file": name, "images": images, "headings": nhead, "findings": len(found)})
all_findings += found
counts = {s: sum(1 for x in all_findings if x["severity"] == s) for s in ("HIGH", "WARN", "INFO")}
if args.json:
print(json.dumps({"files": stats, "counts": counts, "findings": all_findings}, indent=2))
else:
print(f"Markdown accessibility check: {len(files)} file(s), {sum(s['images'] for s in stats)} image(s)")
print(f"HIGH {counts['HIGH']} WARN {counts['WARN']} INFO {counts['INFO']}")
for s in stats:
items = [x for x in all_findings if x["file"] == s["file"]]
print(f"\n{s['file']} ({s['images']} images, {s['headings']} headings): {len(items)} finding(s)")
for x in items:
print(f" {x['line']:>4} {x['severity']:<4} {x['rule']:<24} {x['message']}")
print("\nAutomated checks cover only part of accessibility; review alt text meaning, reading order, and contrast by hand.")
bad = counts["HIGH"] + (counts["WARN"] if args.fail_on == "warn" else 0)
return 1 if bad else 0
if __name__ == "__main__":
sys.exit(main())
पूरा प्रॉम्प्ट देखने के लिए साइन इन करें
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
उपयोग
यह प्रॉम्प्ट productivity के साथ उपयोग के लिए डिज़ाइन किया गया है। ऊपर प्रॉम्प्ट सामग्री कॉपी करें और अपने पसंदीदा AI टूल में पेस्ट करें।
सर्वोत्तम परिणामों के लिए, आप अपनी विशिष्ट आवश्यकताओं के अनुसार प्लेसहोल्डर (वर्ग कोष्ठक या बड़े अक्षरों में दर्शाए गए) को अनुकूलित कर सकते हैं।
चर्चा
0 टिप्पणियाँ