@newjersey/njwds 2.9.0 → 2.9.1

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.
Files changed (82) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +10 -0
  3. package/dist/css/button.css +1 -1
  4. package/dist/css/button.css.map +1 -1
  5. package/dist/css/icon.css +1 -1
  6. package/dist/css/icon.css.map +1 -1
  7. package/dist/css/styles.css +2 -2
  8. package/dist/css/styles.css.map +1 -1
  9. package/package.json +18 -5
  10. package/src/components/02-buttons/buttons.njk +4 -4
  11. package/src/sass/_uswds-theme-custom-styles.scss +12 -326
  12. package/src/sass/_uswds-theme.scss +2 -2
  13. package/src/sass/button.scss +3 -68
  14. package/src/sass/icon.scss +2 -69
  15. package/src/sass/styles.scss +1 -65
  16. package/src/sass/theme/nj-banner/index.scss +1 -0
  17. package/src/sass/theme/nj-banner/nj-banner.scss +57 -0
  18. package/src/sass/theme/nj-button/index.scss +6 -0
  19. package/src/sass/theme/nj-button/nj-button-icon.scss +12 -0
  20. package/src/sass/theme/nj-button/nj-button-outline.scss +55 -0
  21. package/src/sass/theme/nj-button/nj-button-primary.scss +22 -0
  22. package/src/sass/theme/nj-button/nj-button-secondary.scss +15 -0
  23. package/src/sass/theme/nj-button/nj-button-unstyled.scss +48 -0
  24. package/src/sass/theme/nj-button/nj-button.scss +12 -0
  25. package/src/sass/theme/nj-error-message/index.scss +1 -0
  26. package/src/sass/theme/nj-error-message/nj-error-message.scss +19 -0
  27. package/src/sass/theme/nj-forms/index.scss +1 -0
  28. package/src/sass/theme/nj-forms/nj-forms.scss +39 -0
  29. package/src/sass/theme/nj-header/index.scss +1 -0
  30. package/src/sass/theme/nj-header/nj-header.scss +8 -0
  31. package/src/sass/theme/nj-hero/index.scss +1 -0
  32. package/src/sass/theme/nj-hero/nj-hero.scss +6 -0
  33. package/src/sass/theme/nj-icon/index.scss +1 -0
  34. package/src/sass/theme/nj-icon/nj-icon.scss +21 -0
  35. package/src/sass/theme/nj-intro/index.scss +1 -0
  36. package/src/sass/theme/nj-intro/nj-intro.scss +5 -0
  37. package/src/sass/theme/nj-nav/index.scss +1 -0
  38. package/src/sass/theme/nj-nav/nj-nav.scss +7 -0
  39. package/src/sass/theme/nj-prose/index.scss +1 -0
  40. package/src/sass/theme/nj-prose/nj-prose.scss +14 -0
  41. package/src/sass/theme/nj-section/index.scss +1 -0
  42. package/src/sass/theme/nj-section/nj-section.scss +6 -0
  43. package/src/tests/__screenshots__/banner/banner.visual.spec.ts/banner-narrow.png +0 -0
  44. package/src/tests/__screenshots__/banner/banner.visual.spec.ts/banner-wide.png +0 -0
  45. package/src/tests/__screenshots__/button/visual/button-danger.visual.spec.ts/button-danger-narrow.png +0 -0
  46. package/src/tests/__screenshots__/button/visual/button-danger.visual.spec.ts/button-danger-wide.png +0 -0
  47. package/src/tests/__screenshots__/button/visual/button-dark.visual.spec.ts/button-dark-narrow.png +0 -0
  48. package/src/tests/__screenshots__/button/visual/button-dark.visual.spec.ts/button-dark-wide.png +0 -0
  49. package/src/tests/__screenshots__/button/visual/button-secondary-danger.visual.spec.ts/button-secondary-danger-narrow.png +0 -0
  50. package/src/tests/__screenshots__/button/visual/button-secondary-danger.visual.spec.ts/button-secondary-danger-wide.png +0 -0
  51. package/src/tests/__screenshots__/button/visual/button-secondary-dark.visual.spec.ts/button-secondary-dark-narrow.png +0 -0
  52. package/src/tests/__screenshots__/button/visual/button-secondary-dark.visual.spec.ts/button-secondary-dark-wide.png +0 -0
  53. package/src/tests/__screenshots__/button/visual/button-secondary.visual.spec.ts/button-secondary-narrow.png +0 -0
  54. package/src/tests/__screenshots__/button/visual/button-secondary.visual.spec.ts/button-secondary-wide.png +0 -0
  55. package/src/tests/__screenshots__/button/visual/button-tertiary-danger.visual.spec.ts/button-tertiary-danger-narrow.png +0 -0
  56. package/src/tests/__screenshots__/button/visual/button-tertiary-danger.visual.spec.ts/button-tertiary-danger-wide.png +0 -0
  57. package/src/tests/__screenshots__/button/visual/button-tertiary-dark.visual.spec.ts/button-tertiary-dark-narrow.png +0 -0
  58. package/src/tests/__screenshots__/button/visual/button-tertiary-dark.visual.spec.ts/button-tertiary-dark-wide.png +0 -0
  59. package/src/tests/__screenshots__/button/visual/button-tertiary.visual.spec.ts/button-tertiary-narrow.png +0 -0
  60. package/src/tests/__screenshots__/button/visual/button-tertiary.visual.spec.ts/button-tertiary-wide.png +0 -0
  61. package/src/tests/__screenshots__/button/visual/button.visual.spec.ts/button-narrow.png +0 -0
  62. package/src/tests/__screenshots__/button/visual/button.visual.spec.ts/button-wide.png +0 -0
  63. package/src/tests/banner/banner.accessibility.spec.ts +28 -0
  64. package/src/tests/banner/banner.visual.spec.ts +35 -0
  65. package/src/tests/button/accessibility/button-danger.accessibility.spec.ts +30 -0
  66. package/src/tests/button/accessibility/button-dark.accessibility.spec.ts +30 -0
  67. package/src/tests/button/accessibility/button-secondary-danger.accessibility.spec.ts +30 -0
  68. package/src/tests/button/accessibility/button-secondary-dark.accessibility.spec.ts +30 -0
  69. package/src/tests/button/accessibility/button-secondary.accessibility.spec.ts +30 -0
  70. package/src/tests/button/accessibility/button-tertiary-danger.accessibility.spec.ts +30 -0
  71. package/src/tests/button/accessibility/button-tertiary-dark.accessibility.spec.ts +30 -0
  72. package/src/tests/button/accessibility/button-tertiary.accessibility.spec.ts +30 -0
  73. package/src/tests/button/accessibility/button.accessibility.spec.ts +30 -0
  74. package/src/tests/button/visual/button-danger.visual.spec.ts +35 -0
  75. package/src/tests/button/visual/button-dark.visual.spec.ts +35 -0
  76. package/src/tests/button/visual/button-secondary-danger.visual.spec.ts +35 -0
  77. package/src/tests/button/visual/button-secondary-dark.visual.spec.ts +35 -0
  78. package/src/tests/button/visual/button-secondary.visual.spec.ts +35 -0
  79. package/src/tests/button/visual/button-tertiary-danger.visual.spec.ts +35 -0
  80. package/src/tests/button/visual/button-tertiary-dark.visual.spec.ts +35 -0
  81. package/src/tests/button/visual/button-tertiary.visual.spec.ts +35 -0
  82. package/src/tests/button/visual/button.visual.spec.ts +35 -0
