Accessibility improvements

This commit is contained in:
Svilen Markov 2025-03-12 18:01:06 +00:00
parent b8df34309f
commit d7bbf2b8e2
7 changed files with 47 additions and 26 deletions

View file

@ -68,7 +68,7 @@ func newApplication(config *config) (*application, error) {
for w := range column.Widgets {
widget := column.Widgets[w]
app.widgetByID[widget.id()] = widget
app.widgetByID[widget.GetID()] = widget
widget.setProviders(providers)
}

View file

@ -284,7 +284,9 @@ function setupGroups() {
for (let i = 0; i < titles.length; i++) {
titles[i].classList.remove("widget-group-title-current");
titles[i].setAttribute("aria-selected", "false");
tabs[i].classList.remove("widget-group-content-current");
tabs[i].setAttribute("aria-hidden", "true");
}
if (current < t) {
@ -296,7 +298,9 @@ function setupGroups() {
current = t;
title.classList.add("widget-group-title-current");
title.setAttribute("aria-selected", "true");
tabs[t].classList.add("widget-group-content-current");
tabs[t].setAttribute("aria-hidden", "false");
});
}
}
@ -670,6 +674,7 @@ async function setupPage() {
setupLazyImages();
} finally {
pageElement.classList.add("content-ready");
pageElement.setAttribute("aria-busy", "false");
for (let i = 0; i < contentReadyCallbacks.length; i++) {
contentReadyCallbacks[i]();

View file

@ -110,7 +110,7 @@
.visited-indicator:not(.text-truncate)::after,
.visited-indicator.text-truncate::before,
.bookmarks-link:not(.bookmarks-link-no-arrow)::after {
content: '↗';
content: '↗' / "";
margin-left: 0.5em;
display: inline-block;
position: relative;
@ -189,7 +189,7 @@
}
.expand-toggle-button-icon::before {
content: '';
content: '' / "";
font-size: 0.8rem;
transform: rotate(90deg);
line-height: 1;
@ -341,6 +341,19 @@ html, body, .body-content {
height: 100%;
}
h1, h2, h3, h4, h5 {
font: inherit;
}
.visually-hidden {
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
a {
text-decoration: none;
color: inherit;
@ -563,7 +576,7 @@ kbd:active {
}
.summary::after {
content: "◀";
content: "◀" / "";
font-size: 1.2em;
position: absolute;
top: 0;
@ -822,7 +835,7 @@ details[open] .summary::after {
}
.list-horizontal-text > *:not(:last-child)::after {
content: '•';
content: '•' / "";
color: var(--color-text-subdue);
margin: 0 0.4rem;
position: relative;

View file

@ -4,17 +4,18 @@
{{ define "widget-content" }}
<div class="widget-group-header">
<div class="widget-header gap-20">
{{ range $i, $widget := .Widgets }}
<button class="widget-group-title{{ if eq $i 0 }} widget-group-title-current{{ end }}"{{ if ne "" .TitleURL }} data-title-url="{{ .TitleURL }}"{{ end }}>{{ $widget.Title }}</button>
{{ end }}
<div class="widget-header gap-20" role="tablist">
{{- range $i, $widget := .Widgets }}
<button class="widget-group-title{{ if eq $i 0 }} widget-group-title-current{{ end }}"{{ if ne "" .TitleURL }} data-title-url="{{ .TitleURL }}"{{ end }} aria-selected="{{ if eq $i 0 }}true{{ else }}false{{ end }}" arial-level="2" role="tab" aria-controls="widget-{{ .GetID }}-tabpanel-{{ $i }}" id="widget-{{ .GetID }}-tab-{{ $i }}">{{ $widget.Title }}</button>
{{- end }}
</div>
</div>
<div class="widget-group-contents">
{{ range $i, $widget := .Widgets }}
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}">{{ .Render }}</div>
{{ end }}
{{- range $i, $widget := .Widgets }}
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}" id="widget-{{ .GetID }}-tabpanel-{{ $i }}" role="tabpanel" aria-labelledby="widget-{{ .GetID }}-tab-{{ $i }}" aria-hidden="{{ if eq $i 0 }}false{{ else }}true{{ end }}">
{{- .Render -}}
</div>
{{- end }}
</div>
{{ end }}

View file

@ -25,7 +25,7 @@
{{ define "navigation-links" }}
{{ range .App.Config.Pages }}
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ end }}">{{ .Title }}</a>
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ end }}"{{ if eq .Slug $.Page.Slug }} aria-current="page"{{ end }}>{{ .Title }}</a>
{{ end }}
{{ end }}
@ -35,10 +35,10 @@
<div class="header-container content-bounds">
<div class="header flex padding-inline-widget widget-content-frame">
<!-- TODO: Replace G with actual logo, first need an actual logo -->
<div class="logo">{{ if ne "" .App.Config.Branding.LogoURL }}<img src="{{ .App.Config.Branding.LogoURL }}" alt="">{{ else if ne "" .App.Config.Branding.LogoText }}{{ .App.Config.Branding.LogoText }}{{ else }}G{{ end }}</div>
<div class="nav flex grow">
<div class="logo" aria-hidden="true">{{ if ne "" .App.Config.Branding.LogoURL }}<img src="{{ .App.Config.Branding.LogoURL }}" alt="">{{ else if ne "" .App.Config.Branding.LogoText }}{{ .App.Config.Branding.LogoText }}{{ else }}G{{ end }}</div>
<nav class="nav flex grow">
{{ template "navigation-links" . }}
</div>
</nav>
</div>
</div>
{{ end }}
@ -57,17 +57,19 @@
</div>
<div class="content-bounds grow">
<div class="page" id="page">
<main class="page" id="page" aria-live="polite" aria-busy="true">
<h1 class="visually-hidden">{{ .Page.Title }}</h1>
<div class="page-content" id="page-content"></div>
<div class="page-loading-container">
<!-- TODO: add a bigger/better loading indicator -->
<div class="loading-icon"></div>
<div class="visually-hidden">Loading</div>
<div class="loading-icon" aria-hidden="true"></div>
</div>
</div>
</main>
</div>
{{ if not .App.Config.Branding.HideFooter }}
<div class="footer flex items-center flex-column">
<footer class="footer flex items-center flex-column">
{{ if eq "" .App.Config.Branding.CustomFooter }}
<div>
<a class="size-h3" href="https://github.com/glanceapp/glance" target="_blank" rel="noreferrer">Glance</a> {{ if ne "dev" .App.Version }}<a class="visited-indicator" title="Release notes" href="https://github.com/glanceapp/glance/releases/tag/{{ .App.Version }}" target="_blank" rel="noreferrer">{{ .App.Version }}</a>{{ else }}({{ .App.Version }}){{ end }}
@ -75,7 +77,7 @@
{{ else }}
{{ .App.Config.Branding.CustomFooter }}
{{ end }}
</div>
</footer>
{{ end }}
<div class="mobile-navigation-offset"></div>

View file

@ -2,9 +2,9 @@
{{- if not .HideHeader}}
<div class="widget-header">
{{- if ne "" .TitleURL }}
<a href="{{ .TitleURL | safeURL }}" target="_blank" rel="noreferrer" class="uppercase">{{ .Title }}</a>
<h2><a href="{{ .TitleURL | safeURL }}" target="_blank" rel="noreferrer" class="uppercase">{{ .Title }}</a></h2>
{{- else }}
<div class="uppercase">{{ .Title }}</div>
<h2 class="uppercase">{{ .Title }}</h2>
{{- end }}
{{- if .IsWIP }}
<div data-popover-type="html" data-popover-position="above">

View file

@ -121,13 +121,13 @@ type widget interface {
// These need to be exported because they get called in templates
Render() template.HTML
GetType() string
GetID() uint64
initialize() error
requiresUpdate(*time.Time) bool
setProviders(*widgetProviders)
update(context.Context)
setID(uint64)
id() uint64
handleRequest(w http.ResponseWriter, r *http.Request)
setHideHeader(bool)
}
@ -184,7 +184,7 @@ func (w *widgetBase) update(ctx context.Context) {
}
func (w *widgetBase) id() uint64 {
func (w *widgetBase) GetID() uint64 {
return w.ID
}