@astryxdesign/core 0.5.2-canary.1e5db77 → 0.5.2-canary.212c4fd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/astryx.css +2 -0
- package/package.json +2 -2
- package/src/Button/Button.doc.mjs +56 -0
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +56 -0
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Carousel.d.ts","sourceRoot":"","sources":["../../src/Carousel/Carousel.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,KAAK,SAAS,EAOf,MAAM,OAAO,CAAC;AAef,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAKhD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,UAAU,IAAI,IAAI,CAAC;IACnB;;;OAGG;IACH,UAAU,IAAI,IAAI,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC;IACzB;;;OAGG;IACH,aAAa,IAAI,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC9D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACtC,kEAAkE;IAClE,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"Carousel.d.ts","sourceRoot":"","sources":["../../src/Carousel/Carousel.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,KAAK,SAAS,EAOf,MAAM,OAAO,CAAC;AAef,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAKhD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,UAAU,IAAI,IAAI,CAAC;IACnB;;;OAGG;IACH,UAAU,IAAI,IAAI,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC;IACzB;;;OAGG;IACH,aAAa,IAAI,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC9D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACtC,kEAAkE;IAClE,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA+JD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,SAAS,EACT,QAAQ,EACR,GAAO,EACP,UAAiB,EACjB,WAAkB,EAClB,OAAe,EACf,OAAe,EACf,OAAO,EACP,YAAY,EAAE,kBAAkB,EAChC,MAAM,EACN,SAAS,EACT,KAAK,EACL,aAAa,EAAE,MAAM,EACrB,GAAG,SAAS,EACb,EAAE,aAAa,+BAgYf;yBAhZe,QAAQ"}
|
package/dist/astryx.css
CHANGED
|
@@ -1317,7 +1317,9 @@
|
|
|
1317
1317
|
.x16khyan:is(:disabled,[aria-disabled="true"]):not(#\#):not(#\#):not(#\#){cursor:default}
|
|
1318
1318
|
.x1y40yee:popover-open:not(#\#):not(#\#):not(#\#){display:flex}
|
|
1319
1319
|
.x1sik18w:not(:first-child):not(#\#):not(#\#):not(#\#){margin-inline-start:var(--_avatar-group-overlap)}
|
|
1320
|
+
.x17xqekc:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to left,transparent 0%,rgba(0,0,0,.3) 2px,black var(--spacing-1))}
|
|
1320
1321
|
.x1nwey30:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to left,transparent var(--_tab-strip-bleed),black calc(var(--_tab-strip-bleed) + var(--spacing-8)))}
|
|
1322
|
+
.x8ulde1:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to right,transparent 0%,rgba(0,0,0,.3) 2px,black var(--spacing-1))}
|
|
1321
1323
|
.x1rxa4r8:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to right,transparent var(--_tab-strip-bleed),black calc(var(--_tab-strip-bleed) + var(--spacing-8)))}
|
|
1322
1324
|
.x1euntei:focus-within:not(#\#):not(#\#):not(#\#){opacity:1}
|
|
1323
1325
|
.x25t5g8:focus-visible:not(#\#):not(#\#):not(#\#){opacity:1}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.5.2-canary.
|
|
3
|
+
"version": "0.5.2-canary.212c4fd",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -661,7 +661,7 @@
|
|
|
661
661
|
"react-dom": ">=19.0.0"
|
|
662
662
|
},
|
|
663
663
|
"devDependencies": {
|
|
664
|
-
"@astryxdesign/cli": "0.5.2-canary.
|
|
664
|
+
"@astryxdesign/cli": "0.5.2-canary.212c4fd",
|
|
665
665
|
"@babel/cli": "^8.0.4",
|
|
666
666
|
"@babel/core": "^7.29.7",
|
|
667
667
|
"@babel/preset-react": "^8.0.1",
|
|
@@ -23,6 +23,62 @@ export const docs = {
|
|
|
23
23
|
{guidance: false, description: 'Use the destructive variant without a confirmation step for irreversible actions like deleting data.'},
|
|
24
24
|
{guidance: false, description: 'Use a button for navigation. If it only takes the user to another page, use a link instead. Buttons are for actions like saving, deleting, or submitting.'},
|
|
25
25
|
],
|
|
26
|
+
accessibility: [
|
|
27
|
+
{
|
|
28
|
+
name: 'Text label',
|
|
29
|
+
category: 'Color contrast',
|
|
30
|
+
criterion: '1.4.3 Contrast (Minimum)',
|
|
31
|
+
requirement: '4.5:1',
|
|
32
|
+
states: ['Rest', 'Hover', 'Pointer down'],
|
|
33
|
+
description:
|
|
34
|
+
'Button text must have at least 4.5:1 contrast with the button background in every state. For Hover and Pointer down, measure the final background after the overlay is applied.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Essential icon or spinner arc',
|
|
38
|
+
category: 'Color contrast',
|
|
39
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
40
|
+
requirement: '3:1',
|
|
41
|
+
states: ['Icon only', 'Loading'],
|
|
42
|
+
description:
|
|
43
|
+
'An icon used instead of text must have at least 3:1 contrast with the button background. The moving spinner arc must also meet 3:1. An icon beside a visible label does not need its own check.',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Badge text',
|
|
47
|
+
category: 'Color contrast',
|
|
48
|
+
criterion: '1.4.3 Contrast (Minimum)',
|
|
49
|
+
requirement: '4.5:1',
|
|
50
|
+
states: ['Rest', 'Hover', 'Pointer down'],
|
|
51
|
+
description:
|
|
52
|
+
'Badge text inside a button must have at least 4.5:1 contrast with the Badge background. Check all 14 built-in Badge colors in Rest, Hover, and Pointer down on page and surface backgrounds. This covers 336 pairs per mode. Check custom end content separately.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Visible control boundary',
|
|
56
|
+
category: 'Color contrast',
|
|
57
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
58
|
+
requirement: '3:1 if needed',
|
|
59
|
+
states: ['Rest'],
|
|
60
|
+
description:
|
|
61
|
+
'The button edge needs 3:1 contrast only when users need it to see the control. A text-only button can rely on its label.',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'Keyboard focus indicator',
|
|
65
|
+
category: 'Color contrast',
|
|
66
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
67
|
+
requirement: '3:1',
|
|
68
|
+
states: ['Focus visible'],
|
|
69
|
+
description:
|
|
70
|
+
'The focus outline needs at least 3:1 contrast with the area around the button. Check every style. Destructive buttons use a red outline.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Disabled appearance',
|
|
74
|
+
category: 'Color contrast',
|
|
75
|
+
criterion: '1.4.3 and 1.4.11 exceptions',
|
|
76
|
+
requirement: 'Not required',
|
|
77
|
+
states: ['Disabled'],
|
|
78
|
+
description:
|
|
79
|
+
'Disabled controls do not need to meet these contrast ratios.',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
26
82
|
anatomy: [
|
|
27
83
|
{name: 'Icon', required: false, description: 'A leading icon that reinforces the label, like a trash icon on a Delete button.'},
|
|
28
84
|
{name: 'Label', required: true, description: 'The visible text describing the action. Also used as the accessible name.'},
|
|
@@ -39,6 +39,53 @@ export const docs = {
|
|
|
39
39
|
usage: {
|
|
40
40
|
description:
|
|
41
41
|
'ButtonGroup joins related actions into a single connected control. Use it when multiple buttons represent related choices or operations that belong together visually, like copy/cut/paste, or undo/redo.',
|
|
42
|
+
accessibility: [
|
|
43
|
+
{
|
|
44
|
+
name: 'Text label',
|
|
45
|
+
category: 'Color contrast',
|
|
46
|
+
criterion: '1.4.3 Contrast (Minimum)',
|
|
47
|
+
requirement: '4.5:1',
|
|
48
|
+
states: ['Rest', 'Hover', 'Pointer down'],
|
|
49
|
+
description:
|
|
50
|
+
'Text in each button must have at least 4.5:1 contrast with its background in every state. For Hover and Pointer down, measure the final background after the overlay is applied.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Essential icon or spinner arc',
|
|
54
|
+
category: 'Color contrast',
|
|
55
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
56
|
+
requirement: '3:1',
|
|
57
|
+
states: ['Icon only', 'Loading'],
|
|
58
|
+
description:
|
|
59
|
+
'An icon used instead of text must have at least 3:1 contrast with the button background. The moving spinner arc must also meet 3:1. An icon beside a visible label does not need its own check.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'Visible control boundary',
|
|
63
|
+
category: 'Color contrast',
|
|
64
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
65
|
+
requirement: '3:1 if needed',
|
|
66
|
+
states: ['Rest'],
|
|
67
|
+
description:
|
|
68
|
+
'Some groups need a divider or edge to show each button. That divider or edge must have at least 3:1 contrast.',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Keyboard focus indicator',
|
|
72
|
+
category: 'Color contrast',
|
|
73
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
74
|
+
requirement: '3:1',
|
|
75
|
+
states: ['Focus visible'],
|
|
76
|
+
description:
|
|
77
|
+
'The focus outline must have at least 3:1 contrast with the area around it.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'Disabled appearance',
|
|
81
|
+
category: 'Color contrast',
|
|
82
|
+
criterion: '1.4.3 and 1.4.11 exceptions',
|
|
83
|
+
requirement: 'Not required',
|
|
84
|
+
states: ['Disabled'],
|
|
85
|
+
description:
|
|
86
|
+
'Disabled controls do not need to meet these contrast ratios.',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
42
89
|
bestPractices: [
|
|
43
90
|
{guidance: true, description: 'Group buttons that perform related actions on the same object, like copy, cut, paste on selected text.'},
|
|
44
91
|
{guidance: true, description: 'Use the same variant for all buttons in a group so they look like a single connected unit.'},
|
|
@@ -850,4 +850,87 @@ describe('Carousel', () => {
|
|
|
850
850
|
expect(scroller).not.toHaveAttribute('data-padding');
|
|
851
851
|
});
|
|
852
852
|
});
|
|
853
|
+
|
|
854
|
+
describe('edge fade', () => {
|
|
855
|
+
function getScroller() {
|
|
856
|
+
const region = screen.getByRole('region');
|
|
857
|
+
return region.firstElementChild as HTMLElement;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function makeOverflowing(el: HTMLElement, scrollLeft: number) {
|
|
861
|
+
Object.defineProperty(el, 'scrollWidth', {
|
|
862
|
+
value: 500,
|
|
863
|
+
configurable: true,
|
|
864
|
+
});
|
|
865
|
+
Object.defineProperty(el, 'clientWidth', {
|
|
866
|
+
value: 200,
|
|
867
|
+
configurable: true,
|
|
868
|
+
});
|
|
869
|
+
Object.defineProperty(el, 'scrollLeft', {
|
|
870
|
+
value: scrollLeft,
|
|
871
|
+
writable: true,
|
|
872
|
+
configurable: true,
|
|
873
|
+
});
|
|
874
|
+
fireEvent.scroll(el);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function injectedCss(): string {
|
|
878
|
+
let out = '';
|
|
879
|
+
for (const sheet of Array.from(document.styleSheets)) {
|
|
880
|
+
try {
|
|
881
|
+
for (const rule of Array.from(sheet.cssRules)) {
|
|
882
|
+
out += rule.cssText + '\n';
|
|
883
|
+
}
|
|
884
|
+
} catch {
|
|
885
|
+
// Ignore cross-origin sheets.
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return out;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
it('fades the physical left edge when only the start overflows (LTR)', () => {
|
|
892
|
+
render(
|
|
893
|
+
<Carousel aria-label="Fade">
|
|
894
|
+
<div>Item 1</div>
|
|
895
|
+
<div>Item 2</div>
|
|
896
|
+
</Carousel>,
|
|
897
|
+
);
|
|
898
|
+
const scroller = getScroller();
|
|
899
|
+
makeOverflowing(scroller, 300);
|
|
900
|
+
expect(getComputedStyle(scroller).maskImage).toContain(
|
|
901
|
+
'linear-gradient(to right',
|
|
902
|
+
);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it('fades the physical right edge when only the end overflows (LTR)', () => {
|
|
906
|
+
render(
|
|
907
|
+
<Carousel aria-label="Fade">
|
|
908
|
+
<div>Item 1</div>
|
|
909
|
+
<div>Item 2</div>
|
|
910
|
+
</Carousel>,
|
|
911
|
+
);
|
|
912
|
+
const scroller = getScroller();
|
|
913
|
+
makeOverflowing(scroller, 0);
|
|
914
|
+
expect(getComputedStyle(scroller).maskImage).toContain(
|
|
915
|
+
'linear-gradient(to left',
|
|
916
|
+
);
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
it('mirrors both single-edge fade gradients under RTL', () => {
|
|
920
|
+
render(
|
|
921
|
+
<Carousel aria-label="Fade">
|
|
922
|
+
<div>Item 1</div>
|
|
923
|
+
<div>Item 2</div>
|
|
924
|
+
</Carousel>,
|
|
925
|
+
);
|
|
926
|
+
const css = injectedCss();
|
|
927
|
+
expect(css).toMatch(
|
|
928
|
+
/:is\(\[dir="rtl"\][^)]*\)[^{]*\{\s*mask-image:\s*linear-gradient\(to left/,
|
|
929
|
+
);
|
|
930
|
+
expect(css).toMatch(
|
|
931
|
+
/:is\(\[dir="rtl"\][^)]*\)[^{]*\{\s*mask-image:\s*linear-gradient\(to right/,
|
|
932
|
+
);
|
|
933
|
+
});
|
|
934
|
+
});
|
|
935
|
+
|
|
853
936
|
});
|
|
@@ -178,10 +178,16 @@ const styles = stylex.create({
|
|
|
178
178
|
maskImage: 'none',
|
|
179
179
|
},
|
|
180
180
|
fadeStart: {
|
|
181
|
-
maskImage:
|
|
181
|
+
maskImage: {
|
|
182
|
+
default: `linear-gradient(to right, transparent 0%, rgba(0,0,0,0.3) 2px, black ${spacingVars['--spacing-1']})`,
|
|
183
|
+
':is([dir="rtl"] *)': `linear-gradient(to left, transparent 0%, rgba(0,0,0,0.3) 2px, black ${spacingVars['--spacing-1']})`,
|
|
184
|
+
},
|
|
182
185
|
},
|
|
183
186
|
fadeEnd: {
|
|
184
|
-
maskImage:
|
|
187
|
+
maskImage: {
|
|
188
|
+
default: `linear-gradient(to left, transparent 0%, rgba(0,0,0,0.3) 2px, black ${spacingVars['--spacing-1']})`,
|
|
189
|
+
':is([dir="rtl"] *)': `linear-gradient(to right, transparent 0%, rgba(0,0,0,0.3) 2px, black ${spacingVars['--spacing-1']})`,
|
|
190
|
+
},
|
|
185
191
|
},
|
|
186
192
|
fadeBoth: {
|
|
187
193
|
maskImage: `linear-gradient(to right, transparent 0%, rgba(0,0,0,0.3) 2px, black ${spacingVars['--spacing-1']}, black calc(100% - ${spacingVars['--spacing-1']}), rgba(0,0,0,0.3) calc(100% - 2px), transparent 100%)`,
|
|
@@ -74,6 +74,44 @@ export const docs = {
|
|
|
74
74
|
|
|
75
75
|
usage: {
|
|
76
76
|
description: 'A button that shows only an icon with no visible text. Use IconButton in toolbars, table rows, and compact UI where space is tight and the icon is universally understood.',
|
|
77
|
+
accessibility: [
|
|
78
|
+
{
|
|
79
|
+
name: 'Essential icon or spinner arc',
|
|
80
|
+
category: 'Color contrast',
|
|
81
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
82
|
+
requirement: '3:1',
|
|
83
|
+
states: ['Rest', 'Hover', 'Pointer down', 'Loading'],
|
|
84
|
+
description:
|
|
85
|
+
'IconButton has no visible label. Its icon must have at least 3:1 contrast with the button background in Rest, Hover, and Pointer down. The moving spinner arc must also meet 3:1 while loading.',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'Visible control boundary',
|
|
89
|
+
category: 'Color contrast',
|
|
90
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
91
|
+
requirement: '3:1 if needed',
|
|
92
|
+
states: ['Rest'],
|
|
93
|
+
description:
|
|
94
|
+
'The button edge needs 3:1 contrast only when users need it to see the control.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'Keyboard focus indicator',
|
|
98
|
+
category: 'Color contrast',
|
|
99
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
100
|
+
requirement: '3:1',
|
|
101
|
+
states: ['Focus visible'],
|
|
102
|
+
description:
|
|
103
|
+
'The focus outline must have at least 3:1 contrast with the area around the button. Check the red outline on destructive buttons too.',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'Disabled appearance',
|
|
107
|
+
category: 'Color contrast',
|
|
108
|
+
criterion: '1.4.3 and 1.4.11 exceptions',
|
|
109
|
+
requirement: 'Not required',
|
|
110
|
+
states: ['Disabled'],
|
|
111
|
+
description:
|
|
112
|
+
'Disabled controls do not need to meet these contrast ratios.',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
77
115
|
bestPractices: [
|
|
78
116
|
{ guidance: true, description: 'Make the aria-label specific: a trash icon labeled "Delete conversation" is clearer than just "Delete" for screen readers.' },
|
|
79
117
|
{ guidance: true, description: 'Add a tooltip: even a gear icon can mean Settings, Preferences, or Configure.' },
|
|
@@ -98,6 +98,62 @@ export const docs = {
|
|
|
98
98
|
usage: {
|
|
99
99
|
description:
|
|
100
100
|
'A segmented button group that allows users to make a single selection from a small set of mutually exclusive options. Use SegmentedControl when all options should be visible at once and the selection controls a value or mode, not page navigation.',
|
|
101
|
+
accessibility: [
|
|
102
|
+
{
|
|
103
|
+
name: 'Text label',
|
|
104
|
+
category: 'Color contrast',
|
|
105
|
+
criterion: '1.4.3 Contrast (Minimum)',
|
|
106
|
+
requirement: '4.5:1',
|
|
107
|
+
states: ['Rest', 'Hover', 'Selected'],
|
|
108
|
+
description:
|
|
109
|
+
'Each label must have at least 4.5:1 contrast with its segment background. Check unselected, Hover, and selected colors as they appear on screen.',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'Essential icon',
|
|
113
|
+
category: 'Color contrast',
|
|
114
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
115
|
+
requirement: '3:1',
|
|
116
|
+
states: ['Icon only'],
|
|
117
|
+
description:
|
|
118
|
+
'When a segment has no visible label, its icon must have at least 3:1 contrast with the segment background. An icon beside a visible label does not need its own check.',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'Selected state indicator',
|
|
122
|
+
category: 'Color contrast',
|
|
123
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
124
|
+
requirement: '3:1 if relied upon',
|
|
125
|
+
states: ['Selected'],
|
|
126
|
+
description:
|
|
127
|
+
'The selected background must reach 3:1 only when users need it to tell selected from unselected. Label color and weight also show selection.',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'Visible control boundary',
|
|
131
|
+
category: 'Color contrast',
|
|
132
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
133
|
+
requirement: '3:1 if needed',
|
|
134
|
+
states: ['Rest'],
|
|
135
|
+
description:
|
|
136
|
+
'The control edge or segment borders need at least 3:1 contrast when users need them to see the choices.',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'Keyboard focus indicator',
|
|
140
|
+
category: 'Color contrast',
|
|
141
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
142
|
+
requirement: '3:1',
|
|
143
|
+
states: ['Focus visible'],
|
|
144
|
+
description:
|
|
145
|
+
'The focus outline must have at least 3:1 contrast with the area around the segment. Check it on the track and selected background.',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'Disabled appearance',
|
|
149
|
+
category: 'Color contrast',
|
|
150
|
+
criterion: '1.4.3 and 1.4.11 exceptions',
|
|
151
|
+
requirement: 'Not required',
|
|
152
|
+
states: ['Disabled'],
|
|
153
|
+
description:
|
|
154
|
+
'Disabled controls do not need to meet these contrast ratios.',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
101
157
|
bestPractices: [
|
|
102
158
|
{guidance: true, description: 'Use for switching between 2–5 mutually exclusive views or modes where all options should be visible.'},
|
|
103
159
|
{guidance: true, description: 'Provide a descriptive label for the control to ensure the group is accessible to screen readers.'},
|
|
@@ -122,6 +122,62 @@ export const docs = {
|
|
|
122
122
|
usage: {
|
|
123
123
|
description:
|
|
124
124
|
'ToggleButton switches between selected and unselected states to represent a persistent on/off choice. Use it standalone for binary actions like bold, mute, or favorite, or inside a ToggleButtonGroup for single-select or multi-select toolbar controls.',
|
|
125
|
+
accessibility: [
|
|
126
|
+
{
|
|
127
|
+
name: 'Text label',
|
|
128
|
+
category: 'Color contrast',
|
|
129
|
+
criterion: '1.4.3 Contrast (Minimum)',
|
|
130
|
+
requirement: '4.5:1',
|
|
131
|
+
states: ['Unselected', 'Selected', 'Hover', 'Pointer down'],
|
|
132
|
+
description:
|
|
133
|
+
'The label must have at least 4.5:1 contrast with the button background when selected and unselected. For Hover and Pointer down, measure the final background after the overlay is applied.',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'Essential icon or spinner arc',
|
|
137
|
+
category: 'Color contrast',
|
|
138
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
139
|
+
requirement: '3:1',
|
|
140
|
+
states: ['Icon only', 'Loading'],
|
|
141
|
+
description:
|
|
142
|
+
'An icon-only ToggleButton must have at least 3:1 contrast between its icon and button background. The moving spinner arc must also meet 3:1. An icon beside a visible label does not need its own check.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'Selected state indicator',
|
|
146
|
+
category: 'Color contrast',
|
|
147
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
148
|
+
requirement: '3:1 if relied upon',
|
|
149
|
+
states: ['Selected'],
|
|
150
|
+
description:
|
|
151
|
+
'The selected background must reach 3:1 only when users need it to tell selected from unselected. Label weight or a changed icon can also show selection.',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'Visible control boundary',
|
|
155
|
+
category: 'Color contrast',
|
|
156
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
157
|
+
requirement: '3:1 if needed',
|
|
158
|
+
states: ['Rest'],
|
|
159
|
+
description:
|
|
160
|
+
'The button edge needs 3:1 contrast only when users need it to see the control. A visible label or icon can show the control instead.',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'Keyboard focus indicator',
|
|
164
|
+
category: 'Color contrast',
|
|
165
|
+
criterion: '1.4.11 Non-text Contrast',
|
|
166
|
+
requirement: '3:1',
|
|
167
|
+
states: ['Focus visible'],
|
|
168
|
+
description:
|
|
169
|
+
'The focus outline must have at least 3:1 contrast with the area around the button. Check both selected and unselected states.',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'Disabled appearance',
|
|
173
|
+
category: 'Color contrast',
|
|
174
|
+
criterion: '1.4.3 and 1.4.11 exceptions',
|
|
175
|
+
requirement: 'Not required',
|
|
176
|
+
states: ['Disabled'],
|
|
177
|
+
description:
|
|
178
|
+
'Disabled controls do not need to meet these contrast ratios.',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
125
181
|
bestPractices: [
|
|
126
182
|
{guidance: true, description: 'Use a filled or colored icon for the pressed state so users can see the current state at a glance: an outline star vs a solid star, for example.'},
|
|
127
183
|
{guidance: true, description: 'Keep the label identical between pressed and unpressed states. Let the visual treatment (icon, weight, background) communicate the change.'},
|