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,917 +5,22 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>Snap Ally - Accessibility Audit</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
12
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap"
12
13
  rel="stylesheet">
13
- <!-- Bootstrap for functional components only (modals/accordions), custom CSS for styling -->
14
14
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
15
15
  <link rel="stylesheet"
16
16
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
17
- <style>
18
- :root {
19
- --bg-color: #f8fafc;
20
- --primary: #6366f1;
21
- --primary-dark: #4f46e5;
22
- --primary-soft: #eef2ff;
23
- --text-main: #0f172a;
24
- --text-muted: #64748b;
25
-
26
- --critical: <%=data.criticalColor || '#dc2626' %>;
27
- --serious: <%=data.seriousColor || '#ea580c' %>;
28
- --moderate: <%=data.moderateColor || '#f59e0b' %>;
29
- --minor: <%=data.minorColor || '#0ea5e9' %>;
30
-
31
- --glass-bg: rgba(255, 255, 255, 0.85);
32
- --glass-border: rgba(255, 255, 255, 0.6);
33
- --glass-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
34
- --card-radius: 24px;
35
- }
36
-
37
- body {
38
- font-family: 'Inter', sans-serif;
39
- background-color: var(--bg-color);
40
- color: var(--text-main);
41
- padding: 100px 20px 0;
42
- margin: 0;
43
- min-height: 100vh;
44
- display: flex;
45
- flex-direction: column;
46
- }
47
-
48
- /* Fixed Header */
49
- header {
50
- position: fixed;
51
- top: 0;
52
- left: 0;
53
- right: 0;
54
- z-index: 1000;
55
- background: rgba(255, 255, 255, 0.8);
56
- backdrop-filter: blur(20px) saturate(180%);
57
- -webkit-backdrop-filter: blur(20px) saturate(180%);
58
- border-bottom: 1px solid var(--glass-border);
59
- padding: 14px 40px;
60
- display: flex;
61
- justify-content: space-between;
62
- align-items: center;
63
- box-shadow: 0 4px 30px rgba(0, 0, 0, 0.03);
64
- }
65
-
66
- .brand {
67
- display: flex;
68
- align-items: center;
69
- gap: 12px;
70
- text-decoration: none;
71
- }
72
-
73
- .brand-icon {
74
- width: 38px;
75
- height: 38px;
76
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
77
- border-radius: 12px;
78
- display: flex;
79
- align-items: center;
80
- justify-content: center;
81
- color: white;
82
- box-shadow: 0 4px 15px rgba(99, 102, 241, 0.25);
83
- }
84
-
85
- .brand-text {
86
- font-family: 'Outfit', sans-serif;
87
- font-size: 1.4rem;
88
- font-weight: 700;
89
- margin: 0;
90
- background: linear-gradient(to right, var(--primary-dark), #818cf8);
91
- -webkit-background-clip: text;
92
- background-clip: text;
93
- -webkit-text-fill-color: transparent;
94
- }
95
-
96
- footer {
97
- background: rgba(255, 255, 255, 0.7);
98
- backdrop-filter: blur(12px);
99
- -webkit-backdrop-filter: blur(12px);
100
- border-top: 1px solid var(--glass-border);
101
- padding: 24px 40px;
102
- margin-top: auto;
103
- display: flex;
104
- justify-content: space-between;
105
- align-items: center;
106
- color: var(--text-muted);
107
- font-size: 0.9rem;
108
- }
109
-
110
- .powered-by {
111
- display: flex;
112
- align-items: center;
113
- gap: 8px;
114
- font-weight: 500;
115
- }
116
-
117
- .badge-tool {
118
- background: #f1f5f9;
119
- padding: 2px 10px;
120
- border-radius: 8px;
121
- font-weight: 700;
122
- color: #334155;
123
- font-size: 0.8rem;
124
- }
125
-
126
- .verified-icon {
127
- font-size: 18px;
128
- color: var(--primary);
129
- }
130
-
131
- /* Container Overrides */
132
- .container {
133
- max-width: 1200px !important;
134
- margin: 0 auto;
135
- width: 100%;
136
- padding-bottom: 40px;
137
- }
138
-
139
- /* Summary Section */
140
- .page-hero {
141
- background: linear-gradient(135deg, #fff 0%, #f1f5f9 100%);
142
- border: 1px solid var(--glass-border);
143
- border-radius: var(--card-radius);
144
- padding: 32px;
145
- margin-bottom: 32px;
146
- box-shadow: var(--glass-shadow);
147
- display: flex;
148
- justify-content: space-between;
149
- align-items: center;
150
- gap: 24px;
151
- }
152
-
153
- .hero-content h2 {
154
- font-size: 0.75rem;
155
- letter-spacing: 0.15em;
156
- color: var(--text-muted);
157
- margin-bottom: 8px;
158
- font-weight: 700;
159
- }
160
-
161
- .page-url {
162
- font-family: 'Outfit', sans-serif;
163
- font-size: 1.25rem;
164
- font-weight: 600;
165
- word-break: break-all;
166
- color: var(--text-main);
167
- margin: 0;
168
- }
169
-
170
- .stats-pills {
171
- display: flex;
172
- gap: 12px;
173
- }
174
-
175
- .stat-pill {
176
- padding: 8px 16px;
177
- border-radius: 12px;
178
- background: white;
179
- border: 1px solid #e2e8f0;
180
- display: flex;
181
- align-items: center;
182
- gap: 8px;
183
- font-weight: 600;
184
- font-size: 0.9rem;
185
- }
186
-
187
- .stat-pill-passed {
188
- background: #ecfdf5;
189
- border-color: #d1fae5;
190
- color: #047857;
191
- }
192
-
193
- .stat-pill-icon {
194
- color: var(--primary);
195
- }
196
-
197
- /* Violation Cards */
198
- .violation-card {
199
- background: var(--glass-bg);
200
- backdrop-filter: blur(8px);
201
- border-radius: var(--card-radius);
202
- border: 1px solid var(--glass-border);
203
- overflow: hidden;
204
- margin-bottom: 24px;
205
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04);
206
- transition: transform 0.2s ease, box-shadow 0.2s ease;
207
- }
208
-
209
- .violation-card:hover {
210
- transform: translateY(-2px);
211
- box-shadow: 0 12px 30px rgba(0, 0, 0, 0.08);
212
- }
213
-
214
- .violation-header {
215
- padding: 12px 20px;
216
- display: flex;
217
- justify-content: space-between;
218
- align-items: center;
219
- gap: 20px;
220
- background: rgba(255, 255, 255, 0.5);
221
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
222
- }
223
-
224
- .title-meta {
225
- display: flex;
226
- align-items: center;
227
- gap: 16px;
228
- flex-wrap: wrap;
229
- }
230
-
231
- .violation-title {
232
- font-family: 'Outfit', sans-serif;
233
- font-size: 1.25rem;
234
- font-weight: 700;
235
- color: var(--text-main);
236
- display: block;
237
- margin: 0;
238
- }
239
-
240
- .violation-rule-info {
241
- font-size: 0.85rem;
242
- color: var(--text-muted);
243
- margin: 0;
244
- display: flex;
245
- align-items: center;
246
- gap: 8px;
247
- }
248
-
249
- .rule-name {
250
- font-weight: 700;
251
- color: var(--text-main);
252
- }
253
-
254
- .rule-sep {
255
- opacity: 0.5;
256
- }
257
-
258
- .severity-badge {
259
- padding: 6px 16px;
260
- border-radius: 10px;
261
- font-size: 0.75rem;
262
- font-weight: 800;
263
- letter-spacing: 0.5px;
264
- color: #fff;
265
- white-space: nowrap;
266
- }
267
-
268
- .severity-badge.critical {
269
- background: var(--critical);
270
- }
271
-
272
- .severity-badge.serious {
273
- background: var(--serious);
274
- }
275
-
276
- .severity-badge.moderate {
277
- background: var(--moderate);
278
- }
279
-
280
- .severity-badge.minor {
281
- background: var(--minor);
282
- }
283
-
284
- .violation-body {
285
- padding: 16px 20px;
286
- }
287
-
288
- .fix-suggestion {
289
- background: #f0fdf4;
290
- border: 1px solid #bbf7d0;
291
- padding: 12px 16px;
292
- border-radius: 12px;
293
- margin-bottom: 24px;
294
- font-size: 0.95rem;
295
- color: #166534;
296
- display: flex;
297
- gap: 12px;
298
- align-items: flex-start;
299
- }
300
-
301
- .fix-icon {
302
- color: #059669;
303
- font-size: 20px;
304
- }
305
-
306
- .fix-title {
307
- font-weight: 700;
308
- margin-bottom: 4px;
309
- }
310
-
311
- .fix-link {
312
- margin-left: 8px;
313
- color: #059669;
314
- font-weight: 600;
315
- text-decoration: none;
316
- border-bottom: 1.5px solid rgba(5, 150, 105, 0.3);
317
- }
318
-
319
- .fix-link-icon {
320
- font-size: 14px;
321
- vertical-align: middle;
322
- }
323
-
324
- /* Instances */
325
- .instances-title {
326
- font-size: 0.9rem;
327
- font-weight: 700;
328
- color: var(--text-main);
329
- margin-bottom: 16px;
330
- display: flex;
331
- align-items: center;
332
- gap: 8px;
333
- }
334
-
335
- .instances-icon {
336
- font-size: 18px;
337
- color: var(--primary);
338
- }
339
-
340
- /* Bug Preview Card Styling */
341
- .bug-preview-card {
342
- background: #fff;
343
- border: 1px solid #e2e8f0;
344
- border-radius: 12px;
345
- overflow: hidden;
346
- margin-bottom: 16px;
347
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
348
- transition: all 0.2s ease;
349
- }
350
-
351
- .bug-preview-card:hover {
352
- border-color: var(--primary);
353
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.1);
354
- }
355
-
356
- .bug-header {
357
- background: #f8fafc;
358
- padding: 12px 16px;
359
- border-bottom: 1px solid #e2e8f0;
360
- display: flex;
361
- justify-content: space-between;
362
- align-items: center;
363
- }
364
-
365
- .bug-title-tag {
366
- font-family: 'JetBrains Mono', monospace;
367
- font-size: 0.8rem;
368
- color: var(--primary-dark);
369
- background: var(--primary-soft);
370
- padding: 4px 10px;
371
- border-radius: 6px;
372
- font-weight: 600;
373
- }
374
-
375
- .bug-body {
376
- padding: 16px;
377
- display: grid;
378
- grid-template-columns: 1fr 1fr;
379
- gap: 24px;
380
- }
381
-
382
- @media (max-width: 768px) {
383
- .bug-body {
384
- grid-template-columns: 1fr;
385
- }
386
- }
387
-
388
- .bug-section-title {
389
- font-size: 0.75rem;
390
- text-transform: uppercase;
391
- letter-spacing: 0.05em;
392
- color: var(--text-muted);
393
- font-weight: 700;
394
- margin-bottom: 8px;
395
- }
396
-
397
- .bug-content-box {
398
- background: #f8fafc;
399
- border-radius: 8px;
400
- padding: 12px;
401
- font-size: 0.9rem;
402
- color: var(--text-main);
403
- border: 1px solid #f1f5f9;
404
- }
405
-
406
- /* Video Section */
407
- .video-card {
408
- background: white;
409
- border: 1px solid var(--glass-border);
410
- border-radius: var(--card-radius);
411
- padding: 32px;
412
- margin-top: 40px;
413
- box-shadow: var(--glass-shadow);
414
- }
415
-
416
- .video-header {
417
- display: flex;
418
- align-items: center;
419
- gap: 12px;
420
- margin-bottom: 24px;
421
- }
422
-
423
- .video-icon-wrap {
424
- width: 40px;
425
- height: 40px;
426
- background: #fee2e2;
427
- border-radius: 12px;
428
- display: flex;
429
- align-items: center;
430
- justify-content: center;
431
- color: #ef4444;
432
- }
433
-
434
- .video-title {
435
- font-family: 'Outfit', sans-serif;
436
- font-size: 1.4rem;
437
- font-weight: 700;
438
- margin: 0;
439
- }
440
-
441
- .video-container {
442
- border-radius: 16px;
443
- overflow: hidden;
444
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.1);
445
- background: #000;
446
- border: 1px solid #000;
447
- }
448
-
449
- .video-container video {
450
- width: 100%;
451
- height: 100%;
452
- }
453
-
454
- #loader-overlay {
455
- position: fixed;
456
- inset: 0;
457
- background: #fff;
458
- z-index: 10000;
459
- display: flex;
460
- flex-direction: column;
461
- justify-content: center;
462
- align-items: center;
463
- transition: opacity 0.5s ease;
464
- }
465
-
466
- .loader-pulse {
467
- width: 64px;
468
- height: 64px;
469
- background: var(--primary);
470
- border-radius: 16px;
471
- animation: pulse 1.5s infinite ease-in-out;
472
- display: flex;
473
- align-items: center;
474
- justify-content: center;
475
- color: white;
476
- }
477
-
478
- .loader-text {
479
- font-family: 'Outfit';
480
- font-weight: 700;
481
- font-size: 1.2rem;
482
- color: var(--text-main);
483
- margin-top: 24px;
484
- }
485
-
486
- @keyframes pulse {
487
- 0% {
488
- transform: scale(0.95);
489
- box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.5);
490
- }
491
-
492
- 70% {
493
- transform: scale(1);
494
- box-shadow: 0 0 0 20px rgba(99, 102, 241, 0);
495
- }
496
-
497
- 100% {
498
- transform: scale(0.95);
499
- box-shadow: 0 0 0 0 rgba(99, 102, 241, 0);
500
- }
501
- }
502
-
503
- /* Success Hero */
504
- .success-verified-card {
505
- text-align: center;
506
- padding: 80px 0;
507
- background: white;
508
- border-radius: 32px;
509
- border: 1px solid var(--glass-border);
510
- box-shadow: var(--glass-shadow);
511
- }
512
-
513
- .success-hero-icon {
514
- font-size: 80px;
515
- color: #10b981;
516
- margin-bottom: 24px;
517
- filter: drop-shadow(0 4px 12px rgba(16, 185, 129, 0.2));
518
- }
519
-
520
- .success-hero-title {
521
- font-weight: 700;
522
- font-size: 1.8rem;
523
- font-family: 'Outfit', sans-serif;
524
- }
525
-
526
- .success-hero-desc {
527
- max-width: 400px;
528
- margin: 0 auto;
529
- color: var(--text-muted);
530
- }
531
-
532
- /* Modal Styling */
533
- .modal-content {
534
- border-radius: 28px;
535
- border: none;
536
- }
537
-
538
- .modal-header-custom {
539
- border-bottom: none;
540
- padding: 32px 32px 16px;
541
- }
542
-
543
- .modal-title-custom {
544
- font-weight: 700;
545
- font-size: 1.5rem;
546
- font-family: 'Outfit', sans-serif;
547
- }
548
-
549
- .modal-body-custom {
550
- padding: 0 32px 24px;
551
- }
552
-
553
- .modal-desc {
554
- font-size: 0.95rem;
555
- color: var(--text-muted);
556
- margin-bottom: 24px;
557
- }
558
-
559
- .modal-footer-custom {
560
- border-top: none;
561
- padding: 0 32px 32px;
562
- }
563
-
564
- .form-control:focus {
565
- box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
566
- border-color: var(--primary);
567
- }
568
-
569
- .token-input-wrap {
570
- position: relative;
571
- }
572
-
573
- .token-icon {
574
- position: absolute;
575
- left: 12px;
576
- top: 50%;
577
- transform: translateY(-50%);
578
- color: var(--text-muted);
579
- font-size: 20px;
580
- }
581
-
582
- .token-input {
583
- border-radius: 12px;
584
- padding: 14px 14px 14px 44px;
585
- background: #f8fafc;
586
- }
587
-
588
- .btn-init {
589
- border-radius: 14px;
590
- font-weight: 700;
591
- padding: 16px;
592
- background: var(--primary);
593
- }
594
-
595
- /* Premium Buttons */
596
- .btn-premium {
597
- border-radius: 8px;
598
- padding: 8px 16px;
599
- font-weight: 600;
600
- font-size: 0.85rem;
601
- letter-spacing: 0.3px;
602
- display: inline-flex;
603
- align-items: center;
604
- gap: 6px;
605
- transition: all 0.2s ease;
606
- cursor: pointer;
607
- }
608
-
609
- .btn-bug {
610
- background: linear-gradient(135deg, var(--primary), var(--primary-dark));
611
- color: #fff;
612
- border: none;
613
- box-shadow: 0 4px 10px rgba(99, 102, 241, 0.2);
614
- }
615
-
616
- .btn-bug:hover {
617
- transform: translateY(-1px);
618
- box-shadow: 0 6px 15px rgba(99, 102, 241, 0.3);
619
- color: #fff;
620
- }
621
-
622
- .btn-back {
623
- text-decoration: none;
624
- color: var(--text-muted);
625
- font-weight: 600;
626
- display: flex;
627
- align-items: center;
628
- gap: 6px;
629
- }
630
-
631
- .btn-back-icon {
632
- font-size: 20px;
633
- }
634
-
635
- .btn-token {
636
- border-radius: 10px;
637
- font-weight: 700;
638
- padding: 8px 16px;
639
- }
640
-
641
- .btn-token-icon {
642
- vertical-align: middle;
643
- font-size: 18px;
644
- margin-right: 4px;
645
- }
646
-
647
- .screenshot-container {
648
- margin-top: 0;
649
- border-radius: 8px;
650
- overflow: hidden;
651
- border: 1px solid #e2e8f0;
652
- background: #000;
653
- position: relative;
654
- }
655
-
656
- .screenshot-container img {
657
- width: 100%;
658
- height: auto;
659
- display: block;
660
- }
661
-
662
- /* ADO Modal Premium Standardized */
663
- .ado-modal-premium .modal-content {
664
- border-radius: 20px;
665
- border: 1px solid var(--glass-border);
666
- box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
667
- background: rgba(255, 255, 255, 0.98);
668
- backdrop-filter: blur(10px);
669
- overflow: hidden;
670
- }
671
-
672
- .ado-modal-premium .modal-header {
673
- background: #f8fafc;
674
- padding: 24px 32px;
675
- border-bottom: 1px solid var(--glass-border);
676
- }
677
-
678
- .ado-modal-premium .modal-title {
679
- font-family: 'Outfit', sans-serif;
680
- font-weight: 700;
681
- font-size: 1.25rem;
682
- color: var(--text-main);
683
- display: flex;
684
- align-items: center;
685
- gap: 12px;
686
- }
687
-
688
- .ado-modal-premium .modal-body {
689
- padding: 24px 32px;
690
- max-height: 550px;
691
- overflow-y: auto;
692
- }
693
-
694
- .ado-modal-premium .modal-footer {
695
- padding: 12px 32px;
696
- background: #f8fafc;
697
- border-top: 1px solid var(--glass-border);
698
- }
699
-
700
- .ado-field-group {
701
- margin-bottom: 20px;
702
- }
703
-
704
- .ado-form-label {
705
- display: block;
706
- font-weight: 700;
707
- font-size: 0.8rem;
708
- color: var(--text-muted);
709
- margin-bottom: 8px;
710
- text-transform: uppercase;
711
- letter-spacing: 0.5px;
712
- }
713
-
714
- .ado-form-input {
715
- display: block;
716
- width: 100%;
717
- padding: 12px 16px;
718
- font-size: 0.95rem;
719
- font-weight: 400;
720
- line-height: 1.5;
721
- color: var(--text-main);
722
- background-color: #fff;
723
- background-clip: padding-box;
724
- border: 1.5px solid #e2e8f0;
725
- appearance: none;
726
- border-radius: 12px;
727
- transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
728
- }
729
-
730
- .ado-form-input:focus {
731
- color: var(--text-main);
732
- background-color: #fff;
733
- border-color: var(--primary);
734
- outline: 0;
735
- box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
736
- }
737
-
738
- .ado-repro-preview {
739
- background: #f8fafc;
740
- border: 1.5px solid #e2e8f0;
741
- padding: 16px;
742
- border-radius: 12px;
743
- font-size: 0.9rem;
744
- line-height: 1.6;
745
- color: var(--text-main);
746
- }
747
-
748
- .visual-evidence-grid {
749
- display: grid;
750
- grid-template-columns: 1fr 1fr;
751
- gap: 16px;
752
- margin-top: 12px;
753
- }
754
-
755
- .visual-item {
756
- background: #f8fafc;
757
- border: 1px solid #e2e8f0;
758
- border-radius: 12px;
759
- padding: 8px;
760
- }
761
-
762
- .visual-label {
763
- font-size: 0.7rem;
764
- font-weight: 700;
765
- color: var(--text-muted);
766
- text-transform: uppercase;
767
- margin-bottom: 6px;
768
- display: flex;
769
- align-items: center;
770
- gap: 4px;
771
- }
772
-
773
- .screenshot-thumb {
774
- width: 100%;
775
- height: auto;
776
- max-height: 480px;
777
- border-radius: 12px;
778
- object-fit: contain;
779
- background: #000;
780
- border: 1.5px solid #e2e8f0;
781
- display: block;
782
- }
783
-
784
- .video-preview-player {
785
- width: 100%;
786
- height: 280px;
787
- border-radius: 12px;
788
- background: #000;
789
- border: 1.5px solid #e2e8f0;
790
- display: block;
791
- object-fit: contain;
792
- }
793
-
794
- .ado-repro-preview b {
795
- color: var(--primary-dark);
796
- }
797
-
798
- .ado-repro-preview hr {
799
- margin: 16px 0;
800
- opacity: 0.1;
801
- }
802
-
803
- .btn-ado-primary {
804
- background: var(--primary);
805
- color: #fff;
806
- border: none;
807
- padding: 12px 28px;
808
- border-radius: 12px;
809
- font-weight: 700;
810
- transition: all 0.2s;
811
- }
812
-
813
- .btn-ado-primary:hover {
814
- background: var(--primary-dark);
815
- transform: translateY(-1px);
816
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.25);
817
- }
818
17
 
