Dritter Teil. .btn hatte nur primary + outline + sm/lg, daneben lebten mehrere Controls als Eigenbau — teils zeichengleich dupliziert. Tabs: .group-tabs__tab und .filter-bar__tab waren bis auf flex-shrink und 4px Innenabstand identisch. Beide laufen jetzt über eine gemeinsame .tab-Basis, die aria-pressed (Filter) und aria-selected (Tabs) gleich behandelt; von den BEM-Klassen bleibt nur das jeweilige Delta. Innenabstand vereinheitlicht auf --space-m, die Gruppen-Tabs verlieren dadurch 4px pro Seite. Icon-Buttons: .ad-banner__nav-btn und .ad-banner__toggle waren vollständige Duplikate voneinander. Sie tragen jetzt .btn .btn--icon und behalten nur die Glas-Optik als Delta. Der Selektor schreibt .btn mit — utilities.css lädt nach components.css, bei gleicher Spezifität gewänne sonst die rote .btn-Fläche. Trefferflächen auf 44px (WCAG 2.5.5): .btn bekommt min-height und wird inline-flex, damit Icon und Label ohne Zusatz-Markup zentriert liegen. Bezeichnend auch hier — .department-choice__btn hatte inline-flex, align-items und exakt denselben gap bereits selbst deklariert und gibt sie jetzt ab. Controls, die optisch klein bleiben sollen, strecken die Fläche stattdessen unsichtbar per Pseudo-Element (.tap-target, automatisch auch für .btn--sm) — gewertet wird die Trefferfläche, nicht die gezeichnete. Der Banner-Indikator brauchte einen anderen Weg: er trug overflow: hidden, um den Fortschrittsbalken zu beschneiden, was jede Erweiterung wieder abgeschnitten hätte. Der sichtbare 6px-Balken liegt jetzt im ::before, der Fortschritt ist selbst rund — overflow entfällt, der Button ist 48x44px. Haupt-Nav-Links, Dropdown-Trigger und Footer-Social-Links bekommen min-height statt einer Pseudo-Erweiterung: sie stehen in Reihen, wo überlappende Trefferflächen den Nachbarn verdecken würden. In der 60px hohen Kopfleiste ist die Höhe ohnehin nicht sichtbar. Bewusst unverändert: die Formular-Checkbox bleibt bei 24px (WCAG 2.5.8). <input> ist ein Replaced Element, dort greift ::after nicht zuverlässig — und das zugehörige <label> ist klickbar und liefert die große Fläche. .btn--ghost ist angelegt, hat aber aktuell keinen Abnehmer: die vermuteten nackten Links sind bei näherem Hinsehen Bildkarten (.instagram__link), Wrapper (.split__cta) oder eigenständige Karten-Affordanzen (.team-card__cta), keine tertiären Buttons. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
149 lines
3.8 KiB
CSS
149 lines
3.8 KiB
CSS
/* ============================================================
|
|
UTILITIES — Buttons, visually-hidden, kleine Helfer
|
|
============================================================ */
|
|
/* --- Trefferflächen ---------------------------------------------------------
|
|
WCAG 2.5.5 wertet die Trefferfläche, nicht die gezeichnete Fläche. Controls,
|
|
die optisch klein bleiben sollen (dezente Buttons, Slider-Punkte), strecken
|
|
sie deshalb über ein unsichtbares Pseudo-Element auf --tap-min, statt zu
|
|
wachsen. Voraussetzung: das Element darf kein overflow: hidden tragen und
|
|
::after muss frei sein. */
|
|
.tap-target,
|
|
.btn--sm {
|
|
position: relative;
|
|
}
|
|
|
|
.tap-target::after,
|
|
.btn--sm::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
translate: -50% -50%;
|
|
width: 100%;
|
|
height: 100%;
|
|
min-width: var(--tap-min);
|
|
min-height: var(--tap-min);
|
|
}
|
|
|
|
/* --- Buttons ---------------------------------------------------------------
|
|
inline-flex statt inline-block, damit min-height die Trefferfläche sichern
|
|
kann und Icon+Label ohne Zusatz-Markup zentriert nebeneinander liegen. */
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--space-2xs);
|
|
min-height: var(--tap-min);
|
|
padding: var(--btn-pad-y) var(--btn-pad-x);
|
|
white-space: nowrap;
|
|
background: var(--clr-accent);
|
|
color: var(--clr-text);
|
|
font-size: var(--btn-font);
|
|
line-height: var(--btn-line);
|
|
text-decoration: none;
|
|
text-align: center;
|
|
border: 1px solid var(--clr-accent);
|
|
border-radius: var(--radius-btn);
|
|
cursor: pointer;
|
|
transition: background var(--transition), border-color var(--transition),
|
|
color var(--transition), transform var(--transition);
|
|
}
|
|
|
|
/* primary (Default) */
|
|
.btn:hover {
|
|
background: var(--clr-accent-dark);
|
|
border-color: var(--clr-accent-dark);
|
|
color: var(--clr-text);
|
|
}
|
|
|
|
.btn:focus-visible {
|
|
outline: var(--focus-ring);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.btn:active {
|
|
transform: translateY(1px);
|
|
}
|
|
|
|
.btn[disabled],
|
|
.btn[aria-disabled="true"] {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
}
|
|
|
|
/* secondary */
|
|
.btn--outline {
|
|
background: transparent;
|
|
border-color: var(--clr-text);
|
|
}
|
|
|
|
.btn--outline:hover {
|
|
background: var(--clr-glass-bg);
|
|
border-color: var(--clr-text);
|
|
color: var(--clr-text);
|
|
}
|
|
|
|
/* tertiary — ohne Fläche und Rahmen, für Aktionen neben einem primären Button.
|
|
Unterstrichen, damit der Button nicht allein über die Farbe erkennbar ist. */
|
|
.btn--ghost {
|
|
background: transparent;
|
|
border-color: transparent;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.btn--ghost:hover {
|
|
background: var(--clr-glass-bg);
|
|
border-color: transparent;
|
|
color: var(--clr-text);
|
|
}
|
|
|
|
/* Icon-Button — quadratisch, nur ein Icon, Label über aria-label.
|
|
Das Icon selbst skaliert über font-size (siehe --icon-*). */
|
|
.btn--icon {
|
|
gap: 0;
|
|
min-width: var(--tap-min);
|
|
padding: 0;
|
|
border-radius: 999px;
|
|
}
|
|
|
|
/* Größenmodifier — Padding ist em-basiert, skaliert mit der font-size.
|
|
--sm bleibt bewusst optisch klein und holt die 44px über die Trefferfläche;
|
|
dafür muss min-height hier zurückgenommen werden. */
|
|
.btn--sm {
|
|
min-height: 0;
|
|
font-size: var(--btn-font-sm);
|
|
}
|
|
|
|
.btn--lg { font-size: var(--btn-font-lg); }
|
|
|
|
.visually-hidden {
|
|
position: absolute !important;
|
|
width: 1px;
|
|
height: 1px;
|
|
padding: 0;
|
|
margin: -1px;
|
|
overflow: hidden;
|
|
clip-path: inset(50%);
|
|
white-space: nowrap;
|
|
border: 0;
|
|
}
|
|
|
|
/* Inline-Icons (Bootstrap Icons via icon()-Helper). Größe = font-size, Farbe = currentColor. */
|
|
.icon {
|
|
display: inline-block;
|
|
width: 1em;
|
|
height: 1em;
|
|
fill: currentColor;
|
|
vertical-align: -0.125em;
|
|
flex: none;
|
|
}
|
|
|
|
.text-center {
|
|
text-align: center;
|
|
}
|
|
|
|
.text-muted {
|
|
color: var(--clr-text-muted);
|
|
}
|