snap-ally 0.0.2 → 0.1.0-beta

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.
@@ -5,374 +5,18 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>Snap Ally - Test Summary</title>
8
- <!-- Modern Typography -->
8
+
9
+ <!-- Modern Typography and Core Styles -->
9
10
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap" rel="stylesheet">
12
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
12
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap"
13
+ rel="stylesheet">
14
+ <link rel="stylesheet"
15
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
13
16
  <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
14
- <style>
15
- :root {
16
- --bg-color: #f8fafc;
17
- --card-bg: rgba(255, 255, 255, 0.7);
18
- --primary: #4f46e5;
19
- --primary-dark: #4338ca;
20
- --text-main: #1e293b;
21
- --text-muted: #64748b;
22
- --passed: #10b981;
23
- --failed: #ef4444;
24
- --flaky: #f59e0b;
25
- --skipped: #6366f1;
26
- --glass-border: rgba(255, 255, 255, 0.4);
27
- --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.07);
28
-
29
- /* Severity Colors */
30
- --critical: <%= colors.critical %>;
31
- --serious: <%= colors.serious %>;
32
- --moderate: <%= colors.moderate %>;
33
- --minor: <%= colors.minor %>;
34
- }
35
-
36
- * {
37
- box-sizing: border-box;
38
- margin: 0;
39
- padding: 0;
40
- }
41
-
42
- /* Fixed Header */
43
- header {
44
- position: fixed;
45
- top: 0; left: 0; right: 0;
46
- z-index: 1000;
47
- background: rgba(255, 255, 255, 0.8);
48
- backdrop-filter: blur(20px) saturate(180%);
49
- -webkit-backdrop-filter: blur(20px) saturate(180%);
50
- border-bottom: 1px solid var(--glass-border);
51
- padding: 14px 40px;
52
- display: flex;
53
- justify-content: space-between;
54
- align-items: center;
55
- box-shadow: 0 4px 30px rgba(0, 0, 0, 0.03);
56
- }
57
-
58
- .brand {
59
- display: flex;
60
- align-items: center;
61
- gap: 12px;
62
- text-decoration: none;
63
- }
64
-
65
- .brand-icon {
66
- width: 38px;
67
- height: 38px;
68
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
69
- border-radius: 12px;
70
- display: flex;
71
- align-items: center;
72
- justify-content: center;
73
- color: white;
74
- box-shadow: 0 4px 15px rgba(99, 102, 241, 0.25);
75
- }
76
-
77
- header h1 {
78
- font-family: 'Outfit', sans-serif;
79
- font-size: 1.4rem;
80
- font-weight: 700;
81
- margin: 0;
82
- background: linear-gradient(to right, var(--primary-dark), #818cf8);
83
- -webkit-background-clip: text;
84
- background-clip: text;
85
- -webkit-text-fill-color: transparent;
86
- }
87
-
88
- body {
89
- font-family: 'Inter', sans-serif;
90
- background-color: var(--bg-color);
91
- color: var(--text-main);
92
- padding: 100px 20px 0; /* Removed bottom padding */
93
- line-height: 1.5;
94
- display: flex;
95
- flex-direction: column;
96
- min-height: 100vh;
97
- }
98
-
99
- /* Tabs UI */
100
- .tabs-container {
101
- display: flex;
102
- gap: 12px;
103
- margin-bottom: 32px;
104
- padding: 4px;
105
- background: #e2e8f0;
106
- border-radius: 14px;
107
- width: fit-content;
108
- }
109
-
110
- .tab-btn {
111
- padding: 10px 20px;
112
- border: none;
113
- background: none;
114
- font-family: 'Inter', sans-serif;
115
- font-weight: 600;
116
- font-size: 0.9rem;
117
- color: var(--text-muted);
118
- cursor: pointer;
119
- border-radius: 10px;
120
- transition: all 0.2s ease;
121
- }
122
-
123
- .tab-btn.active {
124
- background: white;
125
- color: var(--primary);
126
- box-shadow: 0 4px 12px rgba(0,0,0,0.05);
127
- }
128
-
129
- .tab-content {
130
- display: none;
131
- }
132
-
133
- .tab-content.active {
134
- display: block;
135
- animation: fadeIn 0.3s ease;
136
- }
137
-
138
- @keyframes fadeIn {
139
- from { opacity: 0; transform: translateY(10px); }
140
- to { opacity: 1; transform: translateY(0); }
141
- }
142
-
143
- /* Footer */
144
- footer {
145
- background: rgba(255, 255, 255, 0.7);
146
- backdrop-filter: blur(12px);
147
- -webkit-backdrop-filter: blur(12px);
148
- border-top: 1px solid var(--glass-border);
149
- padding: 24px 40px;
150
- margin-top: auto; /* Sticky footer */
151
- display: flex;
152
- justify-content: space-between;
153
- align-items: center;
154
- color: var(--text-muted);
155
- font-size: 0.9rem;
156
- }
157
-
158
- .powered-by { display: flex; align-items: center; gap: 8px; font-weight: 500; }
159
- .badge-tool { background: #f1f5f9; padding: 2px 10px; border-radius: 8px; font-weight: 700; color: #334155; font-size: 0.8rem; }
160
-
161
- /* Summary Hero */
162
- .summary-hero {
163
- background: #fff;
164
- border: 1px solid var(--glass-border);
165
- border-radius: 24px;
166
- padding: 20px;
167
- margin-bottom: 20px;
168
- box-shadow: var(--glass-shadow);
169
- display: flex;
170
- justify-content: space-between;
171
- align-items: center;
172
- gap: 24px;
173
- }
174
-
175
- .report-status {
176
- display: flex;
177
- align-items: center;
178
- gap: 10px;
179
- font-weight: 500;
180
- }
181
-
182
- .status-icon {
183
- width: 40px;
184
- height: 40px;
185
- border-radius: 12px;
186
- display: flex;
187
- align-items: center;
188
- justify-content: center;
189
- font-size: 24px;
190
- }
191
-
192
- .status-passed { background: #e6f9f3; color: var(--passed); }
193
- .status-failed { background: #fee2e2; color: var(--failed); }
194
-
195
- .status-label { font-size: 0.85rem; color: var(--text-muted); font-weight: 600; text-transform: uppercase; }
196
- .status-val { font-weight: 700; font-size: 1.25rem; text-transform: capitalize; }
197
- .hero-right { text-align: right; }
198
- .gen-date { font-size: 0.85rem; color: var(--text-muted); margin-bottom: 6px; }
199
- .gen-date-val { font-weight: 600; color: var(--text-main); }
200
- .runtime-box { font-size: 0.9rem; color: var(--text-muted); }
201
- .runtime-val { color: var(--text-main); font-size: 1.1rem; }
202
-
203
- /* Grid Layout */
204
- .metrics-grid {
205
- display: grid;
206
- grid-template-columns: repeat(3, 1fr);
207
- gap: 20px;
208
- margin-bottom: 40px;
209
- }
210
-
211
- .metric-card {
212
- background: white;
213
- border-radius: 16px;
214
- padding: 20px;
215
- display: flex;
216
- flex-direction: column;
217
- justify-content: space-between;
218
- gap: 16px;
219
- border: 1px solid #f1f5f9;
220
- box-shadow: 0 4px 12px rgba(0,0,0,0.03);
221
- transition: transform 0.2s, box-shadow 0.2s;
222
- }
223
- .metric-card:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0,0,0,0.06); }
224
-
225
- .metric-card.critical { border-top: 4px solid var(--critical); }
226
- .metric-card.serious { border-top: 4px solid var(--serious); }
227
- .metric-card.moderate { border-top: 4px solid var(--moderate); }
228
- .metric-card.minor { border-top: 4px solid var(--minor); }
229
17
 
230
- .metric-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
231
- .metric-rule { font-family: 'ui-monospace', monospace; font-weight: 700; font-size: 0.9rem; color: var(--text-main); }
232
-
233
- .metric-sev { font-size: 0.75rem; font-weight: 800; text-transform: uppercase; padding: 2px 8px; border-radius: 6px; }
234
- .metric-sev.critical { color: var(--critical); background: rgba(201, 42, 42, 0.1); }
235
- .metric-sev.serious { color: var(--serious); background: rgba(230, 119, 0, 0.1); }
236
- .metric-sev.moderate { color: var(--moderate); background: rgba(202, 138, 4, 0.1); }
237
- .metric-sev.minor { color: var(--minor); background: rgba(8, 145, 178, 0.1); }
238
-
239
- .metric-desc { font-size: 0.85rem; color: var(--text-muted); margin-top: 8px; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
240
- .metric-footer { display: flex; justify-content: space-between; align-items: center; margin-top: auto; }
241
-
242
- .metric-count { font-size: 1.8rem; font-weight: 800; font-family: 'Outfit', sans-serif; line-height: 1; }
243
- .metric-count.critical { color: var(--critical); }
244
- .metric-count.serious { color: var(--serious); }
245
- .metric-count.moderate { color: var(--moderate); }
246
- .metric-count.minor { color: var(--minor); }
247
- .btn-guide { color: var(--text-muted); text-decoration: none; display: flex; align-items: center; gap: 4px; font-size: 0.7rem; font-weight: 700; background: #f1f5f9; padding: 6px 10px; border-radius: 6px; }
248
-
249
- /* Charts */
250
- .chart-box {
251
- background: var(--card-bg);
252
- backdrop-filter: blur(12px);
253
- border: 1px solid var(--glass-border);
254
- border-radius: 24px;
255
- padding: 24px;
256
- box-shadow: var(--glass-shadow);
257
- margin-bottom: 40px;
258
- }
259
-
260
- .chart-title {
261
- font-family: 'Outfit', sans-serif;
262
- font-size: 1.1rem;
263
- margin-bottom: 20px;
264
- color: var(--text-main);
265
- display: flex; justify-content: space-between; align-items: center;
266
- }
267
-
268
- .chart-legend { display: flex; gap: 16px; font-size: 0.85rem; font-weight: 600; }
269
- .legend-item { display: flex; align-items: center; gap: 6px; }
270
- .legend-dot { width: 14px; height: 14px; border-radius: 4px; }
271
- .legend-dot.critical { background: var(--critical); }
272
- .legend-dot.serious { background: var(--serious); }
273
- .legend-dot.moderate { background: var(--moderate); }
274
- .legend-dot.minor { background: var(--minor); }
275
-
276
- /* Success State */
277
- .success-verified-card { text-align: center; padding: 80px 0; background: white; border-radius: 32px; border: 1px solid var(--glass-border); box-shadow: var(--glass-shadow); margin-bottom: 40px; }
278
- .success-hero-icon { font-size: 80px; color: #10b981; margin-bottom: 24px; filter: drop-shadow(0 4px 12px rgba(16,185,129,0.25)); }
279
- .success-hero-title { font-family: 'Outfit', sans-serif; font-size: 1.8rem; color: var(--text-main); margin-bottom: 12px; font-weight: 700; }
280
- .success-hero-desc { color: var(--text-muted); max-width: 480px; font-size: 1rem; margin: 0 auto; }
281
-
282
- /* Results Groups - Accordion Unified Redesign */
283
- .group-section {
284
- margin-bottom: 30px;
285
- border: 1px solid var(--glass-border);
286
- border-radius: 20px;
287
- overflow: hidden;
288
- background: white;
289
- box-shadow: var(--glass-shadow);
290
- }
291
-
292
- .group-header {
293
- background: #eff6ff;
294
- padding: 20px 24px;
295
- font-weight: 700;
296
- color: var(--primary-dark);
297
- display: flex;
298
- justify-content: space-between;
299
- align-items: center;
300
- cursor: pointer;
301
- transition: background 0.2s ease;
302
- }
303
-
304
- .group-header:hover { background: #dbeafe; }
305
- .group-content { padding: 8px 16px 16px; }
306
-
307
- .insights-header { font-family: 'Outfit', sans-serif; font-size: 1.25rem; margin-bottom: 24px; color: var(--text-main); display: flex; align-items: center; gap: 8px; }
308
-
309
- .test-card {
310
- background: #fff;
311
- border-radius: 12px;
312
- padding: 16px 20px;
313
- margin-top: 8px;
314
- display: flex;
315
- align-items: center;
316
- gap: 16px;
317
- border: 1px solid #f1f5f9;
318
- transition: all 0.2s ease;
319
- text-decoration: none;
320
- color: inherit;
321
- }
322
-
323
- .test-card:hover {
324
- border-color: var(--primary);
325
- box-shadow: 0 4px 12px rgba(79, 70, 229, 0.08);
326
- transform: translateX(4px);
327
- background: #f8fafc;
328
- }
329
-
330
- .status-icon-small {
331
- font-size: 20px;
332
- flex-shrink: 0;
333
- }
334
- .status-icon-passed { color: var(--passed); }
335
- .status-icon-failed { color: var(--failed); }
336
- .status-icon-flaky { color: var(--flaky); }
337
- .status-icon-skipped { color: var(--skipped); }
338
-
339
- .test-main-info { flex: 1; display: flex; align-items: center; justify-content: space-between; width: 100%; }
340
- .test-title-group { display: flex; align-items: center; gap: 16px; }
341
- .test-title { font-weight: 600; margin: 0; display: block; }
342
-
343
- .test-meta {
344
- display: flex;
345
- gap: 12px;
346
- font-size: 0.8rem;
347
- color: var(--text-muted);
348
- align-items: center;
349
- }
350
-
351
- .err-badge { background: #fee2e2; color: #ef4444; font-size: 0.7rem; font-weight: 800; padding: 2px 8px; border-radius: 6px; white-space: nowrap; }
352
- .test-dur { font-size: 0.8rem; color: var(--text-muted); font-weight: 700; }
353
-
354
- .browser-chip {
355
- padding: 2px 8px;
356
- border-radius: 6px;
357
- background: #f1f5f9;
358
- color: #475569;
359
- font-weight: 500;
360
- font-size: 0.75rem;
361
- text-transform: capitalize;
362
- }
363
-
364
- .chip-chromium, .chip-desktop-chromium { background: #e0f2fe; color: #0284c7; }
365
- .chip-firefox, .chip-desktop-firefox { background: #ffedd5; color: #c2410c; }
366
- .chip-webkit, .chip-desktop-webkit { background: #f3f4f6; color: #4b5563; }
367
- .chip-mobile-chrome { background: #e0f2fe; color: #0284c7; }
368
- .chip-mobile-safari { background: #f3f4f6; color: #4b5563; }
369
-
370
- .group-toggle-icon { transition: transform 0.2s ease; }
371
- .collapsed .group-toggle-icon { transform: rotate(-90deg); }
372
- .collapsed .group-content { display: none; }
373
-
374
- .container { max-width: 1200px !important; margin: 0 auto; width: 100%; padding-bottom: 40px; }
375
- </style>
18
+ <!-- Snap Ally Shared Template Styles -->
19
+ <link rel="stylesheet" href="global-report-styles.css">
376
20
  </head>
377
21
 
378
22
  <body>
@@ -385,311 +29,178 @@
385
29
  </a>
386
30
  </header>
387
31
 
388
- <div class="container">
32
+ <div class="container" id="report-summary-root" style="display: none;">
33
+
34
+ <!-- Hero Section -->
389
35
  <div class="summary-hero">
390
- <div class="report-status">
391
- <div class="status-icon status-<%= results.status %>">
392
- <span class="material-symbols-outlined"><%= results.statusIcon %></span>
393
- </div>
394
- <div>
395
- <div class="status-label">Report Status</div>
396
- <div class="status-val text-capitalize"><%= results.status %></div>
397
- </div>
398
- </div>
36
+ <div style="display: flex; align-items: center; gap: 16px; margin-bottom: 8px;">
37
+ <h2 style="margin: 0; font-weight: 600; font-size: 1.4rem; color: var(--text-main);">
38
+ Test Execution Summary
39
+ </h2>
40
+ <div class="status-badge" id="summary-status-badge">
41
+ <span class="material-symbols-outlined" id="summary-status-icon" style="font-size: 16px;"></span>
42
+ <span id="summary-status-text"></span>
43
+ </div>
44
+ </div>
399
45
  <div class="hero-right">
400
- <div class="gen-date">Generated on <span id="currentDate" class="gen-date-val"></span></div>
401
- <div class="runtime-box">Total runtime: <strong class="runtime-val"><%= results.duration %></strong></div>
46
+ <div class="gen-date">Generated on: <span class="gen-date-val" id="summary-date"></span></div>
47
+ <div class="runtime-box">Total Runtime: <span class="runtime-val" id="summary-duration"></span></div>
402
48
  </div>
403
49
  </div>
404
50
 
405
- <!-- Tabs Container -->
406
- <div class="tabs-container">
407
- <button class="tab-btn active" onclick="showTab('global', this)">Global View</button>
408
- <% if (results.browserSummaries) {
409
- Object.keys(results.browserSummaries).forEach(function(browser) { %>
410
- <button class="tab-btn" onclick="showTab('<%= browser %>', this)"><%= browser.charAt(0).toUpperCase() + browser.slice(1) %></button>
411
- <% });
412
- } %>
51
+ <!-- Browser Tabs Container -->
52
+ <div class="tabs-container" id="summary-tabs-container">
53
+ <button class="tab-btn active" data-target="global">Global</button>
54
+ <!-- Browser tabs injected here -->
413
55
  </div>
56
+ <template id="browser-tab-btn-template">
57
+ <button class="tab-btn"></button>
58
+ </template>
414
59
 
415
60
  <!-- Global Tab Content -->
416
61
  <div id="tab-global" class="tab-content active">
417
- <% if (results.totalA11yErrorCount > 0) { %>
418
- <div style="margin-bottom: 40px;">
419
- <h2 class="insights-header">
420
- <span class="material-symbols-outlined" style="color: var(--failed);">report_problem</span>
421
- Global Accessibility Insights (Deduplicated)
422
- </h2>
423
- <div class="metrics-grid">
424
- <% Object.entries(results.wcagErrors).sort((a,b) => b[1].count - a[1].count).forEach(function([rule, info]) { %>
425
- <div class="metric-card <%= info.severity %>">
426
- <div>
427
- <div class="metric-header">
428
- <span class="metric-rule"><%= rule %></span>
429
- <span class="metric-sev <%= info.severity %>"><%= info.severity %></span>
430
- </div>
431
- <% if (info.description) { %>
432
- <div class="metric-desc">
433
- <%= info.description %>
434
- </div>
435
- <% } %>
436
- </div>
437
- <div class="metric-footer">
438
- <div class="metric-count <%= info.severity %>"><%= info.count %></div>
439
- <% if (info.helpUrl) { %>
440
- <a href="<%= info.helpUrl %>" target="_blank" class="btn-guide">
441
- <span class="material-symbols-outlined" style="font-size: 16px;">menu_book</span> Guide
442
- </a>
443
- <% } %>
444
- </div>
445
- </div>
446
- <% }); %>
447
- </div>
448
- </div>
449
62
 
450
- <div class="chart-box">
451
- <div class="chart-title">
452
- Global Violation Distribution
453
- <div class="chart-legend">
454
- <span class="legend-item"><span class="legend-dot critical"></span> Critical</span>
455
- <span class="legend-item"><span class="legend-dot serious"></span> Serious</span>
456
- <span class="legend-item"><span class="legend-dot moderate"></span> Moderate</span>
457
- <span class="legend-item"><span class="legend-dot minor"></span> Minor</span>
458
- </div>
459
- </div>
460
- <div id="chart-global-violations"></div>
63
+ <!-- Global Success -->
64
+ <div class="success-verified-card" id="global-success-card" style="display: none;">
65
+ <div class="success-content-layout">
66
+ <div class="success-icon-container">
67
+ <span class="material-symbols-outlined" id="global-success-icon">check_circle</span>
68
+ <div class="icon-pulse"></div>
69
+ </div>
70
+ <div class="success-text-group">
71
+ <h3 class="success-hero-title" id="global-success-title">Compliance Verified</h3>
72
+ <p class="success-hero-desc" id="global-success-desc">Congratulations! No accessibility violations were detected across your audited application.</p>
73
+ <div class="success-badges">
74
+ <span class="success-msg-badge"><span class="material-symbols-outlined">verified</span> 0 Violations Found</span>
75
+ <span class="success-msg-badge"><span class="material-symbols-outlined">health_and_safety</span> 100% Compliant</span>
461
76
  </div>
462
- <% } else { %>
463
- <div class="success-verified-card">
464
- <% if (results.status === 'failed') { %>
465
- <span class="material-symbols-outlined status-icon-failed" style="font-size: 80px; margin-bottom: 24px;">error</span>
466
- <h3 class="success-hero-title">No Accessibility Issues Detected</h3>
467
- <p class="success-hero-desc">
468
- No accessibility violations were found, but the test suite failed due to other functional errors. Please check the individual test details below.
469
- </p>
470
- <% } else { %>
471
- <span class="material-symbols-outlined success-hero-icon">check_circle</span>
472
- <h3 class="success-hero-title">Compliance Verified</h3>
473
- <p class="success-hero-desc">
474
- Congratulations! Your application passed all accessibility checks with flying colors.
475
- </p>
476
- <% } %>
77
+ </div>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Global Errors -->
82
+ <div id="global-errors-container" style="display: none;">
83
+ <div class="metrics-grid" id="global-metrics-grid"></div>
84
+ <div class="chart-box">
85
+ <div class="chart-title">
86
+ Global Violation Distribution
87
+ <div class="chart-legend">
88
+ <span class="legend-item"><span class="legend-dot critical"></span> Critical</span>
89
+ <span class="legend-item"><span class="legend-dot serious"></span> Serious</span>
90
+ <span class="legend-item"><span class="legend-dot moderate"></span> Moderate</span>
91
+ <span class="legend-item"><span class="legend-dot minor"></span> Minor</span>
477
92
  </div>
478
- <% } %>
93
+ </div>
94
+ <div id="chart-global-violations"></div>
95
+ </div>
96
+ </div>
479
97
  </div>
480
98
 
481
- <!-- Browser Specific Tab Contents -->
482
- <% if (results.browserSummaries) {
483
- Object.keys(results.browserSummaries).forEach(function(browser) {
484
- const bStats = results.browserSummaries[browser];
485
- %>
486
- <div id="tab-<%= browser %>" class="tab-content">
487
- <% if (bStats.totalA11yErrorCount > 0) { %>
488
- <div class="metrics-grid">
489
- <% Object.entries(bStats.wcagErrors).sort((a,b) => b[1].count - a[1].count).forEach(function([rule, info]) { %>
490
- <div class="metric-card <%= info.severity %>">
491
- <div>
492
- <div class="metric-header">
493
- <span class="metric-rule"><%= rule %></span>
494
- <span class="metric-sev <%= info.severity %>"><%= info.severity %></span>
495
- </div>
496
- <% if (info.description) { %>
497
- <div class="metric-desc">
498
- <%= info.description %>
499
- </div>
500
- <% } %>
501
- </div>
502
- <div class="metric-footer">
503
- <div class="metric-count <%= info.severity %>"><%= info.count %></div>
504
- </div>
505
- </div>
506
- <% }); %>
507
- </div>
508
-
509
- <div class="chart-box">
510
- <div class="chart-title">
511
- <%= browser.charAt(0).toUpperCase() + browser.slice(1) %> Violations by Rule
512
- <div class="chart-legend">
513
- <span class="legend-item"><span class="legend-dot critical"></span> Critical</span>
514
- <span class="legend-item"><span class="legend-dot serious"></span> Serious</span>
515
- <span class="legend-item"><span class="legend-dot moderate"></span> Moderate</span>
516
- <span class="legend-item"><span class="legend-dot minor"></span> Minor</span>
517
- </div>
518
- </div>
519
- <div id="chart-<%= browser %>-violations"></div>
520
- </div>
521
- <% } else { %>
522
- <div class="success-verified-card">
523
- <span class="material-symbols-outlined success-hero-icon">check_circle</span>
524
- <h3 class="success-hero-title">No Violations in <%= browser.charAt(0).toUpperCase() + browser.slice(1) %></h3>
525
- <p class="success-hero-desc">
526
- Great job! No accessibility issues were found in this browser.
527
- </p>
528
- </div>
529
- <% } %>
99
+ <!-- Browser Tab Content Container -->
100
+ <div id="browser-tabs-content"></div>
101
+
102
+ <template id="browser-tab-content-template">
103
+ <div class="tab-content">
104
+ <!-- Error State -->
105
+ <div class="browser-errors-container" style="display: none;">
106
+ <div class="metrics-grid browser-metrics-grid"></div>
107
+ <div class="chart-box">
108
+ <div class="chart-title">
109
+ <span class="browser-chart-title"></span> Violations by Rule
110
+ <div class="chart-legend">
111
+ <span class="legend-item"><span class="legend-dot critical"></span> Critical</span>
112
+ <span class="legend-item"><span class="legend-dot serious"></span> Serious</span>
113
+ <span class="legend-item"><span class="legend-dot moderate"></span> Moderate</span>
114
+ <span class="legend-item"><span class="legend-dot minor"></span> Minor</span>
115
+ </div>
116
+ </div>
117
+ <div class="browser-chart-container"></div>
118
+ </div>
530
119
  </div>
531
- <% });
532
- } %>
533
120
 
534
- <!-- Results List (Common for all tabs) -->
535
- <div class="results-list" style="margin-top: 40px;">
536
- <h2 style="font-family: 'Outfit', sans-serif; font-size: 1.25rem; margin-bottom: 24px; color: var(--text-main);">Test Suite Details</h2>
537
- <% Object.keys(results.groupedResults).forEach(function(groupKey, index) { %>
538
- <div class="group-section">
539
- <div class="group-header" onclick="this.parentElement.classList.toggle('collapsed')">
540
- <span><%= groupKey %></span>
541
- <span class="material-symbols-outlined group-toggle-icon">expand_more</span>
121
+ <!-- Success State -->
122
+ <div class="success-verified-card browser-success-container" style="display: none;">
123
+ <div class="success-content-layout">
124
+ <div class="success-icon-container">
125
+ <span class="material-symbols-outlined success-hero-icon">verified_user</span>
126
+ <div class="icon-pulse"></div>
127
+ </div>
128
+ <div class="success-text-group">
129
+ <h3 class="success-hero-title browser-success-title">Browser Compliant</h3>
130
+ <p class="success-hero-desc">Great job! No accessibility issues were found in this specific browser environment.</p>
131
+ </div>
542
132
  </div>
543
- <div class="group-content">
544
- <% results.groupedResults[groupKey].forEach(function(test) { %>
545
- <a href="./<%= test.executionReportPath %>" class="test-card" data-browser="<%= test.browser %>">
546
- <span class="material-symbols-outlined status-icon-small status-icon-<%= test.status %>"><%= test.statusIcon %></span>
547
- <div class="test-main-info">
548
- <div class="test-title-group">
549
- <span class="test-title"><%= test.num %>. <%= test.title %></span>
550
- <span class="browser-chip chip-<%= test.browser.toLowerCase() %>"><%= test.browser %></span>
551
- </div>
552
- <div class="test-meta">
553
- <% if (test.a11yErrorCount && test.a11yErrorCount > 0) { %>
554
- <span class="err-badge">
555
- <%= test.a11yErrorCount %> errors
556
- </span>
557
- <% } else if (test.status === 'failed') { %>
558
- <span class="err-badge" style="background: #fee2e2; color: #ef4444; border: 1px solid #fecdd3;">
559
- Functional Error
560
- </span>
561
- <% } %>
562
- <div class="test-dur"><%= test.duration %></div>
563
- </div>
564
- </div>
565
- </a>
566
- <% }); %>
133
+ </div>
134
+ </div>
135
+ </template>
136
+
137
+ <template id="wcag-metric-template">
138
+ <div class="metric-card">
139
+ <div>
140
+ <div class="metric-header">
141
+ <span class="metric-rule"></span>
142
+ <span class="metric-sev"></span>
567
143
  </div>
144
+ <div class="metric-desc" style="display: none;"></div>
568
145
  </div>
569
- <% }); %>
570
- </div>
571
- </div>
572
-
573
- <script>
574
- function showTab(browserId, btn) {
575
- document.querySelectorAll('.tab-content').forEach(tc => tc.classList.remove('active'));
576
- document.querySelectorAll('.tab-btn').forEach(tb => tb.classList.remove('active'));
577
- document.getElementById('tab-' + browserId).classList.add('active');
578
- btn.classList.add('active');
579
-
580
- // Filter Test Suite Details
581
- const allCards = document.querySelectorAll('.test-card');
582
- allCards.forEach(card => {
583
- if (browserId === 'global') {
584
- card.style.display = 'flex';
585
- } else {
586
- if (card.getAttribute('data-browser') === browserId) {
587
- card.style.display = 'flex';
588
- } else {
589
- card.style.display = 'none';
590
- }
591
- }
592
- });
593
-
594
- // Hide empty groups
595
- document.querySelectorAll('.group-section').forEach(group => {
596
- const visibleChildren = group.querySelectorAll('.test-card[style="display: flex;"]');
597
- if (browserId !== 'global' && visibleChildren.length === 0) {
598
- group.style.display = 'none';
599
- } else {
600
- group.style.display = 'block';
601
- }
602
- });
603
-
604
- window.dispatchEvent(new Event('resize'));
605
- }
606
-
607
- function formatDate(date) {
608
- const day = String(date.getDate()).padStart(2, '0');
609
- const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
610
- const month = monthNames[date.getMonth()];
611
- const year = date.getFullYear();
612
- let hours = date.getHours();
613
- const ampm = hours >= 12 ? 'PM' : 'AM';
614
- hours = hours % 12 || 12;
615
- const formattedHours = String(hours).padStart(2, '0');
616
- const minutes = String(date.getMinutes()).padStart(2, '0');
617
- return `${day} ${month} ${year}, ${formattedHours}:${minutes} ${ampm}`;
618
- }
619
- document.getElementById("currentDate").textContent = formatDate(new Date());
620
-
621
- const themeColors = <%- JSON.stringify(colors) %>;
622
-
623
- // --- Chart Generation Function ---
624
- function renderBarChart(elementId, wcagData, title) {
625
- const wcagEntries = Object.entries(wcagData).sort((a, b) => b[1].count - a[1].count);
626
- if (wcagEntries.length === 0) return;
627
-
628
- const chartColors = wcagEntries.map(e => themeColors[e[1].severity] || '#ef4444');
146
+ <div class="metric-footer">
147
+ <div class="metric-count"></div>
148
+ <a target="_blank" class="btn-guide" style="display: none;">
149
+ <span class="material-symbols-outlined" style="font-size: 16px;">menu_book</span> Guide
150
+ </a>
151
+ </div>
152
+ </div>
153
+ </template>
629
154
 
630
- new ApexCharts(document.querySelector("#" + elementId), {
631
- chart: { type: 'bar', height: 350, toolbar: { show: false } },
632
- series: [{ name: 'Violations', data: wcagEntries.map(e => e[1].count) }],
633
- xaxis: {
634
- categories: wcagEntries.map(e => e[0]),
635
- labels: { style: { fontFamily: 'Inter', fontSize: '14px', fontWeight: 700, colors: '#475569' } },
636
- forceNiceScale: true,
637
- decimalsInFloat: 0
638
- },
639
- yaxis: { labels: { style: { fontWeight: 600, fontSize: '15px' } } },
640
- colors: chartColors,
641
- plotOptions: {
642
- bar: {
643
- borderRadius: 6,
644
- horizontal: true,
645
- barHeight: '70%',
646
- distributed: true,
647
- dataLabels: {
648
- position: 'top'
649
- }
650
- }
651
- },
652
- fill: {
653
- type: 'solid',
654
- opacity: 1
655
- },
656
- states: { hover: { filter: { type: 'darken', value: 0.9 } } },
657
- grid: {
658
- borderColor: '#f1f5f9',
659
- padding: { right: 50, left: 10 }
660
- },
661
- dataLabels: {
662
- enabled: true,
663
- textAnchor: 'start',
664
- offsetX: 10,
665
- style: {
666
- fontSize: '16px',
667
- fontWeight: 900,
668
- colors: ['#0f172a']
669
- },
670
- background: { enabled: false },
671
- dropShadow: { enabled: false }
672
- },
673
- legend: { show: false },
674
- tooltip: { theme: 'light', style: { fontFamily: 'Inter' } }
675
- }).render();
676
- }
155
+ <!-- Test Suite Details Accordion -->
156
+ <div class="results-list" style="margin-top: 40px;">
157
+ <h2 style="font-family: 'Outfit', sans-serif; font-size: 1.25rem; margin-bottom: 24px; color: var(--text-main);">
158
+ Test Suite Details</h2>
159
+ <div id="accordion-container"></div>
160
+ </div>
677
161
 
678
- // Render Global Chart
679
- renderBarChart('chart-global-violations', <%- JSON.stringify(results.wcagErrors) %>, 'Global Violations');
162
+ <template id="group-section-template">
163
+ <div class="group-section">
164
+ <div class="group-header">
165
+ <span class="group-title"></span>
166
+ <span class="material-symbols-outlined group-toggle-icon">expand_more</span>
167
+ </div>
168
+ <div class="group-content"></div>
169
+ </div>
170
+ </template>
171
+
172
+ <template id="test-card-template">
173
+ <a class="test-card" data-browser="">
174
+ <span class="material-symbols-outlined status-icon-small"></span>
175
+ <div class="test-main-info">
176
+ <div class="test-title-group">
177
+ <span class="test-title"></span>
178
+ <span class="browser-chip"></span>
179
+ </div>
180
+ <div class="test-meta">
181
+ <span class="err-badge"
182
+ style="display: none; background: #fee2e2; color: #ef4444; font-size: 0.7rem; font-weight: 800; padding: 2px 8px; border-radius: 6px;"></span>
183
+ <div class="test-dur" style="font-size: 0.8rem; color: var(--text-muted); font-weight: 700;"></div>
184
+ </div>
185
+ </div>
186
+ </a>
187
+ </template>
680
188
 
681
- // Render Browser-Specific Charts
682
- <% if (results.browserSummaries) {
683
- Object.keys(results.browserSummaries).forEach(function(browser) { %>
684
- renderBarChart('chart-<%= browser %>-violations', <%- JSON.stringify(results.browserSummaries[browser].wcagErrors) %>, '<%= browser %> Violations');
685
- <% });
686
- } %>
687
- </script>
189
+ </div>
688
190
 
689
191
  <footer>
690
192
  <div class="powered-by">Generated by <span class="badge-tool">Snap Ally</span></div>
691
- <div><span style="opacity: 0.7;">&copy; <%= new Date().getFullYear() %> Snap Ally</span></div>
193
+ <div><span style="opacity: 0.7;">&copy; <span id="year"></span> Snap Ally</span></div>
692
194
  </footer>
195
+
196
+ <!-- Scripts -->
197
+ <script>document.getElementById('year').textContent = new Date().getFullYear();</script>
198
+
199
+ <!-- Injected JSON Payload -->
200
+ <script src="data.js"></script>
201
+
202
+ <!-- Report Logic -->
203
+ <script src="report-app.js"></script>
693
204
  </body>
694
- </html>
695
205
 
206
+ </html>