live region

This commit is contained in:
mi
2025-11-14 18:42:59 +10:00
parent 7e41401ca3
commit f31383800e
4 changed files with 51 additions and 16 deletions

View File

@@ -65,6 +65,19 @@ body {
background-color: var(--color-background); background-color: var(--color-background);
} }
/* Screen reader only content - visually hidden but accessible to assistive technologies */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/* Focus indicators for accessibility */ /* Focus indicators for accessibility */
a:focus, a:focus,
button:focus, button:focus,

View File

@@ -32,6 +32,12 @@
const title = comic.title || `#${comic.number}`; const title = comic.title || `#${comic.number}`;
currentComicNumber = comic.number; currentComicNumber = comic.number;
// Announce comic change to screen readers
const announcer = document.getElementById('comic-announcer');
if (announcer) {
announcer.textContent = `Loaded comic ${title}, dated ${comic.formatted_date || comic.date}`;
}
// Update container class for full-width option // Update container class for full-width option
const container = document.querySelector('.comic-container'); const container = document.querySelector('.comic-container');
if (comic.full_width) { if (comic.full_width) {
@@ -244,12 +250,16 @@
return; return;
} }
const announcer = document.getElementById('comic-announcer');
switch(event.key) { switch(event.key) {
case 'ArrowLeft': case 'ArrowLeft':
// Previous comic // Previous comic
if (currentComicNumber > 1) { if (currentComicNumber > 1) {
event.preventDefault(); event.preventDefault();
loadComic(currentComicNumber - 1); loadComic(currentComicNumber - 1);
} else if (announcer) {
announcer.textContent = 'Already at the first comic';
} }
break; break;
case 'ArrowRight': case 'ArrowRight':
@@ -257,6 +267,8 @@
if (currentComicNumber < totalComics) { if (currentComicNumber < totalComics) {
event.preventDefault(); event.preventDefault();
loadComic(currentComicNumber + 1); loadComic(currentComicNumber + 1);
} else if (announcer) {
announcer.textContent = 'Already at the latest comic';
} }
break; break;
case 'Home': case 'Home':
@@ -264,6 +276,8 @@
if (currentComicNumber > 1) { if (currentComicNumber > 1) {
event.preventDefault(); event.preventDefault();
loadComic(1); loadComic(1);
} else if (announcer) {
announcer.textContent = 'Already at the first comic';
} }
break; break;
case 'End': case 'End':
@@ -271,6 +285,8 @@
if (currentComicNumber < totalComics) { if (currentComicNumber < totalComics) {
event.preventDefault(); event.preventDefault();
loadComic(totalComics); loadComic(totalComics);
} else if (announcer) {
announcer.textContent = 'Already at the latest comic';
} }
break; break;
} }

View File

@@ -5,6 +5,9 @@
{% block og_image %}{{ site_url }}/static/images/thumbs/{{ comic.filename }}{% endblock %} {% block og_image %}{{ site_url }}/static/images/thumbs/{{ comic.filename }}{% endblock %}
{% block content %} {% block content %}
<!-- ARIA live region for screen reader announcements -->
<div aria-live="polite" aria-atomic="true" class="sr-only" id="comic-announcer"></div>
<div class="comic-container{% if comic.full_width %} comic-container-fullwidth{% endif %}{% if comic.plain %} comic-container-plain{% endif %}" data-comic-number="{{ comic.number }}" data-total-comics="{{ total_comics }}"> <div class="comic-container{% if comic.full_width %} comic-container-fullwidth{% endif %}{% if comic.plain %} comic-container-plain{% endif %}" data-comic-number="{{ comic.number }}" data-total-comics="{{ total_comics }}">
{% if not comic.plain %} {% if not comic.plain %}
<div class="comic-header"> <div class="comic-header">

View File

