@conciso/design-system-mcp 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/snapshot/manifests/components.json +187 -27
- package/snapshot/manifests/docs.json +0 -128
- package/snapshot/services/addon-docs/mdx/komponenten-buchungsformular--/303/274bersicht.json +1 -1
- package/snapshot/services/addon-docs/mdx/komponenten-call-to-action-cta-band.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-cards-teaser-card.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-chips-badges-pills-chip.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-code-block-code-block.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-dropdowns-custom-select.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-feedback-snackbar.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-footer-komplett.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-hero-hero-bild.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-inputs-forms-textfeld.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-navigation-topnav.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-sektion-sektion.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-slider-carousel-carousel.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-tabelle-tabelle.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-theme-umschalter-cycle-button.json +18 -0
- package/snapshot/services/addon-docs/mdx/komponenten-zitate-testimonials-blockquote.json +18 -0
- package/snapshot/services/addon-docs/mdx/marke-logo-logo.json +18 -0
- package/snapshot/services/core/docgen/komponenten-call-to-action-downloadcta.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-feature-liste.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-featured-karte.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-icon-karte.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-klickbare-karte.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-statcard.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-statstrip.json +1 -1
- package/snapshot/services/core/docgen/komponenten-cards-teaser-tier-trenner.json +1 -1
- package/snapshot/services/core/docgen/komponenten-chips-badges-pills-bereichs-badge.json +1 -1
- package/snapshot/services/core/docgen/komponenten-chips-badges-pills-pill.json +1 -1
- package/snapshot/services/core/docgen/komponenten-chips-badges-pills-status-badge.json +1 -1
- package/snapshot/services/core/docgen/komponenten-dropdowns-combobox.json +1 -1
- package/snapshot/services/core/docgen/komponenten-footer-oberer-teil.json +1 -1
- package/snapshot/services/core/docgen/komponenten-footer-unterer-teil.json +1 -1
- package/snapshot/services/core/docgen/komponenten-hero-st/303/266rer.json +1 -1
- package/snapshot/services/core/docgen/komponenten-inputs-forms-auswahlfeld.json +1 -1
- package/snapshot/services/core/docgen/komponenten-inputs-forms-checkbox.json +1 -1
- package/snapshot/services/core/docgen/komponenten-inputs-forms-radio.json +1 -1
- package/snapshot/services/core/docgen/komponenten-inputs-forms-skala.json +1 -1
- package/snapshot/services/core/docgen/komponenten-inputs-forms-slider.json +1 -1
- package/snapshot/services/core/docgen/komponenten-inputs-forms-textbereich.json +1 -1
- package/snapshot/services/core/docgen/komponenten-slider-carousel-logocarousel.json +1 -1
- package/snapshot/services/core/docgen/komponenten-tabelle-vergleichstabelle.json +1 -1
- package/snapshot/services/core/docgen/komponenten-theme-umschalter-dropdown.json +1 -1
- package/snapshot/services/core/docgen/komponenten-theme-umschalter-segment.json +1 -1
- package/snapshot/services/core/docgen/komponenten-zitate-testimonials-teamvoice.json +1 -1
- package/snapshot/services/core/docgen/komponenten-zitate-testimonials-testimonial.json +1 -1
- package/src/instructions.mjs +10 -1
- package/snapshot/services/addon-docs/mdx/komponenten-call-to-action--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-cards-teaser--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-chips-badges-pills--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-code-block--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-dropdowns--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-feedback--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-footer--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-hero--/303/274bersicht.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-inputs-forms--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-navigation--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-sektion--/303/274bersicht.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-slider-carousel--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-tabelle--/303/274bersicht.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-theme-umschalter--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/komponenten-zitate-testimonials--verwendung.json +0 -18
- package/snapshot/services/addon-docs/mdx/marke-logo--verwendung.json +0 -18
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-hero--übersicht": {
|
|
4
|
-
"id": "komponenten-hero--übersicht",
|
|
5
|
-
"name": "komponenten-hero--übersicht",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-hero--übersicht": {
|
|
8
|
-
"id": "komponenten-hero--übersicht",
|
|
9
|
-
"name": "Übersicht",
|
|
10
|
-
"path": "./src/docs/komponenten/hero.mdx",
|
|
11
|
-
"title": "Komponenten/Hero",
|
|
12
|
-
"content": "import { Meta, Unstyled } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Hero\" name=\"Übersicht\" />\n\n# Hero\n\nDrei Hero-Varianten für den Seitenkopf: Hero-Image mit Caption-Overlay (21:9,\nStandard auf allen Customer-Pages), Slider-Hero mit Crossfade-Rotation (optional)\nsowie eine reduzierte Sub-Sektion direkt unter dem Hero für CTAs (auf jeder\nCustomer-Page Pflicht). Dazu der Störer, der ausschließlich auf der Startseite über\ndem Hero-Bild liegt. Hero-Image und Störer haben eigene Angular-Bauteile:\n`Komponenten/Hero/Hero-Bild` (`cds-hero-image`) und `Komponenten/Hero/Störer`\n(`cds-stoerer` in `cds-stoerer-set`). Slider-Hero und Sub-Sektion haben keins, ihre\nKlassen kommen direkt aus der CSS-Schicht. Der Slider-Hero teilt seine Track-Mechanik\nmit `Komponenten/Slider & Carousel/Carousel`.\n\nQuelle: `docs/index.html#sec-hero` (Zeilen 2550 bis 2995).\n\n## Hero-Image mit Caption-Overlay\n\nVollbreit, 21:9-Format, Caption als Gradient-Overlay über dem Bild, ohne\nTrack-Mechanik, ohne Buttons und Dots, ohne JavaScript. Klasse `.hero-image` als\n`<figure>`-Container; semantisch ist Bild und Caption ein figure/figcaption-Pattern.\nVerwenden, wenn die Bildaussage stark genug ist, dass keine Rotation nötig wäre, und\nwenn die Marken-Headline besser am Bild verankert sitzt als in einer separaten\nHero-Body-Spalte.\n\nOptionale Eyebrow-Zeile `.hero-image-caption-eyebrow` direkt vor dem Titel,\nVersalien mit Letter-Spacing, identisch zur Eyebrow-Logik im Standard-Hero. Auf\nBereichs-Pages enthält sie typischerweise die drei Werte des Bereichs, auf der\nLanding den Zeit-Ort-Anker.\n\n**Höhe und Skalierung auf breiten Bildschirmen.** Der Hero zielt auf rund 75 % der\nViewport-Höhe. Das 21:9-Format würde auf 4K- oder Ultrawide-Monitoren rechnerisch\naber noch höher werden (auf 3.840 px Breite wären das 1.646 px Höhe). Damit\nGesichter nicht ins Gigantische kippen, ist `.hero-image-media` über\n`max-height:clamp(480px,75vh,900px)` gedeckelt: mindestens 480 px auf flachen\nFenstern, bevorzugt 75 vh, gedeckelt bei 900 px. Dieselbe Logik liegt auf dem\nSlider-Hero. Die Caption bleibt durch `padding-inline:max(var(--s8),calc(50% - 640px))`\nin einer 1280-px-Spalte zentriert.\n\n**Object-Position pro Bild.** Sobald der Cap greift, beschneidet\n`object-fit:cover` das Bild zusätzlich vertikal. Für Bilder mit zentralen\nGesichtern oder asymmetrischer Komposition den passenden `object-position`-Wert\ndirekt am `<img>`-Element setzen (z. B. `style=\"object-position:center 20%\"`).\nDefault ist `center center`.\n\nDas folgende Schema zeigt die Bildfläche im 21:9-Format mit dem Caption-Overlay\ndarüber: Eyebrow, Headline und Sub-Text liegen als Gradient von unten nach oben\nüber dem Bild.\n\n<Unstyled>\n <div style={{ border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', overflow: 'hidden', marginBottom: 'var(--s8)' }}>\n {/* Hero-Image-Schema: Bildfläche mit Caption-Overlay */}\n <div\n style={{\n position: 'relative',\n minHeight: '240px',\n background: 'linear-gradient(135deg,var(--n-300) 0%,var(--n-200) 55%,var(--n-300) 100%)',\n }}\n >\n <div\n style={{\n position: 'absolute',\n top: 'var(--s4)',\n left: 'var(--s5)',\n font: '500 12px/14px var(--font)',\n letterSpacing: '.1em',\n textTransform: 'uppercase',\n color: 'var(--n-500)',\n }}\n >\n Bildfläche · 21:9 · object-fit:cover\n </div>\n {/* Caption-Overlay (.hero-image-caption) */}\n <div\n style={{\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n padding: 'var(--s7) var(--s6) var(--s5)',\n background: 'linear-gradient(to top,rgba(0,0,0,.85) 0%,rgba(0,0,0,.65) 50%,transparent 100%)',\n }}\n >\n <div\n style={{\n font: '500 12px/16px var(--font)',\n letterSpacing: '.1em',\n textTransform: 'uppercase',\n color: 'rgba(255,255,255,.85)',\n marginBottom: 'var(--s2)',\n }}\n >\n ④ Eyebrow, Bereichskontext oder Zeit-Ort\n </div>\n <div style={{ font: '400 28px/34px var(--font-display)', color: '#fff', marginBottom: 'var(--s2)' }}>\n ⑤ Headline (Caption-Title)\n </div>\n <div style={{ font: '400 14px/20px var(--font)', color: 'rgba(255,255,255,.92)', maxWidth: '520px' }}>\n ⑥ Sub-Text: konkret und prägnant, max. 2 Sätze.\n </div>\n </div>\n </div>\n </div>\n</Unstyled>\n\n```html\n<!-- Hero-Image: 21:9, Eyebrow + Titel + Text als Gradient-Overlay -->\n<figure class=\"hero-image\">\n <div class=\"hero-image-media\">\n <img src=\"hero.jpg\" alt=\"Kurze Bildbeschreibung\" loading=\"eager\">\n </div>\n <figcaption class=\"hero-image-caption\">\n <p class=\"hero-image-caption-eyebrow\">Seit 2016 · Dortmund</p>\n <h1 class=\"hero-image-caption-title\">Klare Köpfe. Ruhige Energie.</h1>\n <p class=\"hero-image-caption-text\">KI, Software und Organisationsentwicklung aus Dortmund.</p>\n </figcaption>\n</figure>\n```\n\n### Content-breite Akzentbilder (`.ep-media-band`)\n\nZwischen den Sektionen lockern gelegentlich Akzentbilder (Menschen,\nWorkshop-Atmosphäre) den Lesefluss auf. Anders als das vollbreite Hero-Image laufen\nsie nicht randlos, sondern auf Content-Breite: `.ep-media-band` als `<figure>`\nhängt an derselben Zentrier-Regel wie `.ep-section`\n(`padding-inline:max(var(--s8),calc(50% - 640px))`) und rastet damit pixelgenau auf\ndie 1280-px-Spalte ein, bündig mit Text und Karten. Ein randloses Zwischenbild\nwürde auf breiten Schirmen zu einem sehr breiten, flachen Band mit unkontrolliertem\nBeschnitt: Die Proportionen kippen dann je nach Fensterbreite. Auf Content-Breite\ngedeckelt bleiben sie stabil, den randlosen Auftritt behält allein der Hero. Die\nEcken sind mit `--r-lg` gerundet wie Karten, der Radius liegt auf dem `<img>`, nicht\nauf dem `<figure>` (das ist durch das Gutter-Padding breiter als das sichtbare\nBild). Das Band trägt `margin:var(--s12) 0`, damit es als eigenständiges Element\nauf dem Seitengrund steht und nicht an einer angrenzenden vollbreiten Farbfläche\nklebt.\n\n## Slider-Hero\n\n**Status.** Aktuell auf keiner Beispielseite produktiv eingesetzt, der statische\nHero-Image ist der Standard. Der Slider-Hero ist eine optionale Alternative, wenn\nmehrere gleichwertige Aussagen rotieren sollen (z. B. wechselnde Kundenprojekte auf\neiner Referenz-Übersicht). Aufmerksamkeits-Konkurrenz mit der Marken-Headline\neinplanen.\n\nVollbreit, randlos, 21:9-Format. Caption liegt als Gradient-Overlay über dem Bild.\nModifier-Klasse `.img-slider-hero` zum Basis-Element hinzufügen.\n\n**Übergang.** Crossfade über 600 ms. Slides liegen absolut übereinander; die aktive\nSlide trägt die Klasse `.active` (initial im HTML auf der ersten Slide setzen,\ndamit beim Page-Load nichts unsichtbar ist). Unter `prefers-reduced-motion:reduce`\nentfällt die Transition automatisch.\n\n**Skalierung auf breiten Bildschirmen.** `.img-slider-hero .img-slider-track` ist\nüber `max-height:clamp(420px,64vh,680px)` gedeckelt, damit der Hero auf 4K- und\nUltrawide-Monitoren nicht die gesamte Viewport-Höhe einnimmt. Gleiche Logik wie\nbeim statischen Hero-Image.\n\n**21:9 gegenüber 16:9.** Hero und Slider-Hero laufen auf `.hero-image-media` und\n`.img-slider-hero .img-slider-track` mit `aspect-ratio:21/9`. Der Basis-Slider\naußerhalb des Hero-Kontexts (`.img-slide-media`, genutzt in\n`Komponenten/Slider & Carousel/Carousel`) und `.card-media` in\n`Komponenten/Cards & Teaser/Card` laufen dagegen mit `aspect-ratio:16/9`. Ein Hero\nist breiter und flacher als eine Karte, weil er die volle Seitenbreite trägt statt\neiner begrenzten Spalte.\n\n```html\n<!-- Modifier .img-slider-hero für Vollbild-Darstellung mit Crossfade -->\n<div class=\"img-slider img-slider-hero\"\n role=\"region\" aria-roledescription=\"Bildschirmpräsentation\" aria-label=\"Hero-Bildstrecke\">\n <div class=\"img-slider-track\">\n <!-- erste Slide trägt .active, damit sie ohne JS sofort sichtbar ist -->\n <div class=\"img-slide active\" role=\"group\" aria-roledescription=\"Folie\" aria-label=\"Folie 1 von 4\">\n <div class=\"img-slide-media\">\n <img src=\"hero.jpg\" alt=\"Kurze Bildbeschreibung\">\n </div>\n <div class=\"img-slide-caption\">\n <p class=\"img-slide-caption-title\">Titel</p>\n <p class=\"img-slide-caption-text\">Beschreibung</p>\n </div>\n </div>\n <!-- weitere .img-slide … -->\n </div>\n <button class=\"img-slider-btn img-slider-prev\" aria-label=\"Vorherige Folie\">…</button>\n <button class=\"img-slider-btn img-slider-next\" aria-label=\"Nächste Folie\">…</button>\n <div class=\"img-slider-dots\" role=\"tablist\" aria-label=\"Folien-Navigation\">\n <button class=\"img-dot\" role=\"tab\" aria-selected=\"true\" aria-label=\"Folie 1 von 4\"></button>\n <button class=\"img-dot\" role=\"tab\" aria-selected=\"false\" aria-label=\"Folie 2 von 4\"></button>\n </div>\n</div>\n```\n\n## Reduzierte Sub-Sektion mit CTAs\n\nDirekt unterhalb des Heros sitzt eine schmale, getönte Sektion (`.ep-section`,\nzentriert, Padding `var(--s10)`) mit Eyebrow, kurzer Lead-Zeile und zwei CTAs. Sie\nübernimmt die Funktion „Was tun?“, ohne mit der Hero-Headline zu konkurrieren: Der\nHero macht das Versprechen, die Sektion darunter macht das Angebot. Eine\nAngebots-Sektion direkt unter dem Hero-Image ist auf jeder Customer-Page Pflicht,\nihre Form variiert (schmaler Sub-Strip oder, auf Bereichs-Pages mit\nManifest-Charakter, eine reichere Manifest-Intro).\n\n**Bereichs-Tinting.** Hintergrund und untere Trennlinie folgen dem Page-Bereich,\nnicht pauschal Corporate.\n\n| Page | Eyebrow-Text | Eyebrow-Farbe |\n|---|---|---|\n| Landing | „Verbunden gedacht“ (Brücke-Variante B) | `--co-700` |\n| Angewandte KI | Leistungs-Achsen: „AI.Engineering · AI.Automation · AI.Box“ | `--ki-800` |\n| Effektive Software | Manifest-Intro (Variante C): „Was effektive Software ausmacht“ | `--es-700` |\n| Wirksame Organisationen | Leistungs-Achsen: „Change · Leadership · Struktur“ | `--wo-700` |\n| Unternehmen | Profil-Hub-Achsen: „Team · Karriere · Referenzen“ | `--co-700` |\n\n**CTAs pro Page-Typ.** Filled (primär, Selbst-Erschließung) plus Outlined\n(sekundär, Direkt-Kontakt oder Belege), beide in der Bereichsfarbe.\n\n| Page | Primär (Filled) | Sekundär (Outlined) |\n|---|---|---|\n| Landing | Keine CTAs (Brücke-Variante B) | — |\n| Angewandte KI | Use Case analysieren | Fallstudien ansehen |\n| Effektive Software | Unsere Leistungen ansehen | Gespräch anfragen |\n| Wirksame Organisationen | Transformation starten | Ansatz kennenlernen |\n| Unternehmen | Team kennenlernen | Karriere |\n\nWichtig: Wenn Hero und Sub-Strip zusammen verwendet werden, darf der Sub-Strip\nkeine eigene H1 und keine wiederholte Was/Wo-Aussage tragen, sonst entstehen\nDoppel-Informationen direkt untereinander.\n\n| Variante | Inhalt | Wann einsetzen |\n|---|---|---|\n| A · mit CTAs | Eyebrow, Lead, zwei Buttons (Filled primär, Outlined sekundär) | Standard, wenn die Sub-Sektion zwei direkte Aktionen anbieten soll |\n| B · Brücke ohne CTAs | Eyebrow, Lead, Drei-Spalter mit Bereichs-Eyebrows | Wenn der Sticky-Topnav-Kontakt-Button bereits die primäre Konversion trägt |\n| C · Manifest-Intro | Eyebrow, Lead (erster Satz in `--ty-serif-md`), Fließtext, zwei CTAs, zweispaltiges Grid | Auf Bereichs-Pages, deren Einstieg eine Haltung oder Definition transportiert |\n\nDas folgende Schema zeigt Variante A: Markenrad-Eyebrow, Lead-Zeile und die zwei\nCTAs (Filled primär, Outlined sekundär) auf dem getönten Sub-Strip-Hintergrund.\n\n<Unstyled>\n <div style={{ border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', overflow: 'hidden', marginBottom: 'var(--s8)' }}>\n {/* Sub-Strip-Schema: Markenrad-Eyebrow + Lead + 2 CTAs */}\n <div style={{ background: 'var(--co-50)', textAlign: 'center', padding: 'var(--s7) var(--s6)' }}>\n <div\n className=\"t-co\"\n style={{\n font: '500 12px/16px var(--font)',\n letterSpacing: '.1em',\n textTransform: 'uppercase',\n marginBottom: 'var(--s2)',\n }}\n >\n ⑦ Markenrad-Eyebrow „Ruhig · Klar · Energiegeladen“\n </div>\n <div\n style={{\n font: '400 14px/20px var(--font)',\n color: 'var(--tx-secondary)',\n marginBottom: 'var(--s4)',\n maxWidth: '520px',\n marginLeft: 'auto',\n marginRight: 'auto',\n }}\n >\n ⑧ Lead-Zeile: Brücke zu den CTAs, nicht das Hero-Versprechen echoen.\n </div>\n <div style={{ display: 'flex', gap: 'var(--s3)', justifyContent: 'center', flexWrap: 'wrap' }}>\n <div\n style={{\n background: 'var(--co-700)',\n borderRadius: 'var(--r-full)',\n padding: '8px 20px',\n font: 'var(--ty-name)',\n color: '#fff',\n }}\n >\n ⑨ Primäraktion (Filled)\n </div>\n <div\n className=\"t-co\"\n style={{\n border: '2px solid var(--co-700)',\n borderRadius: 'var(--r-full)',\n padding: '6px 18px',\n font: 'var(--ty-name)',\n }}\n >\n ⑩ Sekundäraktion (Outlined)\n </div>\n </div>\n </div>\n </div>\n</Unstyled>\n\n```html\n<!-- Sub-Strip: Bereichs-Tint, Eyebrow, Lead, 2 CTAs (Werte hier: Landing/Corporate) -->\n<div class=\"ep-section\" style=\"background:var(--co-50);text-align:center;padding-block:var(--s10);border-bottom:1px solid var(--co-100)\">\n <p class=\"ep-hero-eyebrow\" style=\"color:var(--co-700)\">Ruhig · Klar · Energiegeladen</p>\n <p>Schau Dir an, wie wir arbeiten, oder buch direkt ein Erstgespräch.</p>\n <div class=\"ep-hero-ctas\" style=\"justify-content:center\">\n <button class=\"btn btn-filled\" style=\"--c500:var(--co-700)\">Unsere Leistungen</button>\n <button class=\"btn btn-outlined\" style=\"--c500:var(--co-700)\">Gespräch anfragen</button>\n </div>\n</div>\n```\n\n## Störer über dem Hero (Startseite)\n\nKompakte Verweiskacheln, die als Set oben rechts über dem Hero-Bild liegen und je\nauf einen aktuellen Inhalt zeigen: die nächste Veranstaltung, einen neuen\nWissensbeitrag, eine Pressemitteilung oder eine allgemeine Info (neue\nSeminar-Landingpage, neues Produkt). Klassen `.stoerer-hero` (Wrapper um Hero und\nSet), `.stoerer-set`, `.stoerer-list`, `.stoerer`.\n\n**Nur auf der Startseite.** Auf Bereichs- und Detailseiten trägt der Hero genau\neine Aussage; ein zweiter Aufmerksamkeitspunkt daneben würde sie schwächen. Die\nWeiterleitung übernimmt dort die reduzierte Sub-Sektion unter dem Hero.\n\n**Das Ziel ist immer die eigene Seite des Inhalts, nie ein Anker auf der\nStartseite.** Ein Klick auf die Veranstaltungs-Kachel führt auf die\nVeranstaltungs-Detailseite (`Seitenmuster/Veranstaltung`), die Wissens-Kachel auf\nden Beitrag (`Seitenmuster/Wissensbeitrag`). Der Störer ist eine Abkürzung zum\nInhalt, keine Sprungmarke innerhalb der Startseite. Die Sektion bleibt der\nkanonische Ort, sie führt Bild, Anreißer und Kontext, der Störer führt nur Typ,\nTitel und Meta. Daraus folgt eine Pflichtregel: Das Thema-Label spiegelt das\nVokabular der zugehörigen Sektion, es führt kein zweites Wort für dieselbe Domäne\nein.\n\n**Ein Set, keine Einzelkacheln.** Alle Kacheln eines Sets haben dieselbe Breite\n(`--stoerer-w`, Default 22,5rem/360 px), denselben Aufbau, dieselbe\nTypo-Hierarchie und dieselben Abstände. Die Höhe hält `.stoerer-title` gleich:\n`min-height:2lh` reserviert zwei Zeilen, das innere Element kappt längere Titel\nper `line-clamp`. Default sind zwei Kacheln, drei sind das Maximum.\n\n**Eine Farbgebung für alle vier Typen.** Der Inhaltstyp steht im Thema-Label und im\nIcon, nie in der Farbe. Vier Bereichsfarben nebeneinander würden gegeneinander und\ngegen die Hero-Headline arbeiten. Der Akzent ist deshalb durchgehend Corporate\n(`--co-ink`), aus demselben Grund wie bei der Topnav: der Störer ist Chrome über\ndem Bild, kein Bereichsinhalt. Ein `data-area` gibt es hier bewusst nicht.\n\n**Deckende Fläche, kein Glas.** Über einem Foto ist der Grund unbekannt, eine\nhalbtransparente Kachel trägt je nach Bildstelle 2:1 oder 12:1, und keine Regel\nkann das absichern. Auf `--bg-surface` steht der Text auf einer bekannten Fläche\nund ist messbar: Light 5,52 bis 10,92:1, Dark 8,32 bis 10,48:1. Dazu trägt die\nKachel schon im Light einen `--bd-strong`-Rahmen, abweichend von den übrigen\nKarten: auf einer ausgebrannt hellen Bildstelle leistet der schwarze `--e1`-Schatten\nkeine Kante mehr.\n\n**Das Typ-Glyph sitzt im Thema-Label, nicht rechts hinter einem Trenner.** Das\nGlyph ist ein reiner Typ-Marker, also eine Kategorie, und gehört als\nLeading-Element vor das Label. Rechts stand es im Slot für Aktionen und Status,\nauf einer vollständig klickbaren Kachel also doppeldeutig (kann als Button\nlesen). Ohne Icon-Spalte wächst die Textspalte von 269 auf 326 px (plus 21 %), und\ndie realistischen Titel der Startseite klammern nicht mehr.\n\n**Platzierung.** Oben rechts, weil die Caption unten links liegt und die\nSub-Sektion direkt darunter die freie Zone bildet. Das Hero-Bild muss dafür\nausgewählt sein: die Kacheln verdecken das obere rechte Viertel deckend, dort darf\nalso kein tragendes Bildmotiv liegen (Gesichter, Produkt, Logo).\n\n**Container-Query statt Fensterbreite.** Bei 21:9 ist die Hero-Höhe die Breite ×\n9/21. Eine Kachel ist 126 px hoch, drei plus Abstände und der Rand oben brauchen\n426 px Höhe und damit 994 px Hero-Breite. Ob die da ist, sagt der Hero, nicht das\nFenster: Eine Media Query auf 1025 px ließ ein Dreier-Set in einem 1200-px-Fenster\nmit 898-px-Hero 41 px in die Sub-Sektion ragen. Die Entscheidung hängt deshalb an\neiner Container-Query auf `.stoerer-hero`, Schwelle 64,0625rem (1025 px), dem\nDesktop-Tier der Responsive-Strategie:\n\n```css\n.stoerer-hero{position:relative;container-type:inline-size}\n\n@container (min-width:64.0625rem){\n .stoerer-set{position:absolute;top:var(--s6);right:var(--s8)}\n .stoerer-list{display:flex;flex-direction:column;width:var(--stoerer-w)}\n}\n```\n\nAußerhalb der `@container`-Regel (schmaler Hero oder ein Browser ohne\nContainer-Query-Support) bleibt `.stoerer-set` ein normaler Block unter dem Hero,\nein- oder zweispaltig je nach Platz: Das ist der Default-Zweig, und er ist zugleich\nder funktionierende Fall, nie ein Overlay, das halb in die Sub-Sektion ragt.\n\nDas folgende Specimen zeigt alle vier Inhaltstypen nebeneinander: gleiche\nFläche, gleiche Typo-Hierarchie, unterschieden allein durch Thema-Label und\nIcon. Im Einsatz stehen höchstens drei Kacheln zusammen, deshalb trägt dieses\nSpecimen eine breitere, abweichende Grid-Spalte; die `href`-Werte sind\nPlatzhalter, im Einsatz zeigt jede Kachel auf die Detailseite ihres Inhalts\n(siehe Tabelle unten).\n\n<Unstyled>\n {/* Störer-Specimen: alle vier Inhaltstypen nebeneinander */}\n <ul\n className=\"stoerer-list\"\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit,minmax(320px,1fr))',\n width: 'auto',\n marginBottom: 'var(--s8)',\n }}\n >\n <li>\n <a className=\"stoerer\" href=\"#gt-hero-stoerer\">\n <span className=\"stoerer-topic\">\n <svg className=\"stoerer-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5\" />\n </svg>\n Nächste Veranstaltung\n </span>\n <span className=\"stoerer-title\"><span>Effizienz durch n8n</span></span>\n <span className=\"stoerer-meta\">\n <time dateTime=\"2026-12-03\">3. Dezember 2026</time>\n <span aria-hidden=\"true\"> · </span>\n <span className=\"sr-only\">, </span>\n Dortmund\n </span>\n </a>\n </li>\n <li>\n <a className=\"stoerer\" href=\"#gt-hero-stoerer\">\n <span className=\"stoerer-topic\">\n <svg className=\"stoerer-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M12 7.5h1.5m-1.5 3h1.5m-7.5 3h7.5m-7.5 3h7.5m3-9h3.375c.621 0 1.125.504 1.125 1.125V18a2.25 2.25 0 0 1-2.25 2.25M16.5 7.5V18a2.25 2.25 0 0 0 2.25 2.25M16.5 7.5V4.875c0-.621-.504-1.125-1.125-1.125H4.125C3.504 3.75 3 4.254 3 4.875V18a2.25 2.25 0 0 0 2.25 2.25h13.5M6 7.5h3v3H6v-3Z\" />\n </svg>\n Neu im Wissen\n </span>\n <span className=\"stoerer-title\"><span>Refactoring-Schulden ehrlich rechnen</span></span>\n <span className=\"stoerer-meta\">\n <time dateTime=\"2026-04-22\">22. April 2026</time>\n <span aria-hidden=\"true\"> · </span>\n <span className=\"sr-only\">, </span>\n 7 min Lesezeit\n </span>\n </a>\n </li>\n <li>\n <a className=\"stoerer\" href=\"#gt-hero-stoerer\">\n <span className=\"stoerer-topic\">\n <svg className=\"stoerer-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M10.34 15.84c-.688-.06-1.386-.09-2.09-.09H7.5a4.5 4.5 0 1 1 0-9h.75c.704 0 1.402-.03 2.09-.09m0 9.18c.253.962.584 1.892.985 2.783.247.55.06 1.21-.463 1.511l-.657.38c-.551.318-1.26.117-1.527-.461a20.845 20.845 0 0 1-1.44-4.282m3.102.069a18.03 18.03 0 0 1-.59-4.59c0-1.586.205-3.124.59-4.59m0 9.18a23.848 23.848 0 0 1 8.835 2.535M10.34 6.66a23.847 23.847 0 0 0 8.835-2.535m0 0A23.74 23.74 0 0 0 18.795 3m.38 1.125a23.91 23.91 0 0 1 1.014 5.395m-1.014 8.855c-.118.38-.245.754-.38 1.125m.38-1.125a23.91 23.91 0 0 0 1.014-5.395m0-3.46c.495.413.811 1.035.811 1.73 0 .695-.316 1.317-.811 1.73m0-3.46a24.347 24.347 0 0 1 0 3.46\" />\n </svg>\n Pressemitteilung\n </span>\n <span className=\"stoerer-title\"><span>Conciso begleitet Stadtwerke bei der KI-Einführung</span></span>\n <span className=\"stoerer-meta\"><time dateTime=\"2026-08-12\">12. August 2026</time></span>\n </a>\n </li>\n <li>\n <a className=\"stoerer\" href=\"#gt-hero-stoerer\">\n <span className=\"stoerer-topic\">\n <svg className=\"stoerer-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z\" />\n </svg>\n Neues Seminar\n </span>\n <span className=\"stoerer-title\"><span>Scrum Master Kurs, neue Termine ab Oktober</span></span>\n <span className=\"stoerer-meta\">Seminar · 2 Tage</span>\n </a>\n </li>\n </ul>\n</Unstyled>\n\n| Inhaltstyp | Thema-Label | Meta-Zeile | Ziel des Links |\n|---|---|---|---|\n| Veranstaltung | „Nächste Veranstaltung“ | Datum · Ort | Veranstaltungs-Detailseite |\n| Wissensbeitrag | „Neu im Wissen“ | Datum · Lesezeit | Wissensbeitrag |\n| Pressemitteilung | „Pressemitteilung“ | Datum | Presseseite oder einzelne Meldung |\n| Info | Spiegelt das Wort des Ziels, z. B. „Neues Seminar“, „Neues Produkt“ | Kurze Einordnung, z. B. Format und Dauer | Seminar-Landingpage, Produkt- oder Angebotsseite |\n\n```html\n<div class=\"stoerer-hero\">\n <figure class=\"hero-image\">…</figure>\n <aside class=\"stoerer-set\" aria-label=\"Aktuelles\">\n <ul class=\"stoerer-list\">\n <li>\n <a class=\"stoerer\" href=\"/veranstaltungen/effizienz-durch-n8n\">\n <span class=\"stoerer-topic\">\n <svg class=\"stoerer-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" aria-hidden=\"true\">…</svg>\n Nächste Veranstaltung\n </span>\n <span class=\"stoerer-title\"><span>Effizienz durch n8n</span></span>\n <span class=\"stoerer-meta\">\n <time datetime=\"2026-12-03\">3. Dezember 2026</time><span aria-hidden=\"true\"> · </span><span class=\"sr-only\">, </span>Dortmund\n </span>\n </a>\n </li>\n </ul>\n </aside>\n</div>\n```\n\n### Störer · Barrierefreiheit\n\n| Aspekt | Regel |\n|---|---|\n| Kontrast Thema | `--co-ink` auf `--bg-surface`: Light 5,52:1, Dark 8,32:1 |\n| Kontrast Titel | `--tx-primary`: Light 10,92:1, Dark 10,48:1 |\n| Kontrast Meta | `--tx-secondary`: Light 6,29:1, Dark 7,13:1, nicht `--tx-muted` |\n| Deckende Fläche | Text über Foto lässt sich nicht messen. Die Kachel ist deshalb opak, kein `backdrop-filter`, keine Alpha-Fläche |\n| Fokus | `--focus-ring` auf der ganzen Kachel plus `--e3`, ein Tab-Stop je Kachel, kein verschachtelter Link |\n| Hover ohne Farbe | Der Titel unterstreicht (WCAG 1.4.1), ein Schatten- oder Flächen-Zuwachs ist über einem Foto je nach Bildstelle kaum sichtbar |\n| Landmark | `<aside aria-label=\"Aktuelles\">` nach der Caption im DOM: Die H1 kommt zuerst, der Störer danach (WCAG 1.3.2) |\n| Trennzeichen | Der mittige Punkt in der Meta-Zeile ist `aria-hidden`, daneben steht ein `.sr-only`-Komma, sonst verschmelzen Datum und Ort |\n| Icon | `aria-hidden=\"true\"`, es wiederholt nur das Thema-Label und trägt keine eigene Bedeutung |\n\n### Störer · Verwendung\n\n| Aspekt | Regel |\n|---|---|\n| Anzahl | Default zwei, Maximum drei. Eine einzelne Kachel ist erlaubt, wenn nur ein Anlass aktuell ist |\n| Ein Typ, eine Kachel | Kein Typ doppelt im Set. Für mehrere Termine auf die Veranstaltungsübersicht (`Seitenmuster/Veranstaltungsübersicht`) verlinken |\n| Thema-Label | Ein bis drei Wörter, benennt den Inhaltstyp, nicht den Bereich |\n| Vokabular | Ein Domänenwort pro Domäne über Nav, Störer und Sektion |\n| Ziel | Die Detailseite des Inhalts, nie ein Abschnitt der Startseite. Existiert für einen Anlass keine eigene Seite, ist er kein Störer-Kandidat |\n| Dublette | Erlaubt und beabsichtigt: der Störer ist die Abkürzung zu einem Inhalt, der weiter unten seine Sektion hat. Bedingung: gleiches Ziel und gleiches Domänenwort, plus der Titel im zugänglichen Namen |\n| Titel | Der Titel des Inhalts, gekürzt auf das, was in zwei Zeilen trägt, redaktionell, nicht dem `line-clamp` überlassen |\n| Reihenfolge | Redaktionell frei, bewusst ohne Regel, aber sie wechselt nicht pro Seitenaufruf |\n| Aktualität | Ein Störer verfällt: vergangene Termine und Meldungen älter als das gewählte Redaktionsfenster raus |\n\n<div className=\"doc-eyebrow\">Dos & Don'ts</div>\n\n**✓ Tun**\n\n- Alle Kacheln eines Sets in gleicher Breite, gleichem Aufbau und gleicher Typo-Hierarchie halten, sie sind ein Set\n- Den Inhaltstyp über Thema-Label und Icon unterscheiden, die Farbgebung bleibt für alle Typen dieselbe\n- Die ganze Kachel als `<a>` auszeichnen, ein Tab-Stop, sichtbarer Hover und Fokus\n- Titel redaktionell auf zwei Zeilen kürzen, statt sich auf das `line-clamp` zu verlassen\n- Das Thema-Label mit dem Wort der zugehörigen Sektion bilden\n- Das Typ-Glyph als Leading-Element direkt vor sein Label setzen\n- Den Störer der Startseite vorbehalten, auf allen anderen Seiten trägt der Hero eine Aussage\n\n**✕ Nicht tun**\n\n- Kacheln in Bereichsfarben einfärben oder `data-area` setzen, dann konkurrieren sie untereinander und mit dem Hero\n- Halbtransparente Fläche oder `backdrop-filter`, der Kontrast hängt dann von der Bildstelle ab\n- Mehr als drei Kacheln, unterschiedlich große Kacheln, oder eine Kachel mit Bild und Anreißer (das ist dann eine Card und gehört in eine Sektion)\n- Buttons, einen zweiten Link oder ein Schließen-Kreuz in der Kachel, das bricht den einen Tab-Stop\n- Auf einen Anker der eigenen Seite verlinken statt auf die Detailseite\n- Das Glyph in den Trailing-Slot rechts stellen, mit oder ohne Trenner\n- Ein zweites Wort für dieselbe Domäne einführen\n- Das Overlay über eine Media Query an die Fensterbreite hängen statt über eine Container-Query an die Hero-Breite\n\n## Verwandte Seiten\n\n- `Komponenten/Hero/Hero-Bild` (`cds-hero-image`)\n- `Komponenten/Hero/Störer` (`cds-stoerer`, `cds-stoerer-set`)\n- `Komponenten/Slider & Carousel/Carousel` (Track-Mechanik des Slider-Hero)\n- `Komponenten/Cards & Teaser/Card` (16:9-Referenz gegenüber dem 21:9-Hero)\n- `Seitenmuster/Veranstaltung`, `Seitenmuster/Wissensbeitrag` (Ziele der Störer-Kacheln)\n- Doku-Sektion: `docs/index.html#sec-hero`\n",
|
|
13
|
-
"summary": "# Hero Drei Hero-Varianten für den Seitenkopf: Hero-Image mit Caption-Overlay (21:9, Stand..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-inputs-forms--verwendung": {
|
|
4
|
-
"id": "komponenten-inputs-forms--verwendung",
|
|
5
|
-
"name": "komponenten-inputs-forms--verwendung",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-inputs-forms--verwendung": {
|
|
8
|
-
"id": "komponenten-inputs-forms--verwendung",
|
|
9
|
-
"name": "Verwendung",
|
|
10
|
-
"path": "./src/docs/komponenten/inputs-verwendung.mdx",
|
|
11
|
-
"title": "Komponenten/Inputs & Forms",
|
|
12
|
-
"content": "import { Meta } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Inputs & Forms\" name=\"Verwendung\" tags={['angular']} />\n\n# Verwendung\n\nFeldkomponenten (Text · E-Mail · Select · Textarea · Checkbox · Radio), bereichsspezifische Kontaktformulare, das Such-Feld als Landmark, die Newsletter-Anmeldung (Karte und Kompakt-Widget) sowie der Slider. Durchgängig WCAG AA: Labels, `aria-required`, `aria-invalid`, `role=\"alert\"`.\n\n## Wann welche Variante\n\n### Feldkomponenten\n\n| Feld | Wann einsetzen |\n|---|---|\n| Text Input | Name, Firma, Betreff, alle einzeiligen Freitextangaben |\n| E-Mail Input | Immer `type=\"email\"` für automatische Formatvalidierung und Mobile-Keyboard |\n| Select | Vorgegebene Optionsliste (z. B. Bereich, Anliegen), max. 8 Optionen, sonst Freitext |\n| Textarea | Freitext-Nachrichten, mindestens 3 Zeilen Höhe, kein festes Maxlength |\n| Checkbox | Einwilligung (Newsletter, DSGVO), immer mit verlinktem Datenschutzhinweis als echtem `<a class=\"body-link\">` |\n| Radio | 2 bis 6 sich gegenseitig ausschließende Optionen (z. B. bevorzugter Kontaktweg), für mehr Optionen ein Select, für Mehrfachauswahl Checkboxen |\n| Segmented Control | Kompakte Radio-Variante für 2 bis 4 kurze, gleichrangige Optionen nebeneinander (`.seg`), bei längeren Labels oder Beschreibungen die Radio-Liste |\n\nDer Select-Platzhalter (erste Option „Bitte wählen…“) trägt `value=\"\"` plus `disabled` plus `selected`, damit er als unerlaubter Wert zählt: Browser-Validierung erkennt ein Pflicht-Select korrekt als leer, der Text wird nicht versehentlich beim Submit mitgesendet. Radio und Segmented Control bleiben immer **native** `<input type=\"radio\">`, kein nachgebauter Kreis, die Gruppe sitzt in einem `<fieldset>` mit `<legend>`, alle Optionen teilen denselben `name`. In einem Bereichsformular sind die Modifier `.seg-ki`, `.seg-es` und `.seg-wo` Pflicht, sonst sitzt mitten zwischen den Feldern ein Corporate-Akzent und das Formular trägt zwei Bereiche gleichzeitig.\n\n### Kontaktformulare je Bereich\n\nJedes Formular trägt die Akzentfarbe der Brand Area, Header und Submit-Button passen sich an.\n\n| Bereich | Header | Submit | Fokusring |\n|---|---|---|---|\n| Corporate | `--co-700` | `.btn-co` | Teal |\n| Angewandte KI | `--ki-800` | `.btn-ki` | Lime |\n| Effektive Software | `--es-700` | `.btn-es` | Blau |\n| Wirksame Organisationen | `--wo-700` | `.btn-wo` | Grün |\n\n### Adaptive Kontaktseite\n\nStatt eines eigenen Formulars pro Bereich gibt es eine Anfrageseite, die sich am Einstieg orientiert. Default ist die neutrale Corporate-Tönung, kommt die Nutzerin über einen Bereichs-CTA, übernimmt das Formular Farbe, Thema und ein vorbelegtes Anliegen. So bleibt die Seite auch für unentschlossene und bereichsübergreifende Nutzer richtig und vermeidet Near-Duplicate-Seiten.\n\nDer CTA trägt `data-ep=\"kontakt\"` plus optional `data-k-bereich=\"ki|es|wo\"` und `data-k-anliegen=\"…\"`. `applyKontaktContext()` tönt Header, Button und Consent-Häkchen, setzt das Thema-Select und belegt „Dein Anliegen“ vor.\n\n| Einstieg | Tönung | Thema | Anliegen (vorbelegt) |\n|---|---|---|---|\n| Topnav / Footer „Kontakt“ | co | Allgemeine Anfrage | (leer) |\n| AI.Automation · „Workshop anfragen“ | ki | Angewandte KI | AI.Automation Workshop |\n| Effektive Software · „Gespräch anfragen“ | es | Effektive Software | (leer) |\n| Wirksame Organisationen · „Transformation starten“ | wo | Wirksame Organisationen | Wirksame Organisationen: Transformation |\n\nZwei `<input type=\"hidden\">` transportieren den Herkunfts-Kontext stabil, unabhängig vom editierbaren Freitext: `name=\"bereich\"` (Werte `ki`, `es`, `wo`, `co`) ordnet an das richtige Team-Postfach zu, `name=\"unterthema\"` liefert die Basis für eine konkrete Erstantwort. Der Akzent hängt am Formular (`data-accent` am `<form>`), nicht an der ganzen Karte: Telefon, E-Mail und der Maps-Link daneben sind Unternehmens-Kontaktdaten und bleiben Corporate, unabhängig davon, aus welchem Bereich die Anfrage kommt. In Produktion werden diese Felder serverseitig aus Route oder Query-Parameter gerendert, nicht per Client-Skript.\n\n### Such-Feld\n\nPill-förmiges Such-Feld mit Magnifier-Icon links, zentriert auf Listing-Seiten. Kein sichtbares `<label>`: Icon und Placeholder tragen den visuellen Kontext, ein `aria-label` liefert den semantischen Namen für Screenreader. Struktur: Wrapper `<div role=\"search\">` mit eigenem `aria-label` (z. B. „Beiträge durchsuchen“) macht das Such-Feld zur Landmark, die Screenreader-Nutzerinnen direkt anspringen können.\n\n```html\n<div role=\"search\" aria-label=\"Beiträge durchsuchen\" style=\"position:relative\">\n <svg aria-hidden=\"true\" focusable=\"false\">…</svg>\n <input type=\"search\" aria-label=\"Beiträge nach Stichwort durchsuchen\"\n placeholder=\"Beitrag suchen\" autocomplete=\"off\">\n</div>\n```\n\nStil-Konventionen: Pill-Radius (`--r-full`), Border `1.5px solid var(--field-border)`, Hintergrund `--bg-surface`, Font `--ty-body-md`. `autocomplete=\"off\"` verhindert Browser-Vorschläge in einer Filterung.\n\n### Newsletter-Anmeldung\n\nZwei generische Varianten für Landingpages und Inline-Bereiche: eine eigenständige Karte und ein kompaktes Widget. Die Footer-Variante „Contentletter abonnieren“ ist ein eigenes Field-Pattern im Footer (eigene Klassen `.footer-newsletter-form`, `.footer-field`) und dort dokumentiert.\n\nAlle Varianten sprechen die Nutzerin in Du-Form an, das Name-Feld fragt explizit nach dem Vornamen, nicht dem vollen Namen, damit der Zweck klar ist: persönliche Anrede im Contentletter. Optional-Marker entfallen, weil die Required-Sternchen bereits Pflicht- und Optional-Felder trennen, eine zweite „(optional)“-Auszeichnung würde das Formular unnötig laut machen.\n\n| Aspekt | `.field` (Standard-Form) | `.footer-field` (Utility-Form) |\n|---|---|---|\n| Label-Token | `--ty-label-sm`, uppercase, `--tx-secondary` | Identisch |\n| Input-Border | `2px solid --field-border` | `1px solid --field-border` |\n| Input-Höhe | `min-height:44px` | `height:44px` |\n| Error-State | `.field.has-error` | `.footer-field.has-error` |\n\nLabels sind in beiden Patterns identisch (uppercase, `--ty-label-sm`, `--tx-secondary`), damit die Form-Sprache des Systems über Konversions- und Utility-Zonen hinweg konsistent bleibt.\n\n### Slider\n\nNumerische Wertauswahl mit nativer Tastatursteuerung (Pfeiltasten, Pos1, Ende). Basiert auf `<input type=\"range\">` mit verknüpftem `<label>` und semantisch korrektem `<output>`, der Browser setzt `role=\"slider\"`, `aria-valuemin/max/now` automatisch.\n\n```html\n<div class=\"field-slider\">\n <div class=\"field-slider-header\">\n <label class=\"field-slider-label\" for=\"my-slider\">Budget-Rahmen</label>\n <output class=\"field-slider-output slider-co\" for=\"my-slider\" id=\"my-out\">50.000 €</output>\n </div>\n <!-- aria-valuetext trägt Einheit/Format für Screenreader, valuenow allein wäre nur „50000“ -->\n <input class=\"slider slider-co\" type=\"range\" id=\"my-slider\"\n min=\"10000\" max=\"100000\" step=\"5000\" value=\"50000\"\n aria-describedby=\"my-hint\" aria-valuetext=\"50.000 €\">\n <span class=\"helper\" id=\"my-hint\">Schritte: 5.000 €</span>\n</div>\n```\n\n| Taste | Funktion |\n|---|---|\n| Pfeil links / Pfeil unten | Wert um 1 Schritt verringern |\n| Pfeil rechts / Pfeil oben | Wert um 1 Schritt erhöhen |\n| Pos1 | Minimalwert springen |\n| Ende | Maximalwert springen |\n\n## Barrierefreiheit\n\n| Attribut | Pflicht / Zweck |\n|---|---|\n| `label` | Jedes Feld braucht ein sichtbares Label, kein Placeholder als Ersatz |\n| `aria-required` | Pflichtfelder mit `aria-required=\"true\"` und sichtbarem `*` kennzeichnen |\n| `aria-invalid` | Bei Validierungsfehler `aria-invalid=\"true\"` setzen, Screenreader meldet Fehlerfeld |\n| `role=\"alert\"` | Fehlermeldung erhält `role=\"alert\"`, wird sofort vorgelesen, ohne Fokusverschiebung |\n| Link im Label | Verlinkte Rechtstexte in Einwilligungen (Datenschutz, Teilnahmebedingungen) sind echte `<a class=\"body-link\">`, kein `<span>` mit `cursor:pointer`, sonst nicht per Tab erreichbar und für Screenreader kein Link, obwohl dieser Text die Grundlage der Einwilligung ist. Im `<label for>` unkritisch: die Label-Aktivierung läuft bei interaktiven Nachfahren nicht, der Klick auf den Link setzt also kein Häkchen |\n| `autocomplete` | Passende Werte setzen (`name`, `email`, …), erleichtert Ausfüllen auf Mobile |\n\nFeld-Rahmen nutzen den modusbewussten Token `--field-border` mit mindestens 3 zu 1 gegen den Feld-Hintergrund (WCAG 1.4.11). Der Rahmen ist der einzige Umriss des Feldes, sobald es auf einer Karte sitzt, dort ist die Feldfüllung mit `--bg-surface` identisch mit dem Grund. Platzhalter-Text immer explizit auf `--tx-secondary` setzen, nie dem Browser überlassen, sonst unterschreitet die Browser-Standardfarbe im Dark Mode den AA-Kontrast.\n\n## Dos & Don'ts\n\n**Tun**\n\n- Jedes Feld mit sichtbarem Label versehen, Placeholder allein ist keine barrierefreie Beschriftung.\n- Fehlermeldungen direkt unter dem betroffenen Feld anzeigen, kontextnah und sofort lesbar.\n- Bereichsfarbe konsequent für Header und Submit-Button nutzen, das Formular gehört zum Bereichskontext.\n- Nur Pflichtfelder mit `*` markieren, unnötige Pflichtfelder reduzieren die Konversionsrate.\n- Genau ein Submit-Button pro Formular, klare Primäraktion, kein zweiter Filled-Button daneben.\n- Verlinkte Rechtstexte in Einwilligungen als echten `<a>` auszeichnen, damit sie per Tab erreichbar sind.\n\n**Nicht tun**\n\n- Placeholder als einzige Feldbeschriftung, verschwindet beim Tippen und ist nicht barrierefrei.\n- Disabled-Felder ohne Erklärung, die Nutzerin versteht nicht, warum das Feld nicht ausfüllbar ist.\n- Bereichsfarben in einem Formular mischen, jedes Formular gehört zu genau einer Brand Area.\n- Fehlermeldung nur per Farbe signalisieren, immer auch Text und Icon für farbenblinde Nutzende.\n- Mehr als 7 bis 8 Formularfelder ohne Abschnitte, lieber in Schritte oder Gruppen aufteilen.\n- Den Datenschutzhinweis im Einwilligungs-Label als `<span>` mit `cursor:pointer` bauen, er sieht aus wie ein Link und ist keiner.\n\n## Verwandte Seiten\n\n- Textfeld (`Komponenten/Inputs & Forms/Textfeld`)\n- Textbereich (`Komponenten/Inputs & Forms/Textbereich`)\n- Auswahlfeld (`Komponenten/Inputs & Forms/Auswahlfeld`)\n- Radio (`Komponenten/Inputs & Forms/Radio`)\n- Checkbox (`Komponenten/Inputs & Forms/Checkbox`)\n- Slider (`Komponenten/Inputs & Forms/Slider`)\n- Skala (`Komponenten/Inputs & Forms/Skala`)\n- Für Auswahl jenseits des nativen `<select>`: Custom Select und Combobox (`Komponenten/Dropdowns/Verwendung`)\n- Für die Footer-Newsletter-Variante: Footer (`Komponenten/Footer`)\n",
|
|
13
|
-
"summary": "# Verwendung Feldkomponenten (Text · E-Mail · Select · Textarea · Checkbox · Radio), berei..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-navigation--verwendung": {
|
|
4
|
-
"id": "komponenten-navigation--verwendung",
|
|
5
|
-
"name": "komponenten-navigation--verwendung",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-navigation--verwendung": {
|
|
8
|
-
"id": "komponenten-navigation--verwendung",
|
|
9
|
-
"name": "Verwendung",
|
|
10
|
-
"path": "./src/docs/komponenten/navigation-verwendung.mdx",
|
|
11
|
-
"title": "Komponenten/Navigation",
|
|
12
|
-
"content": "import { Meta, Unstyled } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Navigation\" name=\"Verwendung\" tags={['angular']} />\n\n# Verwendung\n\nNavigation führt drei Elemente: Topnav, Breadcrumb und Back-to-Top-Button. Nur die\nTopnav hat ein eigenes Bauteil (Story `Topnav`); Breadcrumb und Back-to-Top sind\nCSS-Rezepte ohne Angular-Komponente.\n\n## Wann welches Element\n\n| Element | Bauteil | Wann einsetzen |\n|---|---|---|\n| Topnav | Story `Topnav` | Auf jeder Customer-Page als persistente Kopfzeile: Logo, vier Top-Items, Kontakt-CTA |\n| Breadcrumb | bauteillos (CSS-Rezept `.article-breadcrumb`) | Auf Unterseiten ab der zweiten Ebene, zeigt den Pfad von der Startseite zur aktuellen Seite |\n| Back-to-Top-Button | bauteillos (CSS-Rezept `.back-to-top`) | Auf jeder Customer-Page, sticky unten rechts, erscheint ab `scrollY > 400px` |\n\n## Topnav: Aufbau\n\n| Element | Regel |\n|---|---|\n| Logo | Immer ganz links, immer sichtbar, Verlässlichkeit in der ersten Sekunde |\n| Top-Items | Vier Einträge: Angewandte KI · Leistungen · Wissen · Unternehmen. „Angewandte KI“, „Leistungen“ und „Unternehmen“ sind Links zur jeweiligen Übersicht mit aufklappbarem Submenü über einen separaten Caret-Button, „Wissen“ ist ein einfacher Link ohne Submenü. Aktiver Eintrag über `aria-current=\"page\"` |\n| CTA-Button | Genau ein Outlined-Button, Label „Kontakt“, Brand-Petrol (`--co-700`), ganz rechts über `margin-left:auto` |\n| Layout und Hintergrund | `.ep-topnav` mit `--bg-surface`, `border-bottom:var(--bd)`, Padding `var(--s4) var(--s8)` (rund 64 px hoch). Ein `gap`-basierter Flow (kein `justify-content`) hält die Nav am Logo, der CTA wird per `margin-left:auto` nach rechts gedrückt, das Layout bleibt stabil, egal wie viele Nav-Items dazukommen |\n\nDas Schema zeigt den Nav-Bar-Wireframe und darunter die zugehörige Callout-Zeile mit den\nnummerierten Hinweisen ①②③ zu Logo, Top-Items und Kontakt-Button.\n\n<Unstyled>\n <div style={{ border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', overflow: 'hidden', marginBottom: 'var(--s8)' }}>\n {/* Nav-Bar-Schema */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--s4)',\n padding: '0 var(--s6)',\n height: '64px',\n background: 'var(--bg-surface)',\n borderBottom: 'var(--bd)',\n }}\n >\n <div\n style={{\n background: 'var(--co-700)',\n borderRadius: 'var(--r-xs)',\n padding: '3px 10px',\n font: '700 12px/16px var(--font)',\n color: '#fff',\n flexShrink: 0,\n }}\n >\n Logo\n </div>\n <div style={{ display: 'flex', gap: 'var(--s5)', flex: 1, alignItems: 'center' }}>\n <div\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 'var(--s1)',\n font: '400 12px/16px var(--font)',\n color: 'var(--tx-secondary)',\n }}\n >\n Angewandte KI <span style={{ opacity: 0.5 }}>▾</span>\n </div>\n <div\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 'var(--s1)',\n font: '400 12px/16px var(--font)',\n color: 'var(--tx-secondary)',\n }}\n >\n Leistungen <span style={{ opacity: 0.5 }}>▾</span>\n </div>\n <div style={{ font: '400 12px/16px var(--font)', color: 'var(--tx-secondary)' }}>Wissen</div>\n <div\n className=\"t-co\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 'var(--s1)',\n font: '600 12px/16px var(--font)',\n }}\n >\n Unternehmen <span style={{ opacity: 0.5 }}>▾</span>\n </div>\n </div>\n <div\n className=\"t-co\"\n style={{\n border: '1.5px solid var(--co-700)',\n borderRadius: 'var(--r-full)',\n padding: '2px 12px',\n font: '600 12px/16px var(--font)',\n flexShrink: 0,\n }}\n >\n Kontakt\n </div>\n </div>\n {/* Callout-Zeile zur Nav */}\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: 'var(--s4)',\n padding: 'var(--s3) var(--s6)',\n background: 'var(--bg-overlay)',\n }}\n >\n <div style={{ font: '400 12px/16px var(--font)', color: 'var(--tx-muted)', flexShrink: 0, width: '80px' }}>\n ① Logo<br />immer links\n </div>\n <div style={{ font: '400 12px/16px var(--font)', color: 'var(--tx-muted)', flex: 1 }}>\n ② Top-Items · 4 Einträge · „Angewandte KI“, „Leistungen“ und „Unternehmen“ mit aufklappbarem Submenü,\n „Wissen“ ohne · aktiver Eintrag in\n Brand-Petrol via <code className=\"token\" style={{ fontSize: '12px' }}>aria-current=\"page\"</code>\n </div>\n <div\n style={{ font: '400 12px/16px var(--font)', color: 'var(--tx-muted)', flexShrink: 0, textAlign: 'right' }}\n >\n ③ Outlined „Kontakt“<br />ganz rechts\n </div>\n </div>\n </div>\n</Unstyled>\n\n**Aktiv-Markierung:** Aktiver Punkt über `aria-current=\"page\"` ausgezeichnet und\nzusätzlich durch einen nicht-farbigen Indikator bestätigt: Top-Level-Links tragen einen\ndezenten Akzent-Unterstrich, Submenü-Einträge eine Tönungsfläche (`--co-50`) plus\nFettung. Die Markierung hängt also nie allein an der Farbe (WCAG 1.4.1). Nur Hover und\ndas bloße Aufklappen markieren ausschließlich über Farbe und Caret-Drehung, den\nUnterstrich gibt es allein im Aktiv-Zustand.\n\n## Topnav-Dropdowns: Disclosure-Pattern\n\nJedes Top-Item mit Submenü besteht aus zwei getrennten Bedienelementen: dem Label-Link\n(`.ep-nav-btn`, ein echtes `<a>`), der direkt zur Übersichtsseite navigiert, und einem\nseparaten Caret-Button (`.ep-nav-item-toggle`), der ausschließlich das Submenü öffnet\nund schließt. Es ist ein Disclosure-Pattern aus Links, kein Menü-Widget, deshalb bewusst\nohne `aria-haspopup`.\n\n- `aria-expanded` (`true`/`false`) und `aria-controls` sitzen auf dem Caret-Button; die JS-Klasse `.is-open` markiert das geöffnete `.ep-nav-has-sub`.\n- Geöffnet wird per Klick/Tap, Tastatur (`Enter`/`Space` auf dem Caret) oder, nur auf Geräten mit echtem Hover (`pointer:fine`), per Hover (JS-gesteuert mit Intent-Verzögerung und unsichtbarer Brücke über den Gap, WCAG 1.4.13).\n- Pfeiltasten (`↓`/`↑`) sowie `Home`/`End` navigieren die Einträge innerhalb des geöffneten Submenüs.\n- `Escape` schließt das Submenü und gibt den Fokus auf den Caret zurück; ein Klick außerhalb, das Heraustabben oder eine Navigation schließen ebenfalls.\n- **Kein reines CSS-`:hover`-Öffnen:** Der Reveal hängt immer an `.is-open`, damit `aria-expanded` mitläuft.\n- **Nie zwei Menüs gleichzeitig offen:** Vor jedem Öffnen ruft das JS `closeAllNavItems`.\n- Der Label-Link navigiert unverändert direkt zur Übersicht, nur der Caret-Button klappt auf; ein Klick auf das Label öffnet oder schließt kein Menü.\n- Mobil (ab 760 px und darunter): ein Hamburger-Button wird injiziert, Submenüs öffnen dort inline statt als schwebendes Overlay.\n- Der Caret-Button hat eine eigene Hit-Area ≥ 24 px (WCAG 2.5.8); mobil liegen Label und Caret in einer Zeile mit ≥ 44 px Tap-Fläche.\n\n## Breadcrumb\n\nAuf Unterseiten (zweite Ebene und tiefer) sitzt über dem Inhalt eine Breadcrumb-Leiste\n(`.article-breadcrumb`). Sie zeigt den Pfad von der Startseite bis zur aktuellen Seite;\ndas letzte Element ist die aktuelle Seite, trägt `aria-current=\"page\"` und ist kein\nLink.\n\n| Aspekt | Regel |\n|---|---|\n| Hülle | Eigene `.ep-section` mit `padding-block:var(--s5)` und `--bg-surface`, direkt unter der Topnav; das `<nav>` mit `margin-bottom:0` |\n| Ausrichtung | Immer linksbündig, auch auf redaktionellen Seiten (Wissensbeitrag, Stellen-Detail). Titel und Lead dürfen darunter zentriert sein, der Breadcrumb bleibt links |\n| Pfad | Startseite → … → aktuelle Seite; Trenner `.article-breadcrumb-sep` (dekorativ, `aria-hidden`); letztes Element `aria-current=\"page\"`, ohne Link |\n| Nicht | Den Breadcrumb nicht in einen zentrierten `.article-header` einbetten, sonst wird er mittig ausgerichtet und bekommt einen abweichenden Abstand zur Nav |\n\n## Back-to-Top-Button\n\nGlobaler Page-Button, der nach `scrollY > 400px` sanft eingeblendet wird und beim Klick\nsmooth zum Seitenanfang scrollt. Sitzt sticky unten rechts (`position:fixed`), damit er\nauf langen Seiten ohne Scrollen erreichbar bleibt; typischer Anwendungsfall:\nWissensbeiträge, Bereichsseiten, Listen-Übersichten. Gehört auf jede Customer-Page, auch\nLanding-Pages, Detailseiten und Beispielartikel.\n\n- `aria-label=\"Nach oben scrollen\"`, der reine Caret-Icon-Button braucht eine textuelle Beschriftung.\n- `type=\"button\"`, verhindert Submit-Verhalten in einem Form-Kontext.\n- Tastatur: per `Tab` erreichbar, `Enter`/`Space` löst aus, keine zusätzliche ARIA-Rolle nötig (natives `<button>`).\n- Reduced Motion: bei `prefers-reduced-motion: reduce` springt die Seite ohne Smooth-Scroll-Animation an den Anfang (WCAG 2.3.3).\n- Touch-Target: 44×44 px erfüllt WCAG 2.5.5 (Target Size).\n- Fokus-Ring: `var(--focus-ring)` bei Tastaturfokus, sichtbar gegen den weißen Hintergrund und gegen farbige Inhalte.\n\n## Barrierefreiheit\n\n| Aspekt | Regel |\n|---|---|\n| Semantik | `<nav>` mit `aria-label=\"Hauptnavigation\"`, wenn mehrere Navigationsbereiche auf einer Seite stehen, listen Screenreader alle Landmarks auf |\n| Aktiver Eintrag | `aria-current=\"page\"` auf dem aktiven Nav-Link setzen, der visuelle Active-State allein ist für Screenreader nicht erkennbar |\n| Tastatur | Alle Nav-Items per `Tab`; `Enter`/`Space` öffnet das Submenü; `↓`/`↑` und `Home`/`End` navigieren die Einträge; `Esc` schließt (Fokus zurück zum Toggle), vollständig ohne Maus bedienbar |\n| Dropdown-Zustand | Das Label ist ein `<a>`-Link zur Übersicht; ein separater Caret-`<button>` trägt per JS `aria-expanded` und `aria-controls` auf das Submenü (Disclosure aus Links, kein Menü-Widget, daher kein `aria-haspopup`); geöffnet wird per Klick/Tastatur und, additiv nur auf `pointer:fine`, per Hover (JS-gesteuert über `.is-open`, nie rein per CSS-`:hover`) |\n| Skip-Link | „Zum Inhalt springen“-Link als erstes fokussierbares Element, ermöglicht Tastaturnutzern das Überspringen langer Navigationen |\n\n## Dos & Don'ts\n\n**Tun**\n\n- Aktiven Eintrag über `aria-current=\"page\"` und zusätzlich einen nicht-farbigen Indikator markieren (Unterstrich beziehungsweise Tönungsfläche plus Fettung), nie nur über Farbe (WCAG 1.4.1)\n- Genau einen Outlined-Kontakt-Button ganz rechts, per `margin-left:auto` vom Nav-Flow abgesetzt\n- Label-Link und Caret-Button als getrennte Bedienelemente führen: Label navigiert, Caret klappt auf\n- Breadcrumb immer linksbündig setzen, auch wenn Titel und Lead darunter zentriert sind\n- Back-to-Top auf jeder Customer-Page einbinden, inklusive Fallback auf einen Hard-Jump bei `prefers-reduced-motion`\n\n**Nicht tun**\n\n- Dropdown-Menüs rein über CSS `:hover` öffnen, nicht per Tastatur erreichbar; `aria-expanded` für den Toggle nutzen\n- Vom IA-Schema (Angewandte KI · Leistungen · Wissen · Unternehmen plus Kontakt) abweichen oder zusätzliche Top-Items ergänzen, das verwässert die Customer-Page-Navigation\n- Breadcrumb in einen zentrierten `.article-header` einbetten, sonst verschiebt sich seine Ausrichtung\n- Zwei Submenüs gleichzeitig offen lassen, vor jedem Öffnen muss das vorherige schließen\n- Back-to-Top ohne `aria-label` oder ohne Fallback bei `prefers-reduced-motion` ausliefern\n\n## Verwandte Seiten\n\n- Komponenten/Navigation/Topnav\n- Komponenten/Hero (Zusammenspiel von Topnav und Hero)\n",
|
|
13
|
-
"summary": "# Verwendung Navigation führt drei Elemente: Topnav, Breadcrumb und Back-to-Top-Button. Nu..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-sektion--übersicht": {
|
|
4
|
-
"id": "komponenten-sektion--übersicht",
|
|
5
|
-
"name": "komponenten-sektion--übersicht",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-sektion--übersicht": {
|
|
8
|
-
"id": "komponenten-sektion--übersicht",
|
|
9
|
-
"name": "Übersicht",
|
|
10
|
-
"path": "./src/docs/komponenten/sektion.mdx",
|
|
11
|
-
"title": "Komponenten/Sektion",
|
|
12
|
-
"content": "import { Meta } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Sektion\" name=\"Übersicht\" />\n\n# Sektion\n\nDas strukturelle Gerüst, in dem auf den Beispielseiten praktisch jeder andere\nBaustein sitzt: `.ep-section` mit dem optionalen Kopf-Trio aus Kicker, Überschrift\nund Lead. 133 Vorkommen in den Beispielseiten, 85 davon mit Kopfzeile. Das\nAngular-Bauteil ist `Komponenten/Sektion/Sektion` (`[cdsSection]`).\n\nQuelle: `docs/index.html#sec-section`.\n\n## Aufbau\n\nDrei Klassen bilden den Kopf: `.ep-section-label` (Kicker, bereichsfärbbar über\n`.t-co` / `.t-ki` / `.t-es` / `.t-wo`), `.ep-section-h2` (Überschrift) und\n`.ep-section-sub` (Lead). Alle drei sind Beiwerk: Eine Sektion besteht auch ganz\nohne Kopf, nur aus ihrem Inhalt darunter.\n\n```html\n<div class=\"ep-section\" style=\"background:var(--n-50)\">\n <div class=\"ep-section-label t-co\">Was wir tun</div>\n <h2 class=\"ep-section-h2\">So ist eine Sektion aufgebaut.</h2>\n <p class=\"ep-section-sub\">Kicker, Überschrift und Lead sind Beiwerk, der Inhalt darunter ist frei.</p>\n <!-- beliebiger Inhalt -->\n</div>\n```\n\n## Angular-Wrapper\n\n`[cdsSection]` ist ein Attributselektor statt eines eigenen Elements\n(`docs/adr/0008-selektortyp-der-wrapper-komponenten.md`). Der\nKonsument wählt das Tag: `<section cdsSection>` oder `<div cdsSection>`. Die\nKomponente setzt `aria-labelledby`, wenn ein zugänglicher Name verfügbar ist:\nentweder die eigene Überschrift oder eine per `labelledBy` übergebene id einer\nÜberschrift außerhalb der Komponente. Fehlt beides, setzt sie kein\n`aria-labelledby`.\n\n## Verwendung\n\nDie Fläche gehört der Seite, nicht dem Bauteil. Sektionen wechseln im Rhythmus\nzwischen `--bg-surface` und `--n-50`, auf einzelnen Sektionsflächen kommt\nzusätzlich eine Bereichstönung als Akzent vor. Welche Fläche eine Sektion trägt,\nhängt von ihren Nachbarn ab, und nur die Seite kennt diese Nachbarn. Deshalb setzt\n`.ep-section` selbst keinen Hintergrund, und `[cdsSection]` hat keinen\n`background`-Input: Die Fläche kommt per Klasse oder Inline-Style direkt an das\nElement, das `cdsSection` trägt (`<section cdsSection style=\"background:…\">`).\n\n## Dos & Don'ts\n\n**Tun**\n\n- Die Hintergrundfläche als Klasse oder Style auf dem Element setzen, das `cdsSection` trägt, im Wechsel mit den Nachbar-Sektionen.\n- Kicker, Überschrift und Lead weglassen, wenn eine Sektion keinen eigenen Kopf braucht.\n- Ohne eigene Überschrift `labelledBy` auf eine vorhandene Überschrift setzen, damit die Sektion einen zugänglichen Namen behält.\n\n**Nicht tun**\n\n- Eine Hintergrundfarbe fest in die Komponente einbauen, sie kennt ihre Nachbar-Sektionen nicht.\n- Mehrere getönte Sektionen direkt hintereinander setzen, das wirkt schnell bunt statt ruhig.\n- Im Angular-Wrapper `<section cdsSection>` ohne Überschrift und ohne `labelledBy` schreiben, wenn `<div cdsSection>` genügt: die Sektion bliebe für Screenreader ohnehin namenlos, das Tag verspricht aber eine Landmark, die nie entsteht.\n",
|
|
13
|
-
"summary": "# Sektion Das strukturelle Gerüst, in dem auf den Beispielseiten praktisch jeder andere Ba..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-slider-carousel--verwendung": {
|
|
4
|
-
"id": "komponenten-slider-carousel--verwendung",
|
|
5
|
-
"name": "komponenten-slider-carousel--verwendung",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-slider-carousel--verwendung": {
|
|
8
|
-
"id": "komponenten-slider-carousel--verwendung",
|
|
9
|
-
"name": "Verwendung",
|
|
10
|
-
"path": "./src/docs/komponenten/slider-verwendung.mdx",
|
|
11
|
-
"title": "Komponenten/Slider & Carousel",
|
|
12
|
-
"content": "import { Meta } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Slider & Carousel\" name=\"Verwendung\" tags={['angular']} />\n\n# Verwendung\n\nSlider & Carousel führt zwei getrennte Formen: das Bild-Carousel zur Seiten-Integration\n(Story `Carousel`) und das Kundenlogo-Karussell (Story `LogoCarousel`) als CSS-Laufband.\nBeide sind WCAG 2.1 AA konform und respektieren `prefers-reduced-motion`. Die\nSlider-Hero-Variante für den Seitenkopf ist unter Komponenten/Hero dokumentiert.\n\n## Bild-Carousel · Verwendung\n\nEingebettet in Seiteninhalt, maximal 780 px breit, abgerundete Ecken (16:9). Caption\nerscheint unterhalb des Bildes auf weißem Grund, keine Modifier-Klasse nötig, nur die\nBasis-Klasse `.img-slider`. Übergang per Crossfade über 600 ms; unter\n`prefers-reduced-motion: reduce` entfällt die Transition automatisch.\n\n### Barrierefreiheit & ARIA\n\n| Attribut | Zweck |\n|---|---|\n| `role=\"region\"` | Semantischer Landmark für den gesamten Slider |\n| `aria-roledescription` | „Bildschirmpräsentation“, Screenreader liest den Typ vor |\n| `role=\"group\"` auf Slide | Fasst Bild und Caption je Folie zusammen |\n| `aria-hidden=\"true\"` | Inaktive Slides werden übersprungen (setzt JS) |\n| `aria-label` auf Button | „Vorherige / Nächste Folie“, kein Icon-only |\n| `role=\"tablist\"` Dots | Dot-Navigation als Tab-Gruppe, bekanntes Tastatur-Pattern |\n\n| Taste | Funktion |\n|---|---|\n| ← | Vorherige Folie |\n| → | Nächste Folie |\n| Tab | Fokus auf Prev/Next-Button oder Dot |\n| Enter / Space | Dot oder Button aktivieren |\n\n### Dos & Don'ts\n\n**Tun**\n\n- Immer aussagekräftiges `alt`-Attribut bei echten Bildern, kein `alt=\"\"` bei inhaltlichen Fotos\n- Caption für jede Folie, gibt dem Inhalt auch ohne Bild ausreichend Kontext\n- Bilder mit `object-fit: cover` und fester Höhe einsetzen, verhindert Layout-Sprünge beim Laden\n\n**Nicht tun**\n\n- Kein Auto-Play ohne Pause-Button, WCAG 2.1 Kriterium 2.2.2 verbietet unkontrollierte Bewegung\n- Mehr als 6 bis 7 Slides, Nutzende verlieren die Orientierung, lieber eine separate Galerie verwenden\n- Kartentypen oder Seitenverhältnisse innerhalb eines Carousels mischen, einheitliche Bildgröße halten\n\n## Kundenlogo-Karussell · Verwendung\n\nDiskrete Sets von je 5 Logos, die alle 6 Sekunden per Crossfade wechseln. Pausiert\nautomatisch bei Hover und Tastatur-Fokus, zusätzlich manueller Pause-Button (WCAG\n2.2.2). Pagination-Dots erlauben gezieltes Springen, Tastatur-Navigation über Pfeil-,\nHome- und End-Taste. Die Logos sitzen randlos auf einer hellen Platte (`--bg-plate`),\ndie in beiden Themes hell bleibt, weil Kundenlogos meist nur in einer farbigen\nbeziehungsweise dunklen Fassung ohne Dark-Variante vorliegen. Kein Hover-Effekt, da die\nLogos nicht verlinkt sind, ein Lift würde fälschlich Klickbarkeit suggerieren.\n\n### Barrierefreiheit\n\n| Attribut | Zweck |\n|---|---|\n| `role=\"region\"` + `aria-roledescription` | Semantischer Landmark mit Karussell-Hinweis für Screenreader |\n| `aria-hidden` | Inaktive Sets werden Screenreader-seitig ausgeblendet, nur das sichtbare Set wird vorgelesen |\n| `role=\"group\"` pro Set | Logos als Gruppe gebündelt mit eigenem `aria-label` („Set 1 von 2“) |\n| `role=\"tablist\"` + `role=\"tab\"` | Pagination-Dots als Tab-Pattern, `aria-selected` markiert das aktive Set, Roving-Tabindex |\n| `aria-pressed` auf Pause-Button | Toggle-State (pausiert / läuft) für Screenreader hörbar |\n| Tastatur-Navigation | Pfeil-Tasten wechseln Sets, Home / End springen zum ersten / letzten Set, Pause via Space oder Enter |\n| `prefers-reduced-motion` | Auto-Rotation und Crossfade deaktiviert, Set 1 bleibt statisch sichtbar, Dots bleiben nutzbar |\n\n### Dos & Don'ts\n\n**Tun**\n\n- Genau 5 Logos pro Set, gleiche Anzahl in allen Sets für gleichmäßiges Grid\n- Logos als SVG einbinden, skalieren verlustfrei auf jede Displaydichte\n- Einheitliche Tile-Höhe (72 px), unterschiedliche Logo-Proportionen über `object-fit: contain` angleichen\n- Pro Set eine eindeutige `id` für die `aria-controls`-Verknüpfung der Dots\n\n**Nicht tun**\n\n- Keine Kundennamen in bunten Markenfarben, Logos einheitlich grau oder schwarz halten\n- Mehr als 3 Sets vermeiden, ab 4 Sets wird die Pagination unübersichtlich und wirkt zu werblich\n- Intervall unter 5 Sekunden setzen, gibt Lesenden keine Zeit, ein Logo aufzunehmen\n\n## Bewegungs-Anforderung (prefers-reduced-motion)\n\nDie globale Barrierefreiheits-Sektion (Grundlagen/Barrierefreiheit) hält für\n`prefers-reduced-motion: reduce` die Basisregel fest: `animation-duration`,\n`transition-duration` und `scroll-behavior` fallen auf `0.01ms` beziehungsweise `auto`\nzurück (WCAG 2.3.3 AAA). Für die beiden Karussell-Typen gilt zusätzlich:\n\n- **Bild-Carousel:** die Crossfade-Transition (600 ms) entfällt automatisch, keine gesonderte Handhabung im Markup nötig.\n- **Kundenlogo-Karussell:** bei aktiver Reduktion bleibt das erste Set sichtbar, ohne Auto-Wechsel; die Pagination-Dots bleiben bedienbar.\n\nVon der Ausnahme unberührt bleibt der 2-Pixel-Hover-Lift auf klickbaren Karten und dem\nStörer: Er läuft auch unter `prefers-reduced-motion: reduce` weiter, weil er reine\nKlickbarkeit signalisiert. Auto-Bewegung ist davon getrennt zu betrachten, nur sie\nrespektieren die Carousels.\n\n## Verwandte Seiten\n\n- Komponenten/Slider & Carousel/Carousel\n- Komponenten/Slider & Carousel/LogoCarousel\n- Komponenten/Hero (Slider-Hero-Variante)\n- Grundlagen/Barrierefreiheit (Basisregel `prefers-reduced-motion`)\n",
|
|
13
|
-
"summary": "# Verwendung Slider & Carousel führt zwei getrennte Formen: das Bild-Carousel zur Seiten-I..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-tabelle--übersicht": {
|
|
4
|
-
"id": "komponenten-tabelle--übersicht",
|
|
5
|
-
"name": "komponenten-tabelle--übersicht",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-tabelle--übersicht": {
|
|
8
|
-
"id": "komponenten-tabelle--übersicht",
|
|
9
|
-
"name": "Übersicht",
|
|
10
|
-
"path": "./src/docs/komponenten/tabelle.mdx",
|
|
11
|
-
"title": "Komponenten/Tabelle",
|
|
12
|
-
"content": "import { Meta } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Tabelle\" name=\"Übersicht\" />\n\n# Tabelle\n\nBarrierefreie Datentabellen mit `<caption>`, `scope`-Attributen und\nTastaturnavigation, in zwei Varianten: Standard und Gestreift, dazu eine\naufklappbare Vergleichstabelle. Standard und Gestreift deckt das Angular-Bauteil\n`Komponenten/Tabelle/Tabelle` (`cds-table`) ab: es liefert `.tbl-wrap` samt\nTastaturzugriff und Name sowie `.tbl`/`.tbl--striped`, der Konsument projiziert\n`<thead>`/`<tbody>`/`<tfoot>` unverändert hinein. Die aufklappbare Vergleichstabelle\ndeckt `Komponenten/Tabelle/Vergleichstabelle` (`cds-compare`) ab: anders als\n`cds-table` bekommt sie einen Daten-Input (`columns`/`rows`), weil ihre Zellen auf\nJa/Nein oder eine kurze Angabe begrenzt sind.\n\nQuelle: `docs/index.html#sec-table` (Zeilen 4979 bis 5208).\n\n## Standard\n\nZeilentrenner, Hover-Highlight, numerische Spalten rechtsbündig. Der\nScroll-Container ist per Tastatur fokussierbar (`tabindex=\"0\"`).\n\n```html\n<div class=\"tbl-wrap\" tabindex=\"0\" role=\"region\" aria-label=\"Leistungsübersicht Tabelle\">\n <table class=\"tbl\">\n <caption>Beratungsleistungen im Überblick</caption>\n <thead>\n <tr>\n <th scope=\"col\">Leistung</th>\n <th scope=\"col\">Format</th>\n <th scope=\"col\" data-num>Dauer (Tage)</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">KI-Readiness Assessment</th>\n <td>Workshop & Analyse</td>\n <td data-num>2</td>\n </tr>\n </tbody>\n </table>\n</div>\n```\n\nNumerische Spalten bekommen `data-num` an `<th>`/`<td>`, nicht eine eigene Klasse:\n`.tbl td[data-num]`/`.tbl th[data-num]` richten rechtsbündig aus und schalten auf\ntabellarische Ziffern (`font-variant-numeric: tabular-nums`) um.\n\n## Gestreift\n\nModifier `.tbl--striped` für Tabellen mit vielen Zeilen, verbessert die Lesbarkeit\nohne zusätzliche Rahmen.\n\n```html\n<div class=\"tbl-wrap\" tabindex=\"0\" role=\"region\" aria-label=\"Veranstaltungskalender Tabelle\">\n <table class=\"tbl tbl--striped\">\n <caption>Veranstaltungen & Workshops 2025</caption>\n <thead>\n <tr>\n <th scope=\"col\">Datum</th>\n <th scope=\"col\">Veranstaltung</th>\n <th scope=\"col\">Status</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>15. Jan 2025</td>\n <th scope=\"row\">KI im Unternehmensalltag, Einstiegsworkshop</th>\n <td>Abgeschlossen</td>\n </tr>\n </tbody>\n </table>\n</div>\n```\n\nSortierbare Spaltenüberschriften nutzen `.tbl-sort` (ein `<button>` in der `<th>`)\nzusammen mit `aria-sort=\"ascending\"` bzw. `\"descending\"` auf der `<th>`; `.tbl--striped`\nund `.tbl-sort` lassen sich kombinieren, gestreift und sortierbar für\ndatenintensive Ansichten.\n\n## Vergleichstabelle (aufklappbar)\n\nFür den zeilenweisen Vergleich zweier oder mehrerer Pakete beziehungsweise Tarife.\nPreiskarten tragen Preis und drei bis vier Highlights (Konversion), die vollständige\nMerkmalsliste steckt in der aufklappbaren Tabelle (Tiefenvergleich auf Abruf). So\nbleibt der Schnell-Scan kurz, und Karten und Tabelle überschneiden sich nicht.\nErprobt auf der Beispielseite Angewandte KI, AI.Box (Leistungspakete).\n\nAufklappbar via nativem `<details>`/`<summary>` (`.ep-compare`, standardmäßig zu,\ngleiche Mechanik wie das Inhaltsverzeichnis in `Seitenmuster/Wissensbeitrag`).\nTabelle `.ep-compare-table` mit `<caption class=\"sr-only\">` und `scope`-Attributen.\nDie empfohlene Spalte wird über `.ep-compare-pro` (bereichsgetönt) hervorgehoben.\nEnthalten/Nicht enthalten als `.ep-compare-yes` (✓) beziehungsweise `.ep-compare-no`\n(−), jeweils mit `.sr-only`-Text, da Farbe und Glyph allein keine Bedeutung tragen\n(WCAG 1.4.1).\n\n```html\n<details class=\"ep-compare\">\n <summary class=\"ep-compare-summary\">Alle Funktionen vergleichen</summary>\n <div class=\"ep-compare-table-wrap\">\n <table class=\"ep-compare-table\">\n <caption class=\"sr-only\">Funktionsvergleich Core und Pro</caption>\n <thead>\n <tr>\n <th scope=\"col\">Funktion</th>\n <th scope=\"col\">Core</th>\n <th scope=\"col\" class=\"ep-compare-pro\">Pro</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th scope=\"row\">Hosting in Deutschland</th>\n <td><span class=\"ep-compare-yes\" aria-hidden=\"true\">✓</span><span class=\"sr-only\">Enthalten</span></td>\n <td class=\"ep-compare-pro\">…</td>\n </tr>\n </tbody>\n </table>\n </div>\n</details>\n```\n\n## Verwendung\n\n<div className=\"doc-eyebrow\">Wann welche Variante?</div>\n\n| Variante | Wann einsetzen |\n|---|---|\n| Standard | Für Tabellen mit bis zu 6 Zeilen, klare Zeilentrenner reichen zur Orientierung |\n| Gestreift (`.tbl--striped`) | Ab ca. 7 Zeilen, der alternierende Hintergrund verbessert die Lesbarkeit ohne zusätzliche Rahmen |\n| Sortierbar (`.tbl-sort`) | Wenn Nutzer die Reihenfolge selbst bestimmen müssen, Klick auf die Spaltenüberschrift sortiert auf- oder absteigend |\n| Kombination | `.tbl--striped` und `.tbl-sort` lassen sich kombinieren, gestreift und sortierbar für datenintensive Ansichten |\n\n<div className=\"doc-eyebrow\">Barrierefreiheit</div>\n\n| Attribut / Element | Zweck |\n|---|---|\n| `<caption>` | Pflicht, Screenreader lesen den Titel vor, bevor die Zellen vorgelesen werden |\n| `scope=\"col\"` | Auf alle `<th>` im `<thead>`, ordnet Spaltenüberschriften den Zellen darunter zu |\n| `scope=\"row\"` | Wenn die erste Spalte eine Zeilenüberschrift ist, ordnet die Überschrift den Zellen rechts davon zu |\n| `tabindex=\"0\"` | Auf dem Scroll-Container, macht horizontales Scrollen per Tastatur erreichbar |\n| `role=\"region\"` + `aria-label` | Auf dem Scroll-Container, kennzeichnet ihn als benannten Landmark für Screenreader |\n\n<div className=\"doc-eyebrow\">Dos & Don'ts</div>\n\n**✓ Tun**\n\n- `<caption>` und `scope`-Attribute immer setzen, auch bei einfachen Tabellen\n- Numerische Spalten über `data-num` rechtsbündig ausrichten, erleichtert den Vergleich von Zahlen\n- Scroll-Container verwenden statt die Tabelle zu verkleinern, Lesbarkeit hat Vorrang\n- Spaltenüberschriften kurz und eindeutig benennen, maximal zwei bis drei Wörter\n\n**✕ Nicht tun**\n\n- Tabellen für Seiten-Layouts verwenden, dafür CSS Grid oder Flexbox nutzen\n- Mehr als sieben bis acht Spalten, lieber aufteilen oder Prioritäten setzen\n- Langen Fließtext in Zellen, Tabellen sind für strukturierte, scannbare Daten gedacht\n- Nur Farbe zur Bedeutungsvermittlung nutzen, Status-Badges zusätzlich mit Text kennzeichnen (siehe `Komponenten/Chips, Badges & Pills/Status-Badge`)\n\n## Verwandte Seiten\n\n- Doku-Sektion: `docs/index.html#sec-table`\n- `Komponenten/Tabelle/Tabelle` (Angular-Bauteil `cds-table` für Standard und Gestreift)\n- `Komponenten/Tabelle/Vergleichstabelle` (Angular-Bauteil `cds-compare` für die aufklappbare\n Vergleichstabelle)\n- `Komponenten/Chips, Badges & Pills/Status-Badge` (für Status-Zellen)\n",
|
|
13
|
-
"summary": "# Tabelle Barrierefreie Datentabellen mit ``, `scope`-Attributen und Tastaturnavigation, i..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-theme-umschalter--verwendung": {
|
|
4
|
-
"id": "komponenten-theme-umschalter--verwendung",
|
|
5
|
-
"name": "komponenten-theme-umschalter--verwendung",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-theme-umschalter--verwendung": {
|
|
8
|
-
"id": "komponenten-theme-umschalter--verwendung",
|
|
9
|
-
"name": "Verwendung",
|
|
10
|
-
"path": "./src/docs/komponenten/theme-umschalter.mdx",
|
|
11
|
-
"title": "Komponenten/Theme-Umschalter",
|
|
12
|
-
"content": "import { Meta } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Theme-Umschalter\" name=\"Verwendung\" tags={['angular']} />\n\n# Theme-Umschalter · Verwendung\n\n`docs/index.html` hat für den Theme-Umschalter eine eigene Sektion (`sec-theme`,\nÜberschrift „Theme-Umschalter“) mit einem `gt-theme-verwendung`-Block. Deren\nPlatzierungsregeln (welches der drei Bauteile wo eingesetzt wird) folgen hier\nnoch. Diese Seite beschreibt deshalb vor allem das Bauteil aus dem Repo selbst:\nder Angular-Lib\n(`angular-lib/projects/design-system-angular/src/lib/theme-switch/`) und der\nStorybook-Konfiguration (`.storybook/preview.ts`).\n\n## Dreistufiger Modus\n\n`CdsThemeMode` kennt drei Werte: `light`, `dark`, `system`. `system` folgt der\nBetriebssystem-Einstellung (`prefers-color-scheme`). Die Reihenfolge, in der alle\ndrei Umschalter zyklen beziehungsweise Optionen anbieten, ist fest:\n`CDS_THEME_ORDER = ['light', 'dark', 'system']`.\n\n| Modus | Label | Icon-Key |\n|---|---|---|\n| `light` | Hell | `heroSun` |\n| `dark` | Dunkel | `heroMoon` |\n| `system` | System | `heroComputerDesktop` |\n\nAlle drei Umschalter teilen denselben `showSystem`-Parameter: `true` zeigt alle drei\nModi (tri), `false` blendet „System“ aus und zyklt nur zwischen Hell und Dunkel\n(binär). Die Auswahl trifft `cdsThemeModes(showSystem)` in `theme-mode.ts`.\n\n## `themeStore` als einziger Schreiber von `data-theme`\n\nAlle drei Komponenten und die Storybook-Toolbar lesen und schreiben über\n`themeStore` (`theme-mode.ts`), eine modul-globale Quelle, kein Angular-Service:\nJede Story hat ihre eigene App-Instanz mit eigenem Root-Injector, Angular-DI allein\nwürde den Zustand also nicht über alle Stories hinweg teilen. `ThemeModeService` ist\nnur ein dünner Injectable-Wrapper darüber für `inject(ThemeModeService)` im\ngewohnten Muster.\n\n`themeStore` ist die **einzige** Stelle im System, die das Attribut `data-theme` am\n`<html>`-Element setzt oder entfernt:\n\n- `dark` gesetzt → `data-theme=\"dark\"`.\n- `light` → Attribut entfernt (Light ist der Default ohne Attribut).\n- `system` → koppelt live an `window.matchMedia('(prefers-color-scheme: dark)')`\n und reflektiert dessen `matches`-Wert als `dark` oder `light`; ein\n `change`-Listener hält das synchron, solange der Modus `system` bleibt.\n\nEin `document`/`window`-Guard schützt SSR und Prerendering: Ohne `document` wird\ndas Attribut übersprungen, der Modus bleibt im Signal erhalten und greift, sobald im\nBrowser das nächste `apply()` läuft. Die Lib fasst den globalen Cascade sonst nicht\nan (siehe `docs/adr/0001`).\n\n## Synchronisation mit der Storybook-Toolbar\n\n`.storybook/preview.ts` verdrahtet zwei Richtungen:\n\n1. **Store → Toolbar.** `themeStore.subscribe(...)` emittiert bei jeder Änderung\n `UPDATE_GLOBALS` an den Storybook-Channel, damit der globale\n Theme-Toolbar-Schalter mitwandert, wenn eine Story-Komponente den Modus ändert.\n2. **Toolbar → Store.** Ein Decorator ruft bei jedem Kontextwechsel\n `themeStore.setSilent(context.globals['theme'])` auf: `setSilent` wendet den\n Modus an, **ohne** erneut zu emittieren, sonst entstünde eine Rückkopplungs-\n schleife zwischen Toolbar und Store.\n\nDamit bleiben Toolbar, Docs-Vorschau und alle drei Switcher-Komponenten immer\ndenselben Modus zeigen. Das Docs-Chrome selbst (Überschriften, Tabellen) bleibt\ndavon unabhängig immer im Light-Theme (`concisoLight`), weil Storybook Toolbar- und\nDocs-Theme nicht koppelt.\n\n## Dark Mode: der Unterbau aus der CSS-Schicht\n\nDer CSS-Schicht-Vertrag (`docs/GETTING-STARTED.md` § 3) kennt nur ein Attribut:\n\n```js\ndocument.documentElement.setAttribute('data-theme', 'dark'); // dunkel\ndocument.documentElement.removeAttribute('data-theme'); // hell (Default)\n```\n\n**Genau zwei Modi in der ausgelieferten CSS.** Das System kennt Light und Dark,\nkeinen dritten Modus, der der Betriebssystem-Präferenz folgt: `prefers-color-scheme`\nwird im ausgelieferten CSS nicht ausgewertet, Default ist Light. Der dritte,\nUI-seitige Modus „System“ der drei Theme-Umschalter-Komponenten liegt deshalb eine\nEbene **über** der CSS-Schicht: `themeStore` löst `system` selbst über\n`matchMedia` auf und schreibt der CSS-Schicht am Ende immer nur einen der zwei\nZustände, die sie kennt (`data-theme=\"dark\"` gesetzt oder entfernt). Ein Consumer,\nder die Betriebssystem-Präferenz ohne diese Komponenten übernehmen will, wertet sie\nim eigenen Produkt genauso aus:\n\n```js\nif (matchMedia('(prefers-color-scheme: dark)').matches)\n document.documentElement.setAttribute('data-theme', 'dark');\n```\n\n**Anti-Flash-Snippet.** Als erstes Skript im `<head>`, vor dem CSS-Paint, damit beim\nReload nicht kurz Light aufblitzt. Liest die gespeicherte Präferenz aus\n`localStorage`:\n\n```html\n<script>\n (function(){\n try {\n var t = localStorage.getItem('ds-theme');\n if (t && t !== 'light') document.documentElement.setAttribute('data-theme', t);\n } catch(e) {}\n })();\n</script>\n```\n\nPersistenz beim Umschalten: `localStorage.setItem('ds-theme', 'dark' | 'light')`.\nDie drei Angular-Umschalter selbst persistieren nicht; das bleibt Aufgabe des\nConsumers, der `themeStore.subscribe(...)` an die eigene Persistenz anschließt.\n\n**Warum das für jede Komponente gilt (CONTRIBUTING § 5).** `data-theme` ist der\neinzige Schalter im System: Alle Token flippen darüber, keine Komponente wertet\n`prefers-color-scheme` selbst aus oder führt eine eigene dritte Flächen-Stufe ein.\nWeil `themeStore` der einzige Schreiber dieses Attributs ist, bleibt diese Garantie\nauch mit drei unterschiedlichen Umschalter-Bauteilen (Cycle-Button, Segment,\nDropdown) und der Storybook-Toolbar intakt: Es gibt genau eine Quelle der Wahrheit\nfür den Modus, unabhängig davon, über welchen Umschalter er gesetzt wurde.\n\n## Verwandte Seiten\n\n- `Komponenten/Theme-Umschalter/Cycle-Button` (Icon-Button, ein Klick zyklt durch die Modi, vorgesehen für den Header)\n- `Komponenten/Theme-Umschalter/Segment` (Segment-Leiste zum Hovern, immer responsiv und animiert)\n- `Komponenten/Theme-Umschalter/Dropdown` (Custom Select, vorgesehen nur in den Einstellungen, nicht als persistentes Element)\n",
|
|
13
|
-
"summary": "# Theme-Umschalter · Verwendung `docs/index.html` hat für den Theme-Umschalter eine eigene..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"komponenten-zitate-testimonials--verwendung": {
|
|
4
|
-
"id": "komponenten-zitate-testimonials--verwendung",
|
|
5
|
-
"name": "komponenten-zitate-testimonials--verwendung",
|
|
6
|
-
"docs": {
|
|
7
|
-
"komponenten-zitate-testimonials--verwendung": {
|
|
8
|
-
"id": "komponenten-zitate-testimonials--verwendung",
|
|
9
|
-
"name": "Verwendung",
|
|
10
|
-
"path": "./src/docs/komponenten/quotes-verwendung.mdx",
|
|
11
|
-
"title": "Komponenten/Zitate & Testimonials",
|
|
12
|
-
"content": "import { Meta } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Komponenten/Zitate & Testimonials\" name=\"Verwendung\" tags={['angular']} />\n\n# Verwendung\n\nZitate & Testimonials führt vier Formen: Blockquote, Testimonial-Card, Team-Stimme und\ndas offene Zitat. Alle Formen teilen dasselbe semantische Grundgerüst\n(`<figure>` + `<blockquote>` + `<figcaption>`). Nur das offene Zitat hat kein eigenes\nBauteil, es ist ein CSS-Rezept ohne Karten-Chrome.\n\n## Wann welche Variante\n\n| Variante | Bauteil | Wann einsetzen |\n|---|---|---|\n| Blockquote | Story `Blockquote` | Kürzere Zitate (1 bis 3 Sätze, max. 200 Zeichen) im redaktionellen Kontext, eingebettet in Fließtext oder Abschnitte |\n| Testimonial-Card | Story `Testimonial` | Kundenreferenzen als eigenständige visuelle Einheit: Referenz-Sektionen, Hero-nahe Bereiche, Abschluss von Leistungsbeschreibungen, 2 bis 5 Sätze |\n| Team-Stimme | Story `TeamVoice` | Fotostarke, editoriale Variante der Testimonial-Card für Repräsentation (zum Beispiel Karriereseiten), volle Breite, großes Foto seitlich, abwechselnd links und rechts |\n| Offenes Zitat | bauteillos (CSS-Rezept, ohne Karten-Chrome) | Mehrere gleichrangige Stimmen in einer eigenen Sektion (zum Beispiel Workshop-Stimmen), Serif aufrecht statt kursiv, Trennung nur über Haarlinien |\n\n## Abgrenzung: Pull-Quote gegen Blockquote\n\nZwei Muster teilen sich dieselbe typografische Familie, aber nicht denselben Zweck. Ein\n**Pull-Quote** (`.article-pullquote`, dokumentiert unter Seitenmuster/Wissensbeitrag)\nhebt einen Satz aus dem eigenen Lauftext typografisch hervor: ohne Attribution, denn\ndie Autorin zitiert sich nicht selbst, sondern setzt einen Schlüsselsatz visuell ab. Der\n**Blockquote** (`.bq`, das Bauteil dieser Seite) ist dagegen ein wörtliches Zitat einer\ndritten Person (Kundin, Forschungsstimme, Branchen-Statement) mit Attribution (Name und\nRolle) und einer area-getönten Akzent-Box samt Quote-Icon. Beide teilen den\n4-Pixel-Linksakzent in der Bereichsfarbe; der Pull-Quote bleibt bewusst „offen“ (Serif\nkursiv, kein Hintergrund, kein Icon), der Blockquote ist umrahmt. Details zum Pull-Quote\nstehen unter Seitenmuster/Wissensbeitrag.\n\n## Barrierefreiheit\n\n| Aspekt | Regel |\n|---|---|\n| Semantik | `<blockquote>` korrekt für Zitate verwenden, Screenreader kündigen es als „Blockzitat“ an |\n| Dekoration | SVG-Anführungszeichen und Avatar-Initialen mit `aria-hidden=\"true\"` und `focusable=\"false\"` ausblenden |\n| Attribution | `<figcaption>` verknüpft die Quellenangabe semantisch mit dem Zitat, kein zusätzliches ARIA nötig |\n| Kontrast | Zitattext und Name in `--tx-primary`, Rolle in `--tx-secondary`, Avatar-Text geprüft ≥4,5:1 auf Bereichshintergrund in Light und Dark |\n| Anführungszeichen | Im Zitattext direkt eingebettet („…“), `quotes: none` verhindert doppelte Browser-Anführungszeichen |\n\n### Team-Stimme: zusätzliche Regeln\n\n| Aspekt | Regel |\n|---|---|\n| Semantik | `<figure>` + `<blockquote>` + `<figcaption>`, Quote-Icon dekorativ (`aria-hidden=\"true\"`, `focusable=\"false\"`) |\n| Bild-Alt | Beschreibendes `alt`, das keine konkrete Identität behauptet, solange Platzhalter- oder Stimmungsfotos genutzt werden |\n| Elevation | Statisch, daher flacher Rahmen (`--bd-strong`), kein Schatten, keine Klick-Affordanz |\n| Bereichs-Rahmen | Trägt das Bereichssignal und muss die 3:1-Schwelle für grafische Objekte erfüllen: Light `-600`/`-700`/`-500` (3,29 bis 5,66:1 auf Weiß), Dark `-300` (4,25 bis 9,48:1 auf der Karte). Vorher trug er mit `-200` im Light nur 1,27 bis 2,03:1 und mit `-800` im Dark 1,07 bis 1,63:1, war also in beiden Modi kein Signal |\n| Responsive | Unter 768 px stapelt das Grid zu Foto oben, Text unten |\n\n## Dos & Don'ts\n\n**Tun**\n\n- `<figure>` + `<blockquote>` + `<figcaption>` als semantisches Grundgerüst nutzen\n- Echte Kundenstimmen verwenden, keine KI-generierten Platzhalter in der Produktion\n- Bereichsfarbe über `data-area` steuern, keine hardcodierten Farbwerte\n- Zitate authentisch halten: gesprochene Sprache, konkrete Ergebnisse, keine Marketingfloskeln\n- Testimonial-Cards auf gleiche Höhe bringen: `align-items:stretch` im Grid plus `flex:1` auf dem Zitattext hält die Attribution am unteren Rand ausgerichtet\n\n**Nicht tun**\n\n- `<blockquote>` für eingerückte Aufzählungen oder rein visuell hervorgehobenen Text verwenden\n- Anführungszeichen doppelt setzen, entweder im Text oder via CSS `content`, nicht beides\n- Attribution weglassen, jedes Zitat braucht eine überprüfbare Quelle\n- Mehr als 3 Testimonials in einer Reihe, Lesbarkeit und visuelle Balance leiden\n- Dekorative Icons mit `role=\"img\"` oder `alt` versehen, sie sind `aria-hidden` und übermitteln keine Information\n\n## Verwandte Seiten\n\n- Komponenten/Zitate & Testimonials/Blockquote\n- Komponenten/Zitate & Testimonials/Testimonial\n- Komponenten/Zitate & Testimonials/TeamVoice\n- Seitenmuster/Wissensbeitrag (Abgrenzung Pull-Quote gegen Blockquote)\n",
|
|
13
|
-
"summary": "# Verwendung Zitate & Testimonials führt vier Formen: Blockquote, Testimonial-Card, Team-S..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"components": {
|
|
3
|
-
"marke-logo--verwendung": {
|
|
4
|
-
"id": "marke-logo--verwendung",
|
|
5
|
-
"name": "marke-logo--verwendung",
|
|
6
|
-
"docs": {
|
|
7
|
-
"marke-logo--verwendung": {
|
|
8
|
-
"id": "marke-logo--verwendung",
|
|
9
|
-
"name": "Verwendung",
|
|
10
|
-
"path": "./src/docs/marke/logo-verwendung.mdx",
|
|
11
|
-
"title": "Marke/Logo",
|
|
12
|
-
"content": "import { Meta, Unstyled } from '@storybook/addon-docs/blocks';\n\n<Meta title=\"Marke/Logo\" name=\"Verwendung\" tags={['angular']} />\n\n# Verwendung\n\nVerwendungsregeln zum Logo: Größen, Schutzraum, Einsatz nach Hintergrund, Barrierefreiheit und Dos & Don'ts. Die drei Varianten (Default, Light, Dark) zeigt die Story **`Marke/Logo/Logo`**; diese Seite ergänzt die Regeln dazu, inklusive des Hintergrund-Kontexts, den die Story allein nicht zeigt.\n\nDas Logo ist die primäre Markenkennung. Es wird stets als Vektor (SVG) verwendet, nie verzerrt oder eingefärbt.\n\n## Größen\n\nDie Höhe ist die maßgebliche Größenangabe, die Breite ergibt sich proportional aus dem Vektor (Verhältnis ≈ 7,6 : 1). Mindesthöhe nicht unterschreiten, sonst leidet die Lesbarkeit der „o.“-Form.\n\n| Kontext | Höhe | Hinweis |\n|---|---|---|\n| Topnav (Standard) | 24 px | Default-Variante, schaltet im Dark Mode auf Light um |\n| Footer | 32 px | Light-Variante, da Footer-Hintergrund `--n-700` immer dunkel ist |\n| Hero / Marketing-Header | 40 bis 48 px | Wenn das Logo selbst die Aufmerksamkeit tragen soll |\n| Mobile / kompakte Layouts | 20 px | Mindestgröße digital, darunter wird der Punkt schwer erkennbar |\n| Print | mindestens 12 mm | Mindestgröße im Druck, Punkt muss sauber drucken |\n\n## Schutzraum\n\nUm das Logo herum bleibt mindestens die Höhe des „C“ als freier Raum: keine konkurrierenden Elemente, kein Beschnitt durch andere Layout-Komponenten. Als Vielfaches ausgedrückt: die Referenzimplementierung der Doku-Site setzt ein Padding in Höhe des gesamten Logos (1:1-Clearance) an, eine praktikable Annäherung an dieselbe Regel.\n\nDas folgende Diagramm zeigt genau diese 1:1-Clearance: eine gestrichelte Box im Abstand einer vollen Logo-Höhe, hier 36 px, um die Wortmarke.\n\n<Unstyled>\n <div style={{ background: 'var(--bg-surface)', border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', padding: 'var(--s10)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 'var(--s8)' }}>\n {/* Padding 36px = exakte Logo-Image-Höhe (1:1-Clearance, Schutzraum-Regel der Brand Guidelines) */}\n <div style={{ position: 'relative', display: 'inline-block', padding: '36px' }}>\n <div style={{ position: 'absolute', inset: 0, border: '1px dashed var(--co-300)', borderRadius: 'var(--r-sm)', pointerEvents: 'none' }} />\n {/* Theme-abhängiges Logo: --bg-surface ist im Dark Mode dunkel, daher Default (dunkle Wortmarke) im Light, Light-Variante im Dark. Der Wechsel läuft über .logo-themed-default/.logo-themed-light (display:none/block), nicht über Inline-Styles. */}\n <img src=\"./conciso/brand/logo-conciso.svg\" alt=\"\" aria-hidden=\"true\" className=\"logo-themed-default\" style={{ height: '36px', width: 'auto', verticalAlign: 'middle' }} />\n <img src=\"./conciso/brand/logo-conciso-light.svg\" alt=\"\" aria-hidden=\"true\" className=\"logo-themed-light\" style={{ height: '36px', width: 'auto', verticalAlign: 'middle' }} />\n </div>\n </div>\n</Unstyled>\n\n## Verwendung\n\nDie Wahl der Variante richtet sich nach Hintergrund und Kontext. Im Zweifel die Default-Variante auf hellem, neutralem Untergrund verwenden.\n\nSo wirkt jede Variante auf dem Hintergrund, für den sie gedacht ist. Diesen Hintergrund-Kontext liefert die Story `Marke/Logo/Logo` allein nicht:\n\n<Unstyled>\n <div className=\"layout-grid\" style={{ marginBottom: 'var(--s8)' }}>\n <div className=\"col-4\" style={{ background: 'var(--bg-surface)', border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', overflow: 'hidden' }}>\n {/* --bg-plate: im Light reinweiß (der Anwendungsfall der Variante), im Dark gedämpft, damit die helle Platte auf der tiefen Seite nicht blendet. */}\n <div style={{ background: 'var(--bg-plate)', padding: 'var(--s8) var(--s6)', display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '120px' }}>\n <img src=\"./conciso/brand/logo-conciso.svg\" alt=\"Conciso, Default-Variante\" style={{ height: '36px', width: 'auto', display: 'block' }} />\n </div>\n <div style={{ padding: 'var(--s5) var(--s6)', borderTop: 'var(--bd)' }}>\n <div className=\"doc-eyebrow\" style={{ marginBottom: 'var(--s2)' }}>Default</div>\n <div style={{ font: 'var(--ty-title-sm)', color: 'var(--tx-primary)', marginBottom: 'var(--s2)' }}>Vollfarbe</div>\n <p style={{ font: 'var(--ty-body-md)', color: 'var(--tx-secondary)', margin: 0 }}>Wortmarke in <code className=\"token\">--tx-brand</code> mit teal-Punkt in <code className=\"token\">--co-500</code>. Standard für helle Hintergründe.</p>\n </div>\n </div>\n <div className=\"col-4\" style={{ background: 'var(--bg-surface)', border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', overflow: 'hidden' }}>\n <div style={{ background: 'var(--n-700)', padding: 'var(--s8) var(--s6)', display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '120px' }}>\n <img src=\"./conciso/brand/logo-conciso-light.svg\" alt=\"Conciso, helle Variante\" style={{ height: '36px', width: 'auto', display: 'block' }} />\n </div>\n <div style={{ padding: 'var(--s5) var(--s6)', borderTop: 'var(--bd)' }}>\n <div className=\"doc-eyebrow\" style={{ marginBottom: 'var(--s2)' }}>Light</div>\n <div style={{ font: 'var(--ty-title-sm)', color: 'var(--tx-primary)', marginBottom: 'var(--s2)' }}>Weiß</div>\n <p style={{ font: 'var(--ty-body-md)', color: 'var(--tx-secondary)', margin: 0 }}>Vollständig weiß, für dunkle Hintergründe (Footer, Hero-Gradients, Dark Mode).</p>\n </div>\n </div>\n <div className=\"col-4\" style={{ background: 'var(--bg-surface)', border: 'var(--bd-strong)', borderRadius: 'var(--r-lg)', overflow: 'hidden' }}>\n {/* Panel-Hintergrund fest hell (#F5F7F7), nicht das theme-reaktive Token: das Monochrom-Logo ist fix in --tx-brand gezeichnet und bräuchte im Dark Mode sonst dunkel-auf-dunkel. */}\n <div style={{ background: '#F5F7F7', padding: 'var(--s8) var(--s6)', display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '120px' }}>\n <img src=\"./conciso/brand/logo-conciso-dark.svg\" alt=\"Conciso, monochrom-dunkle Variante\" style={{ height: '36px', width: 'auto', display: 'block' }} />\n </div>\n <div style={{ padding: 'var(--s5) var(--s6)', borderTop: 'var(--bd)' }}>\n <div className=\"doc-eyebrow\" style={{ marginBottom: 'var(--s2)' }}>Dark</div>\n <div style={{ font: 'var(--ty-title-sm)', color: 'var(--tx-primary)', marginBottom: 'var(--s2)' }}>Monochrom</div>\n <p style={{ font: 'var(--ty-body-md)', color: 'var(--tx-secondary)', margin: 0 }}>Komplett in <code className=\"token\">--tx-brand</code>, ohne teal-Punkt. Für Druck, Stempel, einfarbige Anwendungen oder wenn der Akzent visuell stört.</p>\n </div>\n </div>\n </div>\n</Unstyled>\n\n| Hintergrund | Variante | Begründung |\n|---|---|---|\n| Weiß / hell-neutral | Default | Volle Brand-Identität mit teal-Akzent |\n| Dunkel (mindestens 60 % Schwarzanteil) | Light | Vollständig weiß für maximale Lesbarkeit |\n| Bereichsfarbe (`--co/ki/es/wo-500`) | Light oder Dark | Default vermeiden, der teal-Punkt konkurriert mit der Bereichsfarbe |\n| Foto / unruhiger Hintergrund | Light auf Scrim | Halbtransparente dunkle Fläche unter Logo legen, dann Light verwenden |\n| Druck monochrom, Stempel, Embossing | Dark | Kein Akzent verfügbar, komplett einfarbig |\n| Dark-Mode-Interfaces | Light (auto-swap) | Wechsel via `[data-theme=\"dark\"]`-Selektor |\n\n### Barrierefreiheit\n\n| Aspekt | Regel |\n|---|---|\n| Alt-Text | Inhaltlich tragend (zum Beispiel Footer-Brand): `alt=\"Conciso\"`. Dekorativ daneben (zum Beispiel Topnav-Wortmarke neben Text-Logo): `alt=\"\"` mit `aria-hidden=\"true\"` |\n| Kontrast | Mindestens 3:1 zwischen Logo und Hintergrund (WCAG 1.4.11, Non-Text Contrast). Default auf Weiß: 11,7:1. Light auf `--n-700`: 15,3:1 |\n| Theme-Wechsel | Logo-Swap via CSS und `[data-theme=\"dark\"]`-Selektor, funktioniert ohne JS und respektiert die Nutzer-Präferenz |\n| Skalierung | SVG ist auflösungsunabhängig, bleibt scharf bei beliebigem Browser-Zoom (WCAG 1.4.4) |\n\n### Dos & Don'ts\n\n✓ Tun\n\n- Default-Variante auf hellen, neutralen Hintergründen: der teal-Punkt verankert die Brand-Identität.\n- Light-Variante auf dunklen Hintergründen ab `--n-700` oder Hero-Gradients.\n- Dark-Variante (monochrom) für Druck, Stempel oder wo der teal-Punkt visuell stört.\n- Schutzraum von mindestens der „C“-Höhe um das Logo herum freihalten.\n- Logo proportional skalieren (Höhe als Leitmaß), der Vektor bleibt in jeder Größe scharf.\n- Theme-Swap nutzen: `.logo-themed-default` + `.logo-themed-light`, schaltet automatisch via `[data-theme]`.\n\n✕ Nicht tun\n\n- Logo stauchen, dehnen, rotieren oder schief stellen: immer proportional und gerade.\n- Den teal-Punkt durch andere Bereichsfarben ersetzen (KI-Lime, ES-Blau, WO-Grün), Default ist immer `--co-500`.\n- Default-Variante auf dunklem Hintergrund einsetzen: die dunkle Schrift verschwindet, der teal-Punkt wird zur isolierten Insel.\n- Logo unter 20 px Höhe digital oder 12 mm im Druck verwenden, der Punkt zerfällt.\n- Logo als PNG oder JPG einbinden, immer SVG verwenden, damit es scharf bleibt und der Theme-Swap funktioniert.\n- Logo als dekoratives Element in Texten, Mustern oder Hintergrundgrafiken einsetzen, es bleibt eigenständig.\n",
|
|
13
|
-
"summary": "# Verwendung Verwendungsregeln zum Logo: Größen, Schutzraum, Einsatz nach Hintergrund, Bar..."
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|