Files
tsv08kulmbach-website/public/assets/css/utilities.css
fs eacf9449e0 Design-System: Button-Varianten konsolidieren, 44px-Trefferflächen
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
2026-07-27 10:28:02 +02:00

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);
}