819
- .btn-ado-secondary {
820
- background: #fff;
821
- color: var(--text-main);
822
- border: 1.5px solid #e2e8f0;
823
- padding: 12px 28px;
824
- border-radius: 12px;
825
- font-weight: 700;
826
- transition: all 0.2s;
827
- }
828
-
829
- .btn-ado-secondary:hover {
830
- background: #f8fafc;
831
- border-color: #cbd5e1;
832
- }
833
-
834
- /* Custom select arrow for ado-form-input */
835
- select.ado-form-input {
836
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
837
- background-repeat: no-repeat;
838
- background-position: right 1rem center;
839
- background-size: 16px 12px;
840
- }
841
-
842
- .modal-xl {
843
- max-width: 1000px !important;
844
- width: 95% !important;
845
- }
846
-
847
- /* Restoration of Bug List UI */
848
- .bug-list-item {
849
- border: 1px solid #e2e8f0;
850
- border-radius: 12px;
851
- margin-bottom: 12px;
852
- overflow: hidden;
853
- background: #fff;
854
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
855
- }
856
-
857
- .bug-item-header {
858
- background: #fff;
859
- padding: 14px 20px;
860
- display: flex;
861
- justify-content: space-between;
862
- align-items: center;
863
- cursor: pointer;
864
- transition: background 0.2s;
865
- }
866
-
867
- .bug-item-header:hover {
868
- background: #f8fafc;
869
- }
870
-
871
- .bug-item-header .chevron {
872
- transition: transform 0.3s;
873
- color: var(--text-muted);
874
- }
875
-
876
- .bug-item-header[aria-expanded="true"] .chevron {
877
- transform: rotate(180deg);
878
- }
879
-
880
- .bug-snippet {
881
- font-family: 'JetBrains Mono', monospace;
882
- font-size: 0.85rem;
883
- color: var(--primary-dark);
884
- background: var(--primary-soft);
885
- padding: 6px 12px;
886
- border-radius: 8px;
887
- max-width: 65%;
888
- white-space: nowrap;
889
- overflow: hidden;
890
- text-overflow: ellipsis;
891
- border: 1px solid rgba(99, 102, 241, 0.1);
892
- }
893
-
894
- .bug-details-body {
895
- padding: 20px;
896
- background: #f8fafc;
897
- border-top: 1px solid #f1f5f9;
898
- }
899
-
900
- .log-bug-btn {
901
- display: inline-flex !important;
902
- align-items: center !important;
903
- justify-content: center !important;
904
- gap: 8px !important;
905
- padding: 6px 16px !important;
906
- }
907
-
908
- .log-bug-btn span {
909
- font-size: 18px !important;
910
- margin: 0 !important;
911
- display: inline-block;
912
- vertical-align: middle;
913
- }
914
- </style>
18
+ <!-- Snap Ally Shared Template Styles -->
19
+ <link rel="stylesheet" href="global-report-styles.css">
915
20
  </head>