@@ -0,0 +1,57 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .nj-banner {
4
+ @include typeset($theme-banner-font-family);
5
+ @include border-box-sizing;
6
+ background-color: color($theme-banner-background-color);
7
+ font-size: font-size($theme-banner-font-family, "2xs");
8
+ line-height: line-height($theme-banner-font-family, 1);
9
+ min-height: units($size-touch-target);
10
+ position: relative;
11
+
12
+ &__header {
13
+ @include u-padding-y(1);
14
+ @include set-text-from-bg($theme-banner-background-color);
15
+ // display: flex;
16
+
17
+ a {
18
+ @include set-text-from-bg($theme-banner-background-color);
19
+ text-decoration: none;
20
+
21
+ &:hover,
22
+ &:focus {
23
+ text-decoration: underline;
24
+ }
25
+ } // a
26
+
27
+ li {
28
+ display: none;
29
+
30
+ // Set min-width so the names are hidden before the stateOfNjText wraps to
31
+ // 3 lines
32
+ @media (min-width: 46rem) {
33
+ display: inline;
34
+ }
35
+ @include u-margin-right(1);
36
+ @include u-padding-right(1);
37
+ @include u-border-right(1px);
38
+
39
+ &:last-child {
40
+ display: inline;
41
+ @include u-margin-right(0);
42
+ @include u-padding-right(0);
43
+ @include u-border-right(0);
44
+ }
45
+ } // li
46
+
47
+ &-seal {
48
+ padding-right: units(1);
49
+ } // .nj-banner__header-seal
50
+ } // .nj-banner__header
51
+
52
+ &__inner {
53
+ @include grid-row;
54
+ @include u-flex("align-center");
55
+ padding-right: units(0);
56
+ } // .nj-banner__inner
57
+ }
@@ -0,0 +1,6 @@
1
+ @forward "nj-button";
2
+ @forward "nj-button-icon";
3
+ @forward "nj-button-outline";
4
+ @forward "nj-button-primary";
5
+ @forward "nj-button-secondary";
6
+ @forward "nj-button-unstyled";
@@ -0,0 +1,12 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .usa-button {
4
+ &.nj-button--icon {
5
+ padding-top: units(1.5);
6
+ padding-bottom: 0.875rem;
7
+
8
+ svg {
9
+ vertical-align: sub;
10
+ }
11
+ } // .nj-button--icon
12
+ } // .usa-button
@@ -0,0 +1,55 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Outline buttons (light)
4
+ .usa-button--outline {
5
+ color: color($theme-color-primary-dark);
6
+ box-shadow: inset 0 0 0 2px color($theme-color-primary-dark);
7
+
8
+ &.usa-button--hover,
9
+ &:hover {
10
+ color: color($theme-color-primary-darker);
11
+ box-shadow: inset 0 0 0 2px color($theme-color-primary-darker);
12
+ }
13
+
14
+ &.usa-button--active,
15
+ &:active {
16
+ color: color($theme-color-primary-darkest);
17
+ box-shadow: inset 0 0 0 2px color($theme-color-primary-darkest);
18
+ }
19
+
20
+ // Secondary Buttons (dark)
21
+ &.usa-button--inverse:not(:disabled) {
22
+ color: color($theme-link-reverse-active-color);
23
+ box-shadow: inset 0 0 0 2px color($theme-link-reverse-active-color);
24
+ }
25
+
26
+ &.usa-button--inverse.usa-button--hover:not(:disabled),
27
+ &.usa-button--inverse:hover:not(:disabled) {
28
+ color: color($theme-color-base-lighter);
29
+ box-shadow: inset 0 0 0 2px color($theme-color-base-lighter);
30
+ }
31
+
32
+ &.usa-button--inverse.usa-button--active:not(:disabled),
33
+ &.usa-button--inverse:active:not(:disabled) {
34
+ color: color($theme-color-base-light);
35
+ box-shadow: inset 0 0 0 2px color($theme-color-base-light);
36
+ }
37
+
38
+ // Secondary Buttons (danger)
39
+ &.nj-button--outline-danger:not(:disabled) {
40
+ color: color($theme-color-error);
41
+ box-shadow: inset 0 0 0 2px color($theme-color-error);
42
+ }
43
+ } // .usa-button--outline
44
+
45
+ .usa-button--hover.nj-button--outline-danger:not(:disabled),
46
+ .usa-button--outline.nj-button--outline-danger:hover:not(:disabled) {
47
+ color: color($theme-color-error-dark);
48
+ box-shadow: inset 0 0 0 2px color($theme-color-error-dark);
49
+ }
50
+
51
+ .usa-button--active.nj-button--outline-danger:not(:disabled),
52
+ .usa-button--outline.nj-button--outline-danger:active:not(:disabled) {
53
+ color: color($theme-color-error-darker);
54
+ box-shadow: inset 0 0 0 2px color($theme-color-error-darker);
55
+ }
@@ -0,0 +1,22 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Buttons
4
+ .usa-button {
5
+ // Primary Buttons (dark)
6
+ &.nj-button--primary-dark:not(:disabled) {
7
+ color: color($theme-text-color);
8
+ background-color: color($theme-color-base-lightest);
9
+ }
10
+
11
+ &.nj-button--primary-dark.usa-button--hover:not(:disabled),
12
+ &.nj-button--primary-dark:hover:not(:disabled) {
13
+ color: color($theme-text-color);
14
+ background-color: color($theme-color-base-lighter);
15
+ }
16
+
17
+ &.nj-button--primary-dark.usa-button--active:not(:disabled),
18
+ &.nj-button--primary-dark:active:not(:disabled) {
19
+ color: color($theme-text-color);
20
+ background-color: color($theme-color-base-light);
21
+ }
22
+ } // .usa-button
@@ -0,0 +1,15 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .usa-button--secondary {
4
+ background-color: color($theme-color-error);
5
+
6
+ &.usa-button--hover,
7
+ &:hover {
8
+ background-color: color($theme-color-error-dark);
9
+ }
10
+
11
+ &.usa-button--active,
12
+ &:active {
13
+ background-color: color($theme-color-error-darker);
14
+ }
15
+ } // .usa-button--secondary
@@ -0,0 +1,48 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Unstyled buttons (light)
4
+ .usa-button--unstyled {
5
+ border-radius: units(0.5);
6
+ padding: units(0.5);
7
+ font-weight: $theme-font-weight-bold;
8
+ height: auto;
9
+ }
10
+ .usa-button--unstyled.usa-button--hover,
11
+ .usa-button--unstyled:hover {
12
+ color: color($theme-color-primary-darker);
13
+ }
14
+
15
+ .usa-button--unstyled.usa-button--active,
16
+ .usa-button--unstyled:active {
17
+ color: color($theme-color-primary-darkest);
18
+ }
19
+
20
+ // Unstyled Buttons (dark)
21
+ .usa-button--unstyled.nj-button--unstyled-dark {
22
+ color: color($theme-text-reverse-color);
23
+ }
24
+
25
+ .usa-button--unstyled.nj-button--unstyled-dark.usa-button--hover,
26
+ .usa-button--unstyled.nj-button--unstyled-dark:hover {
27
+ color: color($theme-color-base-lighter);
28
+ }
29
+
30
+ .usa-button--unstyled.nj-button--unstyled-dark.usa-button--active,
31
+ .usa-button--unstyled.nj-button--unstyled-dark:active {
32
+ color: color($theme-color-base-light);
33
+ }
34
+
35
+ // Unstyled Buttons (danger)
36
+ .usa-button--unstyled.nj-button--unstyled-danger:not(:disabled) {
37
+ color: color($theme-color-error);
38
+ }
39
+
40
+ .usa-button--hover.nj-button--unstyled-danger:not(:disabled),
41
+ .usa-button--unstyled.nj-button--unstyled-danger:hover:not(:disabled) {
42
+ color: color($theme-color-error-dark);
43
+ }
44
+
45
+ .usa-button--active.nj-button--unstyled-danger:not(:disabled),
46
+ .usa-button--unstyled.nj-button--unstyled-danger:active:not(:disabled) {
47
+ color: color($theme-color-error-darker);
48
+ }
@@ -0,0 +1,12 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Buttons
4
+ .usa-button {
5
+ height: units(6);
6
+ padding: units(2) units(2.5);
7
+
8
+ &:focus,
9
+ &.usa-focus {
10
+ outline-color: color($theme-color-primary-vivid);
11
+ }
12
+ } // .usa-button
@@ -0,0 +1 @@
1
+ @forward "nj-error-message";
@@ -0,0 +1,19 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .nj-error-message-container {
4
+ margin-top: units(1.5);
5
+ display: flex;
6
+ align-items: flex-start;
7
+ color: color($theme-color-error-dark);
8
+ }
9
+
10
+ .nj-error-message-container {
11
+ > svg {
12
+ flex-shrink: 0;
13
+ margin-right: units(1);
14
+ }
15
+ }
16
+
17
+ .usa-error-message {
18
+ padding: units(0);
19
+ }
@@ -0,0 +1 @@
1
+ @forward "nj-forms";
@@ -0,0 +1,39 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Radio Buttons
4
+ .usa-radio {
5
+ background: none;
6
+ }
7
+
8
+ .usa-radio__label::before {
9
+ box-shadow: 0 0 0 2px color($theme-color-base);
10
+ }
11
+
12
+ // Radio & Checkbox tile variants
13
+ .usa-radio__input--tile:checked + [class*="__label"],
14
+ .usa-checkbox__input--tile:checked + [class*="__label"] {
15
+ background-color: color($theme-color-primary-lighter);
16
+ }
17
+
18
+ .usa-radio__input--tile + [class*="__label"],
19
+ .usa-checkbox__input--tile + [class*="__label"] {
20
+ border-color: color($theme-color-base-light);
21
+ }
22
+ .usa-radio__input.usa-radio__input--tile:checked + [class*="__label"]::before,
23
+ .usa-checkbox__input.usa-checkbox__input--tile:checked + [class*="__label"]::before {
24
+ box-shadow:
25
+ 0 0 0 2px #005ea2,
26
+ inset 0 0 0 2px #cfe8ff;
27
+ }
28
+
29
+ .usa-form-group--error {
30
+ padding-left: units(2.5);
31
+ }
32
+
33
+ .nj-checkbox--error > .usa-checkbox__label::before {
34
+ box-shadow: 0 0 0 2px color($theme-color-error-dark);
35
+ }
36
+
37
+ .nj-radio--error > .usa-radio__label::before {
38
+ box-shadow: 0 0 0 2px color($theme-color-error-dark);
39
+ }
@@ -0,0 +1 @@
1
+ @forward "nj-header";
@@ -0,0 +1,8 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Header - prevent wrapping of logo text
4
+ .usa-header--extended {
5
+ .usa-logo {
6
+ max-width: 50%;
7
+ }
8
+ } // .usa-header--extended
@@ -0,0 +1 @@
1
+ @forward "nj-hero";
@@ -0,0 +1,6 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // hero
4
+ .usa-hero__callout {
5
+ background-color: color("secondary-darker");
6
+ }
@@ -0,0 +1 @@
1
+ @forward "nj-icon";
@@ -0,0 +1,21 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .usa-icon {
4
+ &:not(
5
+ .usa-icon--size-3,
6
+ .usa-icon--size-4,
7
+ .usa-icon--size-5,
8
+ .usa-icon--size-6,
9
+ .usa-icon--size-7,
10
+ .usa-icon--size-8,
11
+ .usa-icon--size-9
12
+ ) {
13
+ height: 20px;
14
+ width: 20px;
15
+ }
16
+
17
+ &.nj-icon--size-scale {
18
+ height: 1em;
19
+ width: 1em;
20
+ }
21
+ } // .usa-icon
@@ -0,0 +1 @@
1
+ @forward "nj-intro";
@@ -0,0 +1,5 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .usa-intro {
4
+ letter-spacing: -0.4px;
5
+ }
@@ -0,0 +1 @@
1
+ @forward "nj-nav";
@@ -0,0 +1,7 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .usa-nav__secondary-links {
4
+ a {
5
+ color: color("base-darker");
6
+ }
7
+ } // .usa-nav__secondary-links
@@ -0,0 +1 @@
1
+ @forward "nj-prose";
@@ -0,0 +1,14 @@
1
+ @use "uswds-core" as *;
2
+
3
+ .usa-prose {
4
+ // Tighten letter spacing for h1 and intro text
5
+ > h1 {
6
+ letter-spacing: -0.4px;
7
+ }
8
+
9
+ // Wrap long links that would break page container
10
+ a {
11
+ overflow-wrap: break-word;
12
+ word-wrap: break-word;
13
+ }
14
+ } // .usa-prose
@@ -0,0 +1 @@
1
+ @forward "nj-section";
@@ -0,0 +1,6 @@
1
+ @use "uswds-core" as *;
2
+
3
+ // Section
4
+ .usa-section--dark {
5
+ background-color: color("secondary-darker");
6
+ }
@@ -0,0 +1,28 @@
1
+ import { test, expect } from "@playwright/test";
2
+ import AxeBuilder from "@axe-core/playwright";
3
+
4
+ // The base URL for your locally served components.
5
+ const BASE_URL = "http://localhost:3000";
6
+
7
+ // The specific page URL for the Banner component preview.
8
+ const PAGE_URL = `${BASE_URL}/components/preview/banner`;
9
+
10
+ // Group all accessibility tests for the Banner component under a single describe block.
11
+ test.describe("Banner – accessibility", () => {
12
+ // Primary axe-core scan for this component.
13
+ test("has no detectable a11y violations", async ({ page }) => {
14
+ // Open the page at the URL defined above.
15
+ // Using `networkidle` ensures that all network requests (CSS, fonts, JS) have
16
+ // finished before running the accessibility scan.
17
+ await page.goto(PAGE_URL);
18
+ await page.waitForLoadState("networkidle");
19
+
20
+ // .include(".nj-banner") ensures the scan is scoped to the Banner itself.
21
+ const accessibilityScanResults = await new AxeBuilder({ page }).include(".nj-banner").analyze();
22
+
23
+ // If axe finds any violations, this test will fail, and Playwright will
24
+ // report detailed information about each issue (impact, rule, affected nodes).
25
+ // The empty array assertion ensures only a clean, violation-free component passes.
26
+ expect(accessibilityScanResults.violations).toEqual([]);
27
+ });
28
+ });
@@ -0,0 +1,35 @@
1
+ import { test, expect } from "@playwright/test";
2
+
3
+ const BASE_URL = "http://localhost:3000";
4
+ const PAGE_URL = `${BASE_URL}/components/preview/banner`;
5
+
6
+ const viewports = [
7
+ { name: "narrow", width: 393, height: 800 },
8
+ { name: "wide", width: 1280, height: 800 },
9
+ ];
10
+
11
+ test.describe("Banner - visual regression", () => {
12
+ for (const viewport of viewports) {
13
+ test(`renders correctly (${viewport.name})`, async ({ page }) => {
14
+ await page.setViewportSize({
15
+ width: viewport.width,
16
+ height: viewport.height,
17
+ });
18
+
19
+ // Stabilize rendering:
20
+ await page.emulateMedia({ reducedMotion: "reduce" });
21
+
22
+ // Open the page at the URL defined above.
23
+ // Using `networkidle` ensures that all network requests (CSS, fonts, JS) have
24
+ // finished before running the test.
25
+ await page.goto(PAGE_URL);
26
+ await page.waitForLoadState("networkidle");
27
+
28
+ // Full-page screenshot
29
+ await expect(page).toHaveScreenshot(`banner-${viewport.name}.png`, {
30
+ fullPage: true,
31
+ maxDiffPixelRatio: 0.01, // allow a 1px difference
32
+ });
33
+ });
34
+ }
35
+ });
@@ -0,0 +1,30 @@
1
+ import { test, expect } from "@playwright/test";
2
+ import AxeBuilder from "@axe-core/playwright";
3
+
4
+ // The base URL for your locally served components.
5
+ const BASE_URL = "http://localhost:3000";
6
+
7
+ // The specific page URL for the Button component preview.
8
+ const PAGE_URL = `${BASE_URL}/components/preview/buttons--primary-(danger)`;
9
+
10
+ // Group all accessibility tests for the Button component under a single describe block.
11
+ test.describe("Button, danger - accessibility", () => {
12
+ // Primary axe-core scan for this component.
13
+ test("has no detectable a11y violations", async ({ page }) => {
14
+ // Open the page at the URL defined above.
15
+ // Using `networkidle` ensures that all network requests (CSS, fonts, JS) have
16
+ // finished before running the accessibility scan.
17
+ await page.goto(PAGE_URL);
18
+ await page.waitForLoadState("networkidle");
19
+
20
+ // .include(".usa-button") ensures the scan is scoped to the Button itself.
21
+ const accessibilityScanResults = await new AxeBuilder({ page })
22
+ .include(".usa-button")
23
+ .analyze();
24
+
25
+ // If axe finds any violations, this test will fail, and Playwright will
26
+ // report detailed information about each issue (impact, rule, affected nodes).
27
+ // The empty array assertion ensures only a clean, violation-free component passes.
28
+ expect(accessibilityScanResults.violations).toEqual([]);
29
+ });
30
+ });
@@ -0,0 +1,30 @@
1
+ import { test, expect } from "@playwright/test";
2
+ import AxeBuilder from "@axe-core/playwright";
3
+
4
+ // The base URL for your locally served components.
5
+ const BASE_URL = "http://localhost:3000";
6
+
7
+ // The specific page URL for the Button component preview.
8
+ const PAGE_URL = `${BASE_URL}/components/preview/buttons--primary-(dark)`;
9
+
10
+ // Group all accessibility tests for the Button component under a single describe block.
11
+ test.describe("Button, dark - accessibility", () => {
12
+ // Primary axe-core scan for this component.
13
+ test("has no detectable a11y violations", async ({ page }) => {
14
+ // Open the page at the URL defined above.
15
+ // Using `networkidle` ensures that all network requests (CSS, fonts, JS) have
16
+ // finished before running the accessibility scan.
17
+ await page.goto(PAGE_URL);
18
+ await page.waitForLoadState("networkidle");
19
+
20
+ // .include(".usa-button") ensures the scan is scoped to the Button itself.
21
+ const accessibilityScanResults = await new AxeBuilder({ page })
22
+ .include(".usa-button")
23
+ .analyze();
24
+
25
+ // If axe finds any violations, this test will fail, and Playwright will
26
+ // report detailed information about each issue (impact, rule, affected nodes).
27
+ // The empty array assertion ensures only a clean, violation-free component passes.
28
+ expect(accessibilityScanResults.violations).toEqual([]);
29
+ });
30
+ });
@@ -0,0 +1,30 @@
1
+ import { test, expect } from "@playwright/test";
2
+ import AxeBuilder from "@axe-core/playwright";
3
+
4
+ // The base URL for your locally served components.
5
+ const BASE_URL = "http://localhost:3000";
6
+
7
+ // The specific page URL for the Button component preview.
8
+ const PAGE_URL = `${BASE_URL}/components/preview/buttons--secondary-(danger)`;
9
+
10
+ // Group all accessibility tests for the Button component under a single describe block.
11
+ test.describe("Button, secondary danger - accessibility", () => {
12
+ // Primary axe-core scan for this component.
13
+ test("has no detectable a11y violations", async ({ page }) => {
14
+ // Open the page at the URL defined above.
15
+ // Using `networkidle` ensures that all network requests (CSS, fonts, JS) have
16
+ // finished before running the accessibility scan.
17
+ await page.goto(PAGE_URL);
18
+ await page.waitForLoadState("networkidle");
19
+
20
+ // .include(".usa-button") ensures the scan is scoped to the Button itself.
21
+ const accessibilityScanResults = await new AxeBuilder({ page })
22
+ .include(".usa-button")
23
+ .analyze();
24
+
25
+ // If axe finds any violations, this test will fail, and Playwright will
26
+ // report detailed information about each issue (impact, rule, affected nodes).
27
+ // The empty array assertion ensures only a clean, violation-free component passes.
28
+ expect(accessibilityScanResults.violations).toEqual([]);
29
+ });
30
+ });
@@ -0,0 +1,30 @@
1
+ import { test, expect } from "@playwright/test";
2
+ import AxeBuilder from "@axe-core/playwright";
3
+
4
+ // The base URL for your locally served components.
5
+ const BASE_URL = "http://localhost:3000";
6
+
7
+ // The specific page URL for the Button component preview.
8
+ const PAGE_URL = `${BASE_URL}/components/preview/buttons--secondary-(dark)`;
9
+
10
+ // Group all accessibility tests for the Button component under a single describe block.
11
+ test.describe("Button, secondary dark - accessibility", () => {
12
+ // Primary axe-core scan for this component.
13
+ test("has no detectable a11y violations", async ({ page }) => {
14
+ // Open the page at the URL defined above.
15
+ // Using `networkidle` ensures that all network requests (CSS, fonts, JS) have
16
+ // finished before running the accessibility scan.
17
+ await page.goto(PAGE_URL);
18
+ await page.waitForLoadState("networkidle");
19
+
20
+ // .include(".usa-button") ensures the scan is scoped to the Button itself.
21
+ const accessibilityScanResults = await new AxeBuilder({ page })
22
+ .include(".usa-button")
23
+ .analyze();
24
+
25
+ // If axe finds any violations, this test will fail, and Playwright will
26
+ // report detailed information about each issue (impact, rule, affected nodes).
27
+ // The empty array assertion ensures only a clean, violation-free component passes.
28
+ expect(accessibilityScanResults.violations).toEqual([]);
29
+ });
30
+ });