- I made both surfaces friendlier to screen readers.
CMS changes landed in [layout.php](/Users/tyemeclifford/Documents/GH/blog/themes/neon/layout.php), [style.css](/Users/tyemeclifford/Documents/GH/blog/themes/neon/assets/style.css), [app.js](/Users/tyemeclifford/Documents/GH/blog/themes/neon/assets/app.js), and [lone-embed.php](/Users/tyemeclifford/Documents/GH/blog/lone-embed.php): skip link, named search/forms, current page state, live notices/empty states, labelled media/embed controls, visible focus states, theme control pressed states, and a focus-trapped/lightbox dialog with proper ARIA. Mac app changes landed in [main.m](/Users/tyemeclifford/Documents/GH/blog/macclient/AppKitClient/main.m), and I rebuilt [TCMS](/Users/tyemeclifford/Documents/GH/blog/macclient/TCMS.app/Contents/MacOS/TCMS): AppKit controls/tables/fields/previews now get accessibility labels/help/value text, workspace sections are named, media/comment tables expose useful context, and status messages are announced via macOS accessibility notifications.
This commit is contained in:
+34
-31
@@ -11,13 +11,13 @@ $description = $item['summary'] ?? $site['description'] ?? '';
|
||||
$postCard = static function (array $entry): void { ?>
|
||||
<article class="post-card">
|
||||
<div class="post-card__meta">
|
||||
<a href="<?= View::url('category/' . ContentRepository::slugify((string) $entry['category'])) ?>"><?= View::e($entry['category']) ?></a>
|
||||
<span><?= View::date((string) $entry['date']) ?></span>
|
||||
<a href="<?= View::url('category/' . ContentRepository::slugify((string) $entry['category'])) ?>" aria-label="Category: <?= View::e($entry['category']) ?>"><?= View::e($entry['category']) ?></a>
|
||||
<time datetime="<?= View::e($entry['date']) ?>"><?= View::date((string) $entry['date']) ?></time>
|
||||
</div>
|
||||
<h2><a href="<?= View::itemUrl($entry) ?>"><?= View::e($entry['title']) ?></a></h2>
|
||||
<p><?= View::e($entry['summary']) ?></p>
|
||||
<?php if (!empty($entry['tags'])): ?>
|
||||
<div class="tag-row">
|
||||
<div class="tag-row" aria-label="Tags">
|
||||
<?php foreach ($entry['tags'] as $tag): ?>
|
||||
<a href="<?= View::url('tag/' . ContentRepository::slugify((string) $tag)) ?>"><?= View::e($tag) ?></a>
|
||||
<?php endforeach; ?>
|
||||
@@ -35,9 +35,9 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<?php endif; ?>
|
||||
<?php foreach (($pagination['numbers'] ?? []) as $number): ?>
|
||||
<?php if ((int) $number === (int) $pagination['page']): ?>
|
||||
<span class="pager pager--current" aria-current="page"><?= (int) $number ?></span>
|
||||
<span class="pager pager--current" aria-current="page" aria-label="Page <?= (int) $number ?>"><?= (int) $number ?></span>
|
||||
<?php else: ?>
|
||||
<a class="pager pager--number" href="<?= $url((int) $number) ?>"><?= (int) $number ?></a>
|
||||
<a class="pager pager--number" href="<?= $url((int) $number) ?>" aria-label="Go to page <?= (int) $number ?>"><?= (int) $number ?></a>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
<?php if ($pagination['has_next']): ?>
|
||||
@@ -61,9 +61,10 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<script defer src="<?= View::asset('app.js') ?>"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#content">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<a class="brand" href="<?= View::url('') ?>">
|
||||
<span class="brand__mark"></span>
|
||||
<span class="brand__mark" aria-hidden="true"></span>
|
||||
<span>
|
||||
<strong><?= View::e($site['title'] ?? "Ty Clifford's Content Management System") ?></strong>
|
||||
<small><?= View::e($site['tagline'] ?? '') ?></small>
|
||||
@@ -71,40 +72,42 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
</a>
|
||||
|
||||
<nav class="main-nav" aria-label="Primary">
|
||||
<a href="<?= View::url('') ?>">Home</a>
|
||||
<a href="<?= View::url('') ?>"<?php if (($routeType ?? '') === 'home'): ?> aria-current="page"<?php endif; ?>>Home</a>
|
||||
<?php foreach ($navPages as $pageItem): ?>
|
||||
<a href="<?= View::itemUrl($pageItem) ?>"><?= View::e($pageItem['title']) ?></a>
|
||||
<a href="<?= View::itemUrl($pageItem) ?>"<?php if (($slug ?? '') === ($pageItem['slug'] ?? null)): ?> aria-current="page"<?php endif; ?>><?= View::e($pageItem['title']) ?></a>
|
||||
<?php endforeach; ?>
|
||||
<a href="<?= View::url('feed.xml') ?>">RSS</a>
|
||||
</nav>
|
||||
|
||||
<form class="search-form" action="<?= View::url('search') ?>" method="get">
|
||||
<input type="search" name="q" value="<?= View::e($search_query ?? '') ?>" placeholder="Search" aria-label="Search">
|
||||
<form class="search-form" action="<?= View::url('search') ?>" method="get" role="search" aria-label="Site search">
|
||||
<label class="sr-only" for="site-search">Search posts and pages</label>
|
||||
<input id="site-search" type="search" name="q" value="<?= View::e($search_query ?? '') ?>" placeholder="Search" autocomplete="off">
|
||||
<button type="submit" aria-label="Search">Go</button>
|
||||
</form>
|
||||
|
||||
<div class="theme-tools" aria-label="Theme controls">
|
||||
<button class="mode-toggle" type="button" data-mode-toggle aria-label="Toggle light mode">Light</button>
|
||||
<div class="theme-tools" role="group" aria-label="Theme controls">
|
||||
<button class="mode-toggle" type="button" data-mode-toggle aria-pressed="false">Light</button>
|
||||
<?php foreach (['green', 'blue', 'red', 'pink', 'purple', 'orange'] as $accent): ?>
|
||||
<button class="swatch swatch--<?= $accent ?>" type="button" data-accent="<?= $accent ?>" aria-label="<?= ucfirst($accent) ?> accent"></button>
|
||||
<button class="swatch swatch--<?= $accent ?>" type="button" data-accent="<?= $accent ?>" aria-pressed="false" aria-label="Use <?= $accent ?> accent"></button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-shell">
|
||||
<main class="site-shell" id="content" tabindex="-1">
|
||||
<section class="content-lane">
|
||||
<?php if ($flash): ?>
|
||||
<div class="notice notice--<?= View::e($flash['type']) ?>"><?= View::e($flash['message']) ?></div>
|
||||
<div class="notice notice--<?= View::e($flash['type']) ?>" role="<?= ($flash['type'] ?? '') === 'error' ? 'alert' : 'status' ?>" aria-live="<?= ($flash['type'] ?? '') === 'error' ? 'assertive' : 'polite' ?>"><?= View::e($flash['message']) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($template === 'listing'): ?>
|
||||
<header class="page-heading">
|
||||
<p><?= View::e($site['description'] ?? '') ?></p>
|
||||
<h1><?= View::e($heading ?? $title ?? '') ?></h1>
|
||||
<h1 id="page-title"><?= View::e($heading ?? $title ?? '') ?></h1>
|
||||
</header>
|
||||
<?php if (($search_query ?? null) !== null): ?>
|
||||
<form class="wide-search" action="<?= View::url('search') ?>" method="get">
|
||||
<input type="search" name="q" value="<?= View::e($search_query) ?>" placeholder="Search posts and pages" aria-label="Search posts and pages">
|
||||
<form class="wide-search" action="<?= View::url('search') ?>" method="get" role="search" aria-label="Search archive">
|
||||
<label class="sr-only" for="archive-search">Search posts and pages</label>
|
||||
<input id="archive-search" type="search" name="q" value="<?= View::e($search_query) ?>" placeholder="Search posts and pages" autocomplete="off">
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
@@ -114,7 +117,7 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if (empty($items)): ?>
|
||||
<p class="empty-state"><?= View::e($empty_message ?? 'No posts found.') ?></p>
|
||||
<p class="empty-state" role="status"><?= View::e($empty_message ?? 'No posts found.') ?></p>
|
||||
<?php endif; ?>
|
||||
<?php $renderPagination($pagination, $pagination_url); ?>
|
||||
<?php elseif ($template === 'single'): ?>
|
||||
@@ -122,12 +125,12 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<header class="article__header">
|
||||
<?php if ($item['type'] === 'post'): ?>
|
||||
<div class="post-card__meta">
|
||||
<a href="<?= View::url('category/' . ContentRepository::slugify((string) $item['category'])) ?>"><?= View::e($item['category']) ?></a>
|
||||
<span><?= View::date((string) $item['date']) ?></span>
|
||||
<a href="<?= View::url('category/' . ContentRepository::slugify((string) $item['category'])) ?>" aria-label="Category: <?= View::e($item['category']) ?>"><?= View::e($item['category']) ?></a>
|
||||
<time datetime="<?= View::e($item['date']) ?>"><?= View::date((string) $item['date']) ?></time>
|
||||
<span><?= View::e($item['author']) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<h1><?= View::e($item['title']) ?></h1>
|
||||
<h1 id="page-title"><?= View::e($item['title']) ?></h1>
|
||||
<?php if (!empty($item['summary'])): ?>
|
||||
<p><?= View::e($item['summary']) ?></p>
|
||||
<?php endif; ?>
|
||||
@@ -136,7 +139,7 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<?= $item['html'] ?>
|
||||
</div>
|
||||
<?php if (!empty($item['tags'])): ?>
|
||||
<footer class="tag-row article-tags">
|
||||
<footer class="tag-row article-tags" aria-label="Tags">
|
||||
<?php foreach ($item['tags'] as $tag): ?>
|
||||
<a href="<?= View::url('tag/' . ContentRepository::slugify((string) $tag)) ?>"><?= View::e($tag) ?></a>
|
||||
<?php endforeach; ?>
|
||||
@@ -148,7 +151,7 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<section class="comments" id="comments">
|
||||
<h2>Comments</h2>
|
||||
<?php if (empty($approved_comments)): ?>
|
||||
<p class="empty-state">No comments yet.</p>
|
||||
<p class="empty-state" role="status">No comments yet.</p>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($approved_comments as $comment): ?>
|
||||
<article class="comment">
|
||||
@@ -162,15 +165,15 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
|
||||
<form class="comment-form" action="<?= View::url('comment/' . $item['slug']) ?>" method="post">
|
||||
<div class="form-grid">
|
||||
<label>Name <input name="author" required maxlength="120"></label>
|
||||
<label>Email <input type="email" name="email" required maxlength="180"></label>
|
||||
<label>Name <input name="author" required maxlength="120" autocomplete="name"></label>
|
||||
<label>Email <input type="email" name="email" required maxlength="180" autocomplete="email"></label>
|
||||
</div>
|
||||
<label>Website <input type="url" name="site_url" maxlength="220"></label>
|
||||
<label class="honey-field">Leave this empty <input name="<?= View::e($config->get('comments.honeypot_field', 'website_url')) ?>" tabindex="-1" autocomplete="off"></label>
|
||||
<label>Website <input type="url" name="site_url" maxlength="220" autocomplete="url"></label>
|
||||
<label class="honey-field" aria-hidden="true">Leave this empty <input name="<?= View::e($config->get('comments.honeypot_field', 'website_url')) ?>" tabindex="-1" autocomplete="off"></label>
|
||||
<label>Comment <textarea name="body" required rows="6" maxlength="5000"></textarea></label>
|
||||
<?php if ($config->get('comments.captcha', true)): ?>
|
||||
<input type="hidden" name="captcha_token" value="<?= View::e($captcha['token']) ?>">
|
||||
<label><?= View::e($captcha['question']) ?> <input name="captcha_answer" required inputmode="numeric"></label>
|
||||
<label>Captcha: <?= View::e($captcha['question']) ?> <input name="captcha_answer" required inputmode="numeric" autocomplete="off"></label>
|
||||
<?php endif; ?>
|
||||
<button type="submit">Post Comment</button>
|
||||
</form>
|
||||
@@ -178,7 +181,7 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<section class="error-view">
|
||||
<h1><?= View::e($title ?? 'Error') ?></h1>
|
||||
<h1 id="page-title"><?= View::e($title ?? 'Error') ?></h1>
|
||||
<p><?= View::e($message ?? 'Something went wrong.') ?></p>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
@@ -196,7 +199,7 @@ $renderPagination = static function (array $pagination, callable $url): void { ?
|
||||
$socialUrl = View::url(ltrim($socialUrl, '/'));
|
||||
}
|
||||
?>
|
||||
<a href="<?= View::e($socialUrl) ?>" rel="me noopener" target="_blank"><?= View::e($entry['label'] ?? $network) ?></a>
|
||||
<a href="<?= View::e($socialUrl) ?>" rel="me noopener" target="_blank"><?= View::e($entry['label'] ?? $network) ?><span class="sr-only"> opens in a new tab</span></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user