916
21
 
917
22
  <body>
918
- <div id="loader-overlay">
23
+ <div id="loader-overlay" style="position: fixed; inset: 0; background: #fff; z-index: 10000; display: flex; flex-direction: column; justify-content: center; align-items: center; transition: opacity 0.5s ease; display: none;">
919
24
  <div class="loader-pulse">
920
25
  <span class="material-symbols-outlined" style="font-size: 32px;">query_stats</span>
921
26
  </div>
@@ -923,7 +28,7 @@
923
28
  </div>
924
29
 
925
30
  <header>
926
- <a href="#" class="brand" style="cursor: default;">
31
+ <a href="#" class="brand" style="cursor: default; text-decoration: none;">
927
32
  <div class="brand-icon">
928
33
  <span class="material-symbols-outlined" style="font-size: 22px;">accessibility_new</span>
929
34
  </div>
@@ -942,471 +47,247 @@
942
47
  </div>
943
48
  </header>
944
49
 
945
- <main class="container">
50
+ <div class="container" id="accessibility-report-root" style="display: none;">
51
+
52
+ <!-- Hero Section -->
946
53
  <section class="page-hero">
947
54
  <div class="hero-content">
948
55
  <h2>Target Resource</h2>
949
- <p class="page-url">
950
- <%= data.pageKey %>
951
- </p>
56
+ <p class="page-url" id="a11y-page-url"></p>
952
57
  </div>
