fix: refactor breadcrumb partial to WAI-ARIA best-practice pattern
- Replace <div>+<i> structure with <nav aria-label>+<ol>+<li> - Move separators from DOM to CSS pseudo-elements (li+li::before) - Add hover/focus-visible states and color differentiation - Replace hard-coded px values with design tokens - Explicitly reset shell nav/ol/li rules for self-contained styling - Add i18n key for breadcrumb nav landmark label Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1316,5 +1316,6 @@
|
||||
"Fail": "Fehler",
|
||||
"Environment": "Umgebung",
|
||||
"Modules": "Module",
|
||||
"Version": "Version"
|
||||
"Version": "Version",
|
||||
"Breadcrumb": "Breadcrumb"
|
||||
}
|
||||
|
||||
@@ -1316,5 +1316,6 @@
|
||||
"Fail": "Fail",
|
||||
"Environment": "Environment",
|
||||
"Modules": "Modules",
|
||||
"Version": "Version"
|
||||
"Version": "Version",
|
||||
"Breadcrumb": "Breadcrumb"
|
||||
}
|
||||
|
||||
@@ -8,24 +8,25 @@ if (!$items) {
|
||||
}
|
||||
$lastIndex = count($items) - 1;
|
||||
?>
|
||||
<div class="app-breadcrumb">
|
||||
<?php foreach ($items as $index => $item): ?>
|
||||
<?php
|
||||
$label = (string) ($item['label'] ?? '');
|
||||
$href = (string) ($item['href'] ?? '');
|
||||
$path = (string) ($item['path'] ?? '');
|
||||
$isLast = $index === $lastIndex;
|
||||
if ($href === '' && $path !== '') {
|
||||
$href = lurl($path);
|
||||
}
|
||||
?>
|
||||
<?php if ($href !== '' && !$isLast): ?>
|
||||
<a href="<?php e($href); ?>"><?php e($label); ?></a>
|
||||
<?php else: ?>
|
||||
<span<?php if ($isLast): ?> aria-current="page"<?php endif; ?>><?php e($label); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (!$isLast): ?>
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<nav class="app-breadcrumb" aria-label="<?php e(t('Breadcrumb')); ?>">
|
||||
<ol>
|
||||
<?php foreach ($items as $index => $item): ?>
|
||||
<?php
|
||||
$label = (string) ($item['label'] ?? '');
|
||||
$href = (string) ($item['href'] ?? '');
|
||||
$path = (string) ($item['path'] ?? '');
|
||||
$isLast = $index === $lastIndex;
|
||||
if ($href === '' && $path !== '') {
|
||||
$href = lurl($path);
|
||||
}
|
||||
?>
|
||||
<li>
|
||||
<?php if ($href !== '' && !$isLast): ?>
|
||||
<a href="<?php e($href); ?>"><?php e($label); ?></a>
|
||||
<?php else: ?>
|
||||
<span<?php if ($isLast): ?> aria-current="page"<?php endif; ?>><?php e($label); ?></span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
@@ -1,15 +1,74 @@
|
||||
@layer components {
|
||||
/* Breadcrumb navigation — separator-delimited path links. */
|
||||
.app-breadcrumb {
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
/*
|
||||
* Breadcrumb navigation — WAI-ARIA breadcrumb pattern with CSS-only separators.
|
||||
*
|
||||
* Self-contained: explicitly resets inherited shell nav/ol/li rules
|
||||
* (app-shell.css lines 2844-2956) to avoid layout side-effects.
|
||||
*/
|
||||
.app-breadcrumb {
|
||||
display: block;
|
||||
justify-content: initial;
|
||||
overflow: initial;
|
||||
margin-bottom: 0.375rem;
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--app-line-height);
|
||||
}
|
||||
|
||||
.app-breadcrumb a {
|
||||
color: inherit;
|
||||
.app-breadcrumb ol {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.375rem;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.app-breadcrumb li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.app-breadcrumb li::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.app-breadcrumb li + li::before {
|
||||
content: "/";
|
||||
color: var(--app-muted-color);
|
||||
font-size: 0.75em;
|
||||
margin-inline-end: 0.375rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.app-breadcrumb a {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: var(--app-muted-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
border-radius: var(--app-border-radius);
|
||||
transition: color var(--app-transition);
|
||||
}
|
||||
|
||||
.app-breadcrumb a:hover {
|
||||
color: var(--app-primary-hover);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: var(--app-text-underline-offset);
|
||||
}
|
||||
|
||||
.app-breadcrumb a:focus-visible {
|
||||
background: color-mix(in srgb, var(--app-contrast) 8%, transparent);
|
||||
color: var(--app-contrast);
|
||||
outline: var(--app-outline-width) solid var(--app-primary-focus);
|
||||
outline-offset: 0.125rem;
|
||||
}
|
||||
|
||||
.app-breadcrumb [aria-current="page"] {
|
||||
color: var(--app-color);
|
||||
font-weight: 500;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user