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",
|
"Fail": "Fehler",
|
||||||
"Environment": "Umgebung",
|
"Environment": "Umgebung",
|
||||||
"Modules": "Module",
|
"Modules": "Module",
|
||||||
"Version": "Version"
|
"Version": "Version",
|
||||||
|
"Breadcrumb": "Breadcrumb"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1316,5 +1316,6 @@
|
|||||||
"Fail": "Fail",
|
"Fail": "Fail",
|
||||||
"Environment": "Environment",
|
"Environment": "Environment",
|
||||||
"Modules": "Modules",
|
"Modules": "Modules",
|
||||||
"Version": "Version"
|
"Version": "Version",
|
||||||
|
"Breadcrumb": "Breadcrumb"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user