953
58
  <div class="stats-pills">
954
- <% if (!data.errors || data.errors.length===0) { %>
955
- <div class="stat-pill stat-pill-passed">
956
- <span class="material-symbols-outlined text-success">check_circle</span>
957
- <span>Passed</span>
958
- </div>
959
- <% } %>
960
- <div class="stat-pill">
961
- <span class="material-symbols-outlined stat-pill-icon">bug_report</span>
962
- <span>
963
- <%= (data.errors ? data.errors.reduce((acc, err)=> acc + err.total, 0) : 0) %> Issues
964
- </span>
965
- </div>
59
+ <div class="stat-pill stat-pill-passed" id="a11y-pill-passed" style="display: none;">
60
+ <span class="material-symbols-outlined" style="font-size: 20px;">verified</span>
61
+ No Violations
62
+ </div>
63
+
64
+ <div class="stat-pill" id="a11y-pill-failed"
65
+ style="display: none;">
66
+ <span class="material-symbols-outlined stat-pill-icon">bug_report</span>
67
+ <span><span id="a11y-failed-count"></span> Issues</span>
68
+ </div>
966
69
  </div>
967
70
  </section>
968
71
 
969
- <!-- Modal for ADO Token -->
970
- <div class="modal fade" id="tokenModal" tabindex="-1">
971
- <div class="modal-dialog modal-dialog-centered">
972
- <div class="modal-content">
973
- <form id="tokenForm">
974
- <div class="modal-header modal-header-custom">
975
- <h5 class="modal-title modal-title-custom">Azure DevOps Access</h5>
976
- <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
977
- </div>
978
- <div class="modal-body modal-body-custom">
979
- <p class="modal-desc">Securely provide your Personal Access Token (PAT) to enable automated bug reporting.
980
- </p>
981
- <div class="mb-3">
982
- <div class="token-input-wrap">
983
- <span class="material-symbols-outlined token-icon">lock</span>
984
- <input type="password" class="form-control token-input" id="tokenInput" placeholder="Enter PAT Token"
985
- required>
986
- </div>
987
- </div>
988
- </div>
989
- <div class="modal-footer modal-footer-custom">
990
- <button type="submit" class="btn btn-primary w-100 btn-init">Initialize Session</button>
991
- </div>
992
- </form>
72
+ <!-- Video Card -->
73
+ <div class="video-card" id="a11y-video-card" style="display: none;">
74
+ <div class="video-header">
75
+ <div class="video-icon-wrap">
76
+ <span class="material-symbols-outlined">videocam</span>
993
77
  </div>
