mirror of
https://github.com/kremalicious/portfolio.git
synced 2024-12-22 17:23:22 +01:00
accessibility fixes
This commit is contained in:
parent
93fa06bf1b
commit
20b285a760
@ -13,13 +13,13 @@ export default function Availability() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LazyMotion features={domAnimation}>
|
<LazyMotion features={domAnimation}>
|
||||||
<m.aside
|
<m.section
|
||||||
variants={moveInBottom}
|
variants={moveInBottom}
|
||||||
className={className}
|
className={className}
|
||||||
{...getAnimationProps(shouldReduceMotion)}
|
{...getAnimationProps(shouldReduceMotion)}
|
||||||
>
|
>
|
||||||
<p dangerouslySetInnerHTML={{ __html: html }} />
|
<p dangerouslySetInnerHTML={{ __html: html }} />
|
||||||
</m.aside>
|
</m.section>
|
||||||
</LazyMotion>
|
</LazyMotion>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ export default function Footer() {
|
|||||||
return (
|
return (
|
||||||
<footer className={`h-card ${styles.footer}`}>
|
<footer className={`h-card ${styles.footer}`}>
|
||||||
<LogoUnit small />
|
<LogoUnit small />
|
||||||
<Networks small />
|
<Networks label="Networks Footer" small />
|
||||||
|
|
||||||
<p className={styles.actions}>
|
<p className={styles.actions}>
|
||||||
<Vcard />
|
<Vcard />
|
||||||
|
@ -17,7 +17,7 @@ export default function Header({ small }: Props) {
|
|||||||
return (
|
return (
|
||||||
<header className={`${styles.header} ${small ? styles.small : ''}`}>
|
<header className={`${styles.header} ${small ? styles.small : ''}`}>
|
||||||
<LogoUnit small={small} />
|
<LogoUnit small={small} />
|
||||||
{!small ? <Networks /> : null}
|
{!small ? <Networks label="Networks" /> : null}
|
||||||
<div className={styles.meta}>
|
<div className={styles.meta}>
|
||||||
{!small ? (
|
{!small ? (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
|
24
src/components/Location/Flag.tsx
Normal file
24
src/components/Location/Flag.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -1,24 +1,9 @@
|
|||||||
import { LazyMotion, domAnimation, m, useReducedMotion } from 'framer-motion'
|
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 styles from './index.module.css'
|
||||||
import { useLocation } from '../../hooks/useLocation'
|
import { useLocation } from '../../hooks/useLocation'
|
||||||
import RelativeTime from '@yaireo/relative-time'
|
import RelativeTime from '@yaireo/relative-time'
|
||||||
|
import { Flag } from './Flag'
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Location() {
|
export default function Location() {
|
||||||
const { now, next } = useLocation()
|
const { now, next } = useLocation()
|
||||||
@ -28,23 +13,28 @@ export default function Location() {
|
|||||||
|
|
||||||
return now?.city ? (
|
return now?.city ? (
|
||||||
<LazyMotion features={domAnimation}>
|
<LazyMotion features={domAnimation}>
|
||||||
<m.aside
|
<m.section
|
||||||
|
aria-label="Location"
|
||||||
variants={moveInTop}
|
variants={moveInTop}
|
||||||
className={styles.location}
|
className={styles.location}
|
||||||
{...getAnimationProps(shouldReduceMotion)}
|
{...getAnimationProps(shouldReduceMotion)}
|
||||||
>
|
>
|
||||||
<Flag countryCode={now?.country_code} />
|
<Flag country={{ code: now.country_code, name: now.country }} />
|
||||||
{now?.city} <span>Now</span>
|
{now?.city} <span>Now</span>
|
||||||
<div className={styles.next}>
|
<div className={styles.next}>
|
||||||
{next?.city && (
|
{next?.city && (
|
||||||
<>
|
<>
|
||||||
{isDifferentCountry && <Flag countryCode={next.country_code} />}
|
{isDifferentCountry && (
|
||||||
|
<Flag
|
||||||
|
country={{ code: next.country_code, name: next.country }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{next.city}{' '}
|
{next.city}{' '}
|
||||||
<span>{relativeTime.from(new Date(next.date_start))}</span>
|
<span>{relativeTime.from(new Date(next.date_start))}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</m.aside>
|
</m.section>
|
||||||
</LazyMotion>
|
</LazyMotion>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,22 @@ import Link from 'next/link'
|
|||||||
import Logo from '../../images/logo.svg'
|
import Logo from '../../images/logo.svg'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import resume from '../../../_content/resume.json'
|
import resume from '../../../_content/resume.json'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
small?: boolean
|
small?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LogoUnit({ small }: Props) {
|
export default function LogoUnit({ small }: Props) {
|
||||||
|
const router = useRouter()
|
||||||
|
const { pathname } = router
|
||||||
|
const isHome = pathname === '/'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={`${styles.logounit} ${small ? styles.small : null}`}
|
className={`${styles.logounit} ${small ? styles.small : null}`}
|
||||||
href="/"
|
href="/"
|
||||||
|
aria-current={isHome ? 'page' : null}
|
||||||
>
|
>
|
||||||
<Logo className={styles.logo} />
|
<Logo className={styles.logo} />
|
||||||
<h1 className={`p-name ${styles.title}`}>
|
<h1 className={`p-name ${styles.title}`}>
|
||||||
|
@ -3,6 +3,7 @@ import Head from 'next/head'
|
|||||||
const MetaFavicons = () => {
|
const MetaFavicons = () => {
|
||||||
return (
|
return (
|
||||||
<Head>
|
<Head>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1"></meta>
|
||||||
{/*
|
{/*
|
||||||
Stop the favicon madness
|
Stop the favicon madness
|
||||||
https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs
|
https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs
|
||||||
|
@ -4,7 +4,7 @@ import Meta from '.'
|
|||||||
describe('Meta', () => {
|
describe('Meta', () => {
|
||||||
it('renders without crashing', async () => {
|
it('renders without crashing', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<Meta title="Hello World" />, {
|
render(<Meta title="Hello World" description="Hello" />, {
|
||||||
container: document.head
|
container: document.head
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -13,7 +13,7 @@ describe('Meta', () => {
|
|||||||
|
|
||||||
it('renders without crashing with slug', async () => {
|
it('renders without crashing with slug', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<Meta title="Hello World" slug="hello" />, {
|
render(<Meta title="Hello World" description="Hello" slug="hello" />, {
|
||||||
container: document.head
|
container: document.head
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,7 @@ const Meta = ({
|
|||||||
slug
|
slug
|
||||||
}: {
|
}: {
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description: string
|
||||||
image?: string
|
image?: string
|
||||||
slug?: string
|
slug?: string
|
||||||
}) => {
|
}) => {
|
||||||
@ -24,7 +24,7 @@ const Meta = ({
|
|||||||
|
|
||||||
{/* <!-- Essential META Tags --> */}
|
{/* <!-- Essential META Tags --> */}
|
||||||
<title>{title}</title>
|
<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:title" content={title} />
|
||||||
<meta property="og:image" content={`${meta.url}/${image}`} />
|
<meta property="og:image" content={`${meta.url}/${image}`} />
|
||||||
<meta property="og:url" content={url} />
|
<meta property="og:url" content={url} />
|
||||||
|
@ -9,7 +9,12 @@ export const NetworkLink = ({ name, url }: { name: string; url: string }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LazyMotion features={domAnimation}>
|
<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} />
|
<Icon name={name} />
|
||||||
<span className={styles.title}>{name}</span>
|
<span className={styles.title}>{name}</span>
|
||||||
</m.a>
|
</m.a>
|
||||||
|
@ -3,13 +3,13 @@ import Networks from '.'
|
|||||||
|
|
||||||
describe('Networks', () => {
|
describe('Networks', () => {
|
||||||
it('renders correctly from data file values', () => {
|
it('renders correctly from data file values', () => {
|
||||||
const { container } = render(<Networks />)
|
const { container } = render(<Networks label="Networks" />)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
expect(container.firstChild.nodeName).toBe('ASIDE')
|
expect(container.firstChild.nodeName).toBe('SECTION')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders correctly in small variant', () => {
|
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.firstChild).toBeInTheDocument()
|
||||||
expect(container.querySelector('.small')).toBeInTheDocument()
|
expect(container.querySelector('.small')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,7 @@ import { getAnimationProps } from '../Transitions'
|
|||||||
import { NetworkLink } from './NetworkLink'
|
import { NetworkLink } from './NetworkLink'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
label: string
|
||||||
small?: boolean
|
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 shouldReduceMotion = useReducedMotion()
|
||||||
const animationProps = getAnimationProps(shouldReduceMotion)
|
const animationProps = getAnimationProps(shouldReduceMotion)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LazyMotion features={domAnimation}>
|
<LazyMotion features={domAnimation}>
|
||||||
<m.aside
|
<m.section
|
||||||
|
aria-label={label}
|
||||||
variants={containerVariants}
|
variants={containerVariants}
|
||||||
{...animationProps}
|
{...animationProps}
|
||||||
className={small ? styles.small : styles.networks}
|
className={small ? styles.small : styles.networks}
|
||||||
@ -41,7 +43,7 @@ export default function Networks({ small }: Props) {
|
|||||||
url={profile.url}
|
url={profile.url}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</m.aside>
|
</m.section>
|
||||||
</LazyMotion>
|
</LazyMotion>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ export default function ProjectLinks({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.projectLinks}>
|
<div className={styles.projectLinks}>
|
||||||
<h3 className={styles.title}>
|
<h2 className={styles.title}>
|
||||||
Links <span>Learn more on the interwebz.</span>
|
Links <span>Learn more on the interwebz.</span>
|
||||||
</h3>
|
</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{links.map(({ title, url, icon }) => (
|
{links.map(({ title, url, icon }) => (
|
||||||
|
@ -2,9 +2,9 @@ import styles from './index.module.css'
|
|||||||
|
|
||||||
const ProjectTechstack = ({ techstack }: { techstack: string[] }) => (
|
const ProjectTechstack = ({ techstack }: { techstack: string[] }) => (
|
||||||
<div className={styles.projectTechstack}>
|
<div className={styles.projectTechstack}>
|
||||||
<h3 className={styles.title}>
|
<h2 className={styles.title}>
|
||||||
Tools & Technologies <span>The tech stack I was involved with.</span>
|
Tools & Technologies <span>The tech stack I was involved with.</span>
|
||||||
</h3>
|
</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{techstack.map((tech) => (
|
{techstack.map((tech) => (
|
||||||
<li key={tech}>{tech}</li>
|
<li key={tech}>{tech}</li>
|
||||||
|
@ -45,7 +45,7 @@ export default function Project({ project }: { project: ProjectType }) {
|
|||||||
<ProjectImage
|
<ProjectImage
|
||||||
className={styles.fullContainer}
|
className={styles.fullContainer}
|
||||||
image={image}
|
image={image}
|
||||||
alt={title}
|
alt={`Showcase image no. ${i + 1} for ${title}`}
|
||||||
key={i}
|
key={i}
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
// give priority to the first image
|
// give priority to the first image
|
||||||
|
@ -20,13 +20,13 @@ export default function ProjectPreview({
|
|||||||
<Link href={`/${slug}`} className={styles.project} key={slug}>
|
<Link href={`/${slug}`} className={styles.project} key={slug}>
|
||||||
<ProjectImage
|
<ProjectImage
|
||||||
image={image}
|
image={image}
|
||||||
alt={title}
|
alt={`Showcase image for ${title}`}
|
||||||
sizes="(max-width: 1090px) 100vw, 40vw"
|
sizes="(max-width: 1090px) 100vw, 40vw"
|
||||||
priority={imagePriority}
|
priority={imagePriority}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<footer className={styles.meta}>
|
<footer className={styles.meta}>
|
||||||
<h1 className={styles.title}>{title}</h1>
|
<h2 className={styles.title}>{title}</h2>
|
||||||
</footer>
|
</footer>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,7 @@ type Props = {
|
|||||||
|
|
||||||
export default function Projects({ projects }: Props) {
|
export default function Projects({ projects }: Props) {
|
||||||
return (
|
return (
|
||||||
<section className={styles.projects}>
|
<nav className={styles.projects}>
|
||||||
{projects.length > 0 &&
|
{projects.length > 0 &&
|
||||||
projects.map((project, i) => (
|
projects.map((project, i) => (
|
||||||
<ProjectPreview
|
<ProjectPreview
|
||||||
@ -20,6 +20,6 @@ export default function Projects({ projects }: Props) {
|
|||||||
slug={project.slug}
|
slug={project.slug}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</section>
|
</nav>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
|
|
||||||
.repos {
|
.repos {
|
||||||
composes: projects from '../Projects/index.module.css';
|
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));
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ describe('Repository', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { container } = render(<Repository repo={repo1 as Repo} />)
|
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
|
repo1.html_url
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -17,9 +17,9 @@ export default function Repository({ repo }: { repo: Repo }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.repo}>
|
<div className={styles.repo}>
|
||||||
<h1 className={styles.repoTitle}>
|
<h3 className={styles.repoTitle}>
|
||||||
<a href={repoLink}>{isExternal ? full_name : name}</a>
|
<a href={repoLink}>{isExternal ? full_name : name}</a>
|
||||||
</h1>
|
</h3>
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
<p className={styles.meta}>
|
<p className={styles.meta}>
|
||||||
{name === 'portfolio' || name === 'blog'
|
{name === 'portfolio' || name === 'blog'
|
||||||
@ -35,7 +35,10 @@ export default function Repository({ repo }: { repo: Repo }) {
|
|||||||
<Icon name="GitHub" /> GitHub
|
<Icon name="GitHub" /> GitHub
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href={`${html_url}/stargazers`}>
|
<a
|
||||||
|
aria-label={`${stargazers_count} stars`}
|
||||||
|
href={`${html_url}/stargazers`}
|
||||||
|
>
|
||||||
<Icon name="Star" /> {stargazers_count}
|
<Icon name="Star" /> {stargazers_count}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -17,7 +17,7 @@ export default function ThemeSwitch() {
|
|||||||
content="black-translucent"
|
content="black-translucent"
|
||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<aside className={styles.themeSwitch}>
|
<aside aria-label="Theme Switch" className={styles.themeSwitch}>
|
||||||
<label className={styles.checkbox}>
|
<label className={styles.checkbox}>
|
||||||
<span className={styles.label}>Toggle Night Mode</span>
|
<span className={styles.label}>Toggle Night Mode</span>
|
||||||
<ThemeToggleInput />
|
<ThemeToggleInput />
|
||||||
|
@ -3,7 +3,7 @@ import Meta from '../../components/Meta'
|
|||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
title: string
|
title: string
|
||||||
description?: string
|
description: string
|
||||||
image?: string
|
image?: string
|
||||||
slug?: string
|
slug?: string
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ import NotFound from '../components/404'
|
|||||||
import Page from '../layouts/Page'
|
import Page from '../layouts/Page'
|
||||||
|
|
||||||
const pageMeta = {
|
const pageMeta = {
|
||||||
title: `NotFound`
|
title: `Shenanigans`,
|
||||||
|
description: 'Page not found.'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NotFoundPage() {
|
export default function NotFoundPage() {
|
||||||
|
Loading…
Reference in New Issue
Block a user