Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve accessibility and address violations #81

Merged
merged 2 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ export const App = () => {

return isReady ? (
<AppProvider config={config} isMobile={isMobile}>
<div className="app">
<main className="app">
<Toggle />
<Content />
<Buttons />
<Footer />
<Particles />
</div>
</main>
</AppProvider>
) : (
<></>
Expand Down
10 changes: 4 additions & 6 deletions src/App/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,36 @@ import { Email, GitHub, LinkedIn, Resume } from 'icons';
export const config: Config = {
name: {
display: 'Adam Alston',
aria: 'My name is Adam Alston',
},
title: {
display: 'Software Engineer',
aria: 'I am a software engineer',
},
buttons: [
{
name: 'github',
display: 'GitHub',
aria: 'Visit my GitHub profile',
ariaLabel: 'GitHub profile (opens in new window)',
icon: <GitHub />,
href: 'https://github.com/adamalston/',
},
{
name: 'linked-in',
display: 'LinkedIn',
aria: 'Visit my LinkedIn profile',
ariaLabel: 'LinkedIn profile (opens in new window)',
icon: <LinkedIn />,
href: 'https://www.linkedin.com/in/adam-alston/',
},
{
name: 'resume',
display: 'Resume',
aria: 'View my resume in Google Drive',
ariaLabel: 'Resume in Google Drive (opens in new window)',
icon: <Resume />,
href: 'https://drive.google.com/file/d/1VQ_Oeim_e92QEMi64ejGWY5Hf4RRxfeJ/view',
},
{
name: 'email',
display: 'Email',
aria: 'Send me an email',
ariaLabel: 'Email contact (opens in new window)',
icon: <Email />,
href: 'mailto:aalston9@gmail.com',
},
Expand Down
12 changes: 5 additions & 7 deletions src/Index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ describe('application tests', () => {
element: HTMLElement,
display: RegExp,
link?: string,
skipA11yNameCheck?: boolean,
) => {
expect(element).toBeVisible();
expect(element).toHaveAccessibleName();
expect(element).toHaveAccessibleDescription();
if (!skipA11yNameCheck) expect(element).toHaveAccessibleName();
expect(element).toHaveTextContent(display);
if (link) expect(element).toHaveAttribute('href', link);
};
Expand All @@ -54,26 +54,25 @@ describe('application tests', () => {

expect(parent).toBeVisible();
expect(parent).toHaveAccessibleName();
expect(parent).toHaveAccessibleDescription();
expect(parent).toHaveAttribute('href', link);
};

it('should render name: Adam Alston', () => {
const element = screen.getByTestId('name');

checkContent(element, /^Adam Alston$/);
checkContent(element, /^Adam Alston$/, undefined, true);
});

it('should render title: Software Engineer', () => {
const element = screen.getByTestId('title');

checkContent(element, /^Software Engineer$/);
checkContent(element, /^Software Engineer$/, undefined, true);
});

it('should render creator', () => {
const element = screen.getByTestId('creator');

checkContent(element, /^Adam Alston$/, 'https://www.adamalston.com');
checkContent(element, /^Adam Alston$/, 'https://www.adamalston.com/');
});

it('should render link to source code', () => {
Expand Down Expand Up @@ -129,7 +128,6 @@ describe('application tests', () => {
expect(toggle).toHaveAccessibleDescription();

expect(particles).toBeVisible();
expect(particles).toHaveAccessibleName();

// site should default to the dark theme
expect(toggle).toBeChecked();
Expand Down
9 changes: 8 additions & 1 deletion src/appearance/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ export const options: ISourceOptions = {
},
},
color: {
value: ['c912ed', '00bfff', '22dd22', 'ffd500', 'ff8000', 'ff2600'],
value: [
'ff2600', // Red
'ff8000', // Orange
'ffd500', // Yellow
'22dd22', // Green
'00bfff', // Blue
'c912ed', // Violet (Purple)
],
},
shape: {
type: 'circle',
Expand Down
15 changes: 9 additions & 6 deletions src/appearance/themes.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { Themes } from 'types';

// Enhance contrast by using white and black text with reduced opacity over
// colored backgrounds instead of gray.
// https://m2.material.io/design/color/text-legibility.html#text-backgrounds
export const themes: Themes = {
dark: {
key: 'dark',
primaryTextColor: '#fff',
secondaryTextColor: '#ccc',
tertiaryTextColor: '#999',
secondaryTextColor: '#ffffffdd',
tertiaryTextColor: '#ffffff99',
background: '#000',
shadowColor: 'rgba(0, 0, 0, 0.5)',
shadowColor: '#0000007f',
},
light: {
key: 'light',
primaryTextColor: '#000',
secondaryTextColor: '#333',
tertiaryTextColor: '#777',
secondaryTextColor: '#000000dd',
tertiaryTextColor: '#00000099',
background: '#fff',
shadowColor: 'rgba(255, 255, 255, 0.5)',
shadowColor: '#ffffff7f',
},
};
9 changes: 4 additions & 5 deletions src/components/Buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,18 @@ export const Buttons = () => {

return (
<Container $theme={theme}>
{config.buttons.map(({ name, display, aria, icon, href }) => (
{config.buttons.map(({ name, display, ariaLabel, icon, href }) => (
<span className="button-container" key={name}>
<a
data-v2={`button-${display}`}
className="button"
aria-label={ariaLabel}
href={href}
target="_blank"
rel="noopener noreferrer"
aria-label={aria}
title={aria}
target="_blank"
>
<div className="icon">{icon}</div>
<span className="icon_title" data-v2={display}>
<span data-v2={display} className="icon_title">
{display}
</span>
</a>
Expand Down
19 changes: 5 additions & 14 deletions src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ const sharedStyles = css`
`;

const C = {
Name: styled.div<{ $theme: Theme }>`
Name: styled.h1<{ $theme: Theme }>`
${sharedStyles};
font-size: 6rem;
margin: 0;
color: ${({ $theme }) => $theme.primaryTextColor};
@media only screen and (max-device-width: 820px) and (-webkit-min-device-pixel-ratio: 2) {
font-size: 4.5rem;
}
`,
Title: styled.div<{ $theme: Theme }>`
Title: styled.h2<{ $theme: Theme }>`
${sharedStyles};
font-size: 3.5rem;
margin: 4rem 0;
Expand All @@ -36,20 +37,10 @@ export const Content = () => {

return (
<>
<C.Name
data-v2="name"
aria-label={config.name.aria}
title={config.name.aria}
$theme={theme}
>
<C.Name data-v2="name" $theme={theme}>
{config.name.display}
</C.Name>
<C.Title
data-v2="title"
aria-label={config.title.aria}
title={config.title.aria}
$theme={theme}
>
<C.Title data-v2="title" $theme={theme}>
{config.title.display}
</C.Title>
</>
Expand Down
25 changes: 13 additions & 12 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AppContext } from 'App/AppContext';
import { Theme } from 'types';

const F = {
Container: styled.div<{ $isMobile: boolean }>`
Container: styled.footer<{ $isMobile: boolean }>`
position: absolute;
bottom: 0;
right: 0;
Expand All @@ -19,8 +19,11 @@ const F = {
`,
Link: styled.a<{ $theme: Theme }>`
transition: color 0.5s linear;
text-decoration: none;
color: ${({ $theme }) => $theme.secondaryTextColor};
text-decoration: none;
&:hover {
text-decoration: underline;
}
`,
};

Expand All @@ -29,17 +32,14 @@ export const Footer = () => {

return (
<F.Container $isMobile={isMobile}>
<F.Text
data-v2="footer"
aria-label="Designed and built by Adam Alston"
$theme={theme}
>
<F.Text data-v2="footer" $theme={theme}>
{'Designed and built by '}
<F.Link
data-v2="creator"
href="https://www.adamalston.com"
aria-label="Adam's website"
title="A link to Adam's personal website"
aria-label="Adam Alston's personal website (opens in new window)"
href="https://www.adamalston.com/"
rel="noopener noreferrer"
target="_blank"
$theme={theme}
>
{'Adam Alston'}
Expand All @@ -49,9 +49,10 @@ export const Footer = () => {
{' | '}
<F.Link
data-v2="source"
aria-label="Source code for this website (opens in new window)"
href="https://github.com/adamalston/v2/"
aria-label="Source code"
title="View this website's source code in GitHub"
rel="noopener noreferrer"
target="_blank"
$theme={theme}
>
{'Source'}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Particles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const Particles = () => {
const { theme } = useContext(AppContext);

return (
<P.Container data-v2="particles" aria-label="Particles" $theme={theme}>
<P.Container data-v2="particles" $theme={theme}>
<ReactParticles width="100vw" height="100vh" options={options} />
</P.Container>
);
Expand Down
31 changes: 25 additions & 6 deletions src/components/Toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Moon, Sun } from 'icons';
import { Theme } from 'types';

const T = {
Container: styled.main`
Container: styled.div`
position: fixed;
z-index: 1;
top: 1rem;
Expand Down Expand Up @@ -46,11 +46,26 @@ const T = {
transition: background-color 0.5s linear;
font-size: 0.5rem;
`,
VisuallyHidden: styled.span`
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
white-space: nowrap;
`,
};

export const Toggle = () => {
const { theme, setTheme } = useContext(AppContext);
const isDark: boolean = theme.key === 'dark';
const ariaLabel = `Currently in ${
isDark ? 'dark' : 'light'
} mode, switch to ${!isDark ? 'dark' : 'light'} mode`;
const toggleDescriptionId = 'toggle-description';

const handleToggle = (checked: boolean) => {
const key: string = checked ? 'dark' : 'light';
Expand All @@ -61,19 +76,23 @@ export const Toggle = () => {

return (
<T.Container>
<T.VisuallyHidden id={toggleDescriptionId}>
Switch between dark and light mode for visual comfort.
</T.VisuallyHidden>
<T.Toggle
data-v2="toggle"
id="toggle"
name="toggle"
type="checkbox"
checked={isDark}
onChange={(event: ChangeEvent<HTMLInputElement>) =>
handleToggle(event.target.checked)
}
aria-label="Theme toggle"
title="Theme toggle"
aria-label={ariaLabel}
aria-describedby={toggleDescriptionId}
onChange={({ target }: ChangeEvent<HTMLInputElement>) => {
handleToggle(target.checked);
}}
/>
<T.Switch htmlFor="toggle" $theme={theme}>
<T.VisuallyHidden>{ariaLabel}</T.VisuallyHidden>
{isDark ? <Moon /> : <Sun />}
</T.Switch>
</T.Container>
Expand Down
7 changes: 6 additions & 1 deletion src/icons/Email.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export const Email = () => (
<svg role="img" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<svg
aria-label="Email icon"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
d="M464.004 4.326L15.986 262.714c-23 13.3-20.7 47.198 3.8 57.297l140.206 57.997v101.996c0 30.198 37.802 43.298 56.702 20.299l60.703-73.797L403.8 478.704c19.101 7.9 40.702-4.2 43.802-24.7l64.003-417.08c4.1-26.698-24.601-45.897-47.602-32.598zm-272.01 475.678v-88.796l54.501 22.499zm224.008-30.899l-206.208-85.196L409.302 128.12c4.8-5.6-2.9-13.199-8.5-8.4l-255.31 217.59-113.505-46.797L480.004 32.025z"
Expand Down
7 changes: 6 additions & 1 deletion src/icons/GitHub.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export const GitHub = () => (
<svg role="img" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<svg
aria-label="GitHub icon"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
d="M296.133 354.174c49.885-5.891 102.942-24.029 102.942-110.192 0-24.49-8.624-44.448-22.67-59.869 2.266-5.89 9.515-28.114-2.734-58.947 0 0-18.139-5.898-60.759 22.669-18.139-4.983-38.09-8.163-56.682-8.163-19.053 0-39.011 3.18-56.697 8.163-43.082-28.567-61.22-22.669-61.22-22.669-12.241 30.833-4.983 53.057-2.718 58.947-14.061 15.42-22.677 35.379-22.677 59.869 0 86.163 53.057 104.301 102.942 110.192-6.344 5.452-12.241 15.873-14.507 30.387-12.702 5.438-45.808 15.873-65.758-18.592 0 0-11.795-21.31-34.012-22.669 0 0-22.224-.453-1.813 13.592 0 0 14.96 6.812 24.943 32.653 0 0 13.6 43.089 76.179 29.48v38.543c0 5.906-4.53 12.702-15.865 10.89C96.139 438.977 32.2 354.626 32.2 255.77c0-123.807 100.216-224.022 224.03-224.022 123.347 0 224.023 100.216 223.57 224.022 0 98.856-63.946 182.754-152.828 212.688-11.342 2.266-15.873-4.53-15.873-10.89V395.45c.001-20.873-6.811-34.465-14.966-41.276zM512 256.23C512 114.73 397.263 0 256.23 0 114.73 0 0 114.73 0 256.23 0 397.263 114.73 512 256.23 512 397.263 512 512 397.263 512 256.23z"
Expand Down
7 changes: 6 additions & 1 deletion src/icons/LinkedIn.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export const LinkedIn = () => (
<svg role="img" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<svg
aria-label="LinkedIn icon"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill="currentColor"
d="M160.008 423h-70V197h70zm6.984-298.004c0-22.629-18.36-40.996-40.976-40.996C103.312 84 85 102.367 85 124.996 85 147.633 103.313 166 126.016 166c22.617 0 40.976-18.367 40.976-41.004zM422 298.664C422 237.996 409.184 193 338.312 193c-34.054 0-56.914 17.031-66.246 34.742H272V197h-68v226h68V310.79c0-29.388 7.48-57.856 43.906-57.856 35.93 0 37.094 33.605 37.094 59.722V423h69zM512 452V60c0-33.086-26.914-60-60-60H60C26.914 0 0 26.914 0 60v392c0 33.086 26.914 60 60 60h392c33.086 0 60-26.914 60-60zM455.26 32C466.694 32 480 45.305 480 56.74v398.52c0 11.435-13.305 24.74-24.74 24.74H56.74C45.306 480 32 466.695 32 455.26V56.74C32 45.306 45.305 32 56.74 32zM452 40"
Expand Down
1 change: 1 addition & 0 deletions src/icons/Moon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const Moon = () => (
<svg
aria-label="Moon icon"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
Expand Down
Loading