1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-22 17:23:22 +01:00

accessibility fixes

This commit is contained in:
Matthias Kretschmann 2022-11-17 23:46:24 +00:00
parent 93fa06bf1b
commit 20b285a760
Signed by: m
GPG Key ID: 606EEEF3C479A91F
23 changed files with 85 additions and 53 deletions

View File

@ -13,13 +13,13 @@ export default function Availability() {
return (
<LazyMotion features={domAnimation}>
<m.aside
<m.section
variants={moveInBottom}
className={className}
{...getAnimationProps(shouldReduceMotion)}
>
<p dangerouslySetInnerHTML={{ __html: html }} />
</m.aside>
</m.section>
</LazyMotion>
)
}

View File

@ -11,7 +11,7 @@ export default function Footer() {
return (
<footer className={`h-card ${styles.footer}`}>
<LogoUnit small />
<Networks small />
<Networks label="Networks Footer" small />
<p className={styles.actions}>
<Vcard />

View File

@ -17,7 +17,7 @@ export default function Header({ small }: Props) {
return (
<header className={`${styles.header} ${small ? styles.small : ''}`}>
<LogoUnit small={small} />
{!small ? <Networks /> : null}
{!small ? <Networks label="Networks" /> : null}
<div className={styles.meta}>
{!small ? (
<Suspense>

View File

@ -0,0 +1,24 @@
import styles from './index.module.css'
type Props = {
country: {
name: string
code: string
}
}
export function Flag({ country }: Props) {
if (!country?.name || !country?.code) return null
// offset between uppercase ascii and regional indicator symbols
const OFFSET = 127397
const emoji = country.code.replace(/./g, (char) =>
String.fromCodePoint(char.charCodeAt(0) + OFFSET)
)
return (
<span role="img" aria-label={country.name} className={styles.emoji}>
{emoji}
</span>
)
}

View File

@ -1,24 +1,9 @@
import { LazyMotion, domAnimation, m, useReducedMotion } from 'framer-motion'
import { moveInBottom, getAnimationProps, moveInTop } from '../Transitions'
import { getAnimationProps, moveInTop } from '../Transitions'
import styles from './index.module.css'
import { useLocation } from '../../hooks/useLocation'
import RelativeTime from '@yaireo/relative-time'
function Flag({ countryCode }: { countryCode: string }) {
if (!countryCode) return null
// offset between uppercase ascii and regional indicator symbols
const OFFSET = 127397
const emoji = countryCode.replace(/./g, (char) =>
String.fromCodePoint(char.charCodeAt(0) + OFFSET)
)
return (
<span role="img" className={styles.emoji}>
{emoji}
</span>
)
}
import { Flag } from './Flag'
export default function Location() {
const { now, next } = useLocation()
@ -28,23 +13,28 @@ export default function Location() {
return now?.city ? (
<LazyMotion features={domAnimation}>
<m.aside
<m.section
aria-label="Location"
variants={moveInTop}
className={styles.location}
{...getAnimationProps(shouldReduceMotion)}
>
<Flag countryCode={now?.country_code} />
<Flag country={{ code: now.country_code, name: now.country }} />
{now?.city} <span>Now</span>
<div className={styles.next}>
{next?.city && (
<>
{isDifferentCountry && <Flag countryCode={next.country_code} />}
{isDifferentCountry && (
<Flag
country={{ code: next.country_code, name: next.country }}
/>
)}
{next.city}{' '}
<span>{relativeTime.from(new Date(next.date_start))}</span>
</>
)}
</div>
</m.aside>
</m.section>
</LazyMotion>
) : null
}

View File

@ -2,16 +2,22 @@ import Link from 'next/link'
import Logo from '../../images/logo.svg'
import styles from './index.module.css'
import resume from '../../../_content/resume.json'
import { useRouter } from 'next/router'
type Props = {
small?: boolean
}
export default function LogoUnit({ small }: Props) {
const router = useRouter()
const { pathname } = router
const isHome = pathname === '/'
return (
<Link
className={`${styles.logounit} ${small ? styles.small : null}`}
href="/"
aria-current={isHome ? 'page' : null}
>
<Logo className={styles.logo} />
<h1 className={`p-name ${styles.title}`}>

View File

@ -3,6 +3,7 @@ import Head from 'next/head'
const MetaFavicons = () => {
return (
<Head>
<meta name="viewport" content="width=device-width,initial-scale=1"></meta>
{/*
Stop the favicon madness
https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs

View File

@ -4,7 +4,7 @@ import Meta from '.'
describe('Meta', () => {
it('renders without crashing', async () => {
await act(async () => {
render(<Meta title="Hello World" />, {
render(<Meta title="Hello World" description="Hello" />, {
container: document.head
})
})
@ -13,7 +13,7 @@ describe('Meta', () => {
it('renders without crashing with slug', async () => {
await act(async () => {
render(<Meta title="Hello World" slug="hello" />, {
render(<Meta title="Hello World" description="Hello" slug="hello" />, {
container: document.head
})
})

View File

@ -9,7 +9,7 @@ const Meta = ({
slug
}: {
title: string
description?: string
description: string
image?: string
slug?: string
}) => {
@ -24,7 +24,7 @@ const Meta = ({
{/* <!-- Essential META Tags --> */}
<title>{title}</title>
<meta name="description" content={description} />
<meta name="description" content={`${description.slice(0, 200)}...`} />
<meta property="og:title" content={title} />
<meta property="og:image" content={`${meta.url}/${image}`} />
<meta property="og:url" content={url} />

View File

@ -9,7 +9,12 @@ export const NetworkLink = ({ name, url }: { name: string; url: string }) => {
return (
<LazyMotion features={domAnimation}>
<m.a variants={moveInTop} className={linkClasses} href={url}>
<m.a
aria-label={name}
variants={moveInTop}
className={linkClasses}
href={url}
>
<Icon name={name} />
<span className={styles.title}>{name}</span>
</m.a>

View File

@ -3,13 +3,13 @@ import Networks from '.'
describe('Networks', () => {
it('renders correctly from data file values', () => {
const { container } = render(<Networks />)
const { container } = render(<Networks label="Networks" />)
expect(container.firstChild).toBeInTheDocument()
expect(container.firstChild.nodeName).toBe('ASIDE')
expect(container.firstChild.nodeName).toBe('SECTION')
})
it('renders correctly in small variant', () => {
const { container } = render(<Networks small={true} />)
const { container } = render(<Networks label="Networks" small={true} />)
expect(container.firstChild).toBeInTheDocument()
expect(container.querySelector('.small')).toBeInTheDocument()
})

View File

@ -5,6 +5,7 @@ import { getAnimationProps } from '../Transitions'
import { NetworkLink } from './NetworkLink'
type Props = {
label: string
small?: boolean
}
@ -17,13 +18,14 @@ const containerVariants = {
}
}
export default function Networks({ small }: Props) {
export default function Networks({ label, small }: Props) {
const shouldReduceMotion = useReducedMotion()
const animationProps = getAnimationProps(shouldReduceMotion)
return (
<LazyMotion features={domAnimation}>
<m.aside
<m.section
aria-label={label}
variants={containerVariants}
{...animationProps}
className={small ? styles.small : styles.networks}
@ -41,7 +43,7 @@ export default function Networks({ small }: Props) {
url={profile.url}
/>
))}
</m.aside>
</m.section>
</LazyMotion>
)
}

View File

@ -9,9 +9,9 @@ export default function ProjectLinks({
}) {
return (
<div className={styles.projectLinks}>
<h3 className={styles.title}>
<h2 className={styles.title}>
Links <span>Learn more on the interwebz.</span>
</h3>
</h2>
<ul>
{links.map(({ title, url, icon }) => (

View File

@ -2,9 +2,9 @@ import styles from './index.module.css'
const ProjectTechstack = ({ techstack }: { techstack: string[] }) => (
<div className={styles.projectTechstack}>
<h3 className={styles.title}>
<h2 className={styles.title}>
Tools & Technologies <span>The tech stack I was involved with.</span>
</h3>
</h2>
<ul>
{techstack.map((tech) => (
<li key={tech}>{tech}</li>

View File

@ -45,7 +45,7 @@ export default function Project({ project }: { project: ProjectType }) {
<ProjectImage
className={styles.fullContainer}
image={image}
alt={title}
alt={`Showcase image no. ${i + 1} for ${title}`}
key={i}
sizes="100vw"
// give priority to the first image

View File

@ -20,13 +20,13 @@ export default function ProjectPreview({
<Link href={`/${slug}`} className={styles.project} key={slug}>
<ProjectImage
image={image}
alt={title}
alt={`Showcase image for ${title}`}
sizes="(max-width: 1090px) 100vw, 40vw"
priority={imagePriority}
/>
<footer className={styles.meta}>
<h1 className={styles.title}>{title}</h1>
<h2 className={styles.title}>{title}</h2>
</footer>
</Link>
)

View File

@ -8,7 +8,7 @@ type Props = {
export default function Projects({ projects }: Props) {
return (
<section className={styles.projects}>
<nav className={styles.projects}>
{projects.length > 0 &&
projects.map((project, i) => (
<ProjectPreview
@ -20,6 +20,6 @@ export default function Projects({ projects }: Props) {
slug={project.slug}
/>
))}
</section>
</nav>
)
}

View File

@ -7,5 +7,5 @@
.repos {
composes: projects from '../Projects/index.module.css';
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

View File

@ -17,7 +17,7 @@ describe('Repository', () => {
}
const { container } = render(<Repository repo={repo1 as Repo} />)
expect(container.querySelector('h1 > a').getAttribute('href')).toBe(
expect(container.querySelector('h3 > a').getAttribute('href')).toBe(
repo1.html_url
)
})

View File

@ -17,9 +17,9 @@ export default function Repository({ repo }: { repo: Repo }) {
return (
<div className={styles.repo}>
<h1 className={styles.repoTitle}>
<h3 className={styles.repoTitle}>
<a href={repoLink}>{isExternal ? full_name : name}</a>
</h1>
</h3>
<p>{description}</p>
<p className={styles.meta}>
{name === 'portfolio' || name === 'blog'
@ -35,7 +35,10 @@ export default function Repository({ repo }: { repo: Repo }) {
<Icon name="GitHub" /> GitHub
</a>
<a href={`${html_url}/stargazers`}>
<a
aria-label={`${stargazers_count} stars`}
href={`${html_url}/stargazers`}
>
<Icon name="Star" /> {stargazers_count}
</a>
</p>

View File

@ -17,7 +17,7 @@ export default function ThemeSwitch() {
content="black-translucent"
/>
</Head>
<aside className={styles.themeSwitch}>
<aside aria-label="Theme Switch" className={styles.themeSwitch}>
<label className={styles.checkbox}>
<span className={styles.label}>Toggle Night Mode</span>
<ThemeToggleInput />

View File

@ -3,7 +3,7 @@ import Meta from '../../components/Meta'
type Props = {
children: React.ReactNode
title: string
description?: string
description: string
image?: string
slug?: string
}

View File

@ -2,7 +2,8 @@ import NotFound from '../components/404'
import Page from '../layouts/Page'
const pageMeta = {
title: `NotFound`
title: `Shenanigans`,
description: 'Page not found.'
}
export default function NotFoundPage() {