feat: improved tab navigation

This commit is contained in:
Cory Dransfeldt 2024-06-18 15:32:25 -07:00
parent 7943179e16
commit ccce1537fd
No known key found for this signature in database
20 changed files with 171 additions and 59 deletions

View file

@ -39,6 +39,7 @@
/posts/2023/my-default-apps-2023-edition/ /uses 301
/posts/2024/link-blogging-using-readwise/ /posts/2024/link-blogging-using-readwise-reader/ 301
/2022/12/automating-email-cleanup-in-gmail /posts/2022/automating-email-cleanup-in-gmail/ 301
/posts/2023/automate-syndicate-content-mastodon-eleventy/ /posts/2023/automate-and-syndicate-content-from-eleventy-to-mastodon/ 301
# music
/music/artists/hyperdontia-denmark-turkiye /music/artists/hyperdontia-denmark-tuerkiye 301

View file

@ -142,6 +142,7 @@ export default {
normalized['title'] = item['title']
normalized['alt'] = item['title']
normalized['rating'] = item['rating']
normalized['favorite'] = item['favorite']
normalized['subtext'] = item['rating']
}
if (item.type === 'book') {

16
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "coryd.dev",
"version": "19.4.9",
"version": "19.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "coryd.dev",
"version": "19.4.9",
"version": "19.5.0",
"license": "MIT",
"dependencies": {
"@cdransf/api-text": "^1.4.0",
@ -532,9 +532,9 @@
"peer": true
},
"node_modules/@types/node": {
"version": "20.14.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.4.tgz",
"integrity": "sha512-1ChboN+57suCT2t/f8lwtPY/k3qTpuD/qnqQuYoBg6OQOcPyaw7PiZVdGpaZYAvhDDtqrt0oAaM8+oSu1xsUGw==",
"version": "20.14.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz",
"integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1149,9 +1149,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.4.804",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.804.tgz",
"integrity": "sha512-gXMMs2m7aUTdZpORQAvMCyH0JHywSpZxjblSc/C81aDr34jh0hmpplTFcM4AYrYALVmiVT/r63oA3tEG1BPVRw==",
"version": "1.4.805",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz",
"integrity": "sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw==",
"dev": true,
"license": "ISC"
},

View file

@ -1,6 +1,6 @@
{
"name": "coryd.dev",
"version": "19.4.9",
"version": "19.5.0",
"description": "The source for my personal site. Built using 11ty.",
"type": "module",
"scripts": {

View file

@ -1,7 +1,7 @@
<section class="main-title">
<h1>
{% if page.url != '/' %}
<a href="/">{{ meta.siteName }}</a>
<a href="/" tabindex="0">{{ meta.siteName }}</a>
{% else %}
{{ meta.siteName }}
{% endif %}

View file

@ -10,9 +10,11 @@
<div class="header">{{ item.title }}</div>
<div class="subheader flex-centered">
{{ item.year }}
{% if rating and item.rating %}
<span class="rating"> ({{ item.rating }})</span>
{% endif %}
{% unless item.favorite %}
{% if item.rating %}
<span class="rating"> ({{ item.rating }})</span>
{% endif %}
{% endunless %} %}
</div>
{% else %}
<div class="header">{{ item.name }}</div>

View file

@ -9,7 +9,7 @@
{% endif %}
</span>
{% else %}
<a class="{% if icon %}{{ icon | downcase }} icon {% endif %}{{ class }}" href="{{ categoryUrl }}">
<a class="{% if icon %}{{ icon | downcase }} icon {% endif %}{{ class }}" href="{{ categoryUrl }}" tabindex="0">
{% if icon %}
{% tablericon icon link %}
<span>{{ link }}</span>

View file

@ -2,6 +2,7 @@
class="{{ icon }}"
href="{{ link | downcase }}"
rel="me"
title="{{ name }}">
title="{{ name }}"
tabindex="0">
{% tablericon icon name %}
</a>

View file

@ -1,12 +1,18 @@
{% capture js %}
{% render "../../../assets/scripts/menu.js" %}
{% endcapture %}
<script>{{ js }}</script>
<div class="flex-centered">
<input id="menu-toggle" type="checkbox" />
<label class="menu-button-container" for="menu-toggle">
<div class="menu-closed">{% tablericon "menu" "Menu closed" %}</div>
<div class="menu-open">{% tablericon "x" "Menu open" %}</div>
<input id="menu-toggle" type="checkbox" aria-hidden="true" />
<label class="menu-button-container" for="menu-toggle" aria-controls="primary-navigation" aria-expanded="false" tabindex="0">
<div class="menu-closed" aria-hidden="true">{% tablericon "menu" "Menu closed" %}</div>
<div class="menu-open" aria-hidden="true">{% tablericon "x" "Menu open" %}</div>
</label>
<ul class="menu-primary" aria-label="Primary site navigation">
<ul class="menu-primary" aria-label="Primary site navigation" id="primary-navigation" role="menu">
{% for link in nav.menu %}
<li>{% render "partials/nav/link.liquid", page:page, link:link.name, icon:link.icon %}</li>
<li role="menu-item">
{% render "partials/nav/link.liquid", page:page, link:link.name, icon:link.icon %}
</li>
{% endfor %}
</ul>
{% render "partials/nav/theme-toggle.liquid" %}

View file

@ -1,9 +1,7 @@
<script type="module" src="/assets/scripts/components/theme-toggle.js"></script>
<span class="client-side">
<theme-toggle>
<button
class="theme-toggle"
aria-label="Toggle site theme">
<button class="theme-toggle" aria-label="Toggle site theme" role="menuitem" tabindex="0">
<span class="light">
{% tablericon "sun" "Toggle light theme" %}
</span>

View file

@ -1,4 +1,4 @@
window.onload = () => {
window.addEventListener('load', () => {
const initializeButtonSet = (buttonSet) => {
const buttons = buttonSet.querySelectorAll('button')
const buttonIds = Array.from(buttons).map(button => button.getAttribute('data-toggle'))
@ -27,4 +27,4 @@ window.onload = () => {
const buttonSets = document.querySelectorAll('.section-header-buttons')
buttonSets.forEach(initializeButtonSet)
}
})

View file

@ -0,0 +1,64 @@
window.addEventListener('load', () => {
const menuInput = document.getElementById('menu-toggle')
const menuButtonContainer = document.querySelector('.menu-button-container')
const menuItems = document.querySelectorAll('.menu-primary li[role="menu-item"]')
const isMobile = () => window.innerWidth <= 768
const updateTabIndex = () => {
const isExpanded = menuInput.checked
menuButtonContainer.setAttribute('aria-expanded', isExpanded)
menuItems.forEach(item => {
const link = item.querySelector('a')
if (link) link.setAttribute('tabindex', isMobile() && !isExpanded ? '-1' : '0')
})
}
const handleMenuChange = () => {
updateTabIndex()
if (menuInput.checked) {
const firstLink = menuItems[0].querySelector('a')
if (firstLink) firstLink.focus()
} else {
menuButtonContainer.focus()
}
}
updateTabIndex()
menuInput.addEventListener('change', handleMenuChange)
menuButtonContainer.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
menuInput.checked = !menuInput.checked
handleMenuChange()
}
})
menuItems.forEach(item => {
item.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
item.querySelector('a').click()
}
})
})
document.addEventListener('keydown', e => {
if (e.key === 'Escape') {
if (isMobile() && menuInput.checked) {
menuInput.checked = false
handleMenuChange()
}
}
})
window.addEventListener('resize', () => {
updateTabIndex()
if (!isMobile() && menuInput.checked) {
menuInput.checked = false
handleMenuChange()
}
})
})