78
+ <h2 class="video-title">Execution Recording</h2>
79
+ </div>
80
+ <div class="video-container">
81
+ <video controls>
82
+ <source id="a11y-video-source" type="video/webm" />
83
+ Your browser does not support the video tag.
84
+ </video>
994
85
  </div>
995
86
  </div>
996
87
 
997
- <% if(data.errors && data.errors.length) { %>
998
- <div class="violation-section">
999
- <% data.errors.forEach(function(error, errorIndex) { %>
1000
- <div class="violation-card">
1001
- <div class="violation-header">
1002
- <div class="title-meta">
1003
- <span class="violation-title">
1004
- <%= error.id %>
1005
- </span>
1006
- <span class="violation-rule-info">
1007
- <span class="rule-name">
1008
- <%= error.wcagRule %>
1009
- </span>
1010
- <span class="rule-sep">•</span>
1011
- <span>
1012
- <%= error.description %>
1013
- </span>
1014
- </span>
1015
- </div>
1016
- <span class="severity-badge <%= error.severity %>">
1017
- <%= error.total %>
1018
- <%= error.severity %>
1019
- </span>
1020
- </div>
88
+ <!-- Success State -->
89
+ <div class="success-verified-card" id="a11y-success-card" style="display: none;">
90
+ <span class="material-symbols-outlined success-hero-icon">check_circle</span>
91
+ <h3 class="success-hero-title">Compliance Verified</h3>
92
+ <p class="success-hero-desc">Congratulations! This page passed all automated accessibility checks.</p>
93
+ </div>
1021
94
 
1022
- <div class="violation-body">
1023
- <div class="fix-suggestion">
1024
- <span class="material-symbols-outlined fix-icon">lightbulb</span>
1025
- <div>
1026
- <div class="fix-title">Recommended Action</div>
1027
- <%= error.help %>
1028
- <a href="<%= error.helpUrl %>" target="_blank" class="fix-link">Technical Docs <span
1029
- class="material-symbols-outlined fix-link-icon">open_in_new</span></a>
1030
- </div>
1031
- </div>
95
+ <!-- Violations Container -->
96
+ <div id="a11y-violations-container"></div>
97
+
98
+ <template id="violation-card-template">
99
+ <div class="violation-card">
100
+ <div class="violation-header">
101
+ <div class="title-meta">
102
+ <span class="violation-title"></span>
103
+ <span class="violation-rule-info">
104
+ <span class="rule-name"></span>
105
+ <span class="rule-sep">•</span>
106
+ <span class="rule-wcag-tags" style="margin-left: 8px;"></span>
107
+ </span>
108
+ </div>
109
+ <span class="severity-badge"></span>
110
+ </div>
1032
111
 
1033
- <div class="instances-list" style="margin-top: 16px;">
1034
- <% if(error.target && error.target.length) { %>
1035
- <h4 class="instances-title">
1036
- <span class="material-symbols-outlined instances-icon">preview</span>
1037
- Bug Candidates (<%= error.target.length %>)
1038
- </h4>
112
+ <div class="violation-body">
113
+ <div class="fix-suggestion">
114
+ <span class="material-symbols-outlined fix-icon">lightbulb</span>
115
+ <div>
116
+ <div class="fix-title">Recommended Action</div>
117
+ <div><span class="fix-desc"></span> <a target="_blank" class="fix-link">Technical Docs <span
118
+ class="material-symbols-outlined fix-link-icon">open_in_new</span></a></div>
119
+ </div>
120
+ </div>
121
+
122
+ <div class="instances-list" style="margin-top: 16px;">
123
+ <h4 class="instances-title">
124
+ <span class="material-symbols-outlined instances-icon">preview</span>
125
+ Bug Candidates (<span class="instances-count"></span>)
126
+ </h4>
1039
127
 
1040
- <% error.target.forEach(function(target, index) { %>
1041
- <div class="bug-list-item">
1042
- <div class="bug-item-header collapsed" data-bs-toggle="collapse"
1043
- data-bs-target="#details-<%= errorIndex %>-<%= index %>" aria-expanded="false">
1044
- <div class="d-flex align-items-center gap-3" style="max-width: 75%;">
1045
- <span class="material-symbols-outlined chevron">expand_more</span>
1046
- <div class="bug-snippet" title="<%= error.help %>: <%= target.snippet || target.element %>">
1047
- <strong style="color: var(--primary);">
1048
- <%= error.help %>
1049
- </strong>: <%= target.snippet || target.element %>
1050
- </div>
1051
- </div>
1052
- <div class="d-flex gap-2">
1053
- <button class="btn btn-sm btn-bug log-bug-btn" type="button"
1054
- onclick="handleLogBugClick(this, event)"
1055
- data-title="<%= error.help %> (<%= target.snippet || target.element %>)"
1056
- data-axe-id="<%= error.id %>" data-help="<%= error.help %>"
1057
- data-severity="<%= error.severity %>" data-steps="<%= target.stepsJson || '[]' %>"
1058
- data-screenshot="<%= target.screenshotBase64 %>" data-wcag="<%= error.wcagRule %>">
1059
- <span class="material-symbols-outlined" style="font-size: 16px;">bug_report</span>
1060
- Log Bug
1061
- </button>
1062
- </div>
1063
- </div>
128
+ <div class="instances-container"></div>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </template>
133
+
134
+ <template id="bug-list-item-template">
135
+ <div class="bug-list-item">
136
+ <div class="bug-item-header collapsed" data-bs-toggle="collapse"
137
+ data-bs-target="" aria-expanded="false">
138
+ <div class="d-flex align-items-center gap-3" style="max-width: 75%;">
139
+ <span class="material-symbols-outlined chevron">expand_more</span>
140
+ <div class="bug-snippet">
141
+ <strong style="color: var(--primary);" class="bug-rule-name"></strong>: <span class="bug-snippet-text"></span>
142
+ </div>
143
+ </div>
144
+ <div class="d-flex gap-2">
145
+ <button class="btn btn-sm btn-bug log-bug-btn" type="button">
146
+ <span class="material-symbols-outlined" style="font-size: 16px;">bug_report</span>
147
+ Log Bug
148
+ </button>
149
+ </div>
150
+ </div>
1064
151
 
1065
- <div id="details-<%= errorIndex %>-<%= index %>" class="collapse">
1066
- <div class="bug-details-body">
1067
- <!-- Left: Repro Steps -->
1068
- <div style="margin-bottom: 16px;">
1069
- <div class="bug-section-title">Reproduction Steps</div>
1070
- <div class="bug-content-box">
1071
- <% if(target.steps && target.steps.length) { %>
1072
- <ol style="margin: 0; padding-left: 18px; line-height: 1.5;">
1073
- <% target.steps.forEach(function(step) { %>
1074
- <li>
1075
- <%= step %>
1076
- </li>
1077
- <% }); %>
1078
- </ol>
1079
- <% } else { %>
1080
- <span style="color: var(--text-muted); font-style: italic;">No interaction steps
1081
- recorded. Issue found via static scan.</span>
1082
- <% } %>
1083
- </div>
1084
- </div>
152
+ <div id="" class="collapse bug-item-body-collapse">
153
+ <div class="bug-details-body">
154
+ <!-- Left: Repro Steps -->
155
+ <div style="margin-bottom: 16px;">
156
+ <div class="bug-section-title">Reproduction Steps</div>
157
+ <div class="bug-content-box bug-failure-summary">
158
+ <span style="color: var(--text-muted); font-style: italic;">No interaction steps recorded. Issue found via static scan.</span>
159
+ </div>
160
+ </div>
1085
161
 
1086
- <!-- Right: Visual -->
1087
- <div>
1088
- <div class="bug-section-title">Visual Evidence</div>
1089
- <div class="screenshot-container">
1090
- <img src="<%= target.screenshot %>" alt="Violation Evidence">
1091
- </div>
1092
- </div>
1093
- </div>
1094
- </div>
1095
- </div>
1096
- <% }); %>
1097
- <% } %>
162
+ <!-- Right: Visual -->
163
+ <div class="visual-evidence-section" style="display: none;">
164
+ <div class="bug-section-title">Visual Evidence</div>
165
+ <div class="screenshot-container">
166
+ <img class="bug-screenshot" alt="Violation Evidence">
1098
167
  </div>
1099
168
  </div>
1100
169
  </div>
1101
- <% }); %>
170
+ </div>
1102
171
  </div>
172
+ </template>
1103
173
 
1104
- <!-- ADO Bug Preview Modal -->
1105
- <div class="modal fade ado-modal-premium" id="bugPreviewModal" tabindex="-1">
1106
- <div class="modal-dialog modal-xl modal-dialog-centered">
1107
- <div class="modal-content">
1108
- <div class="modal-header">
1109
- <div class="modal-title">
1110
- <span class="material-symbols-outlined" style="color: #cc293d; font-size: 24px;">bug_report</span>
1111
- New Bug Entry - Azure DevOps
174
+ </div>
175
+
176
+ <!-- Modal for ADO Token -->
177
+ <div class="modal fade" id="tokenModal" tabindex="-1">
178
+ <div class="modal-dialog modal-dialog-centered">
179
+ <div class="modal-content">
180
+ <form id="tokenForm">
181
+ <div class="modal-header modal-header-custom">
182
+ <h5 class="modal-title modal-title-custom">Azure DevOps Access</h5>
183
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
184
+ </div>
185
+ <div class="modal-body modal-body-custom">
186
+ <p class="modal-desc">Securely provide your Personal Access Token (PAT) to enable automated bug reporting.</p>
187
+ <div class="mb-3">
188
+ <div class="token-input-wrap">
189
+ <span class="material-symbols-outlined token-icon">lock</span>
190
+ <input type="password" class="form-control token-input" id="tokenInput" placeholder="Enter PAT Token" required>
1112
191
  </div>
1113
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1114
192
  </div>
1115
- <div class="modal-body">
1116
- <div class="ado-field-group">
1117
- <label class="ado-form-label">Title</label>
1118
- <input type="text" id="bugTitleInput" class="ado-form-input" placeholder="Enter bug title">
1119
- </div>
193
+ </div>
194
+ <div class="modal-footer modal-footer-custom">
195
+ <button type="submit" class="btn btn-primary w-100 btn-init">Initialize Session</button>
196
+ </div>
197
+ </form>
198
+ </div>
199
+ </div>
200
+ </div>
1120
201
 
1121
- <div class="row">
1122
- <div class="col-md-6">
1123
- <div class="ado-field-group">
1124
- <label class="ado-form-label">Severity</label>
1125
- <select id="bugSeverityInput" class="ado-form-input">
1126
- <option value="critical">Critical (Priority 1)</option>
1127
- <option value="serious">Serious (Priority 2)</option>
1128
- <option value="moderate">Moderate (Priority 3)</option>
1129
- <option value="minor">Minor (Priority 4)</option>
1130
- </select>
1131
- </div>
1132
- </div>
1133
- <div class="col-md-6">
1134
- <div class="ado-field-group">
1135
- <label class="ado-form-label">Target Area</label>
1136
- <input type="text" id="bugAreaInput" class="ado-form-input" value="Accessibility">
1137
- </div>
1138
- </div>
1139
- </div>
202
+ <!-- ADO Bug Preview Modal -->
203
+ <div class="modal fade ado-modal-premium" id="bugPreviewModal" tabindex="-1">
204
+ <div class="modal-dialog modal-xl modal-dialog-centered">
205
+ <div class="modal-content">
206
+ <div class="modal-header">
207
+ <div class="modal-title">
208
+ <span class="material-symbols-outlined" style="color: #cc293d; font-size: 24px;">bug_report</span>
209
+ New Bug Entry - Azure DevOps
210
+ </div>
211
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
212
+ </div>
213
+ <div class="modal-body">
214
+ <div class="ado-field-group">
215
+ <label class="ado-form-label">Title</label>
216
+ <input type="text" id="bugTitleInput" class="ado-form-input" placeholder="Enter bug title">
217
+ </div>
1140
218
 
219
+ <div class="row">
220
+ <div class="col-md-6">
1141
221
  <div class="ado-field-group">
1142
- <label class="ado-form-label">Reproduction Steps & Description</label>
1143
- <div id="bugReproPreview" class="ado-repro-preview" style="min-height: 150px;"></div>
222
+ <label class="ado-form-label">Severity</label>
223
+ <select id="bugSeverityInput" class="ado-form-input">
224
+ <option value="critical">Critical (Priority 1)</option>
225
+ <option value="serious">Serious (Priority 2)</option>
226
+ <option value="moderate">Moderate (Priority 3)</option>
227
+ <option value="minor">Minor (Priority 4)</option>
228
+ </select>
1144
229
  </div>
1145
-
1146
- <!-- Visual Evidence - Single Column Full Width -->
1147
- <div class="ado-field-group" id="screenshotThumbContainer" style="display: none;">
1148
- <label class="ado-form-label">
1149
- <span class="material-symbols-outlined" style="font-size: 14px; vertical-align: middle;">image</span>
1150
- Violation Screenshot
1151
- </label>
1152
- <img id="bugScreenshotPreview" class="screenshot-thumb" src="" alt="Screenshot">
230
+ </div>
231
+ <div class="col-md-6">
232
+ <div class="ado-field-group">
233
+ <label class="ado-form-label">Target Area</label>
234
+ <input type="text" id="bugAreaInput" class="ado-form-input" value="Accessibility">
1153
235
  </div>
236
+ </div>
237
+ </div>
1154
238
 
1155
- <div class="ado-field-group" id="videoThumbContainer" style="display: none;">
1156
- <label class="ado-form-label">
1157
- <span class="material-symbols-outlined"
1158
- style="font-size: 14px; vertical-align: middle;">videocam</span>
1159
- Session Recording
1160
- </label>
1161
- <video id="bugVideoPreview" class="video-preview-player" controls>
1162
- Your browser does not support the video tag.
1163
- </video>
1164
- </div>
239
+ <div class="ado-field-group">
240
+ <label class="ado-form-label">Reproduction Steps & Description</label>
241
+ <div id="bugReproPreview" class="ado-repro-preview" style="min-height: 150px;"></div>
242
+ </div>
1165
243
 
1166
- <div class="ado-field-group" style="margin-bottom: 0;">
1167
- <div class="ado-repro-preview"
1168
- style="font-size: 0.75rem; background: #fffcf0; border-color: #f9f0c3; padding: 12px; line-height: 1.5;">
1169
- <b>Environment:</b> Playwright engine <b>URL:</b> <span
1170
- style="word-break: break-all; color: var(--primary);">
1171
- <%= data.pageKey %>
1172
- </span>
1173
- </div>
1174
- </div>
1175
- </div>
1176
- <div class="modal-footer">
1177
- <button type="button" class="btn-ado-secondary" data-bs-dismiss="modal">Cancel</button>
1178
- <button type="button" id="confirmBugBtn" class="btn-ado-primary">Create Bug</button>
244
+ <!-- Visual Evidence - Single Column Full Width -->
245
+ <div class="ado-field-group" id="screenshotThumbContainer" style="display: none;">
246
+ <label class="ado-form-label">
247
+ <span class="material-symbols-outlined" style="font-size: 14px; vertical-align: middle;">image</span>
248
+ Violation Screenshot
249
+ </label>
250
+ <img id="bugScreenshotPreview" class="screenshot-thumb" src="" alt="Screenshot">
251
+ </div>
252
+
253
+ <div class="ado-field-group" id="videoThumbContainer" style="display: none;">
254
+ <label class="ado-form-label">
255
+ <span class="material-symbols-outlined" style="font-size: 14px; vertical-align: middle;">videocam</span>
256
+ Session Recording
257
+ </label>
258
+ <video id="bugVideoPreview" class="video-preview-player" controls>
259
+ Your browser does not support the video tag.
260
+ </video>
261
+ </div>
262
+
263
+ <div class="ado-field-group" style="margin-bottom: 0;">
264
+ <div class="ado-repro-preview" style="font-size: 0.75rem; background: #fffcf0; border-color: #f9f0c3; padding: 12px; line-height: 1.5;">
265
+ <b>Environment:</b> Playwright engine • <b>URL:</b> <span id="bugUrlPreview" style="word-break: break-all; color: var(--primary);"></span>
1179
266
  </div>
1180
267
  </div>
1181
268
  </div>
1182
- </div>
1183
- <% } else { %>
1184
- <div class="success-verified-card">
1185
- <span class="material-symbols-outlined success-hero-icon">check_circle</span>
1186
- <h3 class="success-hero-title">Compliance Verified</h3>
1187
- <p class="success-hero-desc">Excellent! No accessibility violations were detected for this target resource.
1188
- </p>
269
+ <div class="modal-footer">
270
+ <button type="button" class="btn-ado-secondary btn btn-outline-secondary" data-bs-dismiss="modal" style="border-radius: 12px; font-weight: 700;">Cancel</button>
271
+ <button type="button" id="confirmBugBtn" class="btn-ado-primary btn btn-primary" style="border-radius: 12px; font-weight: 700;">Create Bug</button>
1189
272
  </div>
1190
- <% } %>
1191
-
1192
- <% if(data.video) { %>
1193
- <section class="video-card">
1194
- <div class="video-header">
1195
- <div class="video-icon-wrap">
1196
- <span class="material-symbols-outlined">videocam</span>
1197
- </div>
1198
- <h2 class="video-title">Audit Session Recording</h2>
1199
- </div>
1200
- <div class="ratio ratio-16x9 video-container">
1201
- <video controls>
1202
- <source src="<%= data.video %>" type="video/webm">
1203
- Your browser does not support the video tag.
1204
- </video>
1205
- </div>
1206
- </section>
1207
- <% } %>
1208
- </main>
273
+ </div>
274
+ </div>
275
+ </div>
1209
276
 
