Improve keyboard navigation

This commit is contained in:
DrMint 2024-05-24 12:16:17 +02:00
parent 22e8aee234
commit f7c0c3f784
13 changed files with 136 additions and 100 deletions

View File

@ -32,35 +32,35 @@ const contactLabel = `${t("footer.socials.contact.title")} - ${t(
<div id="nav" class="when-no-print"> <div id="nav" class="when-no-print">
<p class="font-serif">{t("global.siteName")}</p> <p class="font-serif">{t("global.siteName")}</p>
<div> <div>
<a href="/"> <a class="pressable-label" href="/">
<Icon name="accords" /> <Icon name="accords" />
<p>{t("footer.links.home.title")}</p> <p>{t("footer.links.home.title")}</p>
</a> </a>
<a href="/archives" class="DEV_TODO"> <a class="pressable-label DEV_TODO" href="/archives">
<Icon name="material-symbols:browse" /> <Icon name="material-symbols:browse" />
<p>{"Contents"}</p> <p>{"Contents"}</p>
</a> </a>
<a href="/chronicles" class="DEV_TODO"> <a class="pressable-label DEV_TODO" href="/chronicles">
<Icon name="material-symbols:book-2" /> <Icon name="material-symbols:book-2" />
<p>{"Chronicles"}</p> <p>{"Chronicles"}</p>
</a> </a>
<a href="/changelog" class="DEV_TODO"> <a class="pressable-label DEV_TODO" href="/changelog">
<Icon name="material-symbols:history" /> <Icon name="material-symbols:history" />
<p>{"Changelog"}</p> <p>{"Changelog"}</p>
</a> </a>
<a href="/timeline"> <a class="pressable-label" href="/timeline">
<Icon name="material-symbols:calendar-month" /> <Icon name="material-symbols:calendar-month" />
<p>{t("footer.links.timeline.title")}</p> <p>{t("footer.links.timeline.title")}</p>
</a> </a>
<a href="https://gallery.accords-library.com/posts"> <a class="pressable-label" href="https://gallery.accords-library.com/posts">
<Icon name="material-symbols:perm-media" /> <Icon name="material-symbols:perm-media" />
<p>{t("footer.links.gallery.title")}</p> <p>{t("footer.links.gallery.title")}</p>
</a> </a>
<a href="/videos" class="DEV_TODO"> <a class="pressable-label DEV_TODO" href="/videos">
<Icon name="material-symbols:movie" /> <Icon name="material-symbols:movie" />
<p>{t("footer.links.videos.title")}</p> <p>{t("footer.links.videos.title")}</p>
</a> </a>
<a href="/archives" class="DEV_TODO"> <a class="pressable-label DEV_TODO" href="/archives">
<Icon name="material-symbols:folder-zip" /> <Icon name="material-symbols:folder-zip" />
<p>{t("footer.links.webArchives.title")}</p> <p>{t("footer.links.webArchives.title")}</p>
</a> </a>
@ -166,10 +166,6 @@ const contactLabel = `${t("footer.socials.contact.title")} - ${t(
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
margin-left: -0.6em; margin-left: -0.6em;
@media (max-width: 65rem) {
gap: unset;
}
@media (max-width: 35rem) { @media (max-width: 35rem) {
gap: 0.25em 0.5em; gap: 0.25em 0.5em;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
@ -184,35 +180,6 @@ const contactLabel = `${t("footer.socials.contact.title")} - ${t(
margin-left: unset; margin-left: unset;
font-size: 1.2em; font-size: 1.2em;
} }
& > a {
display: flex;
place-items: center;
text-decoration: none;
gap: 0.4em;
padding: 0.4em 0.6em;
border-radius: 9999px;
@media (max-width: 35rem) {
padding: 0.6em 0.8em;
}
transition: 150ms background-color;
&:hover {
background-color: var(--color-base-250);
}
&:active {
background-color: var(--color-base-300);
}
& > svg {
flex-shrink: 0;
height: 0.75em;
width: 0.75em;
}
}
} }
} }
@ -247,6 +214,7 @@ const contactLabel = `${t("footer.socials.contact.title")} - ${t(
justify-content: flex-start; justify-content: flex-start;
gap: 0.2em; gap: 0.2em;
margin-top: 0.5em; margin-top: 0.5em;
width: fit-content;
& > svg { & > svg {
width: 1em; width: 1em;

View File

@ -362,6 +362,8 @@ const { currentTheme } = Astro.locals;
button { button {
background-color: unset; background-color: unset;
font-size: 1em;
border: unset;
} }
button, button,
@ -450,10 +452,16 @@ const { currentTheme } = Astro.locals;
transition: 150ms background-color; transition: 150ms background-color;
&:hover { &:hover,
&:focus-visible {
background-color: var(--color-base-250); background-color: var(--color-base-250);
} }
&:focus-visible {
outline: 3px solid var(--color-base-1000);
outline-offset: unset;
}
&:active { &:active {
background-color: var(--color-base-300); background-color: var(--color-base-300);
} }
@ -468,12 +476,18 @@ const { currentTheme } = Astro.locals;
transition-duration: 250ms; transition-duration: 250ms;
transition-property: padding-top, box-shadow, background-color, color, border-color; transition-property: padding-top, box-shadow, background-color, color, border-color;
&:hover { &:hover,
&:focus-visible {
--foreground-color: var(--color-base-1000); --foreground-color: var(--color-base-1000);
box-shadow: 0 2px 2px var(--color-shadow-2); box-shadow: 0 2px 2px var(--color-shadow-2);
background-color: var(--color-elevation-1); background-color: var(--color-elevation-1);
} }
&:focus-visible {
outline: 3px solid var(--foreground-color);
outline-offset: unset;
}
&:active { &:active {
transition-duration: 75ms; transition-duration: 75ms;
--foreground-color: var(--color-base-1000); --foreground-color: var(--color-base-1000);

View File

@ -77,6 +77,7 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
display: flex; display: flex;
place-items: center; place-items: center;
overflow-x: scroll; overflow-x: scroll;
padding: 3px; /* This way focus outline isn't cropped by overflow-x */
gap: 8px; gap: 8px;
margin-left: -0.8em; margin-left: -0.8em;
@ -92,6 +93,7 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
place-items: center; place-items: center;
justify-content: flex-end; justify-content: flex-end;
overflow-x: scroll; overflow-x: scroll;
padding: 3px; /* This way focus outline isn't cropped by overflow-x */
@media (max-width: 28rem) { @media (max-width: 28rem) {
justify-content: center; justify-content: center;

View File

@ -30,14 +30,14 @@ const { t } = await getI18n(Astro.locals.currentLocale);
))} ))}
</div> </div>
</div> </div>
<div class="pressable-label"> <button class="pressable-label">
<Icon name="material-symbols:keyboard-return" /> <Icon name="material-symbols:keyboard-return" />
<p> <p>
{t("header.nav.parentPages.label", { {t("header.nav.parentPages.label", {
count: parentPages.length, count: parentPages.length,
})} })}
</p> </p>
</div> </button>
</Tooltip> </Tooltip>
) )
} }

View File

@ -21,20 +21,18 @@ const {
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<a href={href} target={target} rel={rel}> <a class="pressable-label" href={href} target={target} rel={rel}>
<div class="pressable-label"> <Icon name="material-symbols:keyboard-return" />
<Icon name="material-symbols:keyboard-return" /> <p>
<p> <span>{typeLabel}</span>
<span>{typeLabel}</span> {label}
{label} </p>
</p>
</div>
</a> </a>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
<style> <style>
div { a {
height: 16px; height: 16px;
& > p { & > p {

View File

@ -1,7 +1,4 @@
--- ---
import ConditionalWrapper from "./ConditionalWrapper.astro";
import Link from "./Link.astro";
interface Props { interface Props {
href?: string | undefined; href?: string | undefined;
disableRoundedTop?: boolean; disableRoundedTop?: boolean;
@ -12,11 +9,17 @@ const { href, disableRoundedTop = false } = Astro.props;
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<ConditionalWrapper condition={href !== undefined} wrapper={Link} props={{ href: href! }}> {
<div id="card" class:list={{ "rounded-top": !disableRoundedTop }}> href !== undefined ? (
<slot /> <a href={href} id="card" class:list={{ "rounded-top": !disableRoundedTop }}>
</div> <slot />
</ConditionalWrapper> </a>
) : (
<div id="card" class:list={{ "rounded-top": !disableRoundedTop }}>
<slot />
</div>
)
}
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
@ -37,20 +40,27 @@ const { href, disableRoundedTop = false } = Astro.props;
background-color: color-mix(in srgb, var(--color-elevation-2) 30%, transparent); background-color: color-mix(in srgb, var(--color-elevation-2) 30%, transparent);
} }
:global(a) > #card { a#card {
transition-duration: 150ms; transition-duration: 150ms;
transition-property: border-color, box-shadow, background-color; transition-property: border-color, box-shadow, background-color;
&:hover { &:hover,
&:focus-visible {
border-color: var(--color-base-650); border-color: var(--color-base-650);
box-shadow: 0 2px 2px var(--color-shadow-2); box-shadow: 0 2px 2px var(--color-shadow-2);
background-color: color-mix(in srgb, var(--color-elevation-2) 80%, transparent); background-color: color-mix(in srgb, var(--color-elevation-2) 80%, transparent);
} }
&:focus-visible {
outline: 3px solid var(--color-base-650);
outline-offset: unset;
}
&:active { &:active {
border-color: var(--color-base-1000); border-color: var(--color-base-1000);
box-shadow: 0 6px 12px 2px var(--color-shadow-2); box-shadow: 0 6px 12px 2px var(--color-shadow-2);
background-color: var(--color-elevation-2); background-color: var(--color-elevation-2);
outline-color: var(--color-base-1000);
} }
} }
</style> </style>

View File

@ -12,12 +12,10 @@ const { t } = await getI18n(Astro.locals.currentLocale);
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<div class="button"> <div class="button when-no-print">
<a href={url}> <a href={url} class="pressable-label">
<div class="pressable-label"> <Icon name="material-symbols:left-click" />
<Icon name="material-symbols:left-click" /> <p>{t("global.openMediaPage")}</p>
<p>{t("global.openMediaPage")}</p>
</div>
</a> </a>
</div> </div>
@ -30,9 +28,10 @@ const { t } = await getI18n(Astro.locals.currentLocale);
gap: 0.3em; gap: 0.3em;
font-size: 85%; font-size: 85%;
& > a > div { & > a {
padding: 0.3em 0.6em; padding: 0.3em 0.6em;
padding-right: 0.8em; padding-right: 0.8em;
gap: 0.7em;
} }
} }
</style> </style>

View File

@ -44,6 +44,7 @@ const mediaPage = getLocalizedUrl(`/images/${id}`);
sizes={`(max-width: 550px) 90vw, 550px`} sizes={`(max-width: 550px) 90vw, 550px`}
width={width} width={width}
height={height} height={height}
loading="lazy"
/> />
</a> </a>
<OpenMediaPageButton url={mediaPage} /> <OpenMediaPageButton url={mediaPage} />
@ -52,24 +53,34 @@ const mediaPage = getLocalizedUrl(`/images/${id}`);
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
<style> <style>
div { a {
margin-block: 4em; display: block;
display: flex; line-height: 0;
flex-direction: column; box-shadow: 0 5px 20px -10px var(--color-shadow);
gap: 0.5em; border-radius: 16px;
overflow: hidden;
width: fit-content;
margin-bottom: 0.5em;
& > a > img { transition-property: scale, margin-bottom;
width: 100%; transition-duration: 100ms;
height: auto;
border-radius: 16px; &:hover,
box-shadow: 0 5px 20px -10px var(--color-shadow); &:focus-visible {
margin-bottom: -0.5em; scale: 102%;
margin-bottom: 1em;
} }
transition: 100ms scale; &:focus-visible {
outline: 3px solid var(--color-base-1000);
&:hover { outline-offset: unset;
scale: 102%;
} }
} }
img {
max-height: 70vh;
max-width: 100%;
width: auto;
height: auto;
}
</style> </style>

View File

@ -38,11 +38,21 @@ const {
transition-property: text-decoration-color, color; transition-property: text-decoration-color, color;
} }
&:hover > p { border-radius: 16px;
padding: 2px 5px;
margin: -2px -5px;
&:hover > p,
&:focus-visible > p {
color: var(--color-base-750); color: var(--color-base-750);
text-decoration-color: var(--color-base-650); text-decoration-color: var(--color-base-650);
} }
&:focus-visible {
outline: 2px solid var(--color-base-1000);
outline-offset: unset;
}
&:active > p { &:active > p {
color: var(--color-base-650); color: var(--color-base-650);
text-decoration-color: var(--color-base-550); text-decoration-color: var(--color-base-550);

View File

@ -68,9 +68,8 @@ if (updatedBy) {
<Fragment slot="header-aside"> <Fragment slot="header-aside">
{ {
thumbnail && ( thumbnail && (
<a href={getLocalizedUrl(`/images/${thumbnail.id}`)}> <a id="thumbnail" href={getLocalizedUrl(`/images/${thumbnail.id}`)}>
<img <img
id="thumbnail"
src={thumbnail.url} src={thumbnail.url}
srcset={sizesToSrcset(thumbnail.sizes)} srcset={sizesToSrcset(thumbnail.sizes)}
sizes={`(max-width: 550px) 90vw, 550px`} sizes={`(max-width: 550px) 90vw, 550px`}
@ -166,13 +165,19 @@ if (updatedBy) {
} }
#thumbnail { #thumbnail {
max-width: min(35rem, 100%);
max-height: 80vh;
width: auto;
height: auto;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 5px 20px -10px var(--color-shadow); box-shadow: 0 5px 20px -10px var(--color-shadow);
overflow: hidden;
transition: 100ms scale; transition: 100ms scale;
display: block;
width: fit-content;
& > img {
max-width: min(35rem, 100%);
max-height: 80vh;
width: auto;
height: auto;
}
@media (max-width: 1280.5px) { @media (max-width: 1280.5px) {
margin-top: 2em; margin-top: 2em;
@ -182,8 +187,14 @@ if (updatedBy) {
margin-bottom: 2em; margin-bottom: 2em;
} }
&:hover { &:hover,
&:focus-visible {
scale: 102%; scale: 102%;
} }
&:focus-visible {
outline: 3px solid var(--color-base-1000);
outline-offset: unset;
}
} }
</style> </style>

View File

@ -57,10 +57,16 @@ const {
transition: 100ms scale; transition: 100ms scale;
&:hover { &:hover,
&:focus-visible {
scale: 105%; scale: 105%;
} }
&:focus-visible {
outline: 3px solid var(--color-base-1000);
outline-offset: unset;
}
& > div { & > div {
text-align: center; text-align: center;
backdrop-filter: blur(5px); backdrop-filter: blur(5px);

View File

@ -256,6 +256,17 @@ if (price) {
& a { & a {
width: fit-content; width: fit-content;
transition: 100ms scale;
&:hover,
&:focus-visible {
scale: 102%;
}
&:focus-visible {
outline: 3px solid var(--color-base-1000);
outline-offset: unset;
}
& > #thumbnail { & > #thumbnail {
max-width: min(35rem, 100%); max-width: min(35rem, 100%);
@ -263,11 +274,6 @@ if (price) {
width: auto; width: auto;
height: auto; height: auto;
box-shadow: 0 5px 20px -10px var(--color-shadow); box-shadow: 0 5px 20px -10px var(--color-shadow);
transition: 100ms scale;
&:hover {
scale: 102%;
}
} }
} }

View File

@ -111,6 +111,7 @@ const meta = getLocalizedMatch(folder.translations);
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: clamp(6px, 2vmin, 16px); gap: clamp(6px, 2vmin, 16px);
align-items: start;
} }
} }
</style> </style>