View file

@ -1,4 +1,4 @@
window.onload = () => {
window.addEventListener('load', () => {
const button = document.querySelector('[data-toggle-button]')
const content = document.querySelector('[data-toggle-content]')
const text = document.querySelectorAll('[data-toggle-content] p')
@ -21,4 +21,4 @@ window.onload = () => {
button.textContent = 'Show more'
}
});
}
})

View file

@ -35,6 +35,11 @@ body {
height: var(--sizing-xs);
}
a:focus,
a:focus-within {
outline: 2px dashed var(--accent-color);
}
body::-webkit-scrollbar {
width: var(--sizing-md);
height: var(--sizing-md);

View file

@ -1,4 +1,9 @@
button {
&:focus,
&:focus-within {
outline: 2px dashed var(--accent-color);
}
&:not(.theme-toggle) {
border-radius: var(--rounded-full);
padding: var(--sizing-sm) var(--sizing-lg);

View file

@ -18,16 +18,13 @@ textarea {
margin-bottom: var(--sizing-base);
font-weight: var(--font-weight-base);
line-height: var(--line-height-base);
transition-property: border-color;
transition-timing-function: var(--transition-ease-in-out);
transition-duration: var(--transition-duration-default);
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="search"]:focus,
textarea:focus {
border: 1px solid var(--accent-color-hover);
input:focus,
input:focus-within,
textarea:focus,
textarea:focus-within {
outline: 2px dashed var(--accent-color);
}
.search__results {

View file

@ -24,22 +24,25 @@
.menu-button-container {
display: none;
margin-left: var(--sizing-md);
outline: 0;
& .menu-open,
& .menu-closed {
cursor: pointer;
&:focus,
&:focus-within {
outline: 2px dashed var(--accent-color);
}
& svg {
cursor: pointer;
transform: rotate(0deg);
transition-property: transform;
transition-timing-function: var(--transition-ease-in-out);
transition-duration: var(--transition-duration-default);
}
& svg:hover,
& svg:active,
& svg:focus {
&:hover svg,
&:focus svg,
&:focus-within svg,
&:active svg {
stroke: var(--accent-color-hover);
transform: rotate(8deg);
}
@ -49,24 +52,24 @@
display: none;
}
#menu-toggle:checked + .menu-button-container {
& .menu-closed {
display: none;
}
& .menu-open {
display: block;
}
#menu-toggle:checked + .menu-button-container .menu-closed {
display: none;
}
#menu-toggle:not(:checked) + .menu-button-container {
& .menu-closed {
display: block;
}
#menu-toggle:checked + .menu-button-container .menu-open {
display: block;
}
& .menu-open {
display: none;
}
#menu-toggle:not(:checked) + .menu-button-container .menu-closed {
display: block;
}
#menu-toggle:not(:checked) + .menu-button-container .menu-open {
display: none;
}
a[role="menu-item"]:focus {
outline: none;
}
@media (max-width: 768px) {
@ -120,6 +123,17 @@
& .active {
font-size: var(--font-size-lg);
}
&:focus a,
&:focus-within a {
outline: 0;
}
&:focus,
&:focus-within {
border-top: 2px dashed var(--accent-color);
border-bottom: 2px dashed var(--accent-color);
}
}
.menu-button-container {

View file

@ -11,6 +11,18 @@ theme-toggle {
& svg {
cursor: pointer;
transform: rotate(0deg);
transition-property: transform;
transition-timing-function: var(--transition-ease-in-out);
transition-duration: var(--transition-duration-default);
}
&:hover svg,
&:focus svg,
&:focus-within svg,
&:active svg {
stroke: var(--accent-color-hover);
transform: rotate(8deg);
}
& > .light svg { stroke: var(--sun) !important; }

View file

@ -37,8 +37,8 @@
}
& .progress-bar-wrapper {
margin-top: var(--font-size-xs);
max-width: 75%;
margin-bottom: 0;
}
& blockquote.description {
@ -65,6 +65,7 @@
& p {
&.title {
font-size: var(--font-size-xl);
line-height: 1;
}
&.sub-meta {
@ -109,6 +110,7 @@
align-items: start;
& .progress-bar-wrapper {
margin-top: 0;
max-width: 40%;
}
}

View file

@ -36,6 +36,10 @@ permalink: /search.html
renderSearchResults(results)
})
$input.addEventListener('keydown', (event) => {
if (event.key === 'Enter') event.preventDefault()
})
const getSearchResults = (query) => miniSearch.search(query, { prefix: true, fuzzy: 0.2, boost: { title: 2 } }).map(({ id }) => resultsById[id])
const renderSearchResults = (results) => {
$results.innerHTML = results.map(({ title, url }) => {
@ -48,7 +52,7 @@ permalink: /search.html
$results.classList.add('hidden')
}
}
})();
})()
</script>
<form class="search__form" action="https://duckduckgo.com" method="get">
<input class="search__form--input" placeholder="Search" type="search" name="q" autocomplete="off" autofocus>