mirror of
https://github.com/elk-zone/elk.git
synced 2024-11-27 03:39:43 +03:00
feat(a11y): add access keys shortcuts
This commit is contained in:
parent
25fb7c1c97
commit
ec741923f7
19 changed files with 47 additions and 14 deletions
|
@ -6,6 +6,8 @@ defineProps<{
|
|||
back?: boolean
|
||||
/** Do not applying overflow hidden to let use floatable components in title */
|
||||
noOverflowHidden?: boolean
|
||||
/** Show the skip content link */
|
||||
skipContent?: string
|
||||
}>()
|
||||
|
||||
const container = ref()
|
||||
|
@ -26,6 +28,9 @@ const containerClass = computed(() => {
|
|||
|
||||
<template>
|
||||
<div ref="container" :class="containerClass">
|
||||
<SkipContentLink v-if="skipContent">
|
||||
{{ $t(skipContent) }}
|
||||
</SkipContentLink>
|
||||
<div
|
||||
sticky top-0 z10
|
||||
pt="[env(safe-area-inset-top,0)]"
|
||||
|
|
14
components/skip/SkipContent.ts
Normal file
14
components/skip/SkipContent.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// @unocss-include
|
||||
import { accessKeys } from '~/constants/access-keys'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { t } = useI18n()
|
||||
return () => h('a', {
|
||||
id: 'skip',
|
||||
class: 'sr-only',
|
||||
href: '#skip-content',
|
||||
accesskey: accessKeys.SkipContent,
|
||||
}, t(`a11y.skip_navigation`))
|
||||
},
|
||||
})
|
5
components/skip/SkipContentLink.vue
Normal file
5
components/skip/SkipContentLink.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<NuxtLink id="skip-content" sr-only>
|
||||
<slot />
|
||||
</NuxtLink>
|
||||
</template>
|
6
constants/access-keys.ts
Normal file
6
constants/access-keys.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export const accessKeys = {
|
||||
Home: 'S',
|
||||
SkipContent: '1',
|
||||
Search: '2',
|
||||
Shortcuts: '3',
|
||||
} as const
|
|
@ -16,6 +16,7 @@ const isGrayscale = usePreferences('grayscaleMode')
|
|||
|
||||
<template>
|
||||
<div h-full :data-mode="isHydrated && isGrayscale ? 'grayscale' : ''" data-tauri-drag-region>
|
||||
<SkipContent />
|
||||
<main flex w-full mxa lg:max-w-80rem class="native:grid native:sm:grid-cols-[auto_1fr] native:lg:grid-cols-[auto_minmax(600px,2fr)_1fr]">
|
||||
<aside class="native:w-auto w-1/8 md:w-1/6 lg:w-1/5 xl:w-1/4 zen-hide" hidden sm:flex justify-end xl:me-4 native:me-0 relative>
|
||||
<div sticky top-0 w-20 xl:w-100 h-100dvh flex="~ col" lt-xl-items-center>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"loading_titled_page": "Loading {0} page, please wait",
|
||||
"locale_changed": "Language changed to {0}",
|
||||
"locale_changing": "Changing language, please wait",
|
||||
"route_loaded": "Page {0} loaded"
|
||||
"route_loaded": "Page {0} loaded",
|
||||
"skip_navigation": "Skip navigation"
|
||||
},
|
||||
"account": {
|
||||
"authorize": "Authorize to follow",
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"loading_titled_page": "Cargando página {0}, espera por favor",
|
||||
"locale_changed": "Idioma cambiado a {0}",
|
||||
"locale_changing": "Cambiando idioma, espera por favor",
|
||||
"route_loaded": "Página {0} cargada"
|
||||
"route_loaded": "Página {0} cargada",
|
||||
"skip_navigation": "Saltar navegación"
|
||||
},
|
||||
"account": {
|
||||
"authorize": "Autorizar seguimiento",
|
||||
|
|
|
@ -18,7 +18,7 @@ const isRootPath = computed(() => route.name === 'settings')
|
|||
<div>
|
||||
<div min-h-screen flex>
|
||||
<div border="e base" :class="isRootPath ? 'block lg:flex-none flex-1' : 'hidden lg:block'">
|
||||
<MainContent>
|
||||
<MainContent :skip-content="isRootPath ? 'nav.settings' : undefined">
|
||||
<template #title>
|
||||
<div timeline-title-style flex items-center gap-2 @click="$scrollToTop">
|
||||
<div i-ri:settings-3-line />
|
||||
|
|
|
@ -17,7 +17,7 @@ function handleShowCommit() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.about.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.about.label') }}</span>
|
||||
|
|
|
@ -7,7 +7,7 @@ useHydratedHead({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.interface.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.interface.label') }}</span>
|
||||
|
|
|
@ -15,7 +15,7 @@ const status = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.language.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.language.label') }}</span>
|
||||
|
|
|
@ -12,7 +12,7 @@ useHydratedHead({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.notifications.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.notifications.label') }}</span>
|
||||
|
|
|
@ -11,7 +11,7 @@ useHydratedHead({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back>
|
||||
<MainContent back skip-content="settings.notifications.notifications.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<div i-ri:test-tube-line />
|
||||
|
|
|
@ -14,7 +14,7 @@ useHydratedHead({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back>
|
||||
<MainContent back skip-content="settings.notifications.push_notifications.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.notifications.push_notifications.label') }}</span>
|
||||
|
|
|
@ -9,7 +9,7 @@ const userSettings = useUserSettings()
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.preferences.label">
|
||||
<template #title>
|
||||
<h1 text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
{{ $t('settings.preferences.label') }}
|
||||
|
|
|
@ -103,7 +103,7 @@ onReactivated(refreshInfo)
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back>
|
||||
<MainContent back skip-content="settings.profile.appearance.title">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.profile.appearance.title') }}</span>
|
||||
|
|
|
@ -11,7 +11,7 @@ useHydratedHead({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back>
|
||||
<MainContent back skip-content="settings.profile.featured_tags.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<div i-ri:test-tube-line />
|
||||
|
|
|
@ -11,7 +11,7 @@ useHydratedHead({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.profile.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.profile.label') }}</span>
|
||||
|
|
|
@ -66,7 +66,7 @@ async function importTokens() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<MainContent back-on-small-screen>
|
||||
<MainContent back-on-small-screen skip-content="settings.users.label">
|
||||
<template #title>
|
||||
<div text-lg font-bold flex items-center gap-2 @click="$scrollToTop">
|
||||
<span>{{ $t('settings.users.label') }}</span>
|
||||
|
|
Loading…
Reference in a new issue