A11Y Demo
Everyone should equally perceive, understand, navigate, and interact with a website.
Instructions
At every step inspect the document structure, use your keyboard to navigate the page and use your screen reader.
Turn off StylesTurn on a11y ModeTurn on Styles and turn off a11y ModeTurn on a11y modeStylesA11y mode
Table of Contents
Keyboard Controls
Basic keyboard controls to navigate a webpage. Depending on your OS this may differ or there are more shortcuts available.
TabMove to next interactive elementMove to previous interactive elementActivate element (links, buttons, select, etc)spacebarTrigger button (such as to pause/play videos, submit forms, etc), select, etc.escClose opened content (modals, navigation menus, etc) or cancel current actionarrow keysUse the arrow keys to navigate within widgets (e.g. select, radio input, etc) and navigate around a page
Learn moreShiftTab
Shift plus Tab KeyScreen reader
A screen reader is a software program that assists people with visual impairments or other disabilities by reading aloud the content displayed on a screen. It converts the text, images, and even certain interactive elements into speech or Braille, allowing users to understand and navigate digital content such as websites, applications, and documents.
Screen reader on mac
Learn and practice the basics for controlling your Mac using VoiceOver — the screen reader built into macOS.
Get startedScreen reader on Windows
The NVDA screen reader can be downloaded free of charge by anyone.
Get startedPage Structure & Semantics
Semantic HTML improves accessibility by creating a structured, meaningful layout that assistive technologies can easily interpret, benefiting users with disabilities. It also aids search engines in accurately ranking content. Use semantic elements like <section>, <aside>, and <nav> instead of generic <div> elements, and maintain a logical content flow with correctly ordered headings (e.g., <h1>, <h2>, <h3>). This approach ensures an inclusive and universally accessible web experience.
Hello world
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam, vel.
Subsection
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam, vel.
Click meLearn more
1<div id="hello-world" data-scrolltarget="" data-test="page-section">
2 <div data-test="heading">Hello World</div>
3 <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam, vel.
4 </div>
5 <div data-test="heading">Subsection</div>
6 <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam, vel.
7 </div>
8 <div>
9 <span data-test="button">Click me</span>
10 <span data-test="link">Learn more<svg xmlns="http://www.w3.org/2000/svg"
11 width="24" height="24" viewBox="0 0 24 24" fill="none"
12 stroke="currentColor" stroke-width="2" stroke-linecap="round"
13 stroke-linejoin="round">
14 <path d="M5 12l14 0">
15 </path>
16 <path d="M13 18l6 -6">
17 </path>
18 <path d="M13 6l6 6">
19 </path>
20 </svg>
21 </span>
22 </div>
23</div>
Interactive Elements
Avoid attaching click handlers to non-interactive elements, such as <div> or <span>. For navigation, utilize <a> (anchor) elements instead of JavaScript-driven click handlers. For actions requiring click handlers, use <button> elements to ensure accessibility and semantic clarity.
Action 1Open ModalExample link
Learn moreComponent Usage
Repeating yourself is boring and adds extra time. Use components provided by our libraries over custom components whenever possible. Ensure custom components meet accessibility (a11y) requirements.
Do you want to design this over and over?
1import { useId } from '@floating-ui/react';
2import type { ChangeEvent, FC, FocusEvent, MouseEvent } from 'react';
3import { useEffect, useState } from 'react';
4import { useTheme } from 'styled-components';
5
6import { renderInlineAlert } from './helpers/renderInlineAlert';
7import type { InputProps, InputValue } from './Input.interface';
8import {
9 HelperText,
10 StyledInput,
11 StyledInputMask,
12 StyledInputWrapper,
13} from './Input.styled';
14import { inputTheme } from './Input.theme';
15import { PrefixIcon, SuffixIcon } from './partials/InputIcon/InputIcon';
16import type { InputIconState } from './partials/InputIcon/InputIcon.interface';
17import { InputLabel } from './partials/InputLabel/InputLabel';
18
19const mapStateToIconState = {
20 valid: 'valid',
21 error: 'error',
22 disabled: 'disabled',
23 warning: 'warning',
24};
25
26const isValidValue = (value: InputValue | undefined) =>
27 value === 0 ? true : !!value;
28
29export const Input: FC<InputProps> = (props) => {
30 const {
31 autofocus = false,
32 autocomplete = 'on',
33 className,
34 validationMessage,
35 helpText,
36 prefixIconLabel,
37 suffixIconLabel,
38 prefixIcon,
39 suffixIcon,
40 isPrefixIconPressed,
41 isSuffixIconPressed,
42 inputMaskProps,
43 inputProps,
44 inputRef,
45 inputType = 'text',
46 label,
47 onBlur,
48 onChange,
49 onClick,
50 onClickSuffixIcon,
51 onClickPrefixIcon,
52 onResetIconClick,
53 onFocus,
54 onKeyDown,
55 onKeyUp,
56 onMouseEnter,
57 onMouseLeave,
58 placeholder = '',
59 state = 'idle',
60 value,
61 hasPersonallyIdentifiableInformation = false,
62 hasDefaultValidationMessage = false,
63 slot,
64 } = props;
65
66 const [isShrink, changeShrink] = useState < boolean > isValidValue(value);
67 const [isFocus, changeFocus] = useState < boolean > autofocus;
68
69 const theme = useTheme();
70 const uniqueId = useId();
71 const id = inputProps?.id ?? uniqueId;
72
73 useEffect(() => {
74 changeShrink(isFocus || isValidValue(value));
75 }, [isFocus, value]);
76
77 const onMouseEnterHandler = (ev: MouseEvent<HTMLInputElement>) => {
78 if (onMouseEnter) onMouseEnter(ev);
79 };
80
81 const onMouseLeaveHandler = (ev: MouseEvent<HTMLInputElement>) => {
82 if (onMouseLeave) onMouseLeave(ev);
83 };
84
85 const onFocusHandler = (ev: FocusEvent<HTMLInputElement>) => {
86 if (onFocus) onFocus(ev);
87 changeShrink(true);
88 changeFocus(true);
89 };
90
91 const onBlurHandler = (ev: FocusEvent<HTMLInputElement>) => {
92 if (onBlur) onBlur(ev);
93 if (!value) {
94 changeShrink(false);
95 }
96 changeFocus(false);
97 };
98
99 const onChangeHandler = (ev: ChangeEvent<HTMLInputElement>) => {
100 if (onChange) onChange(ev);
101 };
102
103 const onClickHandler = (ev: MouseEvent<HTMLInputElement>) => {
104 ev.stopPropagation();
105 if (onClick) onClick(ev);
106 };
107
108 const onClickSuffixIconHandler = (ev: MouseEvent<HTMLInputElement>) => {
109 ev.stopPropagation();
110
111 if (onClickSuffixIcon) {
112 onClickSuffixIcon(ev);
113 }
114
115 if (onResetIconClick) {
116 onResetIconClick(ev);
117 }
118 };
119
120 const onClickPrefixIconHandler = (ev: MouseEvent<HTMLInputElement>) => {
121 ev.stopPropagation();
122
123 if (onClickPrefixIcon) {
124 onClickPrefixIcon(ev);
125 }
126 };
127
128 const color = inputTheme(theme).color.state[state];
129
130 // @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
131 const iconState: InputIconState = mapStateToIconState[state] || 'default';
132 const isRequired = !!inputProps?.required;
133
134 const combinedInputProps = {
135 ...inputProps,
136 id,
137 placeholder,
138 autoFocus: autofocus,
139 type: inputType,
140 value,
141 onMouseEnter: onMouseEnterHandler,
142 onMouseLeave: onMouseLeaveHandler,
143 onClick: onClickHandler,
144 onFocus: onFocusHandler,
145 onChange: onChangeHandler,
146 onKeyUp,
147 onKeyDown,
148 onBlur: onBlurHandler,
149 disabled: state === 'disabled',
150 };
151
152 const a11yAttrs = {
153 'aria-activedescendant': inputProps?.['aria-activedescendant'],
154 'aria-autocomplete': inputProps?.['aria-autocomplete'],
155 'aria-controls': inputProps?.['aria-controls'],
156 'aria-labelledby': inputProps?.['aria-labelledby'],
157 'aria-invalid': state === 'error',
158 'aria-required': isRequired,
159 'aria-disabled': state === 'disabled',
160 'aria-describedby': helpText ? `${id}-help` : undefined,
161 required: isRequired,
162 };
163
164 const piiAttrs =
165 hasPersonallyIdentifiableInformation && value
166 ? {
167 'data-cs-mask': true,
168 }
169 : {};
170
171 const inlineAlertDataTestAttrs =
172 inputProps?.id || inputMaskProps?.id
173 ? `${inputMaskProps ? inputMaskProps?.id : inputProps?.id}__${state}`
174 : undefined;
175
176 return (
177 <StyledInputWrapper className={className}>
178 {helpText ? (
179 <HelperText variant="bodySmall" data-test="helpText">
180 {helpText}
181 </HelperText>
182 ) : null}
183
184 <InputLabel
185 inputType={inputType}
186 state={state}
187 hasPrefixIcon={!!prefixIcon}
188 hasSuffixIcon={!!suffixIcon}
189 color={color}
190 htmlFor={id}
191 label={label}
192 isShrink={isShrink}>
193 {prefixIcon ? (
194 <PrefixIcon
195 isIconPressed={isPrefixIconPressed}
196 icon={prefixIcon}
197 isDisabled={combinedInputProps.disabled}
198 iconLabel={
199 onClickPrefixIcon && prefixIconLabel ? prefixIconLabel : ''
200 }
201 iconState={iconState}
202 color={
203 state === 'disabled'
204 ? theme[theme.mode].color.grey4
205 : theme[theme.mode].color.grey11
206 }
207 onClick={onClickPrefixIcon ? onClickPrefixIconHandler : undefined}
208 />
209 ) : null}
210 {inputMaskProps ? (
211 <StyledInputMask
212 {...inputMaskProps}
213 {...combinedInputProps}
214 {...a11yAttrs}
215 {...piiAttrs}
216 data-test={inputMaskProps?.name}
217 inputRef={inputRef}
218 autoComplete={autocomplete}
219 />
220 ) : (
221 <StyledInput
222 {...combinedInputProps}
223 {...a11yAttrs}
224 {...piiAttrs}
225 inputType={inputType}
226 isShrink={isShrink}
227 data-test={inputProps?.name}
228 ref={inputRef}
229 hasPrefixIcon={!!prefixIcon}
230 hasSuffixIcon={!!suffixIcon}
231 autoComplete={autocomplete}
232 slot={slot}
233 />
234 )}
235 {!!suffixIcon && (
236 <SuffixIcon
237 isIconPressed={isSuffixIconPressed}
238 icon={suffixIcon}
239 isDisabled={combinedInputProps.disabled}
240 iconLabel={
241 (onClickSuffixIcon || onResetIconClick) && suffixIconLabel
242 ? suffixIconLabel
243 : ''
244 }
245 iconState={iconState}
246 color={
247 state === 'disabled'
248 ? theme[theme.mode].color.grey4
249 : theme[theme.mode].color.grey11
250 }
251 onClick={onClickSuffixIcon ? onClickSuffixIconHandler : undefined}
252 />
253 )}
254 </InputLabel>
255
256 {renderInlineAlert(
257 state,
258 hasDefaultValidationMessage,
259 validationMessage,
260 inlineAlertDataTestAttrs
261 )}
262 </StyledInputWrapper>
263 );
264};
265
266Input.displayName = 'Input';
267
Responsive & Layout
The page works well on different screen sizes - vertical and horizontal orientation. Ensure the page works well when zoomed to at least 200%, with no content overflow issues. Be aware of the difference between 'zooming' and 'change of the default browser font size'. Use global defined variables (theme object) for sizing. Use relative units (e.g., `rem`, `em`, `%`, etc.). Use pixel values only if applicable (e.g. borders).
Respect user settings
A site can react to a users OS or browser configuration. This setting will only have an effect if you have enabled the a11y mode.
Zooming
This setting will be temporary.
command
Cmd plus plus Keycommand
Cmd plus plus KeyReading mode
right to leftColors, Modes & Themes
Ensure sufficient contrast between text and background colors (4.5:1 for text, 3:1 for UI elements). Use global defined variables (theme object) for colors. The page can be used in different color modes (e.g. `light`, `dark`, `high-contrast`, etc)
Motion & Animation
Respect users `prefers reduced motion` settings.
Open ModalWe invest in the world’s potential
Here we focus on markets where technology, innovation, and capital can unlock long-term value and drive economic growth.
Example wrong linkExample right link