gbu-accessibility-package 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.
package/cli.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Accessibility Fixer CLI
5
+ * Command line interface for the accessibility fixer tool
6
+ */
7
+
8
+ const AccessibilityFixer = require('./lib/fixer.js');
9
+ const chalk = require('chalk');
10
+ const path = require('path');
11
+
12
+ // Parse command line arguments
13
+ const args = process.argv.slice(2);
14
+ const options = {
15
+ directory: '.',
16
+ language: 'ja',
17
+ backupFiles: true,
18
+ dryRun: false,
19
+ help: false
20
+ };
21
+
22
+ // Parse arguments
23
+ for (let i = 0; i < args.length; i++) {
24
+ const arg = args[i];
25
+
26
+ switch (arg) {
27
+ case '--help':
28
+ case '-h':
29
+ options.help = true;
30
+ break;
31
+ case '--directory':
32
+ case '-d':
33
+ options.directory = args[++i];
34
+ break;
35
+ case '--language':
36
+ case '-l':
37
+ options.language = args[++i];
38
+ break;
39
+ case '--no-backup':
40
+ options.backupFiles = false;
41
+ break;
42
+ case '--dry-run':
43
+ options.dryRun = true;
44
+ break;
45
+ default:
46
+ if (!arg.startsWith('-')) {
47
+ options.directory = arg;
48
+ }
49
+ }
50
+ }
51
+
52
+ // Show help
53
+ if (options.help) {
54
+ console.log(chalk.blue(`
55
+ 🔧 Accessibility Fixer CLI
56
+
57
+ Usage: node cli.js [options] [directory]
58
+
59
+ Options:
60
+ -d, --directory <path> Target directory (default: current directory)
61
+ -l, --language <lang> Language for lang attribute (default: ja)
62
+ --no-backup Don't create backup files
63
+ --dry-run Preview changes without applying
64
+ -h, --help Show this help message
65
+
66
+ Examples:
67
+ node cli.js # Fix current directory
68
+ node cli.js ./src # Fix src directory
69
+ node cli.js -l en --dry-run ./dist # Preview fixes for dist directory in English
70
+ node cli.js --no-backup ./public # Fix without creating backups
71
+
72
+ Features:
73
+ ✅ Alt attributes for images
74
+ ✅ Lang attributes for HTML
75
+ ✅ Role attributes for accessibility
76
+ ✅ Context-aware text generation
77
+ ✅ Automatic backups
78
+ `));
79
+ process.exit(0);
80
+ }
81
+
82
+ // Main function
83
+ async function main() {
84
+ console.log(chalk.blue('🚀 Starting Accessibility Fixer...'));
85
+ console.log(chalk.gray(`Directory: ${path.resolve(options.directory)}`));
86
+ console.log(chalk.gray(`Language: ${options.language}`));
87
+ console.log(chalk.gray(`Backup: ${options.backupFiles ? 'Yes' : 'No'}`));
88
+ console.log(chalk.gray(`Mode: ${options.dryRun ? 'Dry Run (Preview)' : 'Apply Changes'}`));
89
+ console.log('');
90
+
91
+ const fixer = new AccessibilityFixer({
92
+ language: options.language,
93
+ backupFiles: options.backupFiles,
94
+ dryRun: options.dryRun
95
+ });
96
+
97
+ try {
98
+ // Fix HTML lang attributes
99
+ console.log(chalk.yellow('📝 Step 1: Fixing HTML lang attributes...'));
100
+ const langResults = await fixer.fixHtmlLang(options.directory);
101
+ const langFixed = langResults.filter(r => r.status === 'fixed').length;
102
+ console.log(chalk.green(`✅ Fixed lang attributes in ${langFixed} files`));
103
+ console.log('');
104
+
105
+ // Fix alt attributes
106
+ console.log(chalk.yellow('🖼️ Step 2: Fixing alt attributes...'));
107
+ const altResults = await fixer.fixEmptyAltAttributes(options.directory);
108
+ const altFixed = altResults.filter(r => r.status === 'fixed').length;
109
+ const totalAltIssues = altResults.reduce((sum, r) => sum + (r.issues || 0), 0);
110
+ console.log(chalk.green(`✅ Fixed alt attributes in ${altFixed} files (${totalAltIssues} issues)`));
111
+ console.log('');
112
+
113
+ // Fix role attributes
114
+ console.log(chalk.yellow('🎭 Step 3: Fixing role attributes...'));
115
+ const roleResults = await fixer.fixRoleAttributes(options.directory);
116
+ const roleFixed = roleResults.filter(r => r.status === 'fixed').length;
117
+ const totalRoleIssues = roleResults.reduce((sum, r) => sum + (r.issues || 0), 0);
118
+ console.log(chalk.green(`✅ Fixed role attributes in ${roleFixed} files (${totalRoleIssues} issues)`));
119
+ console.log('');
120
+
121
+ // Summary
122
+ const totalFiles = new Set([
123
+ ...langResults.map(r => r.file),
124
+ ...altResults.map(r => r.file),
125
+ ...roleResults.map(r => r.file)
126
+ ]).size;
127
+
128
+ const totalFixed = new Set([
129
+ ...langResults.filter(r => r.status === 'fixed').map(r => r.file),
130
+ ...altResults.filter(r => r.status === 'fixed').map(r => r.file),
131
+ ...roleResults.filter(r => r.status === 'fixed').map(r => r.file)
132
+ ]).size;
133
+
134
+ const totalIssues = totalAltIssues + totalRoleIssues + langFixed;
135
+
136
+ console.log(chalk.blue('📊 Summary:'));
137
+ console.log(chalk.white(` Total files scanned: ${totalFiles}`));
138
+ console.log(chalk.green(` Files fixed: ${totalFixed}`));
139
+ console.log(chalk.yellow(` Total issues resolved: ${totalIssues}`));
140
+
141
+ if (options.dryRun) {
142
+ console.log(chalk.cyan('\n💡 This was a dry run. Use without --dry-run to apply changes.'));
143
+ } else {
144
+ console.log(chalk.green('\n🎉 All accessibility fixes completed successfully!'));
145
+ if (options.backupFiles) {
146
+ console.log(chalk.gray(' Backup files created with .backup extension'));
147
+ }
148
+ }
149
+
150
+ } catch (error) {
151
+ console.error(chalk.red('❌ Error occurred:'), error.message);
152
+ process.exit(1);
153
+ }
154
+ }
155
+
156
+ // Run the CLI
157
+ main();
package/demo/demo.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Demo script to test accessibility package functionality
5
+ */
6
+
7
+ const path = require('path');
8
+ const { AccessibilityTester, AccessibilityFixer } = require('../index');
9
+ const chalk = require('chalk');
10
+
11
+ async function runDemo() {
12
+ console.log(chalk.blue('🚀 Accessibility Package Demo'));
13
+ console.log(chalk.blue('===============================\n'));
14
+
15
+ try {
16
+ // Test the fixer first
17
+ console.log(chalk.yellow('1. Testing Accessibility Fixer...'));
18
+ const fixer = new AccessibilityFixer({
19
+ language: 'ja',
20
+ dryRun: true // Don't actually modify files in demo
21
+ });
22
+
23
+ // Go to parent directory to find HTML files
24
+ const projectRoot = path.join(__dirname, '../..');
25
+
26
+ console.log(chalk.gray(` Scanning directory: ${projectRoot}`));
27
+
28
+ const langResults = await fixer.fixHtmlLang(projectRoot);
29
+ console.log(chalk.green(` ✅ Lang attribute check: ${langResults.length} files scanned`));
30
+
31
+ const altResults = await fixer.fixEmptyAltAttributes(projectRoot);
32
+ console.log(chalk.green(` ✅ Alt attribute check: ${altResults.length} files scanned`));
33
+
34
+ const mainSuggestions = await fixer.addMainLandmarks(projectRoot);
35
+ console.log(chalk.green(` ✅ Main landmark check: ${mainSuggestions.length} suggestions`));
36
+
37
+ console.log(chalk.yellow('\n2. Testing Accessibility Tester...'));
38
+
39
+ // Find HTML files in project
40
+ const fs = require('fs').promises;
41
+ const files = await fs.readdir(projectRoot);
42
+ const htmlFiles = files.filter(f => f.endsWith('.html')).slice(0, 3); // Test first 3 files
43
+
44
+ if (htmlFiles.length > 0) {
45
+ console.log(chalk.gray(` Found HTML files: ${htmlFiles.join(', ')}`));
46
+
47
+ const tester = new AccessibilityTester({
48
+ baseUrl: 'http://localhost:8080',
49
+ outputDir: path.join(projectRoot, 'accessibility-reports'),
50
+ pages: htmlFiles,
51
+ serverPort: 8080
52
+ });
53
+
54
+ console.log(chalk.gray(' Note: Actual testing requires a running server'));
55
+ console.log(chalk.green(' ✅ Tester configuration ready'));
56
+ } else {
57
+ console.log(chalk.yellow(' ⚠️ No HTML files found for testing'));
58
+ }
59
+
60
+ console.log(chalk.green('\n✅ Demo completed successfully!'));
61
+ console.log(chalk.blue('\n📋 Package is working correctly. You can now use:'));
62
+ console.log(chalk.white(' a11y-fix all --dry-run # Preview fixes'));
63
+ console.log(chalk.white(' a11y-fix all # Apply fixes'));
64
+ console.log(chalk.white(' a11y-test run # Run accessibility tests'));
65
+
66
+ } catch (error) {
67
+ console.error(chalk.red(`❌ Demo failed: ${error.message}`));
68
+ console.error(chalk.gray(error.stack));
69
+ process.exit(1);
70
+ }
71
+ }
72
+
73
+ runDemo();
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html>
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">
11
+ <img src="banner.jpg" alt="">
12
+ <img src="icon.svg" alt="Home Icon">
13
+
14
+ <!-- Links without role -->
15
+ <a href="/home">Home</a>
16
+ <a href="/about">About Us</a>
17
+
18
+ <!-- Buttons without role -->
19
+ <button onclick="submit()">Submit Form</button>
20
+ <button type="button">Regular Button</button>
21
+
22
+ <!-- Clickable elements -->
23
+ <div onclick="navigate()" class="btn">Click Me</div>
24
+ <span onclick="toggle()">Toggle</span>
25
+
26
+ <!-- Navigation -->
27
+ <nav>
28
+ <ul class="nav-menu">
29
+ <li class="nav-item"><a href="/products">Products</a></li>
30
+ <li class="nav-item"><a href="/services">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="">
40
+ </article>
41
+ </main>
42
+
43
+ <footer>
44
+ <p>&copy; 2024 Demo Company</p>
45
+ </footer>
46
+ </body>
47
+ </html>
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html>
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">
11
+ <img src="banner.jpg" alt="">
12
+ <img src="icon.svg" alt="Home Icon">
13
+
14
+ <!-- Links without role -->
15
+ <a href="/home">Home</a>
16
+ <a href="/about">About Us</a>
17
+
18
+ <!-- Buttons without role -->
19
+ <button onclick="submit()">Submit Form</button>
20
+ <button type="button">Regular Button</button>
21
+
22
+ <!-- Clickable elements -->
23
+ <div onclick="navigate()" class="btn">Click Me</div>
24
+ <span onclick="toggle()">Toggle</span>
25
+
26
+ <!-- Navigation -->
27
+ <nav>
28
+ <ul class="nav-menu">
29
+ <li class="nav-item"><a href="/products">Products</a></li>
30
+ <li class="nav-item"><a href="/services">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="">
40
+ </article>
41
+ </main>
42
+
43
+ <footer>
44
+ <p>&copy; 2024 Demo Company</p>
45
+ </footer>
46
+ </body>
47
+ </html>
package/example.js ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Example usage of Accessibility Fixer
3
+ * Demonstrates different ways to use the tool
4
+ */
5
+
6
+ const AccessibilityFixer = require('./lib/fixer.js');
7
+ const chalk = require('chalk');
8
+
9
+ async function example1_BasicUsage() {
10
+ console.log(chalk.blue('\n📝 Example 1: Basic Usage'));
11
+
12
+ const fixer = new AccessibilityFixer({
13
+ language: 'ja',
14
+ backupFiles: true,
15
+ dryRun: true // Preview mode
16
+ });
17
+
18
+ // Fix all accessibility issues
19
+ await fixer.fixHtmlLang('./demo');
20
+ await fixer.fixEmptyAltAttributes('./demo');
21
+ await fixer.fixRoleAttributes('./demo');
22
+ }
23
+
24
+ async function example2_EnglishProject() {
25
+ console.log(chalk.blue('\n📝 Example 2: English Project'));
26
+
27
+ const fixer = new AccessibilityFixer({
28
+ language: 'en',
29
+ backupFiles: false,
30
+ dryRun: false
31
+ });
32
+
33
+ const results = await fixer.fixRoleAttributes('./demo');
34
+ console.log(`Fixed ${results.filter(r => r.status === 'fixed').length} files`);
35
+ }
36
+
37
+ async function example3_StepByStep() {
38
+ console.log(chalk.blue('\n📝 Example 3: Step by Step'));
39
+
40
+ const fixer = new AccessibilityFixer({
41
+ language: 'vi',
42
+ backupFiles: true,
43
+ dryRun: false
44
+ });
45
+
46
+ // Step 1: Fix lang attributes first
47
+ console.log('Step 1: Lang attributes...');
48
+ await fixer.fixHtmlLang('./demo');
49
+
50
+ // Step 2: Fix alt attributes
51
+ console.log('Step 2: Alt attributes...');
52
+ await fixer.fixEmptyAltAttributes('./demo');
53
+
54
+ // Step 3: Fix role attributes
55
+ console.log('Step 3: Role attributes...');
56
+ await fixer.fixRoleAttributes('./demo');
57
+
58
+ console.log('✅ All done!');
59
+ }
60
+
61
+ async function example4_CustomConfig() {
62
+ console.log(chalk.blue('\n📝 Example 4: Custom Configuration'));
63
+
64
+ // Custom configuration for specific needs
65
+ const fixer = new AccessibilityFixer({
66
+ language: 'zh',
67
+ backupFiles: true,
68
+ dryRun: true
69
+ });
70
+
71
+ // Only fix specific issues
72
+ const altResults = await fixer.fixEmptyAltAttributes('./demo');
73
+
74
+ // Analyze results
75
+ const fixedFiles = altResults.filter(r => r.status === 'fixed');
76
+ const totalIssues = altResults.reduce((sum, r) => sum + (r.issues || 0), 0);
77
+
78
+ console.log(`Found ${totalIssues} alt attribute issues in ${fixedFiles.length} files`);
79
+ }
80
+
81
+ async function example5_ErrorHandling() {
82
+ console.log(chalk.blue('\n📝 Example 5: Error Handling'));
83
+
84
+ const fixer = new AccessibilityFixer({
85
+ language: 'ja',
86
+ backupFiles: true,
87
+ dryRun: false
88
+ });
89
+
90
+ try {
91
+ const results = await fixer.fixRoleAttributes('./nonexistent-directory');
92
+ console.log('Results:', results);
93
+ } catch (error) {
94
+ console.error(chalk.red('Error occurred:'), error.message);
95
+ // Handle error appropriately
96
+ }
97
+ }
98
+
99
+ // Run examples
100
+ async function runExamples() {
101
+ console.log(chalk.green('🚀 Accessibility Fixer Examples\n'));
102
+
103
+ await example1_BasicUsage();
104
+ await example2_EnglishProject();
105
+ await example3_StepByStep();
106
+ await example4_CustomConfig();
107
+ await example5_ErrorHandling();
108
+
109
+ console.log(chalk.green('\n✅ All examples completed!'));
110
+ }
111
+
112
+ // Uncomment to run examples
113
+ // runExamples();
114
+
115
+ module.exports = {
116
+ example1_BasicUsage,
117
+ example2_EnglishProject,
118
+ example3_StepByStep,
119
+ example4_CustomConfig,
120
+ example5_ErrorHandling
121
+ };
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * GBU Accessibility Package
3
+ * Main entry point for the package
4
+ */
5
+
6
+ const AccessibilityFixer = require('./lib/fixer.js');
7
+
8
+ module.exports = AccessibilityFixer;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Accessibility Enhancer
3
+ * JavaScript enhancements for better accessibility
4
+ */
5
+
6
+ class AccessibilityEnhancer {
7
+ constructor(config = {}) {
8
+ this.config = {
9
+ language: config.language || 'ja',
10
+ skipLinkText: config.skipLinkText || 'メインコンテンツにスキップ',
11
+ ...config
12
+ };
13
+ }
14
+
15
+ // Generate JavaScript code for accessibility enhancements
16
+ generateEnhancementScript() {
17
+ return `
18
+ /**
19
+ * Accessibility Enhancements
20
+ * Auto-generated by Accessibility Toolkit
21
+ */
22
+
23
+ (function () {
24
+ 'use strict';
25
+
26
+ // Add accessibility labels to slider pagination buttons
27
+ function addSliderAccessibilityLabels() {
28
+ setTimeout(function () {
29
+ // Find all pagination containers
30
+ const paginationContainers = document.querySelectorAll('.splide-custom-pagination, .pagination');
31
+
32
+ paginationContainers.forEach(function (container) {
33
+ const buttons = container.querySelectorAll('button');
34
+ buttons.forEach(function (button, index) {
35
+ const slideNumber = index + 1;
36
+ if (!button.getAttribute('aria-label')) {
37
+ button.setAttribute('aria-label', 'スライド' + slideNumber + 'を表示');
38
+ }
39
+ });
40
+ });
41
+
42
+ // Handle pagination buttons with data-index
43
+ const paginationButtons = document.querySelectorAll('[data-index]');
44
+ paginationButtons.forEach(function (button) {
45
+ const index = button.getAttribute('data-index');
46
+ const slideNumber = parseInt(index) + 1;
47
+ if (!button.getAttribute('aria-label')) {
48
+ button.setAttribute('aria-label', 'スライド' + slideNumber + 'を表示');
49
+ }
50
+ });
51
+
52
+ // Add role="img" and aria-label to picture elements in slides
53
+ const sliderPictures = document.querySelectorAll('.splide__slide picture, .slide picture');
54
+ sliderPictures.forEach(function (picture) {
55
+ if (!picture.getAttribute('role')) {
56
+ picture.setAttribute('role', 'img');
57
+ }
58
+ if (!picture.getAttribute('aria-label')) {
59
+ const slide = picture.closest('.splide__slide, .slide');
60
+ const heading = slide ? slide.querySelector('h4, h3, h2, h1') : null;
61
+ const altText = picture.querySelector('img') ? picture.querySelector('img').getAttribute('alt') : '';
62
+
63
+ let ariaLabel = '';
64
+ if (heading && heading.textContent) {
65
+ ariaLabel = heading.textContent + 'の画像';
66
+ } else if (altText) {
67
+ ariaLabel = altText + 'の画像';
68
+ } else {
69
+ ariaLabel = 'スライド画像';
70
+ }
71
+
72
+ picture.setAttribute('aria-label', ariaLabel);
73
+ }
74
+ });
75
+ }, 1000);
76
+ }
77
+
78
+ // Add skip link for keyboard navigation
79
+ function addSkipLink() {
80
+ const skipLink = document.createElement('a');
81
+ skipLink.href = '#main-content';
82
+ skipLink.textContent = '${this.config.skipLinkText}';
83
+ skipLink.className = 'skip-link sr-only';
84
+ skipLink.style.cssText = \`
85
+ position: absolute;
86
+ top: -40px;
87
+ left: 6px;
88
+ background: #000;
89
+ color: #fff;
90
+ padding: 8px;
91
+ text-decoration: none;
92
+ z-index: 1000;
93
+ border-radius: 4px;
94
+ font-size: 14px;
95
+ \`;
96
+
97
+ skipLink.addEventListener('focus', function () {
98
+ this.style.top = '6px';
99
+ });
100
+
101
+ skipLink.addEventListener('blur', function () {
102
+ this.style.top = '-40px';
103
+ });
104
+
105
+ document.body.insertBefore(skipLink, document.body.firstChild);
106
+ }
107
+
108
+ // Add main content ID for skip link target
109
+ function addMainContentId() {
110
+ const mainElement = document.querySelector('main');
111
+ if (mainElement && !mainElement.id) {
112
+ mainElement.id = 'main-content';
113
+ }
114
+ }
115
+
116
+ // Fix missing form labels
117
+ function fixFormLabels() {
118
+ const inputs = document.querySelectorAll('input[type="text"], input[type="email"], input[type="tel"], textarea');
119
+ inputs.forEach(function(input) {
120
+ if (!input.getAttribute('aria-label') && !input.getAttribute('aria-labelledby')) {
121
+ const placeholder = input.getAttribute('placeholder');
122
+ if (placeholder) {
123
+ input.setAttribute('aria-label', placeholder);
124
+ }
125
+ }
126
+ });
127
+ }
128
+
129
+ // Initialize all enhancements
130
+ function init() {
131
+ if (document.readyState === 'loading') {
132
+ document.addEventListener('DOMContentLoaded', function () {
133
+ addSkipLink();
134
+ addMainContentId();
135
+ addSliderAccessibilityLabels();
136
+ fixFormLabels();
137
+ });
138
+ } else {
139
+ addSkipLink();
140
+ addMainContentId();
141
+ addSliderAccessibilityLabels();
142
+ fixFormLabels();
143
+ }
144
+
145
+ // Run again after delay for dynamic content
146
+ setTimeout(addSliderAccessibilityLabels, 2000);
147
+ }
148
+
149
+ init();
150
+ })();
151
+ `;
152
+ }
153
+
154
+ // Save enhancement script to file
155
+ async saveEnhancementScript(outputPath) {
156
+ const fs = require('fs').promises;
157
+ const script = this.generateEnhancementScript();
158
+ await fs.writeFile(outputPath, script);
159
+ return outputPath;
160
+ }
161
+ }
162
+
163
+ module.exports = AccessibilityEnhancer;