@@ -7,6 +7,9 @@
{% endif %} {% endif %}
{% block content %} {% block content %}
<!-- ARIA live region for screen reader announcements -->
<div aria-live="polite" aria-atomic="true" class="sr-only" id="comic-announcer"></div>
<div class="comic-container" data-comic-number="{{ comic.number }}" data-total-comics="{{ total_comics }}"> <div class="comic-container" data-comic-number="{{ comic.number }}" data-total-comics="{{ total_comics }}">
<div class="comic-header"> <div class="comic-header">
<h1>{{ comic.title if comic.title else '#' ~ comic.number }}</h1> <h1>{{ comic.title if comic.title else '#' ~ comic.number }}</h1>
@@ -34,17 +37,17 @@
{# Icon-based navigation #} {# Icon-based navigation #}
{% if comic.number > 1 %} {% if comic.number > 1 %}
<a href="{{ url_for('comic', comic_id=1) }}" class="btn-icon-nav" aria-label="First"> <a href="{{ url_for('comic', comic_id=1) }}" class="btn-icon-nav" aria-label="First">
<img src="{{ url_for('static', filename='images/icons/first.png') }}" alt="First"> <img src="{{ url_for('static', filename='images/icons/first.png') }}" alt="">
</a> </a>
<a href="{{ url_for('comic', comic_id=comic.number - 1) }}" class="btn-icon-nav" aria-label="Previous"> <a href="{{ url_for('comic', comic_id=comic.number - 1) }}" class="btn-icon-nav" aria-label="Previous">
<img src="{{ url_for('static', filename='images/icons/previous.png') }}" alt="Previous"> <img src="{{ url_for('static', filename='images/icons/previous.png') }}" alt="">
</a> </a>
{% else %} {% else %}
<span class="btn-icon-nav btn-icon-disabled" aria-label="First"> <span class="btn-icon-nav btn-icon-disabled" aria-label="First" aria-disabled="true">
<img src="{{ url_for('static', filename='images/icons/first.png') }}" alt="First"> <img src="{{ url_for('static', filename='images/icons/first.png') }}" alt="">
</span> </span>
<span class="btn-icon-nav btn-icon-disabled" aria-label="Previous"> <span class="btn-icon-nav btn-icon-disabled" aria-label="Previous" aria-disabled="true">
<img src="{{ url_for('static', filename='images/icons/previous.png') }}" alt="Previous"> <img src="{{ url_for('static', filename='images/icons/previous.png') }}" alt="">
</span> </span>
{% endif %} {% endif %}
@@ -52,17 +55,17 @@
{% if comic.number < total_comics %} {% if comic.number < total_comics %}
<a href="{{ url_for('comic', comic_id=comic.number + 1) }}" class="btn-icon-nav" aria-label="Next"> <a href="{{ url_for('comic', comic_id=comic.number + 1) }}" class="btn-icon-nav" aria-label="Next">
<img src="{{ url_for('static', filename='images/icons/next.png') }}" alt="Next"> <img src="{{ url_for('static', filename='images/icons/next.png') }}" alt="">
</a> </a>
<a href="{{ url_for('comic', comic_id=total_comics) }}" class="btn-icon-nav" aria-label="Latest"> <a href="{{ url_for('comic', comic_id=total_comics) }}" class="btn-icon-nav" aria-label="Latest">
<img src="{{ url_for('static', filename='images/icons/latest.png') }}" alt="Latest"> <img src="{{ url_for('static', filename='images/icons/latest.png') }}" alt="">
</a> </a>
{% else %} {% else %}
<span class="btn-icon-nav btn-icon-disabled" aria-label="Next"> <span class="btn-icon-nav btn-icon-disabled" aria-label="Next" aria-disabled="true">
<img src="{{ url_for('static', filename='images/icons/next.png') }}" alt="Next"> <img src="{{ url_for('static', filename='images/icons/next.png') }}" alt="">
</span> </span>
<span class="btn-icon-nav btn-icon-disabled" aria-label="Latest"> <span class="btn-icon-nav btn-icon-disabled" aria-label="Latest" aria-disabled="true">
<img src="{{ url_for('static', filename='images/icons/latest.png') }}" alt="Latest"> <img src="{{ url_for('static', filename='images/icons/latest.png') }}" alt="">
</span> </span>
{% endif %} {% endif %}
{% else %} {% else %}
@@ -71,8 +74,8 @@
<a href="{{ url_for('comic', comic_id=1) }}" class="btn btn-nav">First</a> <a href="{{ url_for('comic', comic_id=1) }}" class="btn btn-nav">First</a>
<a href="{{ url_for('comic', comic_id=comic.number - 1) }}" class="btn btn-nav">Previous</a> <a href="{{ url_for('comic', comic_id=comic.number - 1) }}" class="btn btn-nav">Previous</a>
{% else %} {% else %}
<span class="btn btn-nav btn-disabled">First</span> <span class="btn btn-nav btn-disabled" aria-disabled="true">First</span>
<span class="btn btn-nav btn-disabled">Previous</span> <span class="btn btn-nav btn-disabled" aria-disabled="true">Previous</span>
{% endif %} {% endif %}
<span class="comic-date-display">{{ comic.formatted_date }}</span> <span class="comic-date-display">{{ comic.formatted_date }}</span>
@@ -81,8 +84,8 @@
<a href="{{ url_for('comic', comic_id=comic.number + 1) }}" class="btn btn-nav">Next</a> <a href="{{ url_for('comic', comic_id=comic.number + 1) }}" class="btn btn-nav">Next</a>
<a href="{{ url_for('comic', comic_id=total_comics) }}" class="btn btn-nav">Latest</a> <a href="{{ url_for('comic', comic_id=total_comics) }}" class="btn btn-nav">Latest</a>
{% else %} {% else %}
<span class="btn btn-nav btn-disabled">Next</span> <span class="btn btn-nav btn-disabled" aria-disabled="true">Next</span>
<span class="btn btn-nav btn-disabled">Latest</span> <span class="btn btn-nav btn-disabled" aria-disabled="true">Latest</span>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>