{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "css-diagramm-attr",
 "name": "Balkendiagramm aus Datenattributen",
 "name_en": "Data-driven bar chart with typed attr()",
 "kurz": "Eine echte Tabelle wird zum Balkendiagramm: Die Zahl steht im Attribut, CSS rechnet die Länge, beim Einscrollen wachsen die Balken einmal auf ihren Wert.",
 "kategorie": "Sektionen & Funktionen",
 "kategorie_slug": "sektionen",
 "kategorie_nr": 16,
 "bau_chat": "D",
 "prioritaet": "F",
 "trendstatus": "aufsteigend",
 "belegstatus": "quellenbelegt",
 "website_belege": 0,
 "stimmung": [
  "ruhig",
  "editorial",
  "präzise"
 ],
 "stimmung_roh": [
  "präzise",
  "ruhig",
  "editorial"
 ],
 "technik": [
  "css",
  "scroll-driven-css"
 ],
 "technik_gruppen": [
  "css"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Eine echte Tabelle oder Liste mit Werten wird ohne Skript zum ruhigen Balkendiagramm. Jede Zeile trägt ihren Wert als Zahl, der Balken daneben wächst auf seine Länge. Zahl und Beschriftung bleiben als Text sichtbar. Beim ersten Erscheinen wachsen die Balken einmal auf, danach steht alles.",
  "warum_es_wirkt": "Kennzahlen wirken glaubwürdiger, wenn man Verhältnisse sieht statt hochgezählter Riesenziffern. Weil die Daten im Markup stehen, bleibt alles lesbar, durchsuchbar und druckbar.",
  "wann_einsetzen": "Spendenziel und Mittelverwendung im Verein, Auslastung im Kulturhaus, Kennzahlen im Mittelstand, Tarifvergleich in SaaS. Wenige Zeilen (3 bis 8), echte Werte.",
  "wann_nicht": "Nicht für erfundene oder geschönte Zahlen. Nicht für komplexe Diagramme (dann daten-scrollytelling mit SVG). Nicht vor dem Spendenknopf als Inszenierung. Ohne Fallback nicht, denn Safari kann typisiertes attr() noch nicht.",
  "varianten": [
   "balken: waagerechte Balken neben der Zahl",
   "anteil: gestapelter Balken über die volle Breite (Mittelverwendung)",
   "ziel: Fortschritt zu einem Ziel mit Zielmarke"
  ],
  "umsetzung": "1. Markup als table oder ul. Jede Zeile mit data-wert und zusätzlich style=\"--wert: 42\" (aus CMS oder Bau-Skript), damit alle Browser rechnen.\n2. Balken als ::after oder eigenes Element mit inline-size: calc(var(--wert) / var(--max) * 100%).\n3. Kür: @supports (width: calc(attr(data-wert type(<number>)) * 1%)) setzt --wert aus dem Attribut. Die doppelte Angabe kann dann entfallen.\n4. Einmaliges Wachsen mit scale von 0 auf 1 (transform-origin links) per css-view-reveal-nativ. Nie width animieren.\n5. Zahl in Tabellenziffern neben dem Balken. Farbe aus dem Signalton nur für die hervorgehobene Zeile.\n6. Fallback ohne Custom Property: nur die Tabelle.",
  "mobil": "Beschriftung über dem Balken statt daneben.",
  "reduzierte_bewegung": "Balken stehen sofort in voller Länge.",
  "barrierefreiheit": "Werte als Text in der Tabelle, Balken aria-hidden. Farbe nie einziger Träger, Balken 3:1 gegen den Grund. Bei 320 px und 200 % Zoom einspaltig, Zahlen nicht abgeschnitten. Keine Bewegung über 5 s."
 },
 "messwerte": {
  "easing": "Token des Rezepts (--ease)",
  "dauer_ms": "600–900 einmalig",
  "stagger_ms": "40–60 je Zeile",
  "distanz": "",
  "sonstiges": "3–8 Zeilen",
  "herkunft": "eigene Festlegung",
  "quelle": "eigene Festlegung (Techniken-Recherche 03.11.2026)"
 },
 "kurve": null,
 "gesehen_bei": [
  {
   "seite": "modern-css: Data-driven layouts with typed attr()",
   "url": "https://modern-css.com/attr-driven-layouts-without-js-charts/",
   "auszeichnung": "",
   "beobachtung": "Abgerufen 03.11.2026: Diagramme aus Datenattributen ohne JS. Bestätigt fehlendes Safari und nötigen Fallback.",
   "art": "artikel"
  },
  {
   "seite": "caniuse: attr() type(<number>)",
   "url": "https://caniuse.com/mdn-css_types_attr_type_function_number",
   "auszeichnung": "",
   "beobachtung": "Abgerufen 03.11.2026: Chrome/Edge ab 133, Firefox ab 155. Safari bis 27.2 nein, nur Technology Preview.",
   "art": "artikel"
  },
  {
   "seite": "MDN: Firefox 159 für Entwickler",
   "url": "https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/159",
   "auszeichnung": "",
   "beobachtung": "Laut Techniken-Recherche 03.11.2026 (Nebenbefund 1): Seit Firefox 159 (27.10.2026) laufen scroll-getriebene Animationen (animation-timeline: view()) in allen drei Engines. @supports bleibt als Schutz für ältere Browser richtig.",
   "art": "artikel"
  }
 ],
 "tutorials": [
  {
   "titel": "modern-css: attr()-driven layouts",
   "url": "https://modern-css.com/attr-driven-layouts-without-js-charts/"
  }
 ],
 "kombinierbar_mit": [
  "zahlen-fakten-section",
  "daten-scrollytelling",
  "fortschrittsring",
  "tabellenziffern",
  "spendenmodul-wirkung",
  "vergleichstabelle-sticky",
  "css-view-reveal-nativ"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/sektionen/css-diagramm-attr/",
  "meta": "https://cecodesign.de/lab/fundus/sektionen/css-diagramm-attr/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/sektionen/css-diagramm-attr/demo"
 },
 "herkunft": {
  "quelldateien": [
   "Techniken-Recherche 03.11.2026"
  ],
  "pruefnotizen": [
   "Nur zwei Engines (Chromium, Gecko), deshalb F. Der Gewinn gegenüber einer Custom Property ist klein. Das Muster lohnt vor allem als ruhige Alternative zu zahlen-odometer. fortschrittsring nutzt typisiertes attr() schon mit JS-Fallback. Kein Website-Beleg.",
   "Eigene Prüfung beim Bau 03.11.2026 in Headless Chromium 153: Die Bedingung aus dem Recherche-Entwurf, @supports (width: attr(data-wert type(<number>) * 1%)), ist ungültige Syntax und meldet auch in Chromium „nein“. Richtig ist calc(attr(data-wert type(<number>)) * 1%); so steht es oben in Schritt 3.",
   "Scroll-getriebene Animationen laufen seit Firefox 159 in allen Engines (Nebenbefund 1). Das Wachsen per view() ist deshalb kein Sonderweg mehr; @supports bleibt als Schutz."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Eine echte Tabelle wird zum Balkendiagramm: Die Zahl steht im Attribut, CSS rechnet die Länge, beim Einscrollen wachsen die Balken einmal auf ihren Wert.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "markup": "table mit th scope=row; Zeile tr data-wert=\"4120\" style=\"--w: 4120\"; tbody data-max=\"4500\" style=\"--m: 4500\"; Balken span aria-hidden in eigener Zelle",
   "robuster_weg": "[data-wert] { --wert: var(--w, 0) } · [data-max] { --max: var(--m, 1) }; Inline-Name --w statt --wert, sonst gewinnt der style-Wert immer gegen die attr()-Regel",
   "kuer_attr": "@supports (width: calc(attr(data-wert type(<number>)) * 1%)) { [data-wert] { --wert: attr(data-wert type(<number>), 0) } }",
   "balken": "inline-size: calc(var(--wert) / var(--max) * 100%); Höhe 22px; Hilfslinien repeating-linear-gradient alle calc(1000 / var(--max) * 100%)",
   "anteil": "flex: var(--wert) 1 0 (flex-grow direkt aus dem Wert, keine Summe nötig), gap 3px, Höhe clamp(64px, 9vw, 104px); vierter Teil schraffiert repeating-linear-gradient(-45deg, #002E6D 0 2px, #fff 2px 7px)",
   "ziel": "Füllung calc(var(--wert) / var(--max) * 100%), Zielmarken left: calc(var(--bei) / var(--max) * 100%) aus data-bei; Spur 40px, #EDF5FA mit 1px #9fb3cf",
   "wachsen": "scale 0 1 → 1 1, transform-origin links, animation-timeline: --diagramm (view-timeline der ganzen Grafik), animation-range entry calc(n × 6%) entry calc(70% + n × 6%); vollständig sichtbare Grafik = alle Balken in voller Länge",
   "kurve": "cubic-bezier(0.83, 0, 0.17, 1) (token-kinetisch-inout aus dem Rezept)",
   "rueckfall_zeitleiste": "ohne animation-timeline: IntersectionObserver 25 %, transition scale 800 ms gleiche Kurve, 50 ms Versatz je Zeile, einmalig",
   "rueckfall_tabelle": "ohne Custom Properties bleibt die Tabelle mit allen Werten als Text",
   "farben": "hervorgehoben #002E6D (13 : 1), übrige Balken #6C8AC0 (3,5 : 1 auf Weiß), Anteile #002E6D / #3E67A8 / #6C8AC0 / Schraffur; Spenden-Gelb #FFCA0A nur für den Spendenknopf",
   "ziffern": "Big Shoulders 800, tabular-nums lining-nums; Werte clamp(1.75rem, 1.2rem + 1.6vw, 2.5rem)",
   "probe": "Regler im Pult ändert nur data-wert und den Text der markierten Zeile: im Weg attr() folgt der Balken, im Weg --w inline bleibt er stehen",
   "browser_live": "Headless Chromium 153 (Arbeitskopie, 03.11.2026): typisiertes attr() (calc(attr(data-wert type(<number>)) * 1%)) und animation-timeline: view() laufen. Die Bedingung aus dem Recherche-Entwurf attr(data-wert type(<number>) * 1%) ist ungültig und meldet auch hier nein.",
   "browser_rueckfall": "Wege „--w inline“ und „nur Tabelle“ im selben Browser über das Pult geprüft; der IntersectionObserver-Rückfall läuft nur ohne view() und ist hier nicht live gesehen.",
   "vorfuehrung": "Seite scrollt in 2600 ms (kubisch ein/aus) zur Grafik, hält 2600 ms, beginnt neu; reduziert springt sie nach 900 ms und hält 3200 ms",
   "ui": "150 ms cubic-bezier(0.16, 1, 0.3, 1)"
  },
  "ausloeser": "scroll",
  "touch": "Gleiches Verhalten per Scroll. Unter 760 px steht die Beschriftung über dem Balken (Zeile als Raster, ARIA-Rollen halten die Tabellensemantik), Zahl rechts daneben. Pult unten links, auf dem Telefon eingeklappt.",
  "reduziert": "Keine Wachs-Animation: alle Balken stehen sofort in voller Länge. Die Vorführung springt zur Grafik statt zu scrollen.",
  "korrekturen": {},
  "geprueft": "03.11.2026",
  "notiz": "Rezept verein-nonprofit-wirkung, erfundener Verein Lernbrücke e. V. aus Bernburg (Saale), Jahresbericht mit erfundenen, als Demo gekennzeichneten Zahlen. Big Shoulders und Archivo. Drei Varianten (?v=balken|anteil|ziel), dazu im Pult der Rechenweg (attr(), --w inline, nur Tabelle) und eine Probe, die nur das Attribut ändert. Live gesehen in Headless Chromium 153: typisiertes attr() und view() laufen. Fallstricke: style=\"--wert\" gewinnt immer gegen die attr()-Regel, darum --w; view() je Balken lässt Balken am Bildschirmrand halb gewachsen stehen, darum eine benannte Zeitleiste über die ganze Grafik, die bei vollständiger Sichtbarkeit fertig ist; die @supports-Bedingung aus dem Entwurf ist ungültig. Zugang 03.11.2026: nur info:kontrast-von-hand am Rahmen-Knopf. Selbsturteil 4.",
  "varianten": [
   {
    "id": "balken",
    "name": "Balken neben der Zahl",
    "wann": "Wirkung im Verein, Auslastung im Kulturhaus, Kennzahlen im Mittelstand: 3 bis 8 Zeilen mit echten Werten nebeneinander vergleichen.",
    "werte": {
     "aufbau": "Tabelle Angebot / Balken / Stunden, hervorgehobene Zeile #002E6D",
     "max": "data-max 4500, Hilfslinien je 1000"
    }
   },
   {
    "id": "anteil",
    "name": "Gestapelter Anteil",
    "wann": "Mittelverwendung, Kostenaufteilung, Herkunft der Einnahmen: Teile eines Ganzen über die volle Breite, Werte in der Legende-Tabelle.",
    "werte": {
     "aufbau": "flex-grow aus dem Wert, 4 Teile, Legende als Tabelle Euro / Anteil",
     "schraffur": "vierter Teil ohne Farbe unterscheidbar"
    }
   },
   {
    "id": "ziel",
    "name": "Fortschritt zum Ziel",
    "wann": "Spendenziel mit Bauabschnitten, Kennzahl gegen Jahresziel im Mittelstand: ein Wert, ein Ziel, Zwischenmarken.",
    "werte": {
     "aufbau": "Riesenziffer + Spur mit Füllung und Zielmarken aus data-bei, Abschnitte als Tabelle mit Stand",
     "max": "data-max 48000"
    }
   }
  ]
 }
}
