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:
2026-03-24 14:51:40 +01:00
parent 3c0821fcd7
commit 44620bd786
4 changed files with 96 additions and 34 deletions

View File

@@ -1316,5 +1316,6 @@
"Fail": "Fehler", "Fail": "Fehler",
"Environment": "Umgebung", "Environment": "Umgebung",
"Modules": "Module", "Modules": "Module",
"Version": "Version" "Version": "Version",
"Breadcrumb": "Breadcrumb"
} }

View File

@@ -1316,5 +1316,6 @@
"Fail": "Fail", "Fail": "Fail",
"Environment": "Environment", "Environment": "Environment",
"Modules": "Modules", "Modules": "Modules",
"Version": "Version" "Version": "Version",
"Breadcrumb": "Breadcrumb"
} }

View File

@@ -8,24 +8,25 @@ if (!$items) {
} }
$lastIndex = count($items) - 1; $lastIndex = count($items) - 1;
?> ?>
<div class="app-breadcrumb"> <nav class="app-breadcrumb" aria-label="<?php e(t('Breadcrumb')); ?>">
<?php foreach ($items as $index => $item): ?> <ol>
<?php <?php foreach ($items as $index => $item): ?>
$label = (string) ($item['label'] ?? ''); <?php
$href = (string) ($item['href'] ?? ''); $label = (string) ($item['label'] ?? '');
$path = (string) ($item['path'] ?? ''); $href = (string) ($item['href'] ?? '');
$isLast = $index === $lastIndex; $path = (string) ($item['path'] ?? '');
if ($href === '' && $path !== '') { $isLast = $index === $lastIndex;
$href = lurl($path); if ($href === '' && $path !== '') {
} $href = lurl($path);
?> }
<?php if ($href !== '' && !$isLast): ?> ?>
<a href="<?php e($href); ?>"><?php e($label); ?></a> <li>
<?php else: ?> <?php if ($href !== '' && !$isLast): ?>
<span<?php if ($isLast): ?> aria-current="page"<?php endif; ?>><?php e($label); ?></span> <a href="<?php e($href); ?>"><?php e($label); ?></a>
<?php endif; ?> <?php else: ?>
<?php if (!$isLast): ?> <span<?php if ($isLast): ?> aria-current="page"<?php endif; ?>><?php e($label); ?></span>
<i class="bi bi-chevron-right"></i> <?php endif; ?>
<?php endif; ?> </li>
<?php endforeach; ?> <?php endforeach; ?>
</div> </ol>
</nav>

View File

@@ -1,15 +1,74 @@
@layer components { @layer components {
/* Breadcrumb navigation — separator-delimited path links. */ /*
.app-breadcrumb { * Breadcrumb navigation — WAI-ARIA breadcrumb pattern with CSS-only separators.
margin-bottom: 5px; *
display: flex; * Self-contained: explicitly resets inherited shell nav/ol/li rules
align-items: center; * (app-shell.css lines 2844-2956) to avoid layout side-effects.
gap: 3px; */
font-size: var(--text-xs); .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 { .app-breadcrumb ol {
color: inherit; 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; 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;
}
} }