1210
277
  <footer>
1211
- <div class="powered-by">
1212
- Generated by <span class="badge-tool">Snap Ally</span>
1213
- </div>
1214
- <div>
1215
- <span style="opacity: 0.7;">&copy; <%= new Date().getFullYear() %> Snap Ally</span>
1216
- </div>
278
+ <div class="powered-by">Generated by <span class="badge-tool">Snap Ally</span></div>
279
+ <div><span style="opacity: 0.7;">&copy; <span id="year"></span> Snap Ally</span></div>
1217
280
  </footer>
1218
281
 
282
+ <!-- Scripts -->
283
+ <script>document.getElementById('year').textContent = new Date().getFullYear();</script>
1219
284
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
1220
- <script>
1221
- window.addEventListener('load', function () {
1222
- const loader = document.getElementById('loader-overlay');
1223
- loader.style.opacity = '0';
1224
- setTimeout(() => { loader.style.display = 'none'; }, 500);
1225
- });
1226
-
1227
- document.getElementById('tokenForm').addEventListener('submit', function (e) {
1228
- e.preventDefault();
1229
- const token = document.getElementById('tokenInput').value;
1230
- if (token) {
1231
- sessionStorage.setItem('userToken', token);
1232
- bootstrap.Modal.getInstance(document.getElementById('tokenModal')).hide();
1233
- }
1234
- });
1235
-
1236
- async function uploadAttachment(blob, name) {
1237
- const org = "<%= data.adoOrganization %>";
1238
- const proj = "<%= data.adoProject %>";
1239
- const pat = sessionStorage.getItem('userToken');
1240
- if (!org || !proj || !pat) return null;
1241
-
1242
- const url = `https://dev.azure.com/${org}/${proj}/_apis/wit/attachments?fileName=${name}&api-version=7.1`;
1243
-
1244
- const res = await fetch(url, {
1245
- method: 'POST',
1246
- headers: { 'Content-Type': 'application/octet-stream', 'Authorization': `Basic ${btoa(':' + pat)}` },
1247
- body: blob
1248
- });
1249
- return res.ok ? (await res.json()).url : null;
1250
- }
1251
-
1252
- function handleLogBugClick(btn, event) {
1253
- if (event) {
1254
- event.preventDefault();
1255
- event.stopPropagation();
1256
- }
1257
- const d = btn.dataset;
1258
- createAzureDevOpsBug(d.title, d.axeId, d.help, d.severity, d.steps, d.screenshot, d.wcag);
1259
- }
1260
-
1261
- function createAzureDevOpsBug(title, axeId, help, severity, stepsJson, screenshotBase64, wcag) {
1262
- const pat = sessionStorage.getItem('userToken');
1263
- if (!pat) {
1264
- const tokenModal = bootstrap.Modal.getOrCreateInstance(document.getElementById('tokenModal'));
1265
- tokenModal.show();
1266
- return;
1267
- }
1268
-
1269
- // Populate Modal Fields
1270
- document.getElementById('bugTitleInput').value = `[A11y] ${title}`;
1271
- document.getElementById('bugSeverityInput').value = severity;
1272
-
1273
- const steps = JSON.parse(stepsJson);
1274
- const stepsHtml = steps.length
1275
- ? `<ol>${steps.map(s => `<li>${s}</li>`).join('')}</ol>`
1276
- : `<i>No interaction steps. Issue identified via static scan.</i>`;
1277
-
1278
- document.getElementById('bugReproPreview').innerHTML = `
1279
- <div style="margin-bottom: 4px;"><b>Rule:</b> ${axeId} (${wcag})</div>
1280
- <div style="margin-bottom: 8px;"><b>Recommendation:</b> ${help}</div>
1281
- <div style="border-top: 1px solid #e2e8f0; margin: 8px 0; padding-top: 8px;"><b>Steps:</b></div>
1282
- ${stepsHtml}
1283
- `;
1284
-
1285
- // Visual Evidence
1286
- const screenshotPreview = document.getElementById('bugScreenshotPreview');
1287
- if (screenshotBase64) {
1288
- screenshotPreview.src = `data:image/png;base64,${screenshotBase64}`;
1289
- document.getElementById('screenshotThumbContainer').style.display = 'block';
1290
- } else {
1291
- document.getElementById('screenshotThumbContainer').style.display = 'none';
1292
- }
1293
-
1294
- const videoRawPath = "<%= data.video %>";
1295
- if (videoRawPath) {
1296
- // Fix: Video is in the same folder as the report, no need for '../'
1297
- const videoFinalPath = videoRawPath.startsWith('http') ? videoRawPath : videoRawPath;
1298
- const videoPreview = document.getElementById('bugVideoPreview');
1299
- videoPreview.src = videoFinalPath;
1300
- document.getElementById('videoThumbContainer').style.display = 'block';
1301
- } else {
1302
- document.getElementById('videoThumbContainer').style.display = 'none';
1303
- }
1304
-
1305
- // Store data for submission
1306
- window.currentBugData = { axeId, wcag, help, stepsHtml, screenshotBase64 };
1307
-
1308
- const modalElement = document.getElementById('bugPreviewModal');
1309
- const modal = bootstrap.Modal.getOrCreateInstance(modalElement);
1310
- modal.show();
1311
- }
1312
-
1313
- // Force focus on title input when modal opens to ensure editability
1314
- const bugModalEl = document.getElementById('bugPreviewModal');
1315
- if (bugModalEl) {
1316
- bugModalEl.addEventListener('shown.bs.modal', function () {
1317
- const input = document.getElementById('bugTitleInput');
1318
- if (input) {
1319
- input.focus();
1320
- // Select text for quick editing
1321
- input.select();
1322
- // Ensure not inert/disabled
1323
- input.removeAttribute('readonly');
1324
- input.removeAttribute('disabled');
1325
- }
1326
- });
1327
- }
1328
-
1329
- // Single Global Event Listener for Confirmation
1330
- document.getElementById('confirmBugBtn').addEventListener('click', async () => {
1331
- const btn = document.getElementById('confirmBugBtn');
1332
- const modal = bootstrap.Modal.getInstance(document.getElementById('bugPreviewModal'));
1333
-
1334
- btn.disabled = true;
1335
- btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Creating...';
1336
-
1337
- try {
1338
- await submitFinalBug();
1339
- modal.hide();
1340
- } catch (err) {
1341
- console.error(err);
1342
- } finally {
1343
- btn.disabled = false;
1344
- btn.innerHTML = 'Create Bug';
1345
- }
1346
- });
1347
-
1348
- async function submitFinalBug() {
1349
- const org = "<%= data.adoOrganization %>";
1350
- const proj = "<%= data.adoProject %>";
1351
- const pat = sessionStorage.getItem('userToken');
1352
- const { axeId, wcag, help, stepsHtml, screenshotBase64 } = window.currentBugData;
1353
-
1354
- const title = document.getElementById('bugTitleInput').value;
1355
- const severity = document.getElementById('bugSeverityInput').value;
1356
-
1357
- let screenshotUrl = null;
1358
- if (screenshotBase64) {
1359
- const screenshotBlob = await fetch(`data:image/png;base64,${screenshotBase64}`).then(res => res.blob());
1360
- screenshotUrl = await uploadAttachment(screenshotBlob, 'a11y-issue.png');
1361
- }
1362
-
1363
- const videoRawPath = "<%= data.video %>";
1364
- let videoUrl = null;
1365
- if (videoRawPath) {
1366
- try {
1367
- // Fix: Video is in the same folder as the report
1368
- const videoFinalPath = videoRawPath.startsWith('http') ? videoRawPath : videoRawPath;
1369
- const videoBlob = await fetch(videoFinalPath).then(res => res.blob());
1370
- videoUrl = await uploadAttachment(videoBlob, 'session-recording.webm');
1371
- } catch (e) {
1372
- console.error("Failed to video upload", e);
1373
- }
1374
- }
1375
-
1376
- const combinedReproHtml = `
1377
- <div style="margin-bottom: 12px;"><b>Rule:</b> ${axeId} (${wcag})</div>
1378
- <div style="margin-bottom: 12px;"><b>Recommendation:</b> ${help}</div>
1379
- <hr>
1380
- <div style="margin-bottom: 8px;"><b>Steps to Reproduce:</b></div>
1381
- ${stepsHtml}
1382
- `;
1383
-
1384
- const payload = [
1385
- { op: "add", path: "/fields/System.Title", value: title },
1386
- { op: "add", path: "/fields/Microsoft.VSTS.TCM.ReproSteps", value: combinedReproHtml },
1387
- { op: "add", path: "/fields/System.Description", value: `Found at: <a href="${window.location.href}">${window.location.href}</a>` },
1388
- { op: "add", path: "/fields/Microsoft.VSTS.Common.Priority", value: severity === 'critical' ? 1 : (severity === 'serious' ? 2 : 3) },
1389
- { op: "add", path: "/fields/System.Tags", value: "A11y;SnapAlly;UI-Test" }
1390
- ];
1391
-
1392
- if (screenshotUrl) {
1393
- payload.push({ op: "add", path: "/relations/-", value: { rel: "AttachedFile", url: screenshotUrl, attributes: { comment: "Accessibility Violation Screenshot" } } });
1394
- }
1395
-
1396
- if (videoUrl) {
1397
- payload.push({ op: "add", path: "/relations/-", value: { rel: "AttachedFile", url: videoUrl, attributes: { comment: "Audit Session Recording" } } });
1398
- }
1399
285
 
1400
- const res = await fetch(`https://dev.azure.com/${org}/${proj}/_apis/wit/workitems/$Bug?api-version=7.1`, {
1401
- method: 'POST',
1402
- headers: { 'Content-Type': 'application/json-patch+json', 'Authorization': `Basic ${btoa(':' + pat)}` },
1403
- body: JSON.stringify(payload)
1404
- });
286
+ <!-- Injected JSON Payload -->
287
+ <script src="data.js"></script>
1405
288
 
1406
- if (res.ok) alert('Bug successfully filed in Azure DevOps!');
1407
- else alert('Failed to create bug. Check token and organization settings.');
1408
- }
1409
- </script>
289
+ <!-- Report Logic -->
290
+ <script src="report-app.js"></script>
1410
291
  </body>
1411
292
 
1412
293
  </html>