gbu-accessibility-package 1.4.0 โ†’ 1.6.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.
package/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  ## โœจ Features
10
10
 
11
11
  - ๐Ÿ–ผ๏ธ **Smart Alt Text Generation** - Context-aware alt attributes for images
12
+ - ๐Ÿท๏ธ **Aria Label Support** - Automatic aria-label matching alt text
12
13
  - ๐ŸŒ **HTML Lang Attributes** - Automatic language attribute fixes
13
14
  - ๐ŸŽญ **Role Attributes** - WCAG-compliant role attribute management
14
15
  - ๐Ÿงน **Duplicate Cleanup** - Remove duplicate role attributes
@@ -58,6 +59,7 @@ gbu-a11y [options] [directory/file]
58
59
  Options:
59
60
  -d, --directory <path> Target directory (default: current directory)
60
61
  -l, --language <lang> Language for lang attribute (default: ja)
62
+ --backup Create backup files (default: enabled)
61
63
  --no-backup Don't create backup files
62
64
  --dry-run Preview changes without applying
63
65
  --comprehensive, --all Run all fixes including cleanup (recommended)
@@ -82,7 +84,7 @@ gbu-a11y -l en ./public
82
84
 
83
85
  # Individual fix types
84
86
  gbu-a11y --alt-only # Only fix alt attributes
85
- gbu-a11y --lang-only # Only fix lang attributes
87
+ gbu-a11y --lang-only # Only fix lang attributes
86
88
  gbu-a11y --role-only # Only fix role attributes
87
89
  gbu-a11y --cleanup-only # Only cleanup duplicates
88
90
 
@@ -90,28 +92,29 @@ gbu-a11y --cleanup-only # Only cleanup duplicates
90
92
  gbu-a11y --alt-only --dry-run ./src # Preview alt fixes only
91
93
  gbu-a11y --role-only -l en ./public # Fix roles with English lang
92
94
 
93
- # Fix without creating backups
94
- gbu-a11y --no-backup ./dist
95
+ # Backup options
96
+ gbu-a11y --backup ./dist # Explicitly enable backups (default)
97
+ gbu-a11y --no-backup ./dist # Disable backups for faster processing
95
98
  ```
96
99
 
97
100
  ## ๐Ÿ”ง Programmatic Usage
98
101
 
99
102
  ```javascript
100
- const AccessibilityFixer = require('gbu-accessibility-package');
103
+ const AccessibilityFixer = require("gbu-accessibility-package");
101
104
 
102
105
  const fixer = new AccessibilityFixer({
103
- language: 'en',
106
+ language: "en",
104
107
  backupFiles: true,
105
- dryRun: false
108
+ dryRun: false,
106
109
  });
107
110
 
108
111
  // Fix all accessibility issues
109
112
  async function fixAccessibility() {
110
113
  try {
111
- const results = await fixer.fixAllAccessibilityIssues('./src');
112
- console.log('Fixed files:', results);
114
+ const results = await fixer.fixAllAccessibilityIssues("./src");
115
+ console.log("Fixed files:", results);
113
116
  } catch (error) {
114
- console.error('Error:', error);
117
+ console.error("Error:", error);
115
118
  }
116
119
  }
117
120
 
@@ -121,14 +124,16 @@ fixAccessibility();
121
124
  ## ๐ŸŽฏ Fix Modes
122
125
 
123
126
  ### Individual Fix Options
127
+
124
128
  You can now fix specific accessibility issues individually:
125
129
 
126
130
  - `--alt-only` - Only fix alt attributes for images
127
- - `--lang-only` - Only fix HTML lang attributes
131
+ - `--lang-only` - Only fix HTML lang attributes
128
132
  - `--role-only` - Only fix role attributes
129
133
  - `--cleanup-only` - Only cleanup duplicate role attributes
130
134
 
131
135
  ### Combined Modes
136
+
132
137
  - **Standard mode** (default) - Fixes alt, lang, and role attributes
133
138
  - `--comprehensive` - All fixes including duplicate cleanup
134
139
 
@@ -152,36 +157,42 @@ gbu-a11y --comprehensive
152
157
  ## ๐Ÿ”ง What Gets Fixed
153
158
 
154
159
  ### 1. Alt Attributes
160
+
155
161
  - **Missing alt attributes** โ†’ Adds contextual alt text
156
162
  - **Empty alt attributes** โ†’ Generates meaningful descriptions
157
163
  - **Context-aware generation** โ†’ Uses surrounding text, headings, captions
158
164
 
159
165
  ```html
160
166
  <!-- Before -->
161
- <img src="logo.png">
162
- <img src="chart.jpg" alt="">
167
+ <img src="logo.png" />
168
+ <img src="chart.jpg" alt="" />
163
169
 
164
170
  <!-- After -->
165
- <img src="logo.png" alt="ใƒญใ‚ด">
166
- <img src="chart.jpg" alt="ใ‚ฐใƒฉใƒ•">
171
+ <img src="logo.png" alt="ใƒญใ‚ด" />
172
+ <img src="chart.jpg" alt="ใ‚ฐใƒฉใƒ•" />
167
173
  ```
168
174
 
169
175
  ### 2. HTML Lang Attributes
176
+
170
177
  - **Missing lang attributes** โ†’ Adds specified language
171
178
  - **Empty lang attributes** โ†’ Sets proper language code
172
179
 
173
180
  ```html
174
181
  <!-- Before -->
175
182
  <html>
176
- <html lang="">
177
-
178
- <!-- After -->
179
- <html lang="ja">
180
- <html lang="ja">
183
+ <html lang="">
184
+ <!-- After -->
185
+ <html lang="ja">
186
+ <html lang="ja"></html>
187
+ </html>
188
+ </html>
189
+ </html>
181
190
  ```
182
191
 
183
- ### 3. Role Attributes
184
- - **Images** โ†’ `role="img"`
192
+ ### 3. Role Attributes & Aria Labels
193
+
194
+ - **Images** โ†’ `role="img"` + `aria-label` (matching alt text)
195
+ - **Picture elements** โ†’ Moves `role="img"` from `<picture>` to `<img>` inside
185
196
  - **Links** โ†’ `role="link"`
186
197
  - **Clickable elements** โ†’ `role="button"`
187
198
  - **Navigation lists** โ†’ `role="menubar"`
@@ -189,27 +200,48 @@ gbu-a11y --comprehensive
189
200
 
190
201
  ```html
191
202
  <!-- Before -->
192
- <img src="icon.png" alt="Icon">
203
+ <img src="icon.png" alt="Icon" />
204
+ <picture role="img">
205
+ <img src="photo.jpg" alt="Photo" />
206
+ </picture>
193
207
  <a href="/home">Home</a>
194
208
  <div class="btn-click">Click me</div>
195
209
 
196
210
  <!-- After -->
197
- <img src="icon.png" alt="Icon" role="img">
211
+ <img src="icon.png" alt="Icon" role="img" aria-label="Icon" />
212
+ <picture>
213
+ <img src="photo.jpg" alt="Photo" role="img" aria-label="Photo" />
214
+ </picture>
198
215
  <a href="/home" role="link">Home</a>
199
216
  <div class="btn-click" role="button">Click me</div>
200
217
  ```
201
218
 
202
- ### 4. Duplicate Cleanup
219
+ ### 4. Aria Label Enhancement
220
+
221
+ - **Automatic aria-label** โ†’ Adds `aria-label` matching `alt` text for images
222
+ - **Preserves existing** โ†’ Won't override existing `aria-label` attributes
223
+ - **Smart detection** โ†’ Only adds when `alt` text exists and is not empty
224
+
225
+ ```html
226
+ <!-- Before -->
227
+ <img src="chart.jpg" alt="Sales Chart" />
228
+
229
+ <!-- After -->
230
+ <img src="chart.jpg" alt="Sales Chart" role="img" aria-label="Sales Chart" />
231
+ ```
232
+
233
+ ### 5. Duplicate Cleanup
234
+
203
235
  - **Removes duplicate role attributes**
204
236
  - **Preserves first occurrence**
205
237
  - **Handles mixed quote styles**
206
238
 
207
239
  ```html
208
240
  <!-- Before -->
209
- <img src="test.jpg" role="img" role="img" alt="Test">
241
+ <img src="test.jpg" role="img" role="img" alt="Test" />
210
242
 
211
243
  <!-- After -->
212
- <img src="test.jpg" role="img" alt="Test">
244
+ <img src="test.jpg" role="img" alt="Test" />
213
245
  ```
214
246
 
215
247
  ## ๐ŸŒŸ Smart Alt Text Generation
@@ -217,8 +249,9 @@ gbu-a11y --comprehensive
217
249
  The package uses intelligent context analysis to generate meaningful alt text:
218
250
 
219
251
  ### Context Sources
252
+
220
253
  1. **Title attributes**
221
- 2. **Aria-label attributes**
254
+ 2. **Aria-label attributes**
222
255
  3. **Definition terms (dt elements)**
223
256
  4. **Parent link text**
224
257
  5. **Nearby headings**
@@ -226,6 +259,7 @@ The package uses intelligent context analysis to generate meaningful alt text:
226
259
  7. **Surrounding text content**
227
260
 
228
261
  ### Fallback Patterns
262
+
229
263
  - `logo.png` โ†’ "ใƒญใ‚ด" (Logo)
230
264
  - `icon.svg` โ†’ "ใ‚ขใ‚คใ‚ณใƒณ" (Icon)
231
265
  - `banner.jpg` โ†’ "ใƒใƒŠใƒผ" (Banner)
@@ -235,6 +269,7 @@ The package uses intelligent context analysis to generate meaningful alt text:
235
269
  ## ๐Ÿ“Š Output Examples
236
270
 
237
271
  ### Standard Mode
272
+
238
273
  ```
239
274
  ๐Ÿš€ Starting Accessibility Fixer...
240
275
  ๐Ÿ“ Step 1: Fixing HTML lang attributes...
@@ -243,7 +278,7 @@ The package uses intelligent context analysis to generate meaningful alt text:
243
278
  ๐Ÿ–ผ๏ธ Step 2: Fixing alt attributes...
244
279
  โœ… Fixed alt attributes in 12 files (34 issues)
245
280
 
246
- ๐ŸŽญ Step 3: Fixing role attributes...
281
+ ๐ŸŽญ Step 3: Fixing role attributes...
247
282
  โœ… Fixed role attributes in 8 files (67 issues)
248
283
 
249
284
  ๐Ÿ“Š Summary:
@@ -255,6 +290,7 @@ The package uses intelligent context analysis to generate meaningful alt text:
255
290
  ```
256
291
 
257
292
  ### Comprehensive Mode
293
+
258
294
  ```
259
295
  ๐ŸŽฏ Running comprehensive accessibility fixes...
260
296
  ๐Ÿ“ Step 1: HTML lang attributes...
@@ -265,14 +301,30 @@ The package uses intelligent context analysis to generate meaningful alt text:
265
301
  ๐ŸŽ‰ All accessibility fixes completed!
266
302
  ๐Ÿ“Š Final Summary:
267
303
  Total files scanned: 25
268
- Files fixed: 15
304
+ Files fixed: 15
269
305
  Total issues resolved: 106
270
306
  ```
271
307
 
272
308
  ## ๐Ÿ”’ Safety Features
273
309
 
274
- - **Automatic backups** with `.backup` extension
275
- - **Dry run mode** for safe previewing
310
+ ### Backup Options
311
+ - **Default behavior**: Creates `.backup` files automatically for safety
312
+ - **Disable backups**: Use `--no-backup` for faster processing
313
+ - **Explicit enable**: Use `--backup` to be explicit about backup creation
314
+
315
+ ```bash
316
+ # Safe mode (default) - creates backups
317
+ gbu-a11y --comprehensive
318
+
319
+ # Fast mode - no backups
320
+ gbu-a11y --no-backup --comprehensive
321
+
322
+ # Explicit backup mode
323
+ gbu-a11y --backup --comprehensive
324
+ ```
325
+
326
+ ### Other Safety Features
327
+ - **Dry run mode** for safe previewing with `--dry-run`
276
328
  - **Non-destructive** - only adds missing attributes
277
329
  - **Duplicate prevention** - won't add existing attributes
278
330
  - **Error handling** - continues processing on individual file errors
@@ -280,6 +332,7 @@ The package uses intelligent context analysis to generate meaningful alt text:
280
332
  ## ๐Ÿ› ๏ธ Configuration
281
333
 
282
334
  ### Package.json Scripts
335
+
283
336
  ```json
284
337
  {
285
338
  "scripts": {
@@ -288,19 +341,20 @@ The package uses intelligent context analysis to generate meaningful alt text:
288
341
  "a11y:comprehensive": "gbu-a11y --comprehensive",
289
342
  "a11y:cleanup": "gbu-a11y --cleanup-only",
290
343
  "a11y:alt": "gbu-a11y --alt-only",
291
- "a11y:lang": "gbu-a11y --lang-only",
344
+ "a11y:lang": "gbu-a11y --lang-only",
292
345
  "a11y:role": "gbu-a11y --role-only"
293
346
  }
294
347
  }
295
348
  ```
296
349
 
297
350
  ### CI/CD Integration
351
+
298
352
  ```yaml
299
353
  # GitHub Actions example
300
354
  - name: Check Accessibility
301
355
  run: npx gbu-accessibility-package --dry-run
302
356
 
303
- - name: Fix Accessibility Issues
357
+ - name: Fix Accessibility Issues
304
358
  run: npx gbu-accessibility-package --comprehensive
305
359
  ```
306
360
 
@@ -333,4 +387,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
333
387
 
334
388
  ---
335
389
 
336
- Made with โค๏ธ by the GBU Team
390
+ Made with โค๏ธ by the GBU Team
package/cli.js CHANGED
@@ -14,7 +14,7 @@ const args = process.argv.slice(2);
14
14
  const options = {
15
15
  directory: '.',
16
16
  language: 'ja',
17
- backupFiles: true,
17
+ backupFiles: true, // Default to true for safety
18
18
  dryRun: false,
19
19
  help: false,
20
20
  cleanupOnly: false,
@@ -41,6 +41,9 @@ for (let i = 0; i < args.length; i++) {
41
41
  case '-l':
42
42
  options.language = args[++i];
43
43
  break;
44
+ case '--backup':
45
+ options.backupFiles = true;
46
+ break;
44
47
  case '--no-backup':
45
48
  options.backupFiles = false;
46
49
  break;
@@ -80,6 +83,7 @@ Usage: node cli.js [options] [directory]
80
83
  Options:
81
84
  -d, --directory <path> Target directory (default: current directory)
82
85
  -l, --language <lang> Language for lang attribute (default: ja)
86
+ --backup Create backup files (default: enabled)
83
87
  --no-backup Don't create backup files
84
88
  --dry-run Preview changes without applying
85
89
  --comprehensive, --all Run all fixes including cleanup (recommended)
@@ -90,7 +94,7 @@ Options:
90
94
  -h, --help Show this help message
91
95
 
92
96
  Examples:
93
- node cli.js # Fix current directory (standard fixes)
97
+ node cli.js # Fix current directory (with backups)
94
98
  node cli.js --comprehensive # Run all fixes including cleanup
95
99
  node cli.js --alt-only # Only fix alt attributes
96
100
  node cli.js --lang-only # Only fix lang attributes
@@ -99,6 +103,7 @@ Examples:
99
103
  node cli.js ./src # Fix src directory
100
104
  node cli.js -l en --dry-run ./dist # Preview fixes for dist directory in English
101
105
  node cli.js --no-backup ./public # Fix without creating backups
106
+ node cli.js --backup --comprehensive # Explicitly enable backups with all fixes
102
107
 
103
108
  Features:
104
109
  โœ… Alt attributes for images
@@ -110,6 +115,22 @@ Features:
110
115
  process.exit(0);
111
116
  }
112
117
 
118
+ // Helper function to show completion message with backup info
119
+ function showCompletionMessage(options, mode = 'fixes') {
120
+ if (options.dryRun) {
121
+ console.log(chalk.cyan('\n๐Ÿ’ก This was a dry run. Use without --dry-run to apply changes.'));
122
+ } else {
123
+ console.log(chalk.green(`\n๐ŸŽ‰ ${mode} completed successfully!`));
124
+ if (options.backupFiles) {
125
+ console.log(chalk.gray(' ๐Ÿ“ Backup files created with .backup extension'));
126
+ console.log(chalk.gray(' ๐Ÿ’ก Use --no-backup to disable backups in future runs'));
127
+ } else {
128
+ console.log(chalk.yellow(' โš ๏ธ No backup files created (--no-backup was used)'));
129
+ console.log(chalk.gray(' ๐Ÿ’ก Use --backup to enable backups for safety'));
130
+ }
131
+ }
132
+ }
133
+
113
134
  // Main function
114
135
  async function main() {
115
136
  console.log(chalk.blue('๐Ÿš€ Starting Accessibility Fixer...'));
@@ -143,11 +164,7 @@ async function main() {
143
164
 
144
165
  console.log(chalk.green(`\nโœ… Cleaned duplicate roles in ${cleanupFixed} files`));
145
166
 
146
- if (options.dryRun) {
147
- console.log(chalk.cyan('\n๐Ÿ’ก This was a dry run. Use without --dry-run to apply changes.'));
148
- } else {
149
- console.log(chalk.green('\n๐ŸŽ‰ Cleanup completed successfully!'));
150
- }
167
+ showCompletionMessage(options, 'Cleanup');
151
168
  return;
152
169
 
153
170
  } else if (options.altOnly) {
@@ -159,11 +176,7 @@ async function main() {
159
176
 
160
177
  console.log(chalk.green(`\nโœ… Fixed alt attributes in ${altFixed} files (${totalAltIssues} issues)`));
161
178
 
162
- if (options.dryRun) {
163
- console.log(chalk.cyan('\n๐Ÿ’ก This was a dry run. Use without --dry-run to apply changes.'));
164
- } else {
165
- console.log(chalk.green('\n๐ŸŽ‰ Alt attribute fixes completed successfully!'));
166
- }
179
+ showCompletionMessage(options, 'Alt attribute fixes');
167
180
  return;
168
181
 
169
182
  } else if (options.langOnly) {
@@ -174,11 +187,7 @@ async function main() {
174
187
 
175
188
  console.log(chalk.green(`\nโœ… Fixed lang attributes in ${langFixed} files`));
176
189
 
177
- if (options.dryRun) {
178
- console.log(chalk.cyan('\n๐Ÿ’ก This was a dry run. Use without --dry-run to apply changes.'));
179
- } else {
180
- console.log(chalk.green('\n๐ŸŽ‰ Lang attribute fixes completed successfully!'));
181
- }
190
+ showCompletionMessage(options, 'Lang attribute fixes');
182
191
  return;
183
192
 
184
193
  } else if (options.roleOnly) {
@@ -190,11 +199,7 @@ async function main() {
190
199
 
191
200
  console.log(chalk.green(`\nโœ… Fixed role attributes in ${roleFixed} files (${totalRoleIssues} issues)`));
192
201
 
193
- if (options.dryRun) {
194
- console.log(chalk.cyan('\n๐Ÿ’ก This was a dry run. Use without --dry-run to apply changes.'));
195
- } else {
196
- console.log(chalk.green('\n๐ŸŽ‰ Role attribute fixes completed successfully!'));
197
- }
202
+ showCompletionMessage(options, 'Role attribute fixes');
198
203
  return;
199
204
  }
200
205
 
@@ -242,14 +247,7 @@ async function main() {
242
247
  console.log(chalk.green(` Files fixed: ${totalFixed}`));
243
248
  console.log(chalk.yellow(` Total issues resolved: ${totalIssues}`));
244
249
 
245
- if (options.dryRun) {
246
- console.log(chalk.cyan('\n๐Ÿ’ก This was a dry run. Use without --dry-run to apply changes.'));
247
- } else {
248
- console.log(chalk.green('\n๐ŸŽ‰ All accessibility fixes completed successfully!'));
249
- if (options.backupFiles) {
250
- console.log(chalk.gray(' Backup files created with .backup extension'));
251
- }
252
- }
250
+ showCompletionMessage(options, 'All accessibility fixes');
253
251
 
254
252
  // Suggest cleanup if not comprehensive mode
255
253
  console.log(chalk.blue('\n๐Ÿ’ก Pro tip: Use --comprehensive to include duplicate role cleanup!'));
@@ -0,0 +1,32 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Aria Label Test</title>
7
+ </head>
8
+ <body>
9
+ <h1>Aria Label Test Cases</h1>
10
+
11
+ <!-- Test case 1: Image with alt but no aria-label -->
12
+ <img src="test1.jpg" alt="Test image 1" role="img" aria-label="Test image 1">
13
+
14
+ <!-- Test case 2: Image with alt and existing aria-label (should not change) -->
15
+ <img src="test2.jpg" alt="Test image 2" aria-label="Custom label" role="img">
16
+
17
+ <!-- Test case 3: Image with empty alt (should not add aria-label) -->
18
+ <img src="test3.jpg" alt="" role="img">
19
+
20
+ <!-- Test case 4: Image without alt (should generate alt and aria-label) -->
21
+ <img src="logo.png" role="img">
22
+
23
+ <!-- Test case 5: Picture with role and img with alt -->
24
+ <picture>
25
+ <source srcset="responsive.webp" type="image/webp">
26
+ <img src="responsive.jpg" alt="Responsive image" role="img" aria-label="Responsive image">
27
+ </picture>
28
+
29
+ <!-- Test case 6: Image with role but no aria-label -->
30
+ <img src="test6.jpg" alt="Test image 6" role="img" aria-label="Test image 6">
31
+ </body>
32
+ </html>
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Backup Test</title>
6
+ </head>
7
+ <body>
8
+ <h1>Backup Test File</h1>
9
+
10
+ <!-- This will be fixed -->
11
+ <img src="test.jpg" alt="Test image" role="img" aria-label="Test image">
12
+
13
+ <!-- This will also be fixed -->
14
+ <a href="/home" role="link">Home Link</a>
15
+
16
+ <p>This file is used to test backup functionality.</p>
17
+ </body>
18
+ </html>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>No Backup Test</title>
6
+ </head>
7
+ <body>
8
+ <h1>No Backup Test File</h1>
9
+
10
+ <img src="test2.jpg" alt="Test image 2" role="img" aria-label="Test image 2">
11
+ <a href="/about" role="link">About Link</a>
12
+ </body>
13
+ </html>
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Explicit Backup Test</title>
6
+ </head>
7
+ <body>
8
+ <h1>Explicit Backup Test File</h1>
9
+
10
+ <img src="test3.jpg" alt="Test image 3" role="img" aria-label="Test image 3">
11
+ </body>
12
+ </html>
package/lib/fixer.js CHANGED
@@ -563,7 +563,7 @@ class AccessibilityFixer {
563
563
  }
564
564
  ];
565
565
 
566
- // Check for images that need role="img"
566
+ // Check for images that need role="img" and aria-label
567
567
  const images = content.match(/<img[^>]*>/gi) || [];
568
568
  images.forEach((img, index) => {
569
569
  if (!img.includes('role=')) {
@@ -573,6 +573,18 @@ class AccessibilityFixer {
573
573
  element: img.substring(0, 100) + '...'
574
574
  });
575
575
  }
576
+
577
+ // Check for missing aria-label when alt exists
578
+ const hasAriaLabel = /aria-label\s*=/i.test(img);
579
+ const altMatch = img.match(/alt\s*=\s*["']([^"']*)["']/i);
580
+
581
+ if (!hasAriaLabel && altMatch && altMatch[1].trim()) {
582
+ issues.push({
583
+ type: '๐Ÿท๏ธ Missing aria-label',
584
+ description: `Image ${index + 1} should have aria-label matching alt text`,
585
+ element: img.substring(0, 100) + '...'
586
+ });
587
+ }
576
588
  });
577
589
 
578
590
  // Check for button elements with onclick that need role
@@ -637,22 +649,104 @@ class AccessibilityFixer {
637
649
  }
638
650
  });
639
651
 
652
+ // Check for picture elements with role="img" that contain img elements
653
+ const pictureWithImgPattern = /<picture[^>]*role\s*=\s*["']img["'][^>]*>[\s\S]*?<img[^>]*>[\s\S]*?<\/picture>/gi;
654
+ const pictureWithImgMatches = content.match(pictureWithImgPattern) || [];
655
+ pictureWithImgMatches.forEach((pictureBlock, index) => {
656
+ const imgInPicture = pictureBlock.match(/<img[^>]*>/i);
657
+ if (imgInPicture && !/role\s*=/i.test(imgInPicture[0])) {
658
+ issues.push({
659
+ type: '๐Ÿ–ผ๏ธ Role placement',
660
+ description: `Picture ${index + 1} has role="img" but should be on the img element inside`,
661
+ element: pictureBlock.substring(0, 100) + '...'
662
+ });
663
+ }
664
+ });
665
+
640
666
  return issues;
641
667
  }
642
668
 
669
+ fixPictureImgRoles(content) {
670
+ let fixed = content;
671
+
672
+ // Find all picture elements that contain img elements
673
+ const picturePattern = /<picture[^>]*role\s*=\s*["']img["'][^>]*>[\s\S]*?<\/picture>/gi;
674
+ const pictureMatches = content.match(picturePattern) || [];
675
+
676
+ for (const pictureBlock of pictureMatches) {
677
+ // Check if this picture contains an img element
678
+ const imgInPicture = pictureBlock.match(/<img[^>]*>/i);
679
+
680
+ if (imgInPicture) {
681
+ const imgTag = imgInPicture[0];
682
+
683
+ // Check if img already has role attribute
684
+ const imgHasRole = /role\s*=/i.test(imgTag);
685
+
686
+ if (!imgHasRole) {
687
+ // Remove role="img" from picture element
688
+ const pictureWithoutRole = pictureBlock.replace(/\s*role\s*=\s*["']img["']/i, '');
689
+
690
+ // Add role="img" to img element
691
+ let imgWithRole = imgTag.replace(/(<img[^>]*?)(\s*>)/i, '$1 role="img"$2');
692
+
693
+ // Also add aria-label if img has alt but no aria-label
694
+ const imgHasAriaLabel = /aria-label\s*=/i.test(imgWithRole);
695
+ const altMatch = imgWithRole.match(/alt\s*=\s*["']([^"']*)["']/i);
696
+
697
+ if (!imgHasAriaLabel && altMatch && altMatch[1].trim()) {
698
+ const altText = altMatch[1].trim();
699
+ imgWithRole = imgWithRole.replace(/(<img[^>]*?)(\s*>)/i, `$1 aria-label="${altText}"$2`);
700
+ console.log(chalk.yellow(` ๐Ÿท๏ธ Added aria-label="${altText}" to moved img element`));
701
+ }
702
+
703
+ // Replace the img in the modified picture block
704
+ const updatedPictureBlock = pictureWithoutRole.replace(imgTag, imgWithRole);
705
+
706
+ // Replace in the main content
707
+ fixed = fixed.replace(pictureBlock, updatedPictureBlock);
708
+
709
+ console.log(chalk.yellow(` ๐Ÿ–ผ๏ธ Moved role="img" from <picture> to <img> element`));
710
+ }
711
+ }
712
+ }
713
+
714
+ return fixed;
715
+ }
716
+
643
717
  fixRoleAttributesInContent(content) {
644
718
  let fixed = content;
645
719
 
646
- // Fix all images - add role="img" (only if no role exists)
720
+ // Fix picture elements with img children - move role from picture to img
721
+ fixed = this.fixPictureImgRoles(fixed);
722
+
723
+ // Fix all images - add role="img" and aria-label
647
724
  fixed = fixed.replace(
648
725
  /<img([^>]*>)/gi,
649
- (match, fullTag) => {
726
+ (match) => {
727
+ let updatedImg = match;
728
+ let hasChanges = false;
729
+
650
730
  // Check if role attribute already exists
651
- if (/role\s*=/i.test(match)) {
652
- return match; // Return unchanged if role already exists
731
+ if (!/role\s*=/i.test(match)) {
732
+ updatedImg = updatedImg.replace(/(<img[^>]*?)(\s*>)/i, '$1 role="img"$2');
733
+ console.log(chalk.yellow(` ๐Ÿ–ผ๏ธ Added role="img" to image element`));
734
+ hasChanges = true;
653
735
  }
654
- console.log(chalk.yellow(` ๐Ÿ–ผ๏ธ Added role="img" to image element`));
655
- return match.replace(/(<img[^>]*?)(\s*>)/i, '$1 role="img"$2');
736
+
737
+ // Check if aria-label already exists
738
+ if (!/aria-label\s*=/i.test(match)) {
739
+ // Extract alt text to use for aria-label
740
+ const altMatch = match.match(/alt\s*=\s*["']([^"']*)["']/i);
741
+ if (altMatch && altMatch[1].trim()) {
742
+ const altText = altMatch[1].trim();
743
+ updatedImg = updatedImg.replace(/(<img[^>]*?)(\s*>)/i, `$1 aria-label="${altText}"$2`);
744
+ console.log(chalk.yellow(` ๐Ÿท๏ธ Added aria-label="${altText}" to image element`));
745
+ hasChanges = true;
746
+ }
747
+ }
748
+
749
+ return updatedImg;
656
750
  }
657
751
  );
658
752
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbu-accessibility-package",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Automated accessibility fixes for HTML files - Alt attributes, Lang attributes, Role attributes. Smart context-aware alt text generation with individual fix options and comprehensive role attribute management.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -16,6 +16,8 @@
16
16
  "lang-only": "node cli.js --lang-only",
17
17
  "role-only": "node cli.js --role-only",
18
18
  "cleanup-only": "node cli.js --cleanup-only",
19
+ "no-backup": "node cli.js --no-backup",
20
+ "cleanup-backups": "find . -name '*.backup' -type f -delete",
19
21
  "test": "node test-package.js",
20
22
  "demo": "node cli.js --dry-run demo",
21
23
  "prepublishOnly": "npm run test"
@@ -1,45 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="ja">
3
- <head>
4
- <title>Test Duplicate Roles</title>
5
- </head>
6
- <body>
7
- <h1>Test Duplicate Role Attributes</h1>
8
-
9
- <!-- Images with duplicate roles -->
10
- <img src="logo.png" alt="Logo" role="img" role="img" role="img" role="img">
11
- <img src="banner.jpg" alt="Banner" role="img" role="img" role="img" role="img" role="img">
12
-
13
- <!-- Links with duplicate roles -->
14
- <a href="/home" role="link" role="link" role="link" role="link">Home</a>
15
- <a href="/about" role="link" role="link" role="link" role="link" role="link">About</a>
16
-
17
- <!-- Buttons with duplicate roles -->
18
- <button onclick="submit()" role="button" role="button" role="button" role="button">Submit</button>
19
- <button type="button" role="button" role="button" role="button">Click Me</button>
20
-
21
- <!-- Mixed quotes -->
22
- <div onclick="toggle()" role="button" role='button' role="button" role="button">Toggle</div>
23
- <span onclick="show()" role='button' role="button" role="button" role="button">Show</span>
24
-
25
- <!-- Navigation with duplicates -->
26
- <nav>
27
- <ul class="nav-menu" role="menubar" role="menubar" role="menubar" role="menubar">
28
- <li class="nav-item" role="menuitem" role="menuitem" role="menuitem" role="menuitem">
29
- <a href="/products" role="link" role="link" role="link" role="link">Products</a>
30
- </li>
31
- <li class="nav-item" role="menuitem" role="menuitem" role="menuitem" role="menuitem" role="menuitem">
32
- <a href="/services" role="link" role="link" role="link" role="link" role="link">Services</a>
33
- </li>
34
- </ul>
35
- </nav>
36
-
37
- <!-- Complex duplicates -->
38
- <article>
39
- <h2>Article with Issues</h2>
40
- <img src="article1.jpg" alt="Article Image" role="img" role="img" role="img" role="img">
41
- <p>Some content...</p>
42
- <a href="/read-more" role="link" role="link" role="link" role="link" role="link" role="link">Read More</a>
43
- </article>
44
- </body>
45
- </html>
@@ -1,47 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="ja">
3
- <head>
4
- <title>Demo HTML for Accessibility Testing</title>
5
- </head>
6
- <body>
7
- <h1>Sample Website</h1>
8
-
9
- <!-- Images without alt or role -->
10
- <img src="logo.png" alt="Sample Website" role="img" role="img" role="img">
11
- <img src="banner.jpg" alt="Sample Website" role="img" role="img" role="img">
12
- <img src="icon.svg" alt="Home Icon" role="img" role="img" role="img">
13
-
14
- <!-- Links without role -->
15
- <a href="/home" role="link" role="link" role="link">Home</a>
16
- <a href="/about" role="link" role="link" role="link">About Us</a>
17
-
18
- <!-- Buttons without role -->
19
- <button onclick="submit()" role="button" role="button" role="button">Submit Form</button>
20
- <button type="button">Regular Button</button>
21
-
22
- <!-- Clickable elements -->
23
- <div onclick="navigate()" class="btn" role="button" role="button" role="button" role="button" role="button" role="button">Click Me</div>
24
- <span onclick="toggle()" role="button" role="button" role="button">Toggle</span>
25
-
26
- <!-- Navigation -->
27
- <nav>
28
- <ul class="nav-menu" role="menubar" role="menubar" role="menubar">
29
- <li class="nav-item"><a href="/products" role="link" role="link" role="link">Products</a></li>
30
- <li class="nav-item"><a href="/services" role="link" role="link" role="link">Services</a></li>
31
- </ul>
32
- </nav>
33
-
34
- <!-- More content -->
35
- <main>
36
- <article>
37
- <h2>Article Title</h2>
38
- <p>Some content here...</p>
39
- <img src="article-image.jpg" alt="Article Title" role="img" role="img" role="img">
40
- </article>
41
- </main>
42
-
43
- <footer>
44
- <p>&copy; 2024 Demo Company</p>
45
- </footer>
46
- </body>
47
- </html>