TFCZ · Design System

Brand Guide v3.0 · 22.09.2026

Wer wir sind, wie wir klingen und wie wir aussehen. Alle Farben, Logos, Regeln und Bausteine stehen hier, live und zum Kopieren. Klick auf eine Farbe oder einen Verlauf kopiert den Wert. Boxen bleiben in beiden Hintergrund-Modi dunkel & frosted. Für KI: zuerst die Anleitung für KI lesen.

00

Nutzung: Diese Datei ist der Brand Guide

Die Referenz für alles, was nach TFCZ aussehen und klingen soll. Daran halten sich alle, die TFCZ-Material erstellen, Menschen wie KI. Bei Werten gilt system/tokens.css: Farben, Radien, Schatten und Zeiten stehen dort, und der Guide liest sie von dort, statt sie selbst hinzuschreiben. Bei Regeln gilt dieser Guide. Findest du einen Widerspruch, meldest du ihn, statt still einer Seite zu folgen.

Anleitung für KI · zuerst lesen

Wer eine KI auf diesen Guide schickt, braucht nur den Link. Die KI liest diesen Kasten zuerst und arbeitet danach. Du kannst den Text auch kopieren und direkt einfügen.

Anleitung für KI · vor jedem Auftrag
Du arbeitest für den Tischfussball Club Zürich (TFCZ). Dieser Brand Guide ist gross. Lies ihn nicht von vorne bis hinten, sondern geh so vor: 1. Lade zuerst das Verzeichnis https://design.tfcz.ch/system/brandguide-index.json. Es nennt jede Sektion mit Nummer, Titel, Teil, Kapitel, Link und einer Zeile, wofür sie da ist. 2. Schreib dir auf, welche Elemente dein Auftrag braucht (zum Beispiel Button, Karte, Formular, Dialog, Tabelle, Chart, Navigation, Footer), und such jedes davon im Verzeichnis. 3. Gibt es ein Element schon, übernimmst du genau diesen Baustein. Sein HTML steht in https://design.tfcz.ch/system/komponenten.json (Namen im Feld «baustein» des Verzeichnisses), Aussehen und Verhalten kommen aus system/ui.css, ui.js, charts.js und effects.js. Du baust nichts nach und veränderst den Baustein nicht. 4. Gibt es ein Element nicht, sag mir das, bevor du etwas Neues baust, und nenn den Baustein, der am nächsten kommt. 5. Lies danach nur die Sektionen, die dein Auftrag braucht. Immer dazu gehören die Marken-DNA (Sektion 00, gleich unter diesem Kasten), der Ton (Sektion 06) und für Fakten der Steckbrief (Sektion 10). Adresse, Preise und Zeiten erfindest du nicht. Für Touch-Geräte und die Kasse gelten die Masse aus «Grössen & Touch» (Sektion 16). 6. Farben, Radien, Schatten und Zeiten nimmst du aus https://design.tfcz.ch/system/tokens.css, Logos aus Sektion 13. Nichts raten, nichts nachbauen. 7. Nenn mir vor dem Start, welche Bausteine du übernimmst und welche Regeln gelten. Nenn mir bei der Abgabe, was übernommen und was neu ist. Fehlt etwas oder widerspricht sich der Guide, frag nach, statt zu raten.
Marken-DNA · die 8 Kernregeln

Blau oben · Gold unten

Die Brand-Line ist das Markenzeichen. Print/Social = harter Schnitt, keine Verläufe. Screen-UI = Rahmen-Signatur an Fenstern & sticky Bars (blau #5ca7dc oben / gold #cda857 unten, ~3px am abgerundeten Container).

Gold ist knapp

Gold = Auswahl/Aktiv im UI und die zentrale Handlung der Seite: echte Conversion („Mitglied werden") oder Lead-Anfrage („Verfügbarkeit prüfen"). Nie Grossfläche, nie für Info-/Navigations-Buttons. So bleibt Gold wertvoll.

Dunkle, frosted Boxen

Cards, Fenster & Bausteine im Screen-UI immer als DarkNavy-Gradient, leicht transparent + backdrop-blur. Helle Boxen haben dort keinen Platz.

Nunito Sans

Einzige Schrift der Marke. Im UI: 400 / 500 (Fliesstext) / 700 / 900. Fallback: moderne, geometrische Sans-Serif.

Öffentlich hell · intern dunkel

Öffentliche Seiten = Hellblau-Verlauf, ohne Dark-Mode-Umschalter. Login-geschützte Tools (POS, Livestudio, Cockpit …) = Navy.

Responsive, ohne Ausnahme

Breakpoints 640 / 1024 / 1400. Nie ein horizontaler Scrollbalken. Touch: dense but tappable (iPad-first).

Sprache

Deutsch (Schweiz), CHF und Du-Form. Wir gendern nie. Emotion & Vibe kommen vor der Information.

Ein Logo, klare Regeln

Nur Originaldateien, richtige Variante pro Hintergrund, Schutzzone respektieren. Details stehen in Sektion 13.

Teil 1 · Marke

Strategie & Stimme

Das Fundament: wer wir sind, wofür wir stehen, wie wir klingen. Wer eine Seite, einen Post, eine Mail oder einen Flyer macht, startet hier. Die Farbe kommt später.

01

Positionierung

Der eine Satz, der uns einordnet und unterscheidet. Alles, was wir sagen, muss dazu passen.

Der TFCZ ist der grösste Profi-Tischfussballverein der Schweiz, der Ort in Zürich, wo Anfänger und Weltmeister am selben Tisch spielen. Seit 1990 am selben Ort. Positionierungs-Satz · Nordstern für Copy & Design

Kategorie

Ein Tischfussball-Verein in Zürich, der Sportverein, Community und Erlebnisort zugleich ist.

Für wen

Alle, die in Zürich Tischfussball erleben wollen, vom allerersten Mal an der Stange bis zur Weltspitze.

Anders als …

… eine Bar mit Töggelikasten (kein echter Sport) und anders als ein reiner Elite-Club (nicht für alle). Wir sind beides: nahbar wie ein Treffpunkt, ernsthaft wie eine Nati.

Warum glaubwürdig

100+ Mitglieder, WM-/SM-/Nati-/Bundesliga-Sieger im Verein, 10 Leonhart-Turniertische und das Zürich Open mit YouTube-Stream. Dazu über 30 Jahre am selben Ort.

02

Mission, Vision & Werte

Warum es uns gibt, wohin wir wollen, und die Werte, die jede Entscheidung tragen.

Mission: warum

Tischfussball in Zürich für alle zugänglich machen und gleichzeitig Spitzensport auf Weltniveau fördern.

Vision: wohin

Zürichs lebendigste Tischfussball-Community und das sichtbare Aushängeschild des Schweizer Tischfussballs.

Werte

Alle willkommen
Offenheit meinen wir wörtlich. Am selben Tisch stehen alle Levels und jede Herkunft.
Gemeinschaft
Getragen von Ehrenamt und Herzblut; wir fangen jeden Neuen auf.
Spielfreude
Spass zuerst, der Rest kommt von selbst.
Exzellenz
Echter Wettkampf, Weltklasse-Können, ohne Angeberei.
Beständigkeit
Seit 1990 am selben Ort. Diese Tradition schafft Vertrauen.
03

Markenpersönlichkeit & Archetyp

So wäre der TFCZ, wenn er ein Mensch wäre. Dieser Charakter muss in Text, Bild und Ton durchscheinen.

Archetyp: „Der bodenständige Champion". Nahbar für alle (Everyman) und zugleich exzellent wie ein Weltmeister (Hero).

5 Eigenschaften

EinladendEchtSpielfreudigStolz & nahbarVerlässlich

Wenn TFCZ eine Person wäre

Der Kollege, der dich am ersten Abend abholt, dir alles zeigt und dich zum Lachen bringt. Nebenbei ist er Schweizermeister und hängt das nicht an die grosse Glocke.

Wie es klingt / aussieht

Du-Form, kurze warme Sätze, konkret. Visuell: Blau/Gold, dunkel & edel, echte Fotos, viel Bewegung.

04

Markenversprechen & Geschichte

Unser Versprechen an alle und die Geschichte, die wir immer wieder erzählen. Dazu fertige Textbausteine zum Übernehmen.

„Bei uns haben alle am selben Tisch Platz."Markenversprechen · jeder wird aufgefangen, egal welches Level
„Komm rein und sag hallo. Den Rest erledigt das Spiel."Signatur-Satz für CTAs & Schluss-Absätze

Boilerplate · 1 Satz

Der Tischfussball Club Zürich ist der grösste Profi-Tischfussballverein der Schweiz. Seit 1990 stehen bei uns Anfänger und Weltmeister am selben Tisch.

Boilerplate · 3 Sätze

Der TFCZ ist der grösste Profi-Tischfussballverein der Schweiz und seit 1990 in Zürich zuhause. Bei uns spielen Anfänger und Weltmeister am selben Tisch, und unsere Community fängt jeden auf. Ob offenes Training, Turnier, Käsekick oder Firmenevent, willkommen ist jeder.

Boilerplate · 5 Sätze

Der Tischfussball Club Zürich (TFCZ) ist der grösste Profi-Tischfussballverein der Schweiz. Seit 1990 sind wir im selben Clublokal zuhause. Auf 10 Leonhart-Turniertischen trainieren Nationalspieler neben Leuten, die zum ersten Mal an der Stange stehen. Wir tragen Turniere aus, streamen das Zürich Open auf YouTube und feiern beim Käsekick genauso ausgelassen. Komm vorbei, der erste Abend ist gratis.

05

Zielgruppen & Ansprache

Für wen wir schreiben. Je Zielgruppe stehen hier Bedürfnis, Kanal und der passende Handlungsaufruf (CTA).

Einsteiger / Neugierige

Bedürfnis: Unverbindlich reinschnuppern, keine Hürde. Ton: warm, einladend. Kanal: Instagram, Website. CTA: „Erster Abend gratis".

Aktive Mitglieder

Bedürfnis: Termine, spontane Updates, dazugehören. Ton: kurz, direkt, menschlich. Kanal: WhatsApp. CTA: „Komm Dienstag".

Turnier- / Pro-Spieler

Bedürfnis: Ranglisten, Formate, Ernst. Ton: energetisch, sport-fokussiert. Kanal: Website, STF/Coral. CTA: „Jetzt anmelden".

Firmen / HR

Bedürfnis: Teamevent, das hängen bleibt, ohne Aufwand. Ton: professionell-herzlich, lösungsorientiert. Kanal: Website, E-Mail. CTA: „Firmenevent anfragen".

Familien / Kinder

Bedürfnis: Betreute, sichere Freizeit mit Spass. Ton: leicht, herzlich. Kanal: Website, Instagram. CTA: „Kindertraining & Ferienplausch".

06

Tone of Voice

Vier Eigenschaften bestimmen jeden Text, in jedem Kontext. Sportlich-herzlich, keine Corporate-Sprache. Die Tonlage passt sich an (Fun locker, Turnier energetisch, Firma professionell-herzlich).

1 · Einladend

So„Egal wie gut du bist, komm vorbei. Bei uns ist jeder willkommen."
Nicht so„Nur für ernsthafte Spieler mit Erfahrung."

2 · Echt

So„Dienstag, 18 Uhr, Landenbergstrasse 10. Komm rein."
Nicht so„Wir freuen uns, Sie in unseren Räumlichkeiten begrüssen zu dürfen."

3 · Spielfreudig

So„Beim Käsekick töggelen wir, bis der Käse schmilzt."
Nicht so„Veranstaltung mit gastronomischer Verpflegung."

4 · Stolz & nahbar

So„Bei uns trainieren Weltmeister, und die zeigen dir gern den ersten Trick."
Nicht so„Die Elite des Schweizer Tischfussballs unter sich."
07

Botschaften & Messaging

Vier Kernbotschaften (Pillars). Jede Seite und jeder Post trägt klar eine davon. Ein Beleg sorgt dafür, dass es glaubwürdig bleibt.

1 · Alle willkommen

Botschaft: Anfänger bis Profi, am selben Tisch. Beleg: offenes Training, Schnuppern gratis, Community fängt auf. Wofür: Home, Mitglied-werden, Insta-Reels.

2 · Echter Spitzensport

Botschaft: Weltklasse spielt hier. Beleg: WM/SM/Nati, Zürich Open, YouTube-Stream, 10 Leonhart-Tische. Wofür: Turnier-Seiten, Stream, Presse.

3 · Community & Tradition

Botschaft: Seit 1990, getragen von Menschen. Beleg: dasselbe Clublokal in Zürich, Ehrenamt, Herzblut. Wofür: Über uns, Geschichte, Gönner/Sponsoring.

4 · Erlebnis & Spass

Botschaft: Bei uns wird gelacht. Beleg: Käsekick, Mittwochs DYP Challenge, Firmenevents. Wofür: Events, Firmenevent-Landingpage, Social.

08

Sprache & Lexikon

Sprachregeln und Wortschatz, damit alles wie aus einem Guss klingt.

Regeln

Sprache
Deutsch (Schweiz), Du-Form. ss statt ß, CHF, Apostroph bei Tausendern (1'000).
Nie gendern
Kein :in / *innen / Binnen-I, keine Doppelnennungen. Generisches Maskulinum oder neutral („alle", „Leute", „das Team"). Beispiel: «Werd einer von uns» statt «Werd eine:r von uns».
Satzbau
Kurze Sätze, aktiv, konkret. Emotion vor Information, zuerst muss der Vibe rüberkommen.
Fakten
Nie erfinden. Zahlen/Adressen/Preise immer aus dem Steckbrief (Sektion 10) übernehmen. Fehlt dort etwas, frag den Marketingverantwortlichen.
Ort
Immer «Zürich», nie «Wipkingen». Die Adresse lautet «Landenbergstrasse 10, 8037 Zürich». Das gilt für alles, was nach aussen geht.
Schreibstil
Wir schreiben, wie wir an der Bar reden. Keine Gedankenstriche als Stilmittel, kein «nicht X, sondern Y» und keine angekündigten Pointen wie «Das Ergebnis:». Kurze und lange Sätze mischen, und lieber eine echte Zahl als «viele». Texte, die mit KI entstehen, vor dem Veröffentlichen einmal laut lesen.
Kategorien
Die Kategorie heisst «Fun» (statt «Plausch»), «Pro Turniere» (statt «Ranglisten-Turniere»), «Elite» umfasst alles ITSF inkl. SM. Als Event-Name bleibt «Plausch-Turnier» erlaubt.
Sagen wirTöggele, Töggelikasten, kicken, Käsekick, Community, „alle willkommen", Stange, Schnuppern, Mitglied, Gast, Clublokal (statt «Halle»).
Vermeiden wirGender-Notationen, Corporate-Floskeln („Räumlichkeiten", „im Rahmen von"), Anglizismus-Overkill, „Kunden", Ausrufezeichen-Inflation.
09

Schreiben ohne KI-Handschrift

Gilt für alles Geschriebene: Seitentexte, Knopfbeschriftungen, Instagram, WhatsApp, Mails an Mitglieder, Flyer. Ein Sprachmodell sagt das wahrscheinlichste nächste Wort voraus und steuert damit in die Mitte aller Trainingstexte. Die Mitte hat keinen Akzent. Verräterisch sind darum Satzmuster, nicht einzelne Wörter: Wortlisten veralten in Monaten, Muster bleiben. Der Club darf mit einer KI schreiben, solange am Ende der Club spricht.

Wie verbreitet das ist
MessungBefund
Kobak et al., Science Advances, Juli 2025: 15 Mio. Fachtexte, 2010 bis 2024Mindestens 13.5 % der Texte von 2024 maschinell überarbeitet, in Teilbereichen 40 %
Washington Post, geteilte ChatGPT-Verläufe, Mai 2024 bis Juli 2025Der Gedankenstrich stand im Sommer 2025 in über der Hälfte aller Antworten
Pangram Labs, rund 1 Mio. gescannte Beiträge, 202640.5 % der langen Beiträge auf LinkedIn vollständig maschinell, vom Anbieter als Untergrenze bezeichnet

Eine erkennbar menschliche Stimme fällt damit auf. Erkennungswerkzeuge taugen dafür wenig: In einer Untersuchung mit 63 Dozierenden lagen die Trefferquoten beim Raten, und sieben Werkzeuge stuften im Schnitt 61 % der Aufsätze von Nicht-Muttersprachlern falsch als maschinell ein. Das Ziel dieser Regeln ist darum kein bestandener Test, sondern ein Text, der nach Club klingt.

Die drei, die am schnellsten auffallen
  • Gedankenstrich als Stilmittel. Der Satz wird umgebaut, nicht mit einem Komma geflickt. Bereiche behalten ihren Strich («4 - 100 Personen», «1921 - 1923»).
    Vorher Der erste Abend ist gratis - stell dich einfach an den Tisch.
    Nachher Der erste Abend ist gratis. Stell dich einfach an den Tisch.
  • «Nicht X, sondern Y», auch als «nicht nur … sondern auch» oder in zwei getrennten Sätzen. Zusammenziehen zu einem Komma-Satz löst es nicht, die Struktur muss weg.
    Vorher Nicht jede Karriere beginnt im Verein, sondern in der Beiz.
    Nachher Viele Karrieren beginnen in der Beiz.
  • Doppelpunkt-Ankündigung. «Was uns ausmacht:», «Das Ergebnis:». Auch getarnt ohne Doppelpunkt: «Schauen wir uns an, was den Club ausmacht.»
    Vorher Was den TFCZ ausmacht: Freiwillige, die jede Woche aufschliessen.
    Nachher Den TFCZ tragen Freiwillige, die jede Woche aufschliessen.
Verbotene Konstruktionen
KonstruktionBeispielAuflösung
Negativ-Parallelismus«Kein Zufall, sondern Training»Die Positivaussage allein stehen lassen
Doppelpunkt-Ankündigung«Was du wissen musst:»Ankündigung streichen, mit der Aussage beginnen
Substantivierte Einleitung«Das Schöne am Töggelen ist»Mit Verb oder Adjektiv ansetzen
Trikolon in Parallelstruktur«Training, Turniere, Gemeinschaft»Ein Glied streichen, die übrigen ungleich gewichten
Anapher «Wer X / Wer Y»«Wer neu ist, schaut zu. Wer dabei ist, spielt.»Zweite Hälfte anders bauen
Perfekte Parallelstruktur«Der erste Punkt … Der zweite Punkt …»Nach Gewicht ordnen statt nach Zählung
Pointen und Schlüsse
MusterBeispielAuflösung
Spiegel-Pointe«Das ist kein Sport. Das ist Zuhause.»Streichen, die Aussage steht schon davor
Mini-Satz als Effekt«War es nicht.»In den Satz davor holen
Einzeiler zwischen Absätzen«Genau darum geht es hier.»Weg, er zerhackt den Lesefluss
Inszenierte Erkenntnis«Dass mich das packt, überrascht mich nicht.»Die Beobachtung ohne Rahmen hinschreiben
Doppelter SchlussPointe und Frage am EndeNur eine Schlussbewegung
Alliteration als Pointe«Technik statt Talent»Ein Wort ersetzen
Zwangs-Zusammenfassung«Zusammenfassend», «Insgesamt»Mit einem konkreten nächsten Schritt enden

Der letzte Absatz ist die schwächste Stelle jedes maschinellen Textes, weil er nach Schema gebaut wird. Beim TFCZ endet ein Text mit dem, was jemand tun kann: vorbeikommen, sich vormerken lassen, in der Gruppe fragen.

Wiederholung und Leerlauf
MusterBeispielAuflösung
Angehängte Bedeutungsfloskel«…, was die Bedeutung des Vereins unterstreicht»Streichen oder die konkrete Folge nennen
Schein-Spektrum«vom Einsteiger bis zum Weltmeister» als FloskelNur schreiben, wenn beide Enden wirklich am Tisch stehen
Paarige Abstrakta«Leidenschaft und Gemeinschaft»Eine Sache benennen, die man sehen kann
Wortstamm-Wiederholungviermal «spielen / Spiel / Spieler»Synonym oder Satzumbau
Inhaltliche RedundanzDerselbe Gedanke in Absatz 2 und 5Den schwächeren Absatz löschen, nicht umschreiben
Übergangsfloskel«Dabei ist längst bekannt …»Direkt zur nächsten Aussage
Kommentar-Eröffnung«Guter Punkt», «Spannende Frage»Nie als Einstieg, auch nicht in Antworten
Floskeln nach Stelle im Text
StelleFloskel
Einstieg«In der heutigen schnelllebigen Welt …», «Immer mehr Menschen fragen sich …»
Brücke«Es ist wichtig zu beachten, dass …», «Dabei ist zu berücksichtigen …»
Schluss«Zusammenfassend lässt sich sagen …», «Abschliessend sei angemerkt …»
Pathos ohne Inhalt«spielt eine entscheidende Rolle», «vor diesem Hintergrund»

Wer nur diese drei Stellen prüft, findet den grössten Teil. Die Reparatur ist immer dieselbe: Phrase löschen, Satz mit der eigentlichen Aussage beginnen.

Verdächtige Wörter

ganzheitlich · nahtlos · massgeschneidert · facettenreich · zukunftsorientiert · essenziell · bahnbrechend · transformativ · dynamisch · innovativ · hochwertig · vielfältig · kuratieren · entfesseln · befähigen · eintauchen · beleuchten · unterstreichen · navigieren · gewährleisten · optimieren · revolutionieren.
Einzeln beweisen sie nichts, Menschen benutzen diese Wörter auch. Erst drei oder mehr in einem Absatz plus dünner Inhalt ist ein Befund, und dann wird der Absatz neu geschrieben statt geglättet. Solche Listen verfallen: «delve» war 2025 praktisch verschwunden, während der Gedankenstrich gleichzeitig in über der Hälfte aller Antworten stand. Die Struktur ist das Hauptsignal, die Wörter sind ein Nebenindiz.

Gleichmass, Haltung, Formatierung

  • Satzlängen wechseln. Menschen schreiben in Schüben, eine Maschine legt sich eng um zwölf bis achtzehn Wörter. In jedem Abschnitt steht mindestens ein sehr kurzer und ein langer Satz. Fragmente sind erlaubt. Fünf tadellose Sätze von je vierzehn Wörtern sind zusammen ein Befund.
  • Position beziehen. Der Club hat eine Meinung, etwa dass der erste Abend gratis ist und dass am Tisch nur zählt, was du mit dem Ball machst. Absicherung in jede Richtung liest sich wie ein Protokoll. «Aus meiner Sicht» ist in Ordnung, Dauer-Abwägung nicht.
  • Fett ist knapp. Ein Absatz trägt höchstens eine Hervorhebung. Wenn alles hervorgehoben ist, ist nichts hervorgehoben.
  • Kein Stichwort mit Doppelpunkt in der Liste, wenn die Erklärung danach dasselbe noch einmal sagt. Entweder Stichwort oder Satz.
  • Keine Aufzählung, wo ein Satz reicht. Drei Punkte à vier Wörter sind ein Satz mit Kommas.
  • Kein Zeilenumbruch nach jedem Satz und keine Symbole am Zeilenanfang. Die Liste hat ihre Markierung schon.
Zahlen und Konkretheit
VageKonkret
«moderne Ausstattung»«zehn Leonhart-Turniertische, dazu ein Ullrich»
«regelmässig geöffnet»«Dienstag ab 18:00, Mittwoch ab 19:00»
«viele Mitglieder»«über 100 Mitglieder»
«zentral gelegen»«2 Minuten vom Bahnhof Wipkingen»
«schon lange»«seit 1990 im selben Clublokal»

Lässt sich eine Angabe nicht konkret machen, wird sie gestrichen. Tausender mit Hochkomma: 3'000, 12'500, 1'240.50. Jahreszahlen (2026), Postleitzahlen (8037) und Hausnummern bleiben ohne, das sind Kennungen und keine Mengen. Zahlen, die untereinander stehen, laufen mit font-variant-numeric: tabular-nums. Fakten kommen aus dem Steckbrief, nichts wird erfunden.

Je Kanal andere Verräter
KanalWoran man die Maschine erkenntWas TFCZ macht
WebsiteGleich lange Absätze, Überschriften im Bauplan «Adjektiv plus Substantiv»Kurze Kicker, ein Gedanke pro Abschnitt, Zahlen aus dem Steckbrief
Mail an Mitglieder«Ich hoffe, diese Mail erreicht dich gut», höfliche Polsterung, auffällig fehlerfreie AbsätzeMit dem Anlass beginnen, um ein Drittel kürzen, laut mitlesen
InstagramBehauptung im ersten Satz, Umbruch nach jedem Satz, generische Frage am SchlussEin Bild, ein Satz, eine Einladung. Keine Emojis
WhatsApp-GruppeVollständige Sätze mit Marketing-TonSo schreiben, wie am Tisch geredet wird, kurz und direkt
Knöpfe und BeschriftungenFüllwörter wie «Jetzt» oder «Einfach»Das Verb zuerst: «Mitglied werden», «Verfügbarkeit prüfen»

So entsteht ein Text

  • Eigene Stimme einspeisen. Zwei, drei bestehende TFCZ-Texte in den Auftrag geben. Ein Beispiel wirkt stärker als jede Stilbeschreibung.
  • Wenige Verbote gleichzeitig. Gedankenstrich und «nicht X, sondern Y» reichen beim Schreiben. Zu viele Regeln auf einmal machen den Text steif und damit künstlicher.
  • Rohbau von der Maschine, Schluss von Hand. Vor allem der erste und der letzte Satz.
  • Vorlesetest. Würdest du das jemandem am Tisch so sagen? Wenn nein, diesen einen Satz neu.
BefundVorgehen
Drei oder mehr Floskeln in einem Absatz bei dünnem InhaltAbsatz komplett neu schreiben, nicht glätten
Mehrere Strukturmuster zusammen, etwa Trikolon plus Gedankenstrich plus Spiegel-PointeRohbau wegwerfen und aus den Fakten neu schreiben

Was beim TFCZ zusätzlich gilt

  • Du-Form und Schweizer Hochdeutsch, also immer «ss», nie «ß».
  • Wir gendern nie und benutzen das Lexikon aus Sektion 08: Stange, Schnuppern, Mitglied, Gast, Clublokal.
  • Emotion vor Information. Zuerst der Satz, der jemanden abholt, dann die Details.
  • Keine Emojis, auch nicht als Ersatz für ein Icon, und keine Kursive ausser im Zitat (Sektion 14).
  • Der Ort heisst Zürich. «Wipkingen» kennt auswärts kaum jemand, im Text steht es nur, wenn es um den Weg geht.
Vor dem Veröffentlichen durchgehen

Steht ein Gedankenstrich mitten im Satz? Gibt es ein «nicht X, sondern Y»? Kündigt ein Doppelpunkt eine Erkenntnis an? Sind alle Sätze gleich lang? Steht im Text mindestens eine Zahl, ein Name oder ein Ort, den es wirklich gibt? Endet der Text mit einer Zusammenfassung statt mit einem nächsten Schritt? Trägt ein Absatz mehr als eine Hervorhebung?

Was diese Regeln nicht behaupten

  • Kein einzelnes Merkmal ist ein Beweis. Menschen schreiben Gedankenstriche und Dreiklänge völlig legitim. Erst Häufung plus dünner Inhalt zählt.
  • Wortlisten veralten, Strukturmuster halten länger. Was hier als Wort steht, gehört in einem Jahr überprüft.
  • Deutsch ist nicht Englisch. Viele veröffentlichte Marker stammen aus englischen Texten, im Deutschen sind Gedankenstriche ohnehin seltener.
  • Keine Kosmetik. Absichtliche Tippfehler oder eingestreute Mundart machen einen Text nicht menschlicher, nur schlechter.
10

Fakten & Steckbrief

Die belastbaren Fakten für jede Seite. Was hier nicht steht, klärst du mit dem Marketingverantwortlichen.

VereinTischfussball Club Zürich (TFCZ)
OrtLandenbergstrasse 10, 8037 Zürich (seit 1990 dasselbe Clublokal)
GrösseGrösster Profi-Tischfussballverein der Schweiz · 100+ Mitglieder · 20+ pro Abend
TischeLeonhart-Verein: 10 Leonhart + 1 Ullrich permanent; weitere (Garlando, Bonzini, Tornado, Roberto) je Anlass
ÖffnungDi ab 18:00 (Offenes Training) · Mi ab 19:00 (Mittwochs DYP Challenge, Start 20:15) · Events gemäss Ausschreibung
Training mit PhilippDi 18:45–20:15, 14 Einheiten/Semester · gratis für Mitglieder, CHF 100 für Nicht-Mitglieder
MitgliedschaftSchnuppern gratis (danach CHF 5/Eintritt) · Aktiv CHF 200/Jahr · Key Player CHF 250 + Schlüssel · Gönner auf Anfrage
KanäleInstagram @tischfussballclubzuerich · YouTube @tischfussballclubzuerich · WhatsApp (intern) · tfcz.ch
Kontaktinfo@tfcz.ch
Sport-KontextWM/SM/Nati/Bundesliga-Sieger im Verein · Zürich Open (Heim-Turnier) · Pro Turniere STF Regio/STRT · Elite = ITSF

Interne GV-/Finanzzahlen (Umsatz, Zahlungsarten) sind nicht öffentlich. Kommuniziert werden nur Teilnahmezahlen.

11

Playbook: neue Seite in 7 Schritten

Der rote Faden, der Teil 1 (Marke) und Teil 2 (Design) verbindet. Damit baut jeder ohne Spezialist eine markenkonforme Landingpage.

  1. Ziel & Zielgruppe wählen (Sektion 05): Für wen ist die Seite, was soll sie auslösen?
  2. Kernbotschaft festlegen (Sektion 07). Genau eine der vier Pillars trägt die Seite.
  3. Ton wählen (Sektion 06), passend zum Kontext.
  4. Fakten einsetzen (Sektion 10): Zahlen/Adresse/Preise übernehmen, nichts erfinden.
  5. Textbausteine nutzen (Sektion 04): Claim, Boilerplate, Signatur-CTA.
  6. Design bauen (Teil 2): Farben, Typo, Signatur, Buttons, Nav/Burger, Karten, Hintergrund-Modul.
  7. Gegen den Guide prüfen: Kommt jedes Element aus einem bestehenden Baustein, stimmen Farben, Brand-Line und Ton? Erst dann ausliefern.
Teil 2 · Design

Visuelles System

Die Regeln, mit denen die Marke sichtbar wird: Farben, Logo, Schrift, Tokens, Brand-Line, Hover und Auswahl, Icons, Hintergründe und Print/Social. Wer nur einen Flyer macht, braucht Teil 1 und diesen Teil.

12

Farben & Gradients

Kanonische Palette mit Primär- und Neutralfarben, Sekundär-Akzenten und den vier erlaubten 2-Farb-Gradients. Klicken kopiert.

Primär & Neutral
Sekundär-Akzente
Kategorie-Farben (Tokens verweisen auf die Palette oben)
Ausnahme: Livestream

Nur in den Stream-Grafiken (Spielstatistik, Scoreboard, Ballbesitz). Die zwei Teams spielen in Grün und Schwarz, darum tragen die Zahlen die Teamfarbe. Überall sonst gelten die Farben oben.

Team Grün
#57a639
Team-Name, Zahlen
Team Schwarz
#0c0f14
Team-Name, Zahlen
Gradients (max. 2 Farben, primär für Hintergründe)
Blue · #005a94→#4489c7
Blue-Gold · #4489c7→#cda857
Dark Navy · #0d273d→#005a94
Light B-G · #5ca7dc→#e9c475
Werte: Bildschirm = HEX/RGB · Druck = CMYK/Pantone
Blau Primär

Das Blau der Marke: Brand-Line oben, Kategorie «Fun», Akzente

HEX
#5ca7dc
RGB
92 · 167 · 220
Logo-Blau

Logo im Druck, Icon-Ringe, Aktions-Buttons

HEX
#4489c7
RGB
68 · 137 · 199
CMYK
73 · 38 · 0 · 0
Blau dunkel

Kategorie «Pro», dunkle Blauflächen

HEX
#005a94
RGB
0 · 90 · 148
Navy

Dunkler Grund (intern), Text auf Gold und Sand

HEX
#0d273d
RGB
13 · 39 · 61
Gold Primär

Brand-Line unten, Auswahl, Conversion-CTA

HEX
#cda857
RGB
205 · 168 · 87
CMYK
21 · 32 · 78 · 0
Pantone
7563 C
Gold hell

Gold-Hover, goldene Schrift auf dunklem Grund

HEX
#e9c475
RGB
233 · 196 · 117
Sand / TFCZ Neutral

Helle Flächen: Print, Social und Präsentationen. Im Screen-UI nicht verwenden.

HEX
#e6e3da
RGB
230 · 227 · 218
CMYK
9 · 7 · 12 · 0

Klick auf eine Karte kopiert den HEX-Wert. Wo kein CMYK steht, gibt es die Farbe nur am Bildschirm.

So setzt du die Farben ein

Blau und Gold zusammen. Sie sind das Duo der Marke und treten immer gemeinsam auf.

Akzente höchstens 5 %. Grün, Orange und Rot nur neben Blau und Gold, nie allein.

Verlauf aus höchstens zwei Farben, nur die vier gezeigten, für Hintergründe. Nie im Logo, nie in Print- und Social-Brand-Lines.

Schwarz und Weiss für Schrift. Flächen sind Primärfarbe oder Neutral.

Kontrast (WCAG AA, mindestens 4,5 : 1)
Navy auf Gold6,8 : 1
Gold auf Navy6,8 : 1
Weiss auf Navy13,8 : 1
Halbtransparent auf Gold4,28 : 1

Kleine Schrift auf Gold ist immer voll deckendes Navy, nie mit opacity abgeschwächt. Vor dem Ausliefern die Farben gegen diese Werte prüfen.

13

Logos & Verwendung

Logo-Regeln nach TFCZ Brand Manual 2026. Nur die Originaldateien verwenden. Jede Variante lässt sich direkt hier herunterladen.

Varianten & Downloads

Primärlogo (Symbol und Wortmarke) ist der Standard. Das Wappen mit «TFCZ» nur bei wenig Platz und in Absprache mit dem TFCZ-Team. Das Symbol allein nur für Favicon und App-Icon. Jede Zeile zeigt eine Farbfassung, gewählt nach dem Hintergrund.

Blau, für hellen Grund

Primärlogo
Primärlogo
PNG
Wappen
Wappen
PNG
Symbol
Symbol
PNG

Farbig, für dunklen Grund

Primärlogo
Primärlogo
PNG
Wappen
Wappen
PNG
Symbol
Symbol
PNG

Schwarz, einfarbig auf hellem Grund

Primärlogo
Primärlogo
PNG
Wappen
Wappen
PNG
Symbol
Symbol
PNG

Schwarz-weiss, für dunklen Grund

Primärlogo
Primärlogo
PNG
Wappen
Wappen
PNG
Symbol
Symbol
PNG
Master-Pakete für Druck & Produktion (AI, EPS, PDF, PNG)
Welche Version auf welchem Hintergrund
Weiss / hell: die beste Umgebung fürs Logo. Blaue Plain-Version (Farbe) oder schwarze S/W-Version.
TFCZ Neutral & Grau bis 40%: Plain-Version (Farbe oder S/W), kein Graphic Device nötig.
TFCZ Blau: Version mit weissem Graphic Device. Blau ist als Logo-Hintergrund gegenüber Gold zu bevorzugen.
Navy / dunkler als 40% Grau: weisses Graphic Device + weisse Wortmarke (Farb- wie S/W-Einsatz).
TFCZ Gold: erlaubt (Graphic-Device-Version).
Schwarz-Weiss: einfarbige Reproduktion. Schwarz auf hellem Grund, auf dunklem Grund die Weiss-Device-Version. Nur die gelieferten S/W-Dateien verwenden (optisch justiert).
Schutzzone
Schutzzone

Freiraum-Regeln

1Horizontal: Freiraum = Höhe des Z aus der Wortmarke.
2Vertikal: Freiraum = Breite des Z.
3Symbol solo: Freiraum = Abstand zwischen Schildrand und Figur (linke Seite).

Innerhalb der Schutzzone stehen keine Texte, Bilder oder fremden Logos. Im Web ist das Kopf-Logo 38.8px hoch (Mobile 35.2px) und wird nie gestaucht (flex:none, Klick-Verhalten in Sektion 37).

Don'ts
Logo Verzerrt
Verzerrt
Logo Gedreht oder geneigt
Gedreht oder geneigt
Logo Umgefärbt
Umgefärbt
Logo Mit Schatten oder Effekt
Mit Schatten oder Effekt

Immer die Originaldatei nehmen und nur proportional skalieren. Keine andere Schrift in der Wortmarke, Symbol und Wortmarke nie gegeneinander verschieben. Genug Kontrast zum Hintergrund: siehe «Welche Version auf welchem Hintergrund» oben.

Pattern (Schild-Muster)
Auf Hellblau ist das Muster immer heller als der Hintergrund.
Auf Navy wirkt es subtil und verschmilzt mit dem Grund. Farbe anpassbar, aber nie dunkler als der Hintergrund. Print und digital erlaubt.
14

Typografie

Nunito Sans ist die einzige Schrift der Marke (Extralight bis Black verfügbar; im UI: 400 / 500 / 700 / 900). Fallback ohne Nunito Sans: moderne, geometrische Sans-Serif. Letterspacing nur bei UPPERCASE-Labels (.12–.22em). Kicker-Signature als Section-Eyebrow. Kursiv nur für Zitate, sonst nie: weder in Titeln noch in Buttons, Labels oder Fliesstext. 600 (Semibold) gibt es in der Familie, im Web nutzen wir es nicht; an der Kasse steht es für den normalen Text (Sektion 16).

Display · 900Tischfussball Club Zürich
Headline · 800–900Formate & Wettkampf
Subhead · 700Untertitel & Sektionsköpfe
Body · 400/500Standard-Fliesstext im Interface.
Label · 900 UPPERCASEEyebrow · Label
Kicker-SignatureTFCZ · Zürich
Schrift herunterladen

Nunito Sans ist frei nutzbar (SIL Open Font License). Das Paket enthält Regular, Bold und Black als TTF. Medium (500) für Fliesstext gibt es mit der ganzen Familie bei Google Fonts.

15

Tokens: Radius · Spacing · Elevation

Konsistente Skalen statt Zufallswerte. Andock-Elemente runden nur die oberen Ecken (6px 6px 0 0). Elevation nur für Schwebendes: e-2/e-3 gehören zu Modals, Popups, sticky Bars und Hover-Lift. Grosse ruhende Container (Sektionen, Inhalts-Boxen, Grids) tragen keine schweren Drop-Shadows (nur 1px-Border + inset-Highlight, max. e-1). Sonst fluten die Schatten die Lücken und decken das animierte Punktfeld zu.

Border-Radius · live aus tokens.css
Spacing · 4px-Raster
4
8
12
16
20
24
32
48
Motion-Tokens · live aus tokens.css
Elevation
e-1
e-2
e-3
inset-highlight
Technik: die Tokens-Datei

Farben, Radien, Schatten, Zeiten und Easing stehen an einer Stelle, in system/tokens.css. Die Datei lädt vor dem seiten-eigenen CSS. So sieht dieselbe Karte auf jeder Seite gleich aus.

Ladereihenfolge

<head>
  <link rel="stylesheet" href="system/tokens.css">   <!-- 1. Tokens -->
  <style> /* 2. nur noch Seiten-Spezifisches */ </style>
</head>

Kein :root in einer Seite. Referenz ist die Home. Ein geänderter Wert in tokens.css gilt sofort auf allen Seiten.

Regeln

  • Neue Tokens gehören in system/tokens.css, nie in eine Seite.
  • Eine bewusste Ausnahme pro Seite ist erlaubt (die Seite lädt zuletzt). Sie steht dort explizit, damit sie sichtbar und dokumentiert ist.
  • Vor dem Ausliefern zählen: Seiten mit eigenem :root, Soll 0.
  • Noch offen: die geteilte Klassen-Bibliothek (tfcz-base.css). Nur Klasse für Klasse mit Bildvergleich, ein Umbau in einem Schritt hat die Seiten zerlegt.
16

Grössen & Touch

Wie hoch ein Knopf ist, wie gross die Schrift, wie viel Platz dazwischen: je nach Gerät. Sichtbare Höhe und Tippfläche sind zwei Dinge. Ein Knopf darf kleiner aussehen, als er sich antippen lässt. Die Werte stehen als Tokens in system/tokens.css (--tap-min, --ctl-h…, --pos-…, --fs-…, --fw-…).

Web (Maus)Handy & iPadKasse (iPad 11" quer)
Kleinste Tippfläche36 px44 pxmind. 44 px, unsichtbar erweitert
Bedienelement sichtbar36 (klein) · 444834, alle gleich hoch
Listenzeilefrei4444
Hauptaktion525656 bis 64, volle Spaltenbreite
Schrift12 · 14 · 16 · 20 · 24 · 32wie Web14, Nebenzeilen 12, nie unter 11 · Beträge 24
Schriftstärke500 bis 700, Titel 900wie Web500 bis 600 · Hervorhebung 700 · Beträge 800 · nie 900
Abstände4 · 8 · 12 · 16 · 24 · 32
Live · so sieht die Kasse aus (gestrichelt = Tippfläche)
2 × Rivella rotCHF 9.00
1 × Tagesmitgliedschaft Nicht-MitgliedCHF 5.00
TotalCHF 14.00

Die Kategorien sind sichtbar 34 px hoch, tippen lässt sich jede auf 44 px. Die Zeilen sind 44 px, die Hauptaktion 64 px über die ganze Breite.

Regeln für alle Geräte

  • Tippfläche nie unter 44 px auf Touch-Geräten. Ist ein Knopf sichtbar kleiner, wird die Fläche unsichtbar erweitert (::before mit negativem inset).
  • Alle Bedienelemente in derselben Leiste sind gleich hoch.
  • Schrift und Abstände nur von der Skala. Zwischenwerte wie 11, 12.5, 13.5 oder 26 werden beim Umbau auf den nächsten Wert gerundet.
  • .btn-sm nur mit Maus. Auf Touch-Geräten gibt es ihn nicht.
  • Was man antippt, markiert man nicht. Knöpfe, Chips, Reiter, Schalter und Kacheln tragen user-select: none und -webkit-touch-callout: none. Sonst markiert ein Doppelklick die Beschriftung, und auf dem iPad öffnet langes Drücken die Lupe mit «Kopieren». touch-action: manipulation nimmt zusätzlich die 300 ms Pause des Doppeltipp-Zooms. Text, Zahlen, Codes und Adressen bleiben markierbar — sie will man kopieren.

Zusätzlich an der Kasse

  • Sichtbar 34 px, Listenzeile 44 px, Tippfläche immer mindestens 44 px. Grössere sichtbare Knöpfe (48 bis 64 px) wirken an der Kasse überdimensioniert. Einzige Ausnahme ist die Hauptaktion (Karte, Twint).
  • Schrift höchstens 14 px, Nebenzeilen 12 px, nie unter 11 px. Nur Beträge (Total) sind 24 px.
  • Nie Schriftstärke 900. Normal 500 bis 600, Hervorhebung 700, Beträge 800.
  • Nichts verschiebt sich beim Umschalten: feste Breiten, derselbe Rand in jeder Ansicht.
  • Lucide-Icons nie fett: die Strichstärke bleibt fest (vector-effect: non-scaling-stroke) und skaliert nicht mit.
  • Kein Hover: nur :active, keine Dropdowns, keine schwebenden Overlays.
  • Dialoge folgen denselben Massen wie die Oberfläche.
  • Nur Listen scrollen. Jede Aktion ist ohne Scrollen erreichbar.
  • Nur zwei Layouts: hoch und quer, keine weiteren Breakpoints.
Code für Entwickler
/* Kasse: sichtbar 34 px, tippbar 44 px */
.pos-ctl{height:var(--pos-ctl-h); position:relative; font-size:var(--fs-14); font-weight:var(--fw-normal)}
.pos-ctl::before{content:""; position:absolute; left:0; right:0;
  top:calc((var(--pos-ctl-h) - var(--tap-min)) / 2); bottom:calc((var(--pos-ctl-h) - var(--tap-min)) / 2)}
.pos-ctl:active{transform:scale(.96)}          /* kein :hover an der Kasse */
.pos-row{min-height:var(--pos-row-h)}
.pos-cta{height:var(--pos-cta-h); width:100%}
.pos-amount{font-size:var(--fs-24); font-weight:var(--fw-amount)}
/* Was man antippt, markiert man nicht (Text bleibt markierbar) */
.pos-ctl, .pos-cta, button, [role="button"]{
  -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; touch-action:manipulation}
/* Lucide: Strich bleibt fein, egal wie gross das Icon ist */
.pos-ctl svg *{vector-effect:non-scaling-stroke; stroke-width:2}
17

Fenster-Signatur / Brand-Line

Blau oben (#5ca7dc) / Gold unten (#cda857) als border-top/-bottom am abgerundeten Container. An den Ecken läuft die Linie schmaler aus. Bei genau einem Fenster oder sticky Bars: direkt umranden. Bei mehreren Boxen: nur einmal als Seitenrahmen, Boxen dezent.

Einzelnes Fenster

Trägt die Signatur direkt (border-top blau / border-bottom gold, ~3px).

Seite mit mehreren Boxen
Box A: dezent (frosted + 1px-Border)
Box B: dezent

Signatur nur einmal am Seitenrahmen.

18

Hover vs. Auswahl

Hover = Brand-Rahmen (blau oben / gold unten). Auswahl = Gold-Rahmen. Nie beide gleichzeitig am selben Element. „Heute" = goldene Zahl.

Hover mich
zeigt Brand-Rahmen
Ausgewählt
Gold-Rahmen
7
19

Hover-Brand-Rahmen · nie mit Layout-Sprung

Der Brand-Hover (blaue Linie oben / goldene unten) darf die Box-Grösse niemals verändern. Umgesetzt wird er deshalb mit inset box-shadow.

Richtig
Fahr mit der Maus drüberDer Rahmen erscheint, die Box bleibt gleich gross.
Inhalt darunter bleibt stehen
Falsch
Fahr mit der Maus drüberDer Rahmen macht die Box höher, alles darunter rutscht.
Inhalt darunter springt

Auf dem Handy: die Karte antippen.

Code für Entwickler
/* RICHTIG — nur box-shadow, Layout bleibt stabil, folgt der border-radius */
.el{ border:1px solid var(--card-brd); border-radius:8px; }
.el:hover{ border-color:transparent;
  box-shadow: inset 0 2px 0 var(--blue), inset 0 -2px 0 var(--gold); }
/* hat das Element schon eine Elevation-Shadow, kombinieren:
   box-shadow: inset 0 2px 0 var(--blue), inset 0 -2px 0 var(--gold), var(--e-1); */

/* VERBOTEN — border-Dicke im Hover ändert die Boxgrösse, Inhalt springt */
.el:hover{ border-top:2px solid var(--blue); border-bottom:2px solid var(--gold); }
/* ebenfalls verboten: border:0 im Grundzustand → border:2px im Hover
   (auch mit Padding-Kompensation tabu — fehleranfällig) */

Merksatz

Im :hover steht nie ein border-top/border-bottom mit px-Wert. Erlaubt ist nur box-shadow: inset … (plus optional border-color:transparent). Das gilt für den Zurück-Button, Accordion-Header, Chips, Tabs und Cards. Hover-Brand-Rahmen und Gold-Auswahlrahmen nie gleichzeitig am selben Element (Sektion 18). Vor dem Ausliefern jeden neuen Hover gegen Layout-Sprung testen.

20

Icons · Lucide

Keine Emojis, nirgends im Projekt. Jedes Icon kommt aus dieser Bibliothek. Auch Symbol-Glyphen wie Kreuz, Häkchen, Stern oder Pfeil-runter sind Emoji-Ersatz und verboten. Dafür gibt es x, check, star, download. War ein Zeichen reine Dekoration, wird es ersatzlos gestrichen. SVG braucht immer eine Grössenregel: ersetzt du einen Text-Glyph durch ein Lucide-SVG, muss der Button eine ausdrückliche Regel bekommen, etwa .mein-button svg{width:17px;height:17px;display:block;flex:none}. Ohne sie wird das Icon im Flex-Container auf 0 gequetscht und ist unsichtbar. Prüf deshalb nach jedem Icon-Tausch im Browser, ob das Icon sichtbar ist, das Markup allein sagt das nicht. Alle Lucide-Icons (ISC), geladen aus assets/icons/lucide/. Klick kopiert das SVG (stroke = currentColor).

Pfeile im Fliesstext («Kicker → Headline») sind Inhalt, kein Icon und bleiben erlaubt; ein „→" am Ende eines Links oder Buttons wandelt system/components/icons.js automatisch in das Lucide-Icon um.
Quelle: jedes Icon liegt als eigene Datei in assets/icons/lucide/<name>.svg, der Maschinen-Index (auch für KI) ist assets/icons/lucide-index.json mit name, tags, use.
Feste Icon-Bedeutungen · nicht für anderes verwenden

Diese Icons tragen im TFCZ eine feste Bedeutung (Kasse, Okapi, Website). Wer eines davon braucht, meint genau das. Für eine andere Bedeutung ein anderes Icon wählen. Neue feste Zuordnungen werden hier eingetragen, bevor sie gebaut werden.

Personen & Status

  • user-keyKeyplayer
  • user-starMitglied (auch der Artikel «Mitgliedschaft»)
  • user-plusGast
  • usersHelfer (Zahlungsart «Helfer» am Event)

Kassen

  • TFCZ-WappenClubkasse (TFCZ-Wappen als Outline, kein Lucide)
  • hand-coinsExpresskasse
  • calendarEventkasse
  • calendar-checkgeöffnetes Event
  • log-inEvent öffnen

Zustand eines Töggelers (Kachel & Filter)

  • banknote-xEintritt fehlt
  • trophyTurnier (spielt mit, Turnier-Artikel, Gruppe «Turniere»)
  • shopping-cartBetrag offen / Warenkorb
  • door-openAnsicht «Offen» (noch nicht bezahlt)
  • banknote-checkAnsicht «Bezahlt»

Bezahlen

  • credit-cardKarte (SumUp)
  • smartphoneTwint
  • receiptAuf Rechnung / Eventabrechnung
  • circle-checkAls bezahlt markieren (Betrag 0.–)
  • ticket-percentGutschein
  • hand-heartTrinkgeld-Hinweis
  • frownkein Trinkgeld

Aktionen

  • plushinzufügen (immer goldener Knopf)
  • minuseins weniger
  • xschliessen, entfernen, Suche löschen
  • searchsuchen
  • arrow-leftzurück
  • starFavorit
  • layout-gridalle Kategorien
  • lockabschliessen (Club, Event), gesperrt
  • settingsEinstellungen
  • log-outabmelden
  • rotate-cwneu laden
  • triangle-alertWarnung
  • wifi-offVerbindung weg
  • historyInaktivität («Noch da?»)

Sortiment · Kategorien

  • cup-sodaAlkoholfrei
  • beerBier
  • wineWein & Spirituosen
  • utensils-crossedEssen & Snacks
  • packageMaterial
  • trophyTurniere
  • user-plusEintritt & Mitgliedschaft

Sortiment · Artikel

  • faucetWasser
  • citrusPanaché
  • coffeeKaffee/Tee
  • beerBier
  • bottle-wineFlasche Wein
  • wineWein
  • martiniSchnaps
  • sandwichSandwich
  • hand-platterRaclette
  • croissantGipfeli
  • cake-sliceGebäck
  • candySchoggi
  • appleFrüchte
  • volleyballBall
  • ribbonGriffband
  • flameFeuerzeug
  • door-openEintritt
  • glass-waterAlkoholfrei / Shot
Noch zu klären: doppelt belegt
  • user-plusGast und Kategorie «Eintritt & Mitgliedschaft».
  • door-openArtikel «Eintritt» und Ansicht «Offen» (noch nicht bezahlt).
  • glass-waterAlkoholfrei oder Shot, unklar. Die Kategorie «Alkoholfrei» ist schon cup-soda.

Bis das entschieden ist, kein weiteres Element mit diesen Icons bauen.

Darstellung: Lucide, Strich fest (nicht mitskalierend), nie fett, Farbe = Textfarbe.

21

Hintergrund-Animationen & Modi

Zwei Seiten-Hintergründe: Hellblau (öffentlich) & Dunkel-Navy (login-intern). Standard-Effekt ist der Schwerkraft-Wirbel aus dem globalen Modul assets/js/bg-swirl.js. Idle-Verhalten: der Wirbel wirbelt nur bei Mausbewegung. Stoppt die Maus (~140ms), kehren die Punkte langsam & smooth per Genie-Feder (0.010 / 0.91) zurück, und der Loop hält an. Einen Dauer-Idle-Wirbel gibt es nicht. prefers-reduced-motion respektieren. Floating Lines (~116°) bleiben als Alternative erlaubt (Snippets: 01_Web/hintergrund-spielwiese.html). Umschalter oben im Kopf.

Modus (strikt)

Navy = nur interne, login-geschützte Tools (POS, Bar-Display, Livestudio, Design Studio, Cockpit, Admin). Hellblau = jede öffentliche Seite, dort gibt es keinen Umschalter und keinen Dark Mode.

Einbindung (Pflicht)

Jede Seite bindet nur <script defer src="assets/js/bg-swirl.js"></script> ein. Das Modul erstellt Canvas + CSS selbst. Nie inline kopieren, Inline-Kopien veralten. Boxen bleiben dunkel & frosted, ohne schwere Drop-Shadows (Sektion 15).

22

Print, Social & Icons

Flyer, Stories, Posts und Schilder haben ihren eigenen Look. Er kommt aus den echten Vorlagen des Clubs: dem KäseKick-Flyer und den Schildern im Clublokal.

Runde Icons: die Verlaufs-Ausnahme
Insta-Highlights, Profil- & WhatsApp-Bilder: Ring = vertikaler Verlauf #4489c7 → #cda857 (Logo-Blau, nicht #5ca7dc), flach ohne Glanz/3D. Aussenkante 538px · Ringbreite 32px. Scheibe: Radial #005a94 → #0d273d. Icons weiss, ohne Text, auf gleiche Höhe normalisiert. Nur der Hauptchat trägt das Logo statt eines Icons.
Die Zutaten

Hellblau-Verlauf als Grund für Flyer, Story und Post

Blau-Verlauf als Grund für Schilder

Foto läuft unten in Navy aus

Wappen als Wasserzeichen, rund 20 %

Brand-Line: abgerundete Balken, oben blau, unten gold

Komm allein!

Gold-Verlauf nur auf dem wichtigsten Element

tfcz.ch

Schilder: Navy-Fuss mit Goldlinie, Logo weiss

KäseKick

Titel in Versalien, zweites Wort in Gold

Farbwerte für Gestalter
WasWerte
Hellblau-Verlauf (Flyer, Story, Post)#d3eafa → #a8d3f1 → #7fbbe6 → #5ca7dc → #4489c7, von oben nach unten
Blau-Verlauf (Schilder)#005a94 → #2f73b0 → #4489c7, schräg
Gold-Verlauf (Badge)#f3dea4 → #e9c475 → #cda857, mit weissem Ring
Navy (Foto-Auslauf, Schild-Fuss)#0d273d
FarbraumDruck in CMYK, digital in RGB
Formate
1080 × 1080
Post / FlyerDesign Studio
1080 × 1920
StoryMarketingverantwortlicher
1280 × 720
YouTube-ThumbnailDesign Studio
1920 × 1080
Stream-OverlayLivestudio (Sektion 111)
rund
Highlight- und Profil-IconMarketingverantwortlicher
23

Fotos & Galerie

Alle Vereinsfotos liegen in der Galerie unter design.tfcz.ch/galerie. Dort filterst du nach Kategorie, Turnier, Ort und Trikot und lädst jedes Bild direkt herunter.

Schlumpf zuerst

Ein Schlumpf-Foto zeigt das blaue TFCZ-Trikot mit erkennbarem Logo oder Schriftzug. Ein blaues Shirt ohne Vereinslogo zählt nicht. Für alles, was den Verein nach aussen zeigt, also Website, Posts und Flyer, suchst du zuerst ein Schlumpf-Foto. In der Galerie findest du sie über den Filter «Shirt: Schlumpf».

Ein Bild ohne Trikot nimmst du, wenn es kein passendes Schlumpf-Foto gibt oder das Motiv keins braucht, etwa Location, Detail oder Publikum.

Nie nach aussen

Fotos mit dem Tag schwach: Sie sind unscharf oder zu dunkel.

Bilder, auf denen Spieler anderer Clubs gross im Fokus stehen und nichts auf den TFCZ verweist.

Welches Foto wofür
ZweckKategorie in der GalerieWorauf achten
Flyer-HintergrundDetail & Equipment (Tisch-Nahaufnahme, spielende Hände)Bei 40 bis 60 % Deckkraft unter den Text legen, damit er lesbar bleibt.
Event-AnkündigungAtmosphäre & Publikum, Location & BarZeigt, wie sich der Abend anfühlt. Ein volles Lokal wirkt stärker als ein leeres.
Ergebnis-PostMannschaftsfotos, Tag siegerehrungSchlumpf-Foto zuerst, Gesichter gut erkennbar.
Einladung ins TrainingTurnier & Match, Team & EmotionenEchte Spielszenen, Schlumpf-Foto zuerst.
Website-HeroNur eigene Nikon-FotosImmer in der Auflösung full.
Auflösung

Jedes Foto gibt es zweimal. full (rund 1600 px) nimmst du für grosse Flächen wie Hero, Flyer und Story, thumb für kleine Kacheln, Feeds und Vorschauen. Neue Fotos kommen nur über den Foto-Ablauf in die Galerie, dabei werden Kategorie, Anlass und Ort gesetzt (Sektion 24, «Technik: Bilder auf der Website»).

Technik: Bilder auf der Website und Foto-Manager

Grundregel: Inhaltsbilder werden nie fest im HTML/CSS verdrahtet. Jedes Bild lebt im Galerie-CMS als WebP in zwei Auflösungen: assets/fotos/galerie/full/… (grosse Darstellung, ~1600px: Hero, Sektionsbilder) und assets/fotos/galerie/thumb/… (klein: Strips, Feeds, Karussell-Kacheln). Roh-JPG/PNG gehören nicht direkt in Seiten. Ausnahme: Logos/Embleme/Muster = geteilte Assets in assets/img/.

Wie Bilder auf die Seite kommen (Slots)

<!-- src wird beim Veröffentlichen aus mapping.json ins HTML gebacken (echtes Bild, kein Platzhalter) -->
<img data-slot="index.hero" class="hc-img" src="assets/fotos/galerie/full/795.webp" alt="…">
<div data-slot="index.bg-bar"></div>   <!-- Hintergrund-Slot -->

system/tfcz-images.js setzt zur Laufzeit Bild bzw. Background aus assets/fotos/galerie/mapping.json; system/tfcz-carousel.js macht dasselbe für Karussells. mapping.json ist die Laufzeit-Quelle der Wahrheit (Slot → Bild), slots.json hält die Slot-Definitionen, galerie.json den Foto-Katalog. Beim Veröffentlichen bäckt _tools/bake-slots.mjs das gemappte Bild direkt ins src jeder Slot-<img>. So lädt sofort das richtige Bild, ohne Flash und ohne Doppel-Laden; tfcz-images.js ist dann nur noch Fallback.

Bilder ändern nur über den Foto-Manager

  1. Foto-Manager öffnen: assets/fotos/galerie/foto-manager.html.
  2. Pro Slot ein Galerie-Foto wählen, in der Auflösung full oder thumb.
  3. Speichern → schreibt mapping.json (oder „mapping.json exportieren“ und in assets/fotos/galerie/ ersetzen).
  4. node _tools/bake-slots.mjs laufen lassen. Das bäckt die Bilder ins src.
  5. Veröffentlichen, damit es live erscheint.

Neue Fotos kommen ausschliesslich über den tfcz-fotos-Skill in die Galerie: er erzeugt full+thumb-WebP, katalogisiert in galerie.json und fragt vorher nach Event/Kategorie.

Nie

Inhaltsbild-Pfad fest im HTML/CSS Slot-Bild ausserhalb von galerie/ mapping.json von Hand umbiegen, wenn der Foto-Manager es kann
Teil 3 · Komponenten

Bausteine für Bildschirme

Alle 120 Bausteine der Website und des Design Studios, live, mit ihren Regeln und «HTML kopieren» oben rechts. Sie stehen in acht Kapiteln; ein Klick öffnet ein Kapitel, das Inhaltsverzeichnis links springt direkt hinein. Aussehen und Verhalten kommen aus system/ui.css, ui.js, charts.js und effects.js, maschinenlesbar stehen alle in system/komponenten.json.

Aktionen & ButtonsKnöpfe, Links und alles, was eine Handlung auslöst.12 Bausteine
24

CTA-System · Buttons

Wenige Typen, mehr braucht die Seite nicht. Jeder hat genau eine Bedeutung, damit jeder (auch Dritte) weiss, welcher Button wohin gehört und Gold sich nie abnutzt. Alle Buttons kommen aus einem Baustein system/buttons.css; Grösse und Radius stehen als Token in system/tokens.css (siehe unten). Der Text bleibt im HTML.

Hover: Gold, Blau, Rot und Grün heben sich leicht an und leuchten in ihrer Farbe. Ghost-Buttons zeigen den Brand-Rahmen (Sektion 19), Text-Links einen Gold-Unterstrich.

Gold.btn-gold

Conversion & Lead: die eine wichtigste Handlung, also jemanden in den Verein holen oder eine qualifizierte Anfrage auslösen. Nie für reine Navigation („Alle Regeln", „Komm vorbei") oder Formular-Absenden.

Blau.btn-blue

Primär-Aktion: die normale „Jetzt machen"-Handlung wie absenden, öffnen, erstellen, weiter. Darf mehrfach vorkommen.

Ghost.btn-ghost

Navigation & Sekundär: weiterführende oder Nebenhandlungen. Steht oft neben Gold/Blau und tritt bewusst zurück.

Zurück & Nach oben.btn-blue

Zurück-Führung: zur Website, eine Ebene zurück oder nach oben. In Blau, damit sie sofort als Knopf erkennbar ist. «Zur Website» steht im Kopf ganz rechts.

Text.btn-text

Das Training ist am Dienstag, weitere Infos stehen auf der Trainingsseite.

Unterstrich .btn-text: im Fliesstext und überall, wo der Link mitten im Satz steht. Der Unterstrich ist immer da, damit man den Link auch auf dem Handy ohne Hover erkennt; beim Hover wird er voll.

Pfeil im Kreis .btn-text.t-arrow: allein stehend als Schluss unter einer Karte oder einem Abschnitt. Beim Hover füllt sich der Kreis und der Pfeil rückt nach rechts. Nie im Fliesstext.

Rot.btn-danger

Gefahr & Löschen: nur unumkehrbare/verwerfende Handlungen. Selten, damit Rot ein echtes Warnsignal bleibt.

Grün.btn-success

Bestätigen & Hinzufügen: positive Bestätigung (nutzt das Brand-Grün --green).

Icon.btn-icon

Nur-Icon-Button, immer rund. Feste Farb-Semantik: Schliessen/X = Rot (.i-close), Hinzufügen/+ = Grün (.i-add). Sonst neutral (Ghost) oder Blau.

Modifier

.btn-sm = kompakter (Toolbars, „mehr laden"). .btn-icon = rund, nur Icon. Beide an jeden Typ anhängbar.

Social.btn-social

Social-Buttons sind im Ruhezustand immer neutral (frosted) und nehmen ihre Netzwerk-Farbe erst beim Hover an. Fremdfarben passen nicht als Grundfläche ins Brand. Klassen .s-wa / .s-ig / .s-yt / .s-li.

Kein Button

Chips & Toggles sind keine Buttons. Filter-Chips, Sprach-/Ansicht-Umschalter und Profil-Chips behalten bewusst die Pill-Form und tragen kein .btn. Sie folgen --btn-radius nicht. So bleibt der Unterschied „Handlung" (Button) vs. „Auswahl/Filter" (Chip) sichtbar.

Radius/Grösse zentral ändern: system/tokens.css → --btn-radius (Standard 12px), --btn-pad-y/x, --btn-font. Eine Änderung dort gilt auf allen Seiten und hier im Brand Guide. Der Icon-Button bleibt rund.

Social-Icons (rund)

Die kleine Form von .btn-social, wenn nur das Icon Platz hat (Footer, Über uns). Weisser Kreis mit Tooltip, beim Hover in der Plattform-Farbe.

25

Button Group

Zusammengehörige Aktionen in einer Leiste. Die Buttons teilen sich den Rahmen.

Code für Entwickler

Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">

<!-- Button Group -->
<div class="k-bgroup" role="group" aria-label="Event bearbeiten">
  <button class="btn btn-ghost btn-sm" type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
    <path d="m15 5 4 4" /></svg> Bearbeiten</button>
  <button class="btn btn-ghost btn-sm" type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
    <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></svg> Duplizieren</button>
  <button class="btn btn-ghost btn-sm btn-icon" type="button" aria-label="Weitere"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="1" />
    <circle cx="19" cy="12" r="1" />
    <circle cx="5" cy="12" r="1" /></svg></button>
</div>
26

Toggle

Ein Knopf, der an oder aus ist. Aktiv trägt er den goldenen Rahmen wie jede Auswahl.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Toggle -->
<button class="k-toggle" type="button" aria-pressed="false"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" /></svg> Nur Favoriten</button>
27

Kbd

Tastenkürzel im Text, als kleine Tasten gesetzt.

Suche öffnen mit Cmd K

Code für Entwickler

Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">

<!-- Kbd -->
<p class="k-p">Suche öffnen mit <kbd class="k-kbd">Cmd</kbd> <kbd class="k-kbd">K</kbd></p>
Animierte Buttons
28

Rolling Text

Beim Überfahren oder Fokussieren rollt die Beschriftung nach oben weg, eine Kopie rollt von unten nach. Für die wichtigste Aktion einer Seite.

Code für Entwickler

Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">

<!-- Rolling Text -->
<button class="btn btn-blue k-roll" type="button" aria-label="Fürs Training anmelden"><span class="k-roll-in" aria-hidden="true"><span>Fürs Training anmelden</span><span>Fürs Training anmelden</span></span></button>
29

Staggered Rolling Text

Wie Rolling Text, aber Buchstabe für Buchstabe mit kleiner Verzögerung. Gold, weil es hier um die Mitgliedschaft geht.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Staggered Rolling Text -->
<button class="btn btn-gold k-roll-st" type="button" data-stagger="Mitglied werden" aria-label="Mitglied werden">Mitglied werden</button>
30

Add to Basket

Nach dem Klick fliegt ein Ball im Bogen zum Zähler, der Zähler federt, der Knopf bestätigt in Grün.

Plausch-Turnier

Freitag, 9. Oktober

0Anmeldungen
Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Add to Basket -->
<div class="k-atb k-w400">
  <div class="k-item k-grow"><span class="k-item-ic"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14.66v1.626a2 2 0 0 1-.976 1.696A5 5 0 0 0 7 21.978" />
    <path d="M14 14.66v1.626a2 2 0 0 0 .976 1.696A5 5 0 0 1 17 21.978" />
    <path d="M18 9h1.5a1 1 0 0 0 0-5H18" />
    <path d="M4 22h16" />
    <path d="M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z" />
    <path d="M6 9H4.5a1 1 0 0 1 0-5H6" /></svg></span><div class="k-grow"><b>Plausch-Turnier</b><p class="k-muted">Freitag, 9. Oktober</p></div>
    <button class="btn btn-blue btn-sm k-atb-btn" type="button" data-done="Angemeldet"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" />
    <path d="M12 5v14" /></svg><span>Anmelden</span></button></div>
  <span class="k-atb-bag" aria-live="polite"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
    <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
    <path d="M12 11h4" />
    <path d="M12 16h4" />
    <path d="M8 11h.01" />
    <path d="M8 16h.01" /></svg><b class="k-atb-n">0</b><span class="sr-only">Anmeldungen</span></span>
</div>
31

Hold to Confirm

Für Handlungen, die sich nicht rückgängig machen lassen: gedrückt halten, bis der Balken voll ist. Loslassen bricht ab. Geht auch mit Leertaste oder Enter.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Hold to Confirm -->
<button class="btn btn-ghost k-hold" type="button" data-done="Gelöscht" data-done-toast="Eintrag gelöscht"><span class="k-hold-fill" aria-hidden="true"></span><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 11v6" />
    <path d="M14 11v6" />
    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
    <path d="M3 6h18" />
    <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg><span class="k-hold-t">Gedrückt halten zum Löschen</span></button>
32

Anziehungs-Button

Beim Überfahren ziehen sich kleine Punkte in die Mitte des Knopfs, beim Verlassen treiben sie wieder weg.

Code für Entwickler

Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/effects.js"></script>

<!-- Anziehungs-Button -->
<button class="btn btn-blue fx-attract" type="button" data-fx="attract"><span class="fx-attract-dots" aria-hidden="true"></span><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 15 4 4" />
    <path d="M2.352 10.648a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l6.029-6.029a1 1 0 1 1 3 3l-6.029 6.029a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l6.365-6.367A1 1 0 0 0 8.716 4.282z" />
    <path d="m5 8 4 4" /></svg> Mitmachen</button>
33

Partikel-Button

Ein Klick lässt Punkte in Blau und Gold aus dem Knopf springen, der Zähler steigt.

Code für Entwickler

Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/effects.js"></script>

<!-- Partikel-Button -->
<button class="btn btn-ghost fx-part" type="button" data-fx="particles"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.8 11.3 2 22l10.7-3.79" />
    <path d="M4 3h.01" />
    <path d="M22 8h.01" />
    <path d="M15 2h.01" />
    <path d="M22 20h.01" />
    <path d="m22 2-2.24.75a2.9 2.9 0 0 0-1.96 3.12c.1.86-.57 1.63-1.45 1.63h-.38c-.86 0-1.6.6-1.76 1.44L14 10" />
    <path d="m22 13-.82-.33c-.86-.34-1.82.2-1.98 1.11c-.11.7-.72 1.22-1.43 1.22H17" />
    <path d="m11 2 .33.82c.34.86-.2 1.82-1.11 1.98C9.52 4.9 9 5.52 9 6.23V7" />
    <path d="M11 13c1.93 1.93 2.83 4.17 2 5-.83.83-3.07-.07-5-2-1.93-1.93-2.83-4.17-2-5 .83-.83 3.07.07 5 2Z" /></svg> Tor! <b class="fx-count">12</b></button>
34

Tastenkürzel-Button

Knopf mit Tastenkürzel und Glanz beim Überfahren. Cmd K oder Ctrl K löst ihn auch aus.

Code für Entwickler

Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/effects.js"></script>

<!-- Tastenkürzel-Button -->
<button class="btn btn-ghost fx-cmd" type="button" data-fx="cmd"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34" />
    <circle cx="11" cy="11" r="8" /></svg> Suchen <kbd class="k-kbd">Cmd</kbd><kbd class="k-kbd">K</kbd></button>
35

Teilen-Button

Beim Überfahren klappen die Kanäle nacheinander aus dem Knopf. Auf dem Handy per Antippen.

Code für Entwickler

Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/effects.js"></script>

<!-- Teilen-Button -->
<div class="fx-share" data-fx="share"><button class="btn btn-ghost" type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="5" r="3" />
    <circle cx="6" cy="12" r="3" />
    <circle cx="18" cy="19" r="3" />
    <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" />
    <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></svg> Teilen</button><span class="fx-share-row"><button type="button" aria-label="Per WhatsApp teilen" data-done="WhatsApp geöffnet"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" /></svg></button><button type="button" aria-label="Per E-Mail teilen" data-done="E-Mail geöffnet"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" />
    <rect x="2" y="4" width="20" height="16" rx="2" /></svg></button><button type="button" aria-label="Link kopieren" data-done="Link kopiert"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
    <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></svg></button></span></div>
Navigation & SeitengerüstKopf, Menü, Footer und die Wege durch eine Seite.13 Bausteine
36

Menü: Burger & Aufklappen

Überall derselbe Menü-/Listen-Trigger: drei eng gestapelte Zeilen in Blau · Weiss · Gold. Wechselt nie zu einem X (öffnet eine Liste/ein Menü). Animation = Scroll-Loop mit Bounce (Hover & Klick): die Zeilen scrollen gestaffelt nach unten weg und kommen oben smooth wieder rein. Referenz: .nav .burger in index.html.

Beim Öffnen fallen die Menü-Einträge gestaffelt von oben mit leichtem Bounce herein. Auslöser = Scroll-Loop-Burger. Referenz: .drawer in index.html.

37

Logo · Kopf / Menü

Immer dasselbe Logo (assets/img/logo-horizontal-white.png) im Kopf/Menü jeder Seite. Klick führt zur Home. Klick-Feedback = kurze Press-Bounce-Animation (scale runter → kleiner Bounce → zurück). Klick zum Ansehen:

Logo (klickbar)
39

Seitengerüst & Navigation

Jede Seite nutzt dasselbe Gerüst, die Home genauso wie jede Unterseite/Landingpage. Abweichende Breiten oder „nur auf der Home"-Verhalten sind verboten. Das sticky Menü ganz oben in dieser Datei ist die Referenz.

So ist jede Seite aufgebaut
  1. Kopf bleibt beim Scrollen oben. Logo links, «Zur Website» rechts, unten die Brand-Line.
  2. Breite höchstens 1160 px, mittig. Gleich auf jeder Seite.
  3. Abschnitt beginnt mit Kicker (Goldstrich), Titel und kurzer Einleitung.
  4. Footer so breit wie der Inhalt.

Funktioniert auf Handy, Tablet und Computer, ohne seitliches Scrollen. Auf dem Handy öffnet der Burger das Menü. Logo-Klick, Burger und Menü sind überall gleich.

Section-Kopf: Kicker → Headline → Lead (+ Reveal beim Scrollen)
Mitspielen
Werd einer von uns

Sektionen starten mit Eyebrow-Kicker (Goldstrich), Headline 900 und Lead-Text. Beim Scrollen gibt es ein Reveal mit opacity 0→1, translateY(28px)→0, gestaffelt (+0.1s je Stufe). prefers-reduced-motion respektieren.

Technische Masse für Entwickler
Masse & Aufbau
ElementRegel
Inhaltsbreite.wrap{max-width:1160px; padding:0 22px} ist überall identisch, auch im Footer (.fwrap). Nie 1000/1080px o. ä.
Sticky-MenüFluchtet mit .wrap via padding:12px max(22px, calc((100% - 1160px)/2 + 22px))unten abgerundete Ecken 0 0 16px 16pxBrand-Line ~3px (der goldene Streifen läuft über die Rundung schmaler aus). Sticky Bars tragen die Signatur immer, unabhängig von der Boxen-Anzahl.
Anordnung1 · Logo ganz links (flex:none, Link zur Home) · 2 · Nav-Links direkt daneben, links eingerückt (margin-left:18px, nicht über die Breite verteilt) · 3 · ganz rechts immer „Zur Website" mit Pfeil-Icon links (margin-left:auto, Ghost-Stil, nie Gold an dieser Position).
Nav-LinksDie eigene In-Page-Navigation der Unterseite (Anker auf Sektionen dieser Seite, keine Home-Sektionen). Hover = Gold-Unterstrich. ≤860px: Links ausblenden, Zurück-Button bleibt.
Logoassets/img/logo-horizontal-white.png (Sektion 37), Höhe 38.8px (Mobile 35.2px), nie komprimiert. Klick = Press-Bounce, Navigation ~230ms verzögert.
GlobalLogo-Press, Burger „Scroll-Loop", Drop-Cascade-Menü und Brand-Signatur existieren auf jeder Seite identisch. Beim Bauen einer neuen Seite den Nav-Baustein 1:1 aus index.html übernehmen.
Hintergrund<script defer src="assets/js/bg-swirl.js"></script> einbinden, nie pro Seite inline duplizieren (Sektion 21).
Responsive- & Interaktions-Pflichten
  • Voll responsive mit den Referenz-Breakpoints 640 / 1024 / 1400px · POS/Touch: dense but tappable (Listen/Grids statt grosser Cards, iPad-first)
  • Nie ein horizontaler Scrollbalken: Flex-Textkinder bekommen min-width:0, vertikale Scroll-Container explizit overflow-x:hidden
  • Jedes Popup schliesst per Backdrop-Klick, Klick neben dem Inhalt und Esc (Sektion 62)
  • Öffentliche Seiten = Hellblau-Hintergrund ohne Umschalter · Login-Bereiche = Navy · Boxen bleiben dunkel & frosted (Sektion 21)
Technik: der Navigations-Baustein

Ein Nav-Verhalten für alle Seiten, gebaut als Baustein system/components/nav.js. Der Kopf der Seite bleibt, alles andere kommt vom Baustein.

So baut man es ein

<nav class="nav">
  <a class="logo" …></a>
  <div class="navlinks"><a class="lnk" href="#sektion">…</a></div>
  <a class="back" href="https://tfcz.ch/">Zur Website</a>
</nav>

<script src="system/content.js"></script>
<script defer src="system/components/portal.js"></script>
<script defer src="system/components/nav.js"></script>

Burger, Scrim und Drawer baut der Baustein selbst. Die Menü-Struktur steht einmal in system/content.js (TFCZ.content.menu).

Regeln: Nav-Links

  • Hover = leichte hellblaue Fläche (rgba(92,167,220,.16)), kein Strich.
  • Strich = nur die Sektion, in der man gerade ist (Scroll-Spy): aktiv ist die letzte Sektion, deren Oberkante unter der Menüleiste durchgelaufen ist. Oben im Hero: kein Strich. Sonst ist genau einer sichtbar. Statische „active"-Marker sind verboten.
  • Ausrichtung: Home (Links rechts) → goldener Strich unten · Unterseiten (Links links neben dem Logo) → blauer Strich oben. Der Strich sitzt am Wort, nicht an der Leistenkante.
  • Gleich auf allen Seiten: Nav-Höhe, z-index:90, 13.5px, nie unterstrichen.

Regeln: Burger & Drawer

  • Burger auf jeder Seite, immer als äusserstes Element rechts. Der Zurück-Knopf steht dadurch links davon. Der Burger wird nie zum X („Scroll-Loop", Sektion 36).
  • Drawer klappt als Akkordeon nach unten (nie von rechts reinfahren), hängt unter der goldenen Linie, nimmt die volle Resthöhe, rechte Kante bündig mit dem Seiteninhalt.
  • Zugeklappt ist zugeklappt: auch Rand und Schatten müssen weg, sonst bleibt ein goldener Balken unter der Leiste stehen.
  • Schliessen (X · Esc · Scrim · Link) läuft immer über den Portal-Baustein (Sektion 62).
  • CTAs im Drawer: Login = Ghost · Registrieren = Blau · „Mitglied werden" = Gold.

Pfeile & Scroll

  • Ein Pfeil überall: Lucide arrow-right, Hover 3px nach rechts (.22s). system/components/icons.js ersetzt Text-Glyphen („→", „↗") automatisch am Rand von Links und Buttons. Pfeile im Fliesstext sind Inhalt und bleiben.
  • Scroll zu Anker: system/components/scroll.js mit ease-out-cubic (~760 ms), Stopp unter der Menüleiste. scroll-behavior:smooth gehört in keine Seite, es bremst doppelt.
Technik: Starter-Template für neue Seiten

Kopierfertiges Grundgerüst: korrekter Nav-Baustein (Logo links · Links eingerückt · Zurück rechts), 1160px-Raster, Brand-Tokens, Kicker-Sektion, Footer mit Brand-Line und bg-swirl-Einbindung. Damit startet jede neue Seite regelkonform.


  
Weitere Navigation
41

Navigation Menu

Hauptnavigation mit aufklappendem Bereich. Jeder Eintrag erklärt in einer Zeile, wohin er führt.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Navigation Menu -->
<nav class="k-nm" aria-label="Hauptmenü">
  <div class="k-nm-item k-pop-wrap"><button type="button" data-pop>Verein <svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg></button>
    <div class="k-pop k-nm-panel">
      <a href="#" onclick="return false"><b>Über uns</b><span>Wer wir sind und wie der Club tickt</span></a>
      <a href="#" onclick="return false"><b>Geschichte</b><span>Vom ersten Tisch bis zur Weltspitze</span></a>
      <a href="#" onclick="return false"><b>Mitglied werden</b><span>Beiträge und Anmeldung</span></a>
    </div></div>
  <a class="k-nm-link" href="#" onclick="return false">Training</a><a class="k-nm-link" href="#" onclick="return false">Medien</a>
</nav>
42

Menubar

Menüleiste wie in einem Programm, jedes Menü klappt beim Klick auf.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Menubar -->
<div class="k-mbar" role="menubar">
  <div class="k-pop-wrap"><button type="button" role="menuitem" data-pop>Datei</button><div class="k-pop k-menu" role="menu"><button type="button" role="menuitem" data-popclose>Neues Turnier</button><button type="button" role="menuitem" data-popclose>Exportieren</button></div></div><div class="k-pop-wrap"><button type="button" role="menuitem" data-pop>Bearbeiten</button><div class="k-pop k-menu" role="menu"><button type="button" role="menuitem" data-popclose>Rückgängig</button><button type="button" role="menuitem" data-popclose>Einfügen</button></div></div><div class="k-pop-wrap"><button type="button" role="menuitem" data-pop>Ansicht</button><div class="k-pop k-menu" role="menu"><button type="button" role="menuitem" data-popclose>Rangliste</button><button type="button" role="menuitem" data-popclose>Spielplan</button></div></div>
</div>
43

Context Menu

Menü per Rechtsklick auf eine Fläche, auf dem Handy per langem Druck.

Rechtsklick hier
Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Context Menu -->
<div class="k-ctx-wrap"><div class="k-ctx-area" tabindex="0"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 4.1 12 6" />
    <path d="m5.1 8-2.9-.8" />
    <path d="m6 12-1.9 2" />
    <path d="M7.2 2.2 8 5.1" />
    <path d="M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z" /></svg> Rechtsklick hier</div>
<div class="k-pop k-menu k-ctx" role="menu">
  <button type="button" role="menuitem" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" />
    <circle cx="12" cy="12" r="3" /></svg> Ansehen</button>
  <button type="button" role="menuitem" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 15V3" />
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
    <path d="m7 10 5 5 5-5" /></svg> Herunterladen</button>
  <hr class="k-sep">
  <button type="button" role="menuitem" class="k-danger" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 11v6" />
    <path d="M14 11v6" />
    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
    <path d="M3 6h18" />
    <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg> Entfernen</button>
</div></div>
44

Command

Befehlspalette: tippen, filtern, mit Enter ausführen. Gruppen trennen Seiten und Aktionen.

Seiten

Aktionen

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Command -->
<div class="k-cmd k-w400">
  <div class="k-cmd-in"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34" />
    <circle cx="11" cy="11" r="8" /></svg><input type="text" class="k-cmd-q" placeholder="Seite oder Aktion suchen" aria-label="Befehl suchen"></div>
  <div class="k-cmd-list">
    <p class="k-lbl">Seiten</p>
    <button type="button" class="on"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2v4" />
    <path d="M16 2v4" />
    <rect width="18" height="18" x="3" y="4" rx="2" />
    <path d="M3 10h18" /></svg> Kalender</button><button type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14.66v1.626a2 2 0 0 1-.976 1.696A5 5 0 0 0 7 21.978" />
    <path d="M14 14.66v1.626a2 2 0 0 0 .976 1.696A5 5 0 0 1 17 21.978" />
    <path d="M18 9h1.5a1 1 0 0 0 0-5H18" />
    <path d="M4 22h16" />
    <path d="M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z" />
    <path d="M6 9H4.5a1 1 0 0 1 0-5H6" /></svg> Rangliste</button><button type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
    <path d="M16 3.128a4 4 0 0 1 0 7.744" />
    <path d="M22 21v-2a4 4 0 0 0-3-3.87" />
    <circle cx="9" cy="7" r="4" /></svg> Mitglieder</button>
    <p class="k-lbl">Aktionen</p>
    <button type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" />
    <path d="M12 5v14" /></svg> Neues Turnier <kbd class="k-kbd">N</kbd></button><button type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
    <circle cx="9" cy="7" r="4" />
    <line x1="19" x2="19" y1="8" y2="14" />
    <line x1="22" x2="16" y1="11" y2="11" /></svg> Mitglied erfassen</button>
    <p class="k-empty-line" hidden>Nichts gefunden.</p>
  </div>
</div>
45

Sidebar

Seitenleiste einer App. Sie lässt sich auf eine Icon-Leiste zusammenklappen.

Übersicht

Drei Events diese Woche, zwei davon brauchen noch Helfer.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Sidebar -->
<div class="k-sb">
  <aside class="k-sb-side">
    <button class="k-sb-tgl" type="button" aria-label="Seitenleiste umschalten" aria-expanded="true"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" />
    <path d="M9 3v18" /></svg></button>
    <a href="#" onclick="return false" class="on" title="Übersicht"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
    <path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /></svg><span>Übersicht</span></a><a href="#" onclick="return false" class="" title="Events"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2v4" />
    <path d="M16 2v4" />
    <rect width="18" height="18" x="3" y="4" rx="2" />
    <path d="M3 10h18" /></svg><span>Events</span></a><a href="#" onclick="return false" class="" title="Turniere"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14.66v1.626a2 2 0 0 1-.976 1.696A5 5 0 0 0 7 21.978" />
    <path d="M14 14.66v1.626a2 2 0 0 0 .976 1.696A5 5 0 0 1 17 21.978" />
    <path d="M18 9h1.5a1 1 0 0 0 0-5H18" />
    <path d="M4 22h16" />
    <path d="M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z" />
    <path d="M6 9H4.5a1 1 0 0 1 0-5H6" /></svg><span>Turniere</span></a><a href="#" onclick="return false" class="" title="Kontakte"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
    <path d="M16 3.128a4 4 0 0 1 0 7.744" />
    <path d="M22 21v-2a4 4 0 0 0-3-3.87" />
    <circle cx="9" cy="7" r="4" /></svg><span>Kontakte</span></a><a href="#" onclick="return false" class="" title="Einstellungen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
    <circle cx="12" cy="12" r="3" /></svg><span>Einstellungen</span></a>
  </aside>
  <div class="k-sb-main"><b>Übersicht</b><p class="k-muted">Drei Events diese Woche, zwei davon brauchen noch Helfer.</p></div>
</div>
46

Profil-Menü

Menü hinter dem eigenen Avatar: Name, Mitgliedschaft und die wichtigsten Links, Abmelden zuletzt in Rot.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Profil-Menü -->
<div class="k-pop-wrap fx-profile">
  <button class="fx-profile-btn" type="button" data-pop aria-haspopup="menu"><span class="k-shield" style="background-image:url(assets/fotos/galerie/full/781.webp)"></span>Luca <svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg></button>
  <div class="k-pop k-menu" role="menu">
    <div class="fx-profile-head"><span class="k-shield" style="background-image:url(assets/fotos/galerie/full/781.webp)"></span><div><b>Luca Jost</b><span class="badge elite" style="font-size:11px; padding:3px 9px">Key Player</span></div></div>
    <hr class="k-sep">
    <button type="button" role="menuitem" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
    <circle cx="12" cy="7" r="4" /></svg> Profil</button>
    <button type="button" role="menuitem" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 17V7" />
    <path d="M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8" />
    <path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" /></svg> Meine Rechnungen</button>
    <button type="button" role="menuitem" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.268 21a2 2 0 0 0 3.464 0" />
    <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></svg> Benachrichtigungen</button>
    <hr class="k-sep">
    <button type="button" role="menuitem" class="k-danger" data-popclose><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 17 5-5-5-5" />
    <path d="M21 12H9" />
    <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /></svg> Abmelden</button>
  </div>
</div>
47

Werkzeugleiste

Leiste aus Icons. Das gewählte Werkzeug klappt federnd auf und zeigt seinen Namen.

Code für Entwickler

Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/effects.js"></script>

<!-- Werkzeugleiste -->
<div class="fx-tools" role="toolbar" aria-label="Werkzeuge" data-fx="toolbar"><button type="button" aria-pressed="true" aria-label="Auswählen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z" /></svg><span>Auswählen</span></button><button type="button" aria-pressed="false" aria-label="Text"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v16" />
    <path d="M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" />
    <path d="M9 20h6" /></svg><span>Text</span></button><button type="button" aria-pressed="false" aria-label="Bild"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
    <circle cx="9" cy="9" r="2" />
    <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" /></svg><span>Bild</span></button><button type="button" aria-pressed="false" aria-label="Form"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z" />
    <rect x="3" y="14" width="7" height="7" rx="1" />
    <circle cx="17.5" cy="17.5" r="3.5" /></svg><span>Form</span></button><button type="button" aria-pressed="false" aria-label="Kommentar"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z" /></svg><span>Kommentar</span></button></div>
48

Direction

Legt die Schreibrichtung fest. Links nach rechts ist Standard, rechts nach links spiegelt das Layout. Namen und Sätze stehen in <bdi>, damit der Punkt hinter «Luca J.» bleibt.

LJ
Luca J.

hat sich für Dienstag angemeldet

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Direction -->
<div class="k-col k-dir">
  <div class="tabs k-dirsw" role="group" aria-label="Schreibrichtung"><button class="on" type="button" data-dir="ltr">Links nach rechts</button><button type="button" data-dir="rtl">Rechts nach links</button></div>
  <div class="k-dirbox" dir="ltr"><span class="k-av-init">LJ</span><div><b><bdi>Luca J.</bdi></b><p class="k-muted"><bdi>hat sich für Dienstag angemeldet</bdi></p></div><svg class="ki k-dir-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg></div>
</div>
Formulare & EingabenFelder, Auswahl und alles, was jemand ausfüllt.12 Bausteine
49

Formular-Elemente

Fokus = Gold-Rahmen (nicht Browser-Blau). Dropdowns: jede Auswahl-Liste nutzt das Custom-Dropdown .csel, eine dunkle Navy-Liste mit Brand-Line (blau oben / gold unten), Drop-Cascade beim Öffnen und Gold-Haken an der gewählten Option; geschlossen wird per Klick daneben oder Esc. Native Aufklapp-Menüs lassen sich nicht im Brand gestalten und sind darum verboten. Umsetzung: tfczEnhanceSelects() (Code in dieser Datei). Der native <select> bleibt unsichtbar im DOM, Formular-Wert und Validierung funktionieren weiter. Chips, Tabs, Toggle.

Chips Tabs
Technik: der Formular-Baustein

Ein Baustein für alle Formulare. Felder und Mail-Texte stehen in system/forms.js, das Verhalten in system/components/form.js: rendern, Pflichtfelder prüfen, Mail bauen, „Mail geöffnet?"-Fallback, Text kopieren. Acht Formulare auf vier Seiten (fünf Mitglied-Pakete, Training, Firmenevents, Medien).

Live: dasselbe Bauteil wie auf der Website

Die Felder unten kommen aus system/forms.js, nicht aus dieser Datei. Wähle ein Formular, um denselben Wechsel zu sehen, den „Mitglied" bei der Paketwahl macht.

Bitte fülle die markierten Pflichtfelder aus.

So baut man es ein

<form data-tfcz="form" data-form="training" novalidate>
  <div class="fgrid" id="fFields"></div>   <!-- Felder kommen vom Baustein -->
  <button class="btn btn-gold" type="submit">Als Mail senden</button>
  <span class="formerr" id="fErr">Bitte fülle die markierten Pflichtfelder aus.</span>
</form>

<script src="system/forms.js"></script>
<script defer src="system/components/form.js"></script>

Wechselt die Seite das Formular (Mitglied: Paketwahl), setzt sie data-form um und ruft TFCZ.formUI.neu(form). Neu gerendert werden nur die Felder, die Verdrahtung bleibt.

Regeln

  • Der Absender ist der Kunde. Schlusssätze nie aus Vereinssicht, also „Bitte meldet euch mit Verfügbarkeit und Ablauf". Ein „Wir melden uns …" gehört nicht hinein.
  • Nichts darf still verschwinden. Ein optionales Feld, dessen Fehlen eine Aussage ist (Wunschzeit), bekommt leerText und steht dann als „Wunschzeit: noch offen" im Text.
  • Felder nur in system/forms.js, nie im HTML. Empfänger kommt aus system/content.js.
  • Backend: ist window.TFCZ_API gesetzt, geht die Anfrage zusätzlich per POST /api/forms/<id> an den Server. Der mailto-Weg läuft unabhängig weiter, das Formular funktioniert also immer. Die Schnittstelle beschreibt docs/API.md im Website-Repository.
  • Die Farbe des Absenden-Buttons folgt dem Zweck. Ist das Absenden die Conversion/Lead (Mitglied, Training, Firmenevent) → .btn .btn-gold. Ist es eine normale Anfrage (Medien) → .btn .btn-blue. Darum steht in der Live-Demo oben Blau (Medien) und im Einbau-Beispiel Gold (Training).
  • Toast ist global: TFCZ.toast('Text kopiert').

Login-Feld (nur App)

Nur im Login der App (app.tfcz.ch/login). Das Label wandert beim Fokus hoch und wird gold, die Unterlinie wird zum Blau→Gold-Verlauf. Auf der Website gelten die Felder oben.

50

Eingabe-Controls

Die Standard-Controls im TFCZ-Stil (Basis-Satz nach Material, Bootstrap und shadcn). Semantik überall gleich: Häkchen/Bestätigung = Grün · Auswahl = Gold · Aktion = Blau · Fokus = Gold-Rahmen · Hover = Brand-Rahmen nur bei nicht gewählten Elementen (Sektion 18/19).

Checkboxen: rund & animiert · aus = Blau, angehakt = Grün mit weissem Häkchen (Bestätigung)
Radios: checked = Gold-Ring + Gold-Punkt
Toggle · Slider · Stepper
2
Suchfeld

Umschalter (2–3 Optionen)

Glas-Umschalter mit gleitendem Marker, wenn genau eine von zwei oder drei Optionen gilt.

Tabs / Reiter (Ansichten)

Für den Wechsel ganzer Ansichten/Inhalte. Aktiver Reiter = Frost + 3px blaue Oberkante, sitzt auf der goldenen Grundlinie (Blau oben / Gold unten). Für zwei oder drei Optionen innerhalb einer Ansicht nimmst du den Umschalter daneben.

Weitere Eingaben
51

Field

Label, Feld und Hilfetext als Einheit. Der Hilfetext steht unter dem Feld.

Wir rufen nur an, wenn sich am Termin etwas ändert.

Code für Entwickler

Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">

<!-- Field -->
<div class="field k-w400"><label for="k-fd">Telefonnummer</label><input id="k-fd" type="tel" placeholder="079 123 45 67"><p class="k-help">Wir rufen nur an, wenn sich am Termin etwas ändert.</p></div>
52

Input Group

Feld mit Icon davor und Knopf dahinter, als ein Block.

Code für Entwickler

Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">

<!-- Input Group -->
<div class="k-igroup k-w400"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34" />
    <circle cx="11" cy="11" r="8" /></svg><input type="search" placeholder="Mitglied suchen" aria-label="Mitglied suchen"><button class="btn btn-blue btn-sm" type="button">Suchen</button></div>
53

Input OTP

Code aus sechs Ziffern, je ein Feld. Der Fokus springt beim Tippen weiter.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Input OTP -->
<div class="k-otp" role="group" aria-label="Bestätigungscode"><input inputmode="numeric" maxlength="1" aria-label="Ziffer 1"><input inputmode="numeric" maxlength="1" aria-label="Ziffer 2"><input inputmode="numeric" maxlength="1" aria-label="Ziffer 3"><span class="k-otp-dash"></span><input inputmode="numeric" maxlength="1" aria-label="Ziffer 4"><input inputmode="numeric" maxlength="1" aria-label="Ziffer 5"><input inputmode="numeric" maxlength="1" aria-label="Ziffer 6"></div>
54

Combobox

Auswahl mit Suche: tippen filtert die Liste, ein Klick übernimmt den Namen.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Combobox -->
<div class="k-pop-wrap k-combo k-w280">
  <div class="k-igroup"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34" />
    <circle cx="11" cy="11" r="8" /></svg><input type="text" class="k-combo-in" placeholder="Spieler suchen" autocomplete="off" aria-label="Spieler suchen"></div>
  <div class="k-pop k-list" role="listbox">
    <button type="button" role="option" class="k-opt" data-value="Anna Keller">Anna Keller<svg class="ki k-opt-ck" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></button><button type="button" role="option" class="k-opt" data-value="Ben Frei">Ben Frei<svg class="ki k-opt-ck" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></button><button type="button" role="option" class="k-opt" data-value="Luca Jost">Luca Jost<svg class="ki k-opt-ck" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></button><button type="button" role="option" class="k-opt" data-value="Mia Brunner">Mia Brunner<svg class="ki k-opt-ck" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></button><button type="button" role="option" class="k-opt" data-value="Nora Egli">Nora Egli<svg class="ki k-opt-ck" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></button><button type="button" role="option" class="k-opt" data-value="Timo Graf">Timo Graf<svg class="ki k-opt-ck" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></button>
    <p class="k-empty-line" hidden>Niemand gefunden.</p>
  </div>
</div>
55

Date Picker

Datum über einen kleinen Kalender wählen. Das gewählte Datum steht danach im Knopf.

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Date Picker -->
<div class="k-pop-wrap k-datepick">
  <button class="k-selbtn" type="button" data-pop><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2v4" />
    <path d="M16 2v4" />
    <rect width="18" height="18" x="3" y="4" rx="2" />
    <path d="M3 10h18" /></svg><span class="k-dp-val">Datum wählen</span></button>
  <div class="k-pop k-pop-cal"><div class="k-cal"></div></div>
</div>
56

Questionnaire

Fragen Schritt für Schritt: eine Wahl, mehrere Wahlen und Freitext, der sich überspringen lässt.

Frage 1 von 3

Wie oft spielst du?

Frage 2 von 3

Was interessiert dich?

Frage 3 von 3 · freiwillig

Möchtest du uns noch etwas sagen?

Code für Entwickler

Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/ui.js"></script>

<!-- Questionnaire -->
<div class="k-quest k-w400">
  <div class="k-q-prog"><span class="k-q-bar"></span></div>
  <div class="k-q-step on" data-q="1"><p class="k-q-n">Frage 1 von 3</p><h4>Wie oft spielst du?</h4>
    <div class="k-col" role="radiogroup"><label class="ck rd"><input type="radio" name="k-q1"><span class="bx"><span class="dot"></span></span>Zum ersten Mal</label><label class="ck rd"><input type="radio" name="k-q1" checked><span class="bx"><span class="dot"></span></span>Ab und zu</label><label class="ck rd"><input type="radio" name="k-q1"><span class="bx"><span class="dot"></span></span>Jede Woche</label></div></div>
  <div class="k-q-step" data-q="2"><p class="k-q-n">Frage 2 von 3</p><h4>Was interessiert dich?</h4>
    <div class="k-chips"><button type="button" class="chip-f on" data-multi>Offenes Training</button><button type="button" class="chip-f" data-multi>Plausch-Turniere</button><button type="button" class="chip-f" data-multi>Kindertraining</button><button type="button" class="chip-f" data-multi>Liga</button></div></div>
  <div class="k-q-step" data-q="3"><p class="k-q-n">Frage 3 von 3 · freiwillig</p><h4>Möchtest du uns noch etwas sagen?</h4>
    <div class="field"><textarea placeholder="Deine Antwort" aria-label="Deine Antwort"></textarea></div></div>
  <div class="k-q-done" hidden><svg class="ki k-q-ok" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" />
    <path d="m9 12 2 2 4-4" /></svg><h4>Danke für deine Antworten!</h4></div>
  <div class="k-q-nav"><button class="btn btn-ghost btn-sm k-q-back" type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 19-7-7 7-7" />
    <path d="M19 12H5" /></svg> Zurück</button><span class="k-grow"></span><button class="btn btn-ghost btn-sm k-q-skip" type="button" hidden>Überspringen</button><button class="btn btn-blue btn-sm k-q-next" type="button">Weiter</button></div>
</div>
57

Avatar-Wahl

Profilbild in Emblem-Form aus Farben wählen, die Initialen folgen dem eingetippten Namen.

LJ
Code für Entwickler

Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

<!-- im <head> -->
<link rel="stylesheet" href="system/tokens.css">
<link rel="stylesheet" href="system/buttons.css">
<link rel="stylesheet" href="system/ui.css">
<script defer src="system/components/effects.js"></script>

<!-- Avatar-Wahl -->
<div class="fx-av" data-fx="avatar-pick">
  <div class="fx-av-big" style="background:linear-gradient(155deg,var(--blue-mid),var(--blue-dk))">LJ</div>
  <div class="fx-av-opts" role="radiogroup" aria-label="Farbe"><button class="fx-av-opt" type="button" role="radio" aria-checked="true" style="--c:linear-gradient(155deg,var(--blue-mid),var(--blue-dk))">LJ</button><button class="fx-av-opt" type="button" role="radio" aria-checked="false" style="--c:linear-gradient(155deg,var(--gold),#a88635)">LJ</button><button class="fx-av-opt" type="button" role="radio" aria-checked="false" style="--c:linear-gradient(155deg,var(--green),var(--green-dk))">LJ</button><button class="fx-av-opt" type="button" role="radio" aria-checked="false" style="--c:linear-gradient(155deg,#6b7f93,#33475a)">LJ</button></div>
  <div class="field"><label for="fx-av-n">Name</label><input id="fx-av-n" type="text" placeholder="Luca Jost"></div>
  <button class="btn btn-success btn-sm fx-av-save" type="button"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg> Speichern</button>
</div>
58

Datei-Upload

Dateien hineinziehen oder anklicken. PDF, JPG und PNG bis 5 MB laden mit Fortschritt hoch, alles andere wird mit Grund abgewiesen.

Beleg hierher ziehenoder klicken · PDF, JPG, PNG bis 5 MB
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Datei-Upload -->
    <div class="fx-up" data-fx="upload"><div class="fx-drop" role="button" tabindex="0"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12" />
        <path d="m17 8-5-5-5 5" />
        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></svg><b>Beleg hierher ziehen</b><span>oder klicken · PDF, JPG, PNG bis 5 MB</span></div><input type="file" multiple hidden accept=".pdf,.jpg,.jpeg,.png"><ul class="fx-files"></ul></div>
    59

    Team-Auswahl

    Anzahl Personen mit Plus und Minus, die Avatare stapeln sich überlappend und springen federnd dazu.

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Team-Auswahl -->
    <div class="fx-team" data-fx="team" data-start="2" data-names='["Luca Jost","Mia Brunner","Timo Graf","Nora Egli"]'><span class="fx-team-stack"></span><span class="fx-team-n"></span><button class="btn btn-ghost btn-icon btn-sm fx-team-minus" type="button" aria-label="Eine Person weniger"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" /></svg></button><button class="btn btn-ghost btn-icon btn-sm fx-team-plus" type="button" aria-label="Eine Person mehr"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14" />
        <path d="M12 5v14" /></svg></button></div>
    60

    Collapsible

    Ein Bereich, der sich auf- und zuklappt, hier für weitere Termine.

    Dienstag, 29. September · Offenes Training
    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Collapsible -->
    <div class="k-coll k-w400">
      <div class="k-coll-row"><span><b>Dienstag, 29. September</b> · Offenes Training</span></div>
      <div class="k-coll-more" hidden><div class="k-coll-row"><span><b>Samstag, 3. Oktober</b> · Kindertraining</span></div><div class="k-coll-row"><span><b>Freitag, 9. Oktober</b> · Plausch-Turnier</span></div></div>
      <button class="btn btn-ghost btn-sm" type="button" data-coll aria-expanded="false"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 15 5 5 5-5" />
        <path d="m7 9 5-5 5 5" /></svg> <span>2 weitere Termine</span></button>
    </div>
    Fenster & RückmeldungenMeldungen, Dialoge und was nach einer Handlung passiert.9 Bausteine
    61

    Feedback & Status

    Alerts nutzen die Sekundär-Akzente (sparsam, max. ~5% Fläche): Blau = Info · Grün = Erfolg · Orange = Warnung · Rot = Fehler/ungespeichert. Der Spinner trägt die Brand-Line (blau oben / gold unten). Skeletons statt leerer Flächen beim Laden.

    Alerts / Banner
    InfoDas Training am Dienstag beginnt um 19:00.
    ErfolgAnmeldung gespeichert. Bis Dienstag!
    WarnungNur noch 3 Plätze frei.
    Fehler / ungespeichertÄnderungen konnten nicht gespeichert werden. Versuch es bitte nochmal.
    Toast · Progress · Spinner · Zähler-Badge · Tooltip
    3
    Brand-Line auch hier: blau oben · gold unten
    Skeleton (Ladeplatzhalter) · Empty State
    Noch keine AnmeldungenSobald sich jemand anmeldet, erscheint er hier.
    62

    Portal-Schliessen

    Fenster öffnen und schliessen sofort, ohne Auf-/Zu-Animation, auch bei den Easter Eggs. Baustein: system/components/portal.js. Das Schliessen läuft zentral darüber, damit alle Fenster gleich reagieren. Nur der X-Button federt noch (Hover-Quetschung).

    So benutzt man ihn

    Code für Entwickler
    <script defer src="system/components/portal.js"></script>
    
    // Statt das Fenster selbst zu verstecken — zentral schliessen (sofort, ohne Animation):
    TFCZ.portal.close(fensterEl, function () {
      overlay.classList.remove('on');   // aufräumen
    });

    fensterEl = die Box (nicht der Backdrop). Der Callback läuft sofort, ohne Animationsdauer und ohne Zustand.

    Live: öffnen und schliessen

    Schliessen per X, Esc oder Klick daneben.

    Regeln

    • Nie ein Fenster selbst ausblenden (display:none, Klasse entfernen, wegschieben). Jede Schliess-Aktion läuft über TFCZ.portal.close().
    • Keine Auf-/Zu-Animation für Fenster: kein Zusammensinken, kein Verblassen, kein Skalieren aus dem Klickpunkt.
    • Verdrahtet: Menü-Drawer, Tages-Maske, Video-Modal, Bild-Player, Barometer-Sheet, dieses Inhaltsverzeichnis, das Demo-Popup, die Easter-Egg-Popups und das Design Studio.
    • Der X-Button bleibt der Standard-X: runder Kreis, Hover rot, Quetsch-Animation.
    Weitere Fenster
    63

    Alert Dialog

    Rückfrage vor einer Handlung, die sich nicht rückgängig machen lässt. Der gefährliche Knopf ist rot.

    Code für Entwickler

    Braucht die drei Stylesheets und portal.js und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/portal.js"></script>
    <script defer src="system/components/ui.js"></script>
    
    <!-- Alert Dialog -->
    <button class="btn btn-danger" type="button" data-open="k-adlg"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 11v6" />
        <path d="M14 11v6" />
        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
        <path d="M3 6h18" />
        <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg> Turnier löschen</button>
    <div class="k-ov" id="k-adlg" role="alertdialog" aria-modal="true" aria-labelledby="k-adlg-t"><div class="modal k-modal">
      <h3 id="k-adlg-t">Turnier wirklich löschen?</h3>
      <p class="k-muted">Alle Resultate und Anmeldungen dieses Turniers gehen verloren.</p>
      <div class="k-mf"><button class="btn btn-ghost btn-sm" type="button" data-close>Abbrechen</button><button class="btn btn-danger btn-sm" type="button" data-close data-toast="Turnier gelöscht">Löschen</button></div>
    </div></div>
    64

    Sheet

    Panel, das von rechts hereinkommt, für Filter oder Details neben dem Inhalt.

    Code für Entwickler

    Braucht die drei Stylesheets und portal.js und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/portal.js"></script>
    <script defer src="system/components/ui.js"></script>
    
    <!-- Sheet -->
    <button class="btn btn-ghost" type="button" data-open="k-sheet"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 5H3" />
        <path d="M12 19H3" />
        <path d="M14 3v4" />
        <path d="M16 17v4" />
        <path d="M21 12h-9" />
        <path d="M21 19h-5" />
        <path d="M21 5h-7" />
        <path d="M8 10v4" />
        <path d="M8 12H3" /></svg> Filter</button>
    <div class="k-ov k-ov-side" id="k-sheet" role="dialog" aria-modal="true" aria-labelledby="k-sheet-t"><div class="k-sheet">
      <div class="k-mh"><h3 id="k-sheet-t">Filter</h3><button class="xclose" type="button" data-close aria-label="Schliessen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18" />
        <path d="m6 6 12 12" /></svg></button></div>
      <p class="k-lbl">Kategorie</p>
      <div class="k-chips" data-single><button type="button" class="chip-f on">Alle</button><button type="button" class="chip-f">Fun</button><button type="button" class="chip-f">Pro</button><button type="button" class="chip-f">Elite</button></div>
      <div class="k-grow"></div>
      <button class="btn btn-blue" type="button" data-close>Filter anwenden</button>
    </div></div>
    65

    Drawer

    Schublade von unten, vor allem auf dem Handy. Der Griff oben zeigt, dass sie sich wegziehen lässt.

    Code für Entwickler

    Braucht die drei Stylesheets und portal.js und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/portal.js"></script>
    <script defer src="system/components/ui.js"></script>
    
    <!-- Drawer -->
    <button class="btn btn-ghost" type="button" data-open="k-drawer"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2" />
        <path d="M3 15h18" />
        <path d="m9 10 3-3 3 3" /></svg> Öffnungszeiten</button>
    <div class="k-ov k-ov-bottom" id="k-drawer" role="dialog" aria-modal="true" aria-labelledby="k-dr-t"><div class="k-drawer">
      <span class="k-grip"></span>
      <h3 id="k-dr-t">Öffnungszeiten</h3>
      <div class="k-kv"><span>Dienstag</span><b>19:00 bis 23:00</b></div><div class="k-kv"><span>Donnerstag</span><b>19:00 bis 23:00</b></div><div class="k-kv"><span>Samstag</span><b>14:00 bis 18:00</b></div>
      <button class="btn btn-ghost" type="button" data-close>Schliessen</button>
    </div></div>
    66

    Popover

    Kleiner Kasten am Knopf, für eine kurze Eingabe. Schliesst beim Klick daneben.

    Erinnere mich

    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Popover -->
    <div class="k-pop-wrap">
      <button class="btn btn-ghost" type="button" data-pop><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.268 21a2 2 0 0 0 3.464 0" />
        <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></svg> Erinnerung</button>
      <div class="k-pop k-pop-pad"><p class="k-lbl">Erinnere mich</p><div class="tabs" data-single><button type="button" class="on">1 Std. vorher</button><button type="button">1 Tag vorher</button></div><button class="btn btn-blue btn-sm" type="button" data-toast="Erinnerung gesetzt" data-popclose>Speichern</button></div>
    </div>
    67

    Hover Card

    Vorschau beim Überfahren eines Namens, mit Emblem-Avatar und Kurzinfo.

    Organisiert von @lucajost Luca JostAktivmitglied seit 2019 · Turnierleitung

    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Hover Card -->
    <p class="k-p">Organisiert von <span class="k-hc-wrap"><a class="k-mention" href="#" onclick="return false">@lucajost</a>
      <span class="k-hc" role="tooltip"><span class="k-shield" style="background-image:url(assets/fotos/galerie/full/781.webp)"></span><span><b>Luca Jost</b><span class="k-muted">Aktivmitglied seit 2019 · Turnierleitung</span></span></span></span></p>
    Belohnungen
    68

    Unlock- & Feedback-Animationen

    Wiederverwendbare Game-Style-Animationen. Level-Up (z. B. Achievement / Fortschritt) und Sterne-Rating (z. B. Feedback-Bewertung). Keine Emojis, alles SVG/CSS. Klick „Abspielen" zum erneuten Triggern.

    Level-Up

    LEVEL UP
    12

    Sterne-Rating

    69

    Easter-Egg · Emblem-Pop

    Belohnungs-Popup der Eiersuche. Klasse .eegg: Ei fällt rein, wackelt zunehmend (Spannung) und knackt dann auf. Das blaue TFCZ-Emblem springt mit Bounce heraus, und Käse regnet herum. Wiederverwendbar für jeden Egg-Fund.

    Emblem-Pop

    TFCZ Emblem

    Fund-Logik & Belohnung

    Nach dem ersten Fund erscheint der Hinweis, dass ein Login nötig ist, um den Fortschritt zu speichern. Der Hinweis bleibt, bis man angemeldet ist. Ohne Login wird ein Ei beim Refresh wieder aktiv. Ab Login wird der Fortschritt im Profil gespeichert und jeder neue Fund zeigt, wie viele noch offen sind. Motivation (immer sichtbar): 10 gefundene Eier = 1 Gratis-Raclette an einem KäseKick der Wahl.

    Käse-Feier & Performance

    Die Feier lässt Käse-Emoji (thematisch zum Käsekick) über den Bildschirm regnen. Physik: die Käse fallen und bleiben liegen, ohne Aufspringen und ohne Eigendreh. Anklicken/Wischen schleudert sie hoch (zum Wegspicken/Lesen). Quelle: assets/js/tfcz-fx.js → startCheese().

    Performance-Knöpfe (Pflicht bei geräteschwachen Nutzern): Partikel gedeckelt (Mobile ≤600px 120, sonst 200) hält die O(n²)-Kollision günstig · Fallflächen-Canvas dpr 1.5 statt 2 (~1.8× weniger Füllrate) · prefers-reduced-motion überspringt die Feier komplett · Selbst-Stopp: Canvas werden beim Schliessen entfernt (kein Leck, Heap ~1–2 MB). Gemessen 22.07.2026 (Chromium/CDP): Mobile 4× CPU 28→34 fps, 6× 23→30 fps.

    Karten & InhalteKarten, Kacheln und wie Inhalte gerahmt werden.18 Bausteine
    70

    Cards, Tags & Datenreihen

    Immer dunkler Navy-Gradient, leicht transparent + backdrop-blur, nie hell. Angedockte Eyebrow-Tags runden nur oben. Datenreihen: Sieger gold, Verlierer gedimmt.

    Standard-Card

    --card + 1px Border + e-2. Basis-Baustein.

    Akzent-Card

    Kategorie-Streifen links (Fun).

    Gespielte Spiele
    M. Sauter5
    T. Weber3

    Profil-Avatar · Emblem-Form

    Profilbilder haben die Form des TFCZ-Emblems: das Foto füllt die echte Emblem-Silhouette, nie rund und ohne Rahmen. Ohne Foto stehen die Initialen auf dunkelblauem Grund.

    → Voller Guide & Code

    71

    Kategorie-Badges

    So erscheint die Kategorie eines Anlasses im Kalender und bei Turnieren. Die Farben stehen in Sektion 12, die Namen im Lexikon (Sektion 08).

    Fun / für alle Pro Turniere Elite · ITSF Event (extern)
    72

    Paket-Karten

    pcard mit „Beliebt"-Ribbon & Gold-Check-Bullets. Gold-Rahmen = gewählt.

    Schnuppern
    Gratis
    • 1 Abend testen
    • Leihmaterial
    Beliebt
    Aktiv
    CHF 200
    • Freies Training
    • Alle Turniere
    Key Player
    CHF 250
    • Eigener Schlüssel
    • 24/7 Zugang
    Weitere Bausteine
    73

    Item

    Zeile mit Icon, Titel, Beschreibung und einer Aktion rechts.

    Plausch-Turnier

    Freitag, 9. Oktober · 16 Plätze frei

    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Item -->
    <div class="k-item k-w400"><span class="k-item-ic"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14.66v1.626a2 2 0 0 1-.976 1.696A5 5 0 0 0 7 21.978" />
        <path d="M14 14.66v1.626a2 2 0 0 0 .976 1.696A5 5 0 0 1 17 21.978" />
        <path d="M18 9h1.5a1 1 0 0 0 0-5H18" />
        <path d="M4 22h16" />
        <path d="M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z" />
        <path d="M6 9H4.5a1 1 0 0 1 0-5H6" /></svg></span><div class="k-grow"><b>Plausch-Turnier</b><p class="k-muted">Freitag, 9. Oktober · 16 Plätze frei</p></div><button class="btn btn-blue btn-sm" type="button">Anmelden</button></div>
    74

    Separator

    Trennlinie zwischen Gruppen, waagrecht oder senkrecht. Neutral, nie in Gold.

    TFCZ

    Tischfussball Club Zürich


    TrainingTurniereVerein
    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Separator -->
    <div class="k-sep-demo">
      <div><b>TFCZ</b><p class="k-muted">Tischfussball Club Zürich</p></div>
      <hr class="k-sep">
      <div class="k-sep-row"><span>Training</span><span class="k-vsep"></span><span>Turniere</span><span class="k-vsep"></span><span>Verein</span></div>
    </div>
    75

    Aspect Ratio

    Rahmen mit festem Seitenverhältnis, hier 16:9. Das Bild füllt ihn, ohne sich zu verzerren.

    Spielszene am Tisch im Clublokal
    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Aspect Ratio -->
    <div class="k-ratio"><img src="assets/fotos/galerie/full/795.webp" alt="Spielszene am Tisch im Clublokal" loading="lazy"></div>
    76

    Attachment

    Datei im Anhang: Typ, Name, Grösse und Upload-Stand, dazu Herunterladen und Entfernen.

    Anmeldung-Plauschturnier.pdf

    240 KB · hochgeladen

    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Attachment -->
    <div class="k-att k-w400"><span class="k-att-ic"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
        <path d="M14 2v5a1 1 0 0 0 1 1h5" />
        <path d="M10 9H8" />
        <path d="M16 13H8" />
        <path d="M16 17H8" /></svg></span><div class="k-grow"><b>Anmeldung-Plauschturnier.pdf</b><p class="k-muted k-small">240 KB · hochgeladen</p><div class="k-prog-tr k-thin"><span style="transform:scaleX(1)" class="ok"></span></div></div><button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Herunterladen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 15V3" />
        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
        <path d="m7 10 5 5 5-5" /></svg></button><button class="btn btn-ghost btn-icon btn-sm k-x-red" type="button" aria-label="Entfernen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18" />
        <path d="m6 6 12 12" /></svg></button></div>
    77

    Scroll Area

    Kasten mit eigener schmaler Bildlaufleiste, wenn der Inhalt höher ist als der Platz.

    Plausch-Turnier03.10.
    Kindertraining04.10.
    Clubmeisterschaft05.10.
    Liga Runde 406.10.
    Doppel-Nacht07.10.
    Sommerfest08.10.
    Liga Runde 509.10.
    Crazy DYP10.10.
    Jahresabschluss11.10.
    Weihnachtsturnier12.10.
    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Scroll Area -->
    <div class="k-scroll k-w280" tabindex="0" aria-label="Turnierliste"><div class="k-scroll-row"><span>Plausch-Turnier</span><span class="k-muted">03.10.</span></div><div class="k-scroll-row"><span>Kindertraining</span><span class="k-muted">04.10.</span></div><div class="k-scroll-row"><span>Clubmeisterschaft</span><span class="k-muted">05.10.</span></div><div class="k-scroll-row"><span>Liga Runde 4</span><span class="k-muted">06.10.</span></div><div class="k-scroll-row"><span>Doppel-Nacht</span><span class="k-muted">07.10.</span></div><div class="k-scroll-row"><span>Sommerfest</span><span class="k-muted">08.10.</span></div><div class="k-scroll-row"><span>Liga Runde 5</span><span class="k-muted">09.10.</span></div><div class="k-scroll-row"><span>Crazy DYP</span><span class="k-muted">10.10.</span></div><div class="k-scroll-row"><span>Jahresabschluss</span><span class="k-muted">11.10.</span></div><div class="k-scroll-row"><span>Weihnachtsturnier</span><span class="k-muted">12.10.</span></div></div>
    78

    Resizable

    Zwei Bereiche, deren Grenze sich ziehen lässt, auch mit den Pfeiltasten.

    Spielplan
    Rangliste
    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Resizable -->
    <div class="k-res k-w400"><div class="k-res-a"><b>Spielplan</b></div><div class="k-res-h" role="separator" aria-orientation="vertical" tabindex="0" aria-label="Grösse ändern" aria-valuenow="50"><span></span></div><div class="k-res-b"><b>Rangliste</b></div></div>
    Karten-Effekte
    79

    Bento-Raster

    Raster aus Kacheln unterschiedlicher Grösse. Beim Sichtbarwerden zählen die Zahlen hoch und die Balken wachsen.

    Saison 20260Mitglieder
    0Turniere dieses Jahr
    0Leonhart-Tische
    Diese Woche
    • Di · Offenes Training
    • Fr · Plausch-Turnier
    • Sa · Kindertraining
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Bento-Raster -->
    <div class="fx-bento" data-fx="bento">
      <div class="big"><span class="k-kick">Saison 2026</span><span class="num" data-count="108">0</span><span class="lbl2">Mitglieder</span><div class="bars"><i style="--h:0.35"></i><i style="--h:0.5"></i><i style="--h:0.42"></i><i style="--h:0.65"></i><i style="--h:0.58"></i><i style="--h:0.9"></i><i style="--h:0.7"></i></div></div>
      <div><span class="num" data-count="64">0</span><span class="lbl2">Turniere dieses Jahr</span></div>
      <div><span class="num" data-count="10">0</span><span class="lbl2">Leonhart-Tische</span></div>
      <div class="big" style="grid-column:span 3; grid-row:span 1"><span class="lbl2">Diese Woche</span><ul class="tl"><li>Di · Offenes Training</li><li>Fr · Plausch-Turnier</li><li>Sa · Kindertraining</li></ul></div>
    </div>
    80

    Wende-Karte

    Dreht sich beim Überfahren oder Antippen um und zeigt auf der Rückseite, was dazugehört.

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Wende-Karte -->
    <button class="fx-flip" type="button" data-fx="flip" aria-label="Offenes Training, Details zeigen"><div class="fx-flip-in">
      <div class="fx-flip-f" style="background-image:url(assets/fotos/galerie/full/795.webp)"><span class="k-kick">Jeden Dienstag</span><b style="font-size:22px">Offenes Training</b></div>
      <div class="fx-flip-b"><b style="color:#fff; font-size:18px">Was dich erwartet</b><ul><li>Tische ab 19 Uhr offen</li><li>Tipps von Nationalspielern</li><li>Keine Anmeldung nötig</li></ul><span class="btn btn-blue btn-sm">Vorbeikommen</span></div>
    </div></button>
    81

    Karten-Stapel

    Drei Karten liegen gestapelt. Ein Klick fächert sie untereinander auf, ein zweiter legt sie wieder zusammen.

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Karten-Stapel -->
    <button class="fx-stack" type="button" data-fx="stack" aria-expanded="false" aria-label="Mitgliedschaften aufklappen">
      <span class="fx-stack-c"><span class="k-kick">Beliebt</span><b>Aktivmitglied</b><span class="k-muted">Training, Turniere, Stimmrecht</span><span class="price">CHF 240</span></span>
      <span class="fx-stack-c"><span class="k-kick">Für Junge</span><b>Junior</b><span class="k-muted">Bis 18 Jahre, Kindertraining inklusive</span><span class="price">CHF 90</span></span>
      <span class="fx-stack-c"><span class="k-kick">Unterstützen</span><b>Gönner</b><span class="k-muted">Für alle, die den Club tragen wollen</span><span class="price">ab CHF 50</span></span>
    </button>
    82

    Karten-Karussell

    Waagrecht scrollbare Karten mit Bild, Kategorie und Bewertung. Die Pfeile springen je eine Karte weiter.

    Fun
    Plausch-Turnier 4,8
    Elite
    Clubmeisterschaft 4,9
    Event
    Kindertraining 5,0
    Pro
    Liga-Abend 4,7
    Fun
    Doppel-Nacht 4,6
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Karten-Karussell -->
    <div class="fx-cc" data-fx="carousel-cards">
      <div class="fx-cc-track"><article class="fx-cc-card"><div class="ph" style="background-image:url(assets/fotos/galerie/full/795.webp)"><span class="badge fun">Fun</span></div><div class="bd"><b>Plausch-Turnier</b><span class="rate"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" /></svg> 4,8</span></div></article><article class="fx-cc-card"><div class="ph" style="background-image:url(assets/fotos/galerie/full/1050.webp)"><span class="badge elite">Elite</span></div><div class="bd"><b>Clubmeisterschaft</b><span class="rate"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" /></svg> 4,9</span></div></article><article class="fx-cc-card"><div class="ph" style="background-image:url(assets/fotos/galerie/full/1051.webp)"><span class="badge event">Event</span></div><div class="bd"><b>Kindertraining</b><span class="rate"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" /></svg> 5,0</span></div></article><article class="fx-cc-card"><div class="ph" style="background-image:url(assets/fotos/galerie/full/781.webp)"><span class="badge pro">Pro</span></div><div class="bd"><b>Liga-Abend</b><span class="rate"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" /></svg> 4,7</span></div></article><article class="fx-cc-card"><div class="ph" style="background-image:url(assets/fotos/galerie/full/795.webp)"><span class="badge fun">Fun</span></div><div class="bd"><b>Doppel-Nacht</b><span class="rate"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" /></svg> 4,6</span></div></article></div>
      <div class="fx-cc-nav"><button class="btn btn-ghost btn-icon btn-sm fx-cc-prev" type="button" aria-label="Zurück"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg></button><button class="btn btn-ghost btn-icon btn-sm fx-cc-next" type="button" aria-label="Weiter"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg></button></div>
    </div>
    82b

    Ring-Karussell

    Karten auf einem drehbaren 3D-Ring, für eine kleine Auswahl gleichwertiger Dinge, etwa die Turnierformate. Gedreht wird mit den Pfeilen, den Chips, durch Ziehen oder mit den Pfeiltasten. Ein Klick auf eine seitliche Karte holt sie nach vorn, erst die vordere öffnet ihren Link. Die vordere Karte trägt den Goldrand.

    Zwei Blickwinkel: gerade (data-blick="gerade") zeigt den Ring auf Augenhöhe, von oben (data-blick="oben") kippt ihn leicht, damit man sieht, wie viele Karten dazugehören. Gut sind 5 bis 9 Karten. Für lange Listen oder Inhalte, die man vergleichen muss, gilt das Karten-Karussell (82) oder ein Raster.

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>. Die Karten bekommen ihre Lage selbst, der Radius richtet sich nach Kartenbreite und Anzahl.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Ring-Karussell · data-blick="gerade" oder "oben" · data-schalter="blick" zeigt den Umschalter · data-picks="aus" blendet die Chips aus -->
    <div class="fx-ring" data-fx="ring" data-blick="gerade" data-schalter="blick" aria-label="Turnierformate">
      <div class="fx-ring-ring">
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="Käsekick"><div class="ph"><img src="assets/fotos/galerie/full/dsc_7265.webp" alt="" loading="lazy"><span class="badge fun">Fun</span></div><div class="bd"><b>Käsekick</b><span>Letzter Freitag im Monat</span></div></a>
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="Plausch-Turnier"><div class="ph"><img src="assets/fotos/galerie/full/dsc_7250.webp" alt="" loading="lazy"><span class="badge fun">Fun</span></div><div class="bd"><b>Plausch-Turnier</b><span>Jeden Mittwoch, 20:15</span></div></a>
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="Regio Tour Einzel"><div class="ph"><img src="assets/fotos/galerie/full/dsc_6776.webp" alt="" loading="lazy"><span class="badge pro">Pro</span></div><div class="bd"><b>Regio Tour Einzel</b><span>Abends, CHF 10</span></div></a>
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="Regio Tour Doppel"><div class="ph"><img src="assets/fotos/galerie/full/dsc_6890.webp" alt="" loading="lazy"><span class="badge pro">Pro</span></div><div class="bd"><b>Regio Tour Doppel</b><span>Abends, CHF 10 pro Person</span></div></a>
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="Zürich Open"><div class="ph"><img src="assets/fotos/galerie/full/dsc_6815.webp" alt="" loading="lazy"><span class="badge pro">Pro</span></div><div class="bd"><b>Zürich Open</b><span>Samstags, CHF 1'050 Preisgeld</span></div></a>
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="STS · ITSF 250"><div class="ph"><img src="assets/fotos/galerie/full/dsc_6831.webp" alt="" loading="lazy"><span class="badge elite">Elite</span></div><div class="bd"><b>STS · ITSF 250</b><span>Swiss Tablesoccer Series</span></div></a>
        <a class="fx-ring-card" href="#k-ring-karussell" data-label="Swiss Open P4P"><div class="ph"><img src="assets/fotos/galerie/full/708.webp" alt="" loading="lazy"><span class="badge elite">Elite</span></div><div class="bd"><b>Swiss Open P4P</b><span>Uitikon Waldegg</span></div></a>
      </div>
    </div>
    83

    Auszahlung

    Mehrstufiger Ablauf mit Fortschritt: jeder Schritt dreht, bis er grün abgehakt ist, am Ende zeichnet sich ein Häkchen.

    Spesenabrechnung CHF 184.50
    1. Beleg geprüft
    2. Freigegeben
    3. Überwiesen
    Auf dem Weg zu dir
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Auszahlung -->
    <div class="fx-tr" data-fx="transfer">
      <span class="k-kick">Spesenabrechnung</span>
      <span class="fx-tr-amt">CHF 184.50</span>
      <ol class="fx-tr-steps"><li class="fx-tr-step"><i></i>Beleg geprüft</li><li class="fx-tr-step"><i></i>Freigegeben</li><li class="fx-tr-step"><i></i>Überwiesen</li></ol>
      <div class="fx-tr-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>Auf dem Weg zu dir</div>
      <button class="btn btn-success btn-sm fx-tr-btn" type="button">Auszahlung freigeben</button>
    </div>
    84

    Glas-Karte

    Glasfläche über einem Foto: der Hintergrund bricht sich leicht wie hinter echtem Glas. Die Brechung zeigen nur Chromium-Browser, alle anderen zeigen mattes Glas.

    Final heute Abend

    Tisch 1 · Beginn 20:30 · Eintritt frei

    Zum Livestream
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Glas-Karte -->
    <div class="fx-glass-wrap" data-fx="glass" style="background-image:url(assets/fotos/galerie/full/1050.webp)">
      <svg width="0" height="0" style="position:absolute" aria-hidden="true"><filter id="fx-liquid"><feTurbulence type="fractalNoise" baseFrequency=".012" numOctaves="2" seed="4"/><feDisplacementMap in="SourceGraphic" scale="28"/></filter></svg>
      <div class="fx-glass"><b>Final heute Abend</b><p>Tisch 1 · Beginn 20:30 · Eintritt frei</p><span class="btn btn-blue btn-sm">Zum Livestream</span></div>
    </div>
    85

    Punkte-Karte

    Ein Punkteraster weicht dem Mauszeiger aus und leuchtet in seiner Nähe auf.

    InteraktivFahr mit der Maus darüberDie Punkte weichen aus und kehren zurück.
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Punkte-Karte -->
    <div class="fx-panel fx-dots" data-fx="dots"><span class="k-kick">Interaktiv</span><b>Fahr mit der Maus darüber</b><span class="k-muted">Die Punkte weichen aus und kehren zurück.</span></div>
    86

    Spotlight-Karten

    Ein Lichtkegel folgt dem Zeiger, die Karte kippt leicht mit, die anderen treten zurück.

    TrainingJeden Dienstag offen für alle
    TurniereVom Plausch bis zur Liga
    CommunityÜber 100 Mitglieder
    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Spotlight-Karten -->
    <div class="fx-spots" data-fx="spotlight"><div class="fx-spot"><span class="k-item-ic"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2v4" />
        <path d="M16 2v4" />
        <rect width="18" height="18" x="3" y="4" rx="2" />
        <path d="M3 10h18" /></svg></span><b>Training</b><span class="k-muted">Jeden Dienstag offen für alle</span></div><div class="fx-spot"><span class="k-item-ic"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14.66v1.626a2 2 0 0 1-.976 1.696A5 5 0 0 0 7 21.978" />
        <path d="M14 14.66v1.626a2 2 0 0 0 .976 1.696A5 5 0 0 1 17 21.978" />
        <path d="M18 9h1.5a1 1 0 0 0 0-5H18" />
        <path d="M4 22h16" />
        <path d="M6 9a6 6 0 0 0 12 0V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1z" />
        <path d="M6 9H4.5a1 1 0 0 1 0-5H6" /></svg></span><b>Turniere</b><span class="k-muted">Vom Plausch bis zur Liga</span></div><div class="fx-spot"><span class="k-item-ic"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
        <path d="M16 3.128a4 4 0 0 1 0 7.744" />
        <path d="M22 21v-2a4 4 0 0 0-3-3.87" />
        <circle cx="9" cy="7" r="4" /></svg></span><b>Community</b><span class="k-muted">Über 100 Mitglieder</span></div></div>
    87

    Beitrag-Karte

    Ein Beitrag mit Absender, Text, Foto und Antwort. Das Herz zählt mit und wird rot.

    TFCZ vor 2 Stunden

    Was für ein Final! Gratulation an Luca und Jan zur Clubmeisterschaft 2026.

    4

    Mia B. Verdient, starkes Spiel!

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- Beitrag-Karte -->
    <article class="fx-post" data-fx="post">
      <div class="fx-post-h"><span class="k-shield" style="background-image:url(assets/img/emblem-blue.png); background-size:contain; background-repeat:no-repeat"></span><div><b>TFCZ <svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
        <path d="m9 12 2 2 4-4" /></svg></b><span class="k-muted k-small">vor 2 Stunden</span></div></div>
      <p>Was für ein Final! Gratulation an Luca und Jan zur Clubmeisterschaft 2026.</p>
      <div class="fx-post-ph" style="background-image:url(assets/fotos/galerie/full/1050.webp)"></div>
      <div class="fx-post-f"><button class="fx-post-like" type="button" aria-pressed="false" aria-label="Gefällt mir"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5" /></svg><b>38</b></button><span><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" /></svg> 4</span></div>
      <div class="fx-post-reply"><span class="k-shield" style="background-image:url(assets/fotos/galerie/full/781.webp)"></span><p><b style="color:#fff">Mia B.</b> Verdient, starkes Spiel!</p></div>
    </article>
    Daten & ChartsTabellen, Listen, Kalender und Diagramme.21 Bausteine
    88

    Daten & Listen

    Tabellen, Avatare und Akkordeons. Akkordeon-Hover: Brand-Rahmen per inset box-shadow (.acc-head), ohne Layout-Sprung (Sektion 19).

    Standard-Tabelle
    SpielerSiegeTurnierKategorie
    O. Grieder5Zürich OpenPro
    M. Sauter4KäsekickFun
    S. Schöb7SchweizermeisterschaftElite
    Avatare (Initialen, Gold = Key Player oder besondere Rolle)
    VO
    MS
    VO
    MS
    TW
    +5
    Akkordeon

    Schnuppern gratis, Aktivmitglied CHF 200, Key Player CHF 250 (mit eigenem Schlüssel, 24/7).

    Nein, bei uns ist jeder willkommen, vom Anfänger bis zum Weltmeister.

    Landenbergstrasse 10, 8037 Zürich. Seit 1990 am selben Ort.

    89

    Kalender & Dock-Lupe

    Woche = macOS-Dock-Lupe (gehoverter Tag am grössten, Nachbarn geschoben). Monat = Fokuszelle (Gold-Rahmen) zeigt pro Event nur den Kategorie-Badge. Monats-Default = 6 Wochen (aktueller Monat); jeder Klick auf „Mehr laden" hängt +2 Monate an. „Heute" = goldene Zahl.

    Woche · Maus darüberfahren
    Mo5
    Di6
    Mi7
    Do8
    Fr9
    Sa10
    So11
    Monat · Zellen
    7
    8
    Fun
    9
    10
    Derselbe Effekt auf beliebigen Kacheln
    T
    F
    C
    Z
    1
    2
    3
    4
    5

    Der Dock-Effekt steckt in system/components/dock-magnify.js und passt auf jede Reihe mit gleich breiten Kindern: Wochentage, Kacheln, Foto-Streifen, Filter-Chips. Er läuft auf der GPU und ist bei reduzierter Bewegung aus.

    Code für Entwickler
    <div class="myrow">…gleich breite Kinder…</div>
    <script defer src="system/components/dock-magnify.js"></script>
    <script>
    addEventListener('DOMContentLoaded', function(){
      TFCZ.dockMagnify(document.querySelector('.myrow'), {
        amp: 0.34,   // max. Vergrösserung (+34 %)
        sigma: 1.4,  // Reichweite in Zellen
        spread: true // Nachbarn schieben (Dock) statt nur skalieren
      });
    });
    </script>

    Kind-CSS: transform-origin:center bottom; will-change:transform; transition:transform .16s ease. Optionen: fade (entfernte dimmen), scrollFocus (Touch/Strips: beim Scrollen die Mitte vergrössern). Rückgabe: .destroy() / .refresh().

    90

    Fun-Barometer + Kennwert-Balken

    Unten läuft system/components/baro.js, dieselbe Datei wie auf der Home. Web-Pflicht: die vertikale Tube (Spass oben / Wettkampf unten) steht immer zusammen mit dem Beschreibungs-Panel und den Kennwert-Balken. 5 Stufen: Käsekick · Offenes Training · Plausch-Turnier · Pro Turniere · Elite. Auswahl (Hover/Klick) steuert Tube-Level, Kategorie-Akzent und Panel. Die SM gehört zu Elite, ein eigenes „SM-Quali" gibt es nicht. Icons: party-popper / trophy (Lucide, Sektion 20).

    So baut man ihn ein

    Code für Entwickler
    <div data-tfcz="baro"></div>
    <script defer src="system/components/baro.js"></script>

    Die 5 Stufen samt Texten und Kennwerten stehen nur im Baustein, nie ein zweites Barometer bauen. Hover und Auswahl nach Sektion 18. Käsekick (voller Spass) zündet Konfetti; der Info-Knopf öffnet den ausführlichen Text.

    Live: der echte Baustein

    91

    Data Table

    Tabelle zum Sortieren und Auswählen. Klick auf eine Spalte sortiert, die sortierte Spalte ist gold.

    Anna Keller1431
    Ben Frei918
    Luca Jost1740
    Mia Brunner1227
    Nora Egli611

    0 von 5 ausgewählt · Beispieldaten

    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Data Table -->
    <div class="k-col k-dt-wrap"><table class="btable k-table k-dt"><thead><tr><th><label class="ck"><input type="checkbox" class="k-dt-all" aria-label="Alle auswählen"><span class="bx"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span></label></th><th><button type="button" data-sort="1">Name <svg class="ki k-sort-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 16-4 4-4-4" />
        <path d="M17 20V4" />
        <path d="m3 8 4-4 4 4" />
        <path d="M7 4v16" /></svg></button></th><th><button type="button" data-sort="2">Spiele <svg class="ki k-sort-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 16-4 4-4-4" />
        <path d="M17 20V4" />
        <path d="m3 8 4-4 4 4" />
        <path d="M7 4v16" /></svg></button></th><th class="k-num"><button type="button" data-sort="3">Punkte <svg class="ki k-sort-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 16-4 4-4-4" />
        <path d="M17 20V4" />
        <path d="m3 8 4-4 4 4" />
        <path d="M7 4v16" /></svg></button></th></tr></thead>
    <tbody><tr><td><label class="ck"><input type="checkbox" aria-label="Anna Keller auswählen"><span class="bx"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span></label></td><td>Anna Keller</td><td>14</td><td class="k-num">31</td></tr><tr><td><label class="ck"><input type="checkbox" aria-label="Ben Frei auswählen"><span class="bx"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span></label></td><td>Ben Frei</td><td>9</td><td class="k-num">18</td></tr><tr><td><label class="ck"><input type="checkbox" aria-label="Luca Jost auswählen"><span class="bx"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span></label></td><td>Luca Jost</td><td>17</td><td class="k-num">40</td></tr><tr><td><label class="ck"><input type="checkbox" aria-label="Mia Brunner auswählen"><span class="bx"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span></label></td><td>Mia Brunner</td><td>12</td><td class="k-num">27</td></tr><tr><td><label class="ck"><input type="checkbox" aria-label="Nora Egli auswählen"><span class="bx"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg></span></label></td><td>Nora Egli</td><td>6</td><td class="k-num">11</td></tr></tbody></table>
    <p class="k-muted k-small k-dt-count" data-suffix=" · Beispieldaten">0 von 5 ausgewählt · Beispieldaten</p></div>
    Charts
    92

    Area Chart

    Fläche unter der Linie mit Verlauf nach unten. Beim Überfahren zeigt eine Hilfslinie alle Werte des Tages. Beim ersten Sichtbarwerden wischt sich der Chart von links ein.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Area Chart -->
    <figure class="kc" data-kc="area"><script type="application/json">{"title": "Besuche im Clublokal", "note": "Beispieldaten", "x": ["KW 27", "KW 28", "KW 29", "KW 30", "KW 31", "KW 32", "KW 33", "KW 34", "KW 35", "KW 36", "KW 37", "KW 38"], "series": [{"name": "Training", "values": [42, 48, 45, 51, 58, 55, 61, 66, 63, 70, 74, 79]}, {"name": "Turniere", "values": [18, 22, 15, 26, 24, 30, 28, 34, 31, 36, 33, 41]}]}</script></figure>
    93

    Bar Chart

    Balken wachsen gestaffelt von unten. Der höchste Wert ist gold, alle anderen blau.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Bar Chart -->
    <figure class="kc" data-kc="bar"><script type="application/json">{"title": "Neue Anmeldungen pro Monat", "note": "Beispieldaten", "x": ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep"], "series": [{"name": "Anmeldungen", "values": [6, 8, 11, 9, 14, 19, 12, 16, 13]}]}</script></figure>
    94

    Candlestick Chart

    Start, Hoch, Tief und Ende je Zeitraum. Grün ist gestiegen, Rot gesunken; die Kerzen wachsen aus der Mitte.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Candlestick Chart -->
    <figure class="kc" data-kc="candlestick"><script type="application/json">{"title": "Rating-Verlauf pro Woche", "note": "Beispieldaten", "x": ["KW 27", "KW 28", "KW 29", "KW 30", "KW 31", "KW 32", "KW 33", "KW 34", "KW 35", "KW 36", "KW 37", "KW 38"], "ohlc": [[1480, 1502, 1471, 1495], [1495, 1510, 1482, 1488], [1488, 1499, 1460, 1466], [1466, 1490, 1458, 1486], [1486, 1528, 1480, 1521], [1521, 1540, 1512, 1517], [1517, 1523, 1494, 1500], [1500, 1531, 1498, 1529], [1529, 1560, 1522, 1552], [1552, 1558, 1530, 1536], [1536, 1570, 1533, 1566], [1566, 1590, 1558, 1581]]}</script></figure>
    95

    Choropleth Chart

    Fläche nach Wert eingefärbt. Die Kantone stehen als Kacheln in ungefährer Lage, je dunkler, desto mehr.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Choropleth Chart -->
    <figure class="kc" data-kc="choropleth"><script type="application/json">{"title": "Mitglieder nach Wohnkanton", "note": "Beispieldaten", "unit": " Mitglieder", "tiles": [{"id": "BS", "label": "Basel-Stadt", "col": 3, "row": 0, "value": 4}, {"id": "BL", "label": "Basel-Land", "col": 3, "row": 1, "value": 3}, {"id": "SH", "label": "Schaffhausen", "col": 5, "row": 0, "value": 2}, {"id": "TG", "label": "Thurgau", "col": 6, "row": 1, "value": 3}, {"id": "ZH", "label": "Zürich", "col": 5, "row": 1, "value": 71}, {"id": "AG", "label": "Aargau", "col": 4, "row": 1, "value": 9}, {"id": "SO", "label": "Solothurn", "col": 2, "row": 1, "value": 1}, {"id": "JU", "label": "Jura", "col": 1, "row": 1, "value": 0}, {"id": "SG", "label": "St. Gallen", "col": 6, "row": 2, "value": 4}, {"id": "AR", "label": "Appenzell", "col": 7, "row": 2, "value": 1}, {"id": "ZG", "label": "Zug", "col": 5, "row": 2, "value": 5}, {"id": "LU", "label": "Luzern", "col": 4, "row": 2, "value": 3}, {"id": "BE", "label": "Bern", "col": 3, "row": 2, "value": 4}, {"id": "NE", "label": "Neuenburg", "col": 1, "row": 2, "value": 1}, {"id": "SZ", "label": "Schwyz", "col": 6, "row": 3, "value": 3}, {"id": "NW", "label": "Nidwalden", "col": 4, "row": 3, "value": 0}, {"id": "OW", "label": "Obwalden", "col": 3, "row": 3, "value": 1}, {"id": "FR", "label": "Freiburg", "col": 2, "row": 3, "value": 1}, {"id": "VD", "label": "Waadt", "col": 1, "row": 3, "value": 2}, {"id": "GL", "label": "Glarus", "col": 7, "row": 3, "value": 1}, {"id": "UR", "label": "Uri", "col": 5, "row": 3, "value": 0}, {"id": "GR", "label": "Graubünden", "col": 7, "row": 4, "value": 2}, {"id": "TI", "label": "Tessin", "col": 6, "row": 4, "value": 1}, {"id": "VS", "label": "Wallis", "col": 3, "row": 4, "value": 1}, {"id": "GE", "label": "Genf", "col": 0, "row": 4, "value": 2}]}</script></figure>
    96

    Composed Chart

    Balken und Linie auf einer Zeitachse, mit eigener Skala links und rechts.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Composed Chart -->
    <figure class="kc" data-kc="composed"><script type="application/json">{"title": "Spiele und Tore pro Abend", "note": "Beispieldaten", "x": ["1.9.", "3.9.", "8.9.", "10.9.", "15.9.", "17.9.", "22.9.", "24.9."], "bars": {"name": "Gespielte Partien", "values": [38, 22, 44, 27, 51, 30, 47, 35]}, "line": {"name": "Tore pro Partie", "values": [8.2, 7.4, 8.9, 7.9, 9.3, 8.1, 8.8, 8.4]}}</script></figure>
    97

    Funnel Chart

    Stufen, die schmaler werden. Der Tooltip nennt den Anteil der Stufe davor.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Funnel Chart -->
    <figure class="kc" data-kc="funnel"><script type="application/json">{"title": "Vom Besuch zur Mitgliedschaft", "note": "Beispieldaten", "items": [{"label": "Website besucht", "value": 1800}, {"label": "Anfrage gesendet", "value": 240}, {"label": "Probetraining", "value": 96}, {"label": "Mitglied geworden", "value": 31}]}</script></figure>
    98

    Gauge

    Ein Wert auf einer Skala, mit Zeiger. Der Bogen läuft von Blau nach Gold.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Gauge -->
    <figure class="kc" data-kc="gauge"><script type="application/json">{"title": "Helfer für das Sommerfest", "note": "Beispieldaten", "value": 27, "max": 36, "label": "eingeteilt"}</script></figure>
    99

    Heatmap Chart

    Raster aus Wochentag und Uhrzeit, je dunkler, desto mehr los. Die Felder erscheinen diagonal.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Heatmap Chart -->
    <figure class="kc" data-kc="heatmap"><script type="application/json">{"title": "Wann ist im Clublokal am meisten los?", "note": "Beispieldaten", "unit": " Personen", "rows": ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], "cols": ["16 Uhr", "17 Uhr", "18 Uhr", "19 Uhr", "20 Uhr", "21 Uhr", "22 Uhr", "23 Uhr"], "values": [[0, 0, 1, 2, 2, 1, 0, 0], [1, 3, 9, 18, 24, 22, 15, 6], [0, 1, 2, 3, 4, 3, 1, 0], [1, 2, 6, 14, 19, 17, 10, 3], [0, 1, 3, 6, 9, 11, 9, 5], [4, 8, 11, 12, 10, 6, 3, 1], [2, 3, 4, 3, 2, 1, 0, 0]]}</script></figure>
    100

    Line Chart

    Mehrere Verläufe ohne Fläche. Die Hilfslinie zeigt beide Werte am selben Punkt.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Line Chart -->
    <figure class="kc" data-kc="line"><script type="application/json">{"title": "Ranglistenpunkte im Vergleich", "note": "Beispieldaten", "x": ["KW 27", "KW 28", "KW 29", "KW 30", "KW 31", "KW 32", "KW 33", "KW 34", "KW 35", "KW 36", "KW 37", "KW 38"], "series": [{"name": "Luca J.", "values": [112, 118, 121, 119, 127, 134, 131, 138, 142, 140, 149, 153]}, {"name": "Mia B.", "values": [98, 104, 111, 116, 115, 121, 127, 133, 131, 138, 141, 147]}]}</script></figure>
    101

    Live Line Chart

    Neue Werte laufen jede Sekunde von rechts ein. Der Chart pausiert, sobald er nicht mehr zu sehen ist.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Live Line Chart -->
    <figure class="kc" data-kc="live"><script type="application/json">{"title": "Livestream: Zuschauer", "note": "simuliert", "name": "Zuschauer", "start": 60, "min": 20, "max": 120}</script></figure>
    102

    Pie Chart

    Anteile als Ring mit der Summe in der Mitte. Die Stücke laufen im Uhrzeigersinn ein.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Pie Chart -->
    <figure class="kc" data-kc="pie"><script type="application/json">{"title": "Mitglieder nach Art", "note": "Beispieldaten", "centerLabel": "Mitglieder", "items": [{"label": "Aktivmitglied", "value": 64}, {"label": "Passivmitglied", "value": 21}, {"label": "Key Player", "value": 12}, {"label": "Gönner", "value": 11}]}</script></figure>
    103

    Profit/Loss Line

    Eine Linie um die Nulllinie: darüber grün, darunter rot, jeweils mit Fläche.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Profit/Loss Line -->
    <figure class="kc" data-kc="pl"><script type="application/json">{"title": "Kasse: Ergebnis pro Abend", "note": "Beispieldaten", "name": "Ergebnis", "unit": " CHF", "x": ["1.9.", "3.9.", "8.9.", "10.9.", "15.9.", "17.9.", "22.9.", "24.9.", "29.9."], "values": [180, 95, -60, -120, 40, 210, 150, -35, 120]}</script></figure>
    104

    Radar Chart

    Stärken auf mehreren Achsen, zwei Profile übereinander.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Radar Chart -->
    <figure class="kc" data-kc="radar"><script type="application/json">{"title": "Spielerprofil", "note": "Beispieldaten", "axes": ["Pass", "Schuss", "Abwehr", "Ballkontrolle", "Taktik", "Ausdauer"], "series": [{"name": "Luca J.", "values": [82, 91, 64, 77, 70, 58]}, {"name": "Mia B.", "values": [74, 66, 88, 81, 79, 72]}]}</script></figure>
    105

    Ring Chart

    Mehrere Fortschritte als Ringe ineinander. Die Zahl in der Mitte gehört zum äussersten Ring.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Ring Chart -->
    <figure class="kc" data-kc="ring"><script type="application/json">{"title": "Saisonziele", "note": "Beispieldaten", "items": [{"label": "Neue Mitglieder", "value": 31, "max": 40}, {"label": "Turniere", "value": 48, "max": 60}, {"label": "Helferstunden", "value": 410, "max": 800}]}</script></figure>
    106

    Sankey Chart

    Ströme zwischen Stufen, die Breite zeigt die Menge. Wischt sich von links ein.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Sankey Chart -->
    <figure class="kc" data-kc="sankey"><script type="application/json">{"title": "Wie neue Leute zu uns finden", "note": "Beispieldaten", "unit": " Personen", "nodes": [{"id": "web", "label": "Website", "col": 0}, {"id": "fr", "label": "Freunde", "col": 0}, {"id": "tu", "label": "Turnier", "col": 0}, {"id": "pt", "label": "Probetraining", "col": 1}, {"id": "mg", "label": "Mitglied", "col": 2}, {"id": "ns", "label": "Nicht geblieben", "col": 2}], "links": [{"from": "web", "to": "pt", "value": 40}, {"from": "fr", "to": "pt", "value": 38}, {"from": "tu", "to": "pt", "value": 18}, {"from": "pt", "to": "mg", "value": 31}, {"from": "pt", "to": "ns", "value": 65}]}</script></figure>
    107

    Scatter Chart

    Punkte in zwei Dimensionen. Sie erscheinen nacheinander, der Tooltip nennt Name und Werte.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Scatter Chart -->
    <figure class="kc" data-kc="scatter"><script type="application/json">{"title": "Spiele und Siegquote", "note": "Beispieldaten", "xLabel": "Spiele", "yLabel": "Siegquote", "yUnit": " %", "points": [{"label": "Anna K.", "x": 14, "y": 57}, {"label": "Ben F.", "x": 9, "y": 44}, {"label": "Luca J.", "x": 38, "y": 71}, {"label": "Mia B.", "x": 27, "y": 63}, {"label": "Nora E.", "x": 6, "y": 33}, {"label": "Timo G.", "x": 22, "y": 50}, {"label": "Sara L.", "x": 31, "y": 58}, {"label": "Jan M.", "x": 17, "y": 47}, {"label": "Eva R.", "x": 12, "y": 38}, {"label": "Noah S.", "x": 35, "y": 66}]}</script></figure>
    108

    Sunburst Chart

    Hierarchie als Ringe: innen die Gruppe, aussen ihre Teile.

    Code für Entwickler

    Braucht die drei Stylesheets und charts.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/charts.js"></script>
    
    <!-- Sunburst Chart -->
    <figure class="kc" data-kc="sunburst"><script type="application/json">{"title": "Verkauf an der Bar", "note": "Beispieldaten", "unit": " Stück", "children": [{"label": "Bier", "children": [{"label": "Lager", "value": 180}, {"label": "Weizen", "value": 60}, {"label": "Alkoholfrei", "value": 35}]}, {"label": "Softdrinks", "children": [{"label": "Cola", "value": 120}, {"label": "Eistee", "value": 90}, {"label": "Wasser", "value": 70}]}, {"label": "Snacks", "children": [{"label": "Chips", "value": 65}, {"label": "Nüsse", "value": 30}]}, {"label": "Kaffee", "children": [{"label": "Espresso", "value": 40}, {"label": "Café crème", "value": 25}]}]}</script></figure>
    Medien, Chat & KIBilder, Stream, Karte, Unterhaltungen und der Assistent.16 Bausteine
    109

    Bild-Player

    Klickt man ein Bild einer Galerie an, öffnet sich der Player: grosses Bild, Pfeile, Vorschaubilder unten und das X zum Schliessen. Er kommt aus system/components/lightbox.js, derselben Datei wie auf Home und Training. Eine Bilderstrecke hat immer den Player.

    Live: klick ein Bild
    TFCZ Spielszene
    TFCZ Spielszene
    TFCZ Spielszene
    Clublokal

    Schliessen per X, Esc oder Klick neben das Bild (Sektion 62).

    Code für Entwickler
    <!-- 1. Bilder-Gruppe markieren -->
    <section data-lightbox>
      <img src="assets/fotos/action/action-01.jpg" alt="">
    </section>
    
    <!-- 2. Baustein laden -->
    <script defer src="system/components/lightbox.js"></script>
    
    <!-- Eigene Drag-Logik (Home-Marquee): data-lightbox-manual setzen und TFCZ.lightbox.openFrom(imgEl) aufrufen -->
    110

    Getränkeliste

    Ein Baustein, eine Liste. Die Struktur steht in system/drinks.js, die Darstellung in system/components/drinks.js. So gibt es vom Sortiment nur eine Fassung.

    So baut man sie ein

    Code für Entwickler
    <!-- Platzhalter — mehr schreibt die Seite nicht -->
    <div data-tfcz="drinks" data-snacks="0" data-preis="vor Ort"></div>
    
    <script src="system/drinks.js"></script>
    <script defer src="system/components/drinks.js"></script>

    Optionen: data-snacks="0" (Snacks weglassen)data-preis (Text für noch offene Preise)data-spaltendata-zahlung="1" (Zahlungshinweis darunter).

    Live: der echte Baustein

    Regeln

    • Preise kommen aus der API über GET {TFCZ_API}/api/drinks. Sie stehen nie im HTML und nie in system/drinks.js. Dort steht nur die Struktur; preis: null heisst „kommt vom Server".
    • Sobald window.TFCZ_API gesetzt ist (auch nachträglich), holt der Baustein die Live-Liste und rendert neu. Antwortet der Server nicht, bleibt der Platzhalter stehen. Die Seite bricht nie.
    • Sortiment ändern = eine Datei: system/drinks.js. Nie eine Liste in eine Seite kopieren.
    • Icons = Lucide (beer, cup-soda, coffee, cookie, Sektion 20). Kategorie-Zeile in Gold, Preis in Gold, offener Preis gedämpft.
    • Später lesen Bar-Display und Kassensystem dieselbe Quelle.
    111

    Stream · Spielstatistik-Widget

    Vollbild-Grafik (1920×1080), die im Livestream zwischen den Spielen erscheint: eine Statistik pro Screen, Auto-Wechsel alle 5 Sek. Nur die Zahlen tragen Team-Farbe (Grün vs. Schwarz). Die Tisch-Grafik mit Pfeilen zeigt, wo auf dem Tisch die Aktion passiert. Das verstehen auch Neulinge.

    Vollbild öffnen 6 Screens · rotiert alle 5 Sek. · Code: spielstatistik-widget.html · engine.js · pitch.js
    Team-Kennzeichnung: nur die Zahlen
    Grün · #57A639 Schwarz · #0c0f14 + heller Rand

    Sonst strikt Brand: Navy-Hintergrund, Gold nur Marke/Akzent, Blau für Fenster-Signatur & Feldlinien.

    Screens & Ablauf

    Endstand → Aufbau (Pass 5→3) → Abschluss (Schuss) → Torwartzone → Tor aus dem Mittelfeld → Ballverluste. Ein Screen erscheint nur, wenn Daten da sind.

    Do

    1 Statistik pro Screen · grosse Zahlen (mobil lesbar) · Tisch + Pfeile zeigen wo · immer beide Richtungen · nur Zahlen grün/schwarz · Namen voll auf dem Screen, Kurzform im Kopf.

    Don't

    Mehrere Stats auf einen Screen · viel Text · Gold für Teams · schwarze Zahl ohne Rand · Pfeil/Highlight in Gold statt Team-Farbe · Assets extern verlinken.

    Live-Scoreboard

    Scoreboard · Ecken-Overlay

    Kompaktes Live-Board (Sätze · Tore · Timeouts · Set-/Match-Ball · Turnier-Runde), im Stream unten links. Fenster-Signatur (blau oben / gold unten) auf frosted Navy, Nunito Sans. Team-Identität wie beim Stat-Widget: Grün #57A639 vs. Schwarz #0c0f14 + heller Rand. Team-Farbe tragen nur Name & Tore. Sätze bleiben neutral, Gold nur als Akzent (Turnier-Zeile), Blau für Sätze & Set-Ball. Leader = dezenter Glow, nie Gold.

    Vollbild öffnen Live-Widget: sop/public/widgets/scoreboard.html · Position per ?position=bottom-left · Daten via WebSocket//state
    Live-Timer

    Possession-Timer · Ballbesitz

    Zeigt oben rechts, wie lange ein Stab den Ball hält (3er / 5er / Torwart). Die Anzeige zählt live hoch, friert beim Limit ein und wird rot über dem Limit (3er/Torwart 15s, 5er 10s). Fenster-Signatur (blau oben / gold unten) auf frosted Navy, Nunito Sans, goldenes Eyebrow. Gold nur Akzent, nie für Teams.

    Vollbild öffnen Live-Widget: sop/public/widgets/possession.html · Position per ?position=top-right · Trigger POSSESSION_TIMER
    Kamera

    Kamera-Rahmen · Einzel & Split

    Transparentes Overlay für die Kamera-Einblendungen (Player L/R, Rally, Defensive). Es ersetzt die Vollfarbe hinter der Kamera und trägt die Fenster-Signatur blau oben / gold unten, dazu Navy-Rahmen und runde Ecken; die Mitte bleibt durchsichtig für die Kamera. Split = zwei gerahmte Fenster mit Navy-Trenner (neue OBS-Szene). In OBS als Browser-Quelle über die Kamera legen, Grösse = Kamera-Bereich.

    Einzel öffnen Split öffnen Overlay: sop/public/widgets/camera-frame.html · ?mode=single|split · Feintuning ?lw= ?r= ?m=
    112

    Karte · Google Maps

    Ein Baustein, beide Seiten. Home und Firmenevents zeigen dieselbe Karte über system/components/map.js. Es ist Google Maps, keyless (klassischer output=embed, kein API-Key, keine Kosten), hell, mit Pin direkt an der Vereinsadresse. Den Pin setzt die Adresse als Suchbegriff (q=), die Google selbst geocodiert; mit Koordinaten sass er daneben. Die Adresse kommt aus system/content.js (TFCZ.content.ort.label). loading="lazy" lädt die Karte erst beim Hinscrollen. Kein Dark Mode, denn dunkles Google Maps bräuchte die JavaScript-API mit API-Key.

    So baut man sie ein

    <div data-tfcz="map"></div>
    <script src="system/content.js"></script>
    <script defer src="system/components/map.js"></script>

    Optional pro Platzhalter: data-q="Strasse 1, 8000 Ort" (Adresse überschreiben) · data-zoom="16". Zieht der Verein um, ändert man eine Zeile in system/content.js, und beide Karten folgen.

    Live: der echte Baustein

    Layout-Animationen
    113

    Now playing

    Ein Mini-Player öffnet sich zum grossen Player. Bild, Titel und Untertitel wandern dabei an ihre neue Stelle, der Rest blendet ein. Für laufende Inhalte wie den Livestream.

    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Now playing -->
    <div class="k-np">
      <div class="k-np-full" hidden role="region" aria-label="Livestream">
        <div class="k-np-top k-np-rest"><span class="k-np-live">Live</span><button class="k-np-close" type="button" aria-label="Verkleinern"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg></button></div>
        <span class="k-np-cover" style="background-image:url(assets/fotos/galerie/full/1050.webp)"></span>
        <span class="k-np-t">Clubmeisterschaft · Final</span>
        <span class="k-np-s">Tisch 1 · 412 schauen zu</span>
        <div class="k-np-score k-np-rest"><span>Luca J. &amp; Jan M.</span><b>4 : 3</b><span>Timo G. &amp; Ramon K.</span></div>
        <div class="k-prog-tr k-thin k-np-rest"><span style="transform:scaleX(.62)"></span></div>
        <a class="btn btn-blue btn-sm k-np-rest" href="#" onclick="return false"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m17 2-5 5-5-5" />
        <rect width="20" height="15" x="2" y="7" rx="2" /></svg> Zum Stream</a>
      </div>
      <button class="k-np-mini" type="button" aria-label="Livestream öffnen">
        <span class="k-np-cover" style="background-image:url(assets/fotos/galerie/full/1050.webp)"></span>
        <span><span class="k-np-t">Clubmeisterschaft · Final</span><span class="k-np-s">Tisch 1 · 412 schauen zu</span></span>
        <svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16.247 7.761a6 6 0 0 1 0 8.478" />
        <path d="M19.075 4.933a10 10 0 0 1 0 14.134" />
        <path d="M4.925 19.067a10 10 0 0 1 0-14.134" />
        <path d="M7.753 16.239a6 6 0 0 1 0-8.478" />
        <circle cx="12" cy="12" r="2" /></svg>
      </button>
    </div>
    114

    Karte öffnet sich

    Eine Karte wächst aus dem Raster zur Detailansicht und beim Schliessen wieder zurück an ihren Platz. Nur für Inhalte, die sich aus einer Kachel öffnen; Dialoge und Popups schliessen weiterhin sofort.

    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Karte öffnet sich -->
    <div class="k-xc">
      <button class="k-xc-card" type="button"><span class="k-xc-img" style="background-image:url(assets/fotos/galerie/full/1051.webp)"></span><span class="k-xc-k">Samstag, 3. Oktober</span><span class="k-xc-t">Kindertraining</span><template><p>Jeden Samstag von 14 bis 16 Uhr betreuen zwei Trainer eine Gruppe von höchstens zwölf Kindern. Bälle und Tische stellen wir.</p><p>Die ersten zwei Trainings sind gratis, danach gilt der Jahresbeitrag für Junioren.</p><a class="btn btn-blue btn-sm" href="#" onclick="return false">Anmelden</a></template></button><button class="k-xc-card" type="button"><span class="k-xc-img" style="background-image:url(assets/fotos/galerie/full/795.webp)"></span><span class="k-xc-k">Freitag, 9. Oktober</span><span class="k-xc-t">Plausch-Turnier</span><template><p>Doppel mit gelosten Partnern, gespielt wird im Schweizer System und danach im KO. Anmeldung vor Ort bis 19:30.</p><p>Startgeld zehn Franken, für Mitglieder die Hälfte.</p><a class="btn btn-blue btn-sm" href="#" onclick="return false">Anmelden</a></template></button><button class="k-xc-card" type="button"><span class="k-xc-img" style="background-image:url(assets/fotos/galerie/full/781.webp)"></span><span class="k-xc-k">Dienstag, jede Woche</span><span class="k-xc-t">Offenes Training</span><template><p>Ab 19 Uhr sind die Tische für alle offen. Du brauchst keine Anmeldung und keine eigene Ausrüstung.</p><p>Wer mag, bekommt Tipps von Spielerinnen und Spielern aus der Nationalliga.</p><a class="btn btn-blue btn-sm" href="#" onclick="return false">Anmelden</a></template></button>
      <div class="k-xc-ov" hidden><div class="k-xc-panel" role="dialog" aria-modal="true" aria-label="Details">
        <span class="k-xc-img"></span>
        <button class="xclose k-xc-close" type="button" aria-label="Schliessen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18" />
        <path d="m6 6 12 12" /></svg></button>
        <div class="k-xc-head"><span class="k-xc-k"></span><span class="k-xc-t"></span></div>
        <div class="k-xc-body"></div>
      </div></div>
    </div>
    115

    Foto öffnet sich

    Eine Kachel wird zum grossen Bild, über die View-Transition-API des Browsers. Wo der Browser sie nicht kennt, öffnet sich das Bild ohne Übergang.

    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Foto öffnet sich -->
    <div class="k-vt">
      <button class="k-vt-thumb" type="button" data-cap="Spielszene am Tisch" aria-label="Spielszene am Tisch gross anzeigen"><img src="assets/fotos/galerie/full/795.webp" alt="Spielszene am Tisch" loading="lazy"></button><button class="k-vt-thumb" type="button" data-cap="Mannschaft im Clublokal" aria-label="Mannschaft im Clublokal gross anzeigen"><img src="assets/fotos/galerie/full/781.webp" alt="Mannschaft im Clublokal" loading="lazy"></button><button class="k-vt-thumb" type="button" data-cap="Final der Clubmeisterschaft" aria-label="Final der Clubmeisterschaft gross anzeigen"><img src="assets/fotos/galerie/full/1050.webp" alt="Final der Clubmeisterschaft" loading="lazy"></button><button class="k-vt-thumb" type="button" data-cap="Kindertraining" aria-label="Kindertraining gross anzeigen"><img src="assets/fotos/galerie/full/1051.webp" alt="Kindertraining" loading="lazy"></button>
      <div class="k-vt-big" hidden role="dialog" aria-modal="true" aria-label="Foto"><button class="xclose k-vt-close" type="button" aria-label="Schliessen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18" />
        <path d="m6 6 12 12" /></svg></button><img alt=""><p class="k-vt-cap"></p></div>
    </div>
    Chat
    116

    Message

    Eine Nachricht mit Emblem-Avatar, Name, Zeit und Text.

    Luca Jost19:42

    Tisch 3 ist frei, wer spielt noch eine Runde?

    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Message -->
    <div class="k-msg k-w400"><span class="k-shield" style="background-image:url(assets/fotos/galerie/full/781.webp)"></span><div><p class="k-msg-h"><b>Luca Jost</b><span class="k-muted k-small">19:42</span></p><p>Tisch 3 ist frei, wer spielt noch eine Runde?</p></div></div>
    117

    Bubble

    Nachricht als Sprechblase. Eigene rechts in Blau, andere links in Frost, Reaktionen darunter.

    Kommst du heute ins Training?
    Ja, ab 19:30 bin ich da. 2
    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Bubble -->
    <div class="k-bubbles k-w400"><div class="k-bub them">Kommst du heute ins Training?</div><div class="k-bub me">Ja, ab 19:30 bin ich da.<span class="k-react"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" />
        <path d="M7 10v12" /></svg> 2</span></div></div>
    118

    Marker

    Hinweis mitten im Gespräch: Tagestrenner oder Systemnotiz, ruhig und zentriert.

    Heute

    Mia Brunner ist der Gruppe beigetreten

    Code für Entwickler

    Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    
    <!-- Marker -->
    <div class="k-w400"><div class="k-marker"><span>Heute</span></div><p class="k-sysnote"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
        <circle cx="9" cy="7" r="4" />
        <line x1="19" x2="19" y1="8" y2="14" />
        <line x1="22" x2="16" y1="11" y2="11" /></svg> Mia Brunner ist der Gruppe beigetreten</p></div>
    119

    Message Scroller

    Chatverlauf zum Scrollen. Wer nach oben scrollt, bekommt einen Knopf zurück zur neuesten Nachricht.

    Wer bringt am Freitag die Bälle mit?
    Ich nehme zwei Sets mit.
    Super, danke!
    Beginnen wir um 19 Uhr?
    Ja, Auslosung um 19:15.
    Wie viele Anmeldungen haben wir?
    Aktuell 14 Teams.
    Dann brauchen wir alle vier Tische.
    Sind reserviert.
    Code für Entwickler

    Braucht die drei Stylesheets und ui.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/ui.js"></script>
    
    <!-- Message Scroller -->
    <div class="k-ms k-w400"><div class="k-ms-list">
      <div class="k-bub them">Wer bringt am Freitag die Bälle mit?</div><div class="k-bub me">Ich nehme zwei Sets mit.</div><div class="k-bub them">Super, danke!</div><div class="k-bub them">Beginnen wir um 19 Uhr?</div><div class="k-bub me">Ja, Auslosung um 19:15.</div><div class="k-bub them">Wie viele Anmeldungen haben wir?</div><div class="k-bub me">Aktuell 14 Teams.</div><div class="k-bub them">Dann brauchen wir alle vier Tische.</div><div class="k-bub me">Sind reserviert.</div>
    </div><button class="k-ms-jump" type="button" hidden><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14" />
        <path d="m19 12-7 7-7-7" /></svg> Neueste</button></div>
    KI-Assistent
    120

    KI-Eingabe

    Eingabe für den TFCZ-Assistenten: wächst mit dem Text, das Thema lässt sich umschalten, Enter sendet. Der Senden-Knopf ist erst aktiv, wenn etwas dasteht.

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- KI-Eingabe -->
    <div class="fx-ai" data-fx="ai-prompt">
      <textarea rows="1" placeholder="Frag den TFCZ-Assistenten …" aria-label="Frage an den Assistenten"></textarea>
      <div class="fx-ai-bar"><button class="fx-ai-chip fx-ai-topic" type="button" data-topics="Allgemein|Training|Turniere|Kasse"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z" />
        <circle cx="7.5" cy="7.5" r=".5" fill="currentColor" /></svg><span>Allgemein</span></button><button class="fx-ai-chip" type="button" aria-label="Datei anhängen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551" /></svg></button><button class="fx-ai-send" type="button" aria-label="Senden"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 7-7 7 7" />
        <path d="M12 19V5" /></svg></button></div>
    </div>
    121

    KI-Suche

    Wie die KI-Eingabe, dazu ein Schalter, der die Suche auf das Regelwerk ausdehnt. Eingeschaltet trägt er den goldenen Rahmen.

    Code für Entwickler

    Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

    <!-- im <head> -->
    <link rel="stylesheet" href="system/tokens.css">
    <link rel="stylesheet" href="system/buttons.css">
    <link rel="stylesheet" href="system/ui.css">
    <script defer src="system/components/effects.js"></script>
    
    <!-- KI-Suche -->
    <div class="fx-ai" data-fx="ai-search">
      <textarea rows="1" placeholder="Frag den TFCZ-Assistenten …" aria-label="Suche"></textarea>
      <div class="fx-ai-bar"><button class="fx-ai-chip fx-ai-web" type="button" aria-pressed="false"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 7v14" />
        <path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" /></svg><span>Regelwerk</span></button><button class="fx-ai-chip" type="button" aria-label="Datei anhängen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551" /></svg></button><button class="fx-ai-send" type="button" aria-label="Suchen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34" />
        <circle cx="11" cy="11" r="8" /></svg></button></div>
    </div>
    122

    KI arbeitet

    Der Assistent zeigt Schritt für Schritt, was er gerade tut, mit dem TFCZ-Spinner. Erledigtes bekommt ein grünes Häkchen.

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- KI arbeitet -->
      <div class="fx-ai-load" data-fx="ai-loading" data-steps='["Kalender lesen","Freie Tische prüfen","Anmeldungen zählen","Antwort schreiben"]'>
        <span class="spin" aria-hidden="true"></span>
        <ol class="fx-ai-log" aria-live="polite"></ol>
      </div>
      123

      KI denkt nach

      Kurzer Hinweis, solange eine Antwort entsteht. Der Text wechselt und glänzt dabei leicht.

      Denkt nach …
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- KI denkt nach -->
      <div class="fx-ai-think" data-fx="ai-thinking" data-steps='["Denkt nach …","Sucht im Kalender …","Formuliert die Antwort …"]' role="status"><span class="spin fx-spin-sm"></span><span class="fx-shimmer">Denkt nach …</span></div>
      124

      Spracheingabe

      Tippen startet die Aufnahme: der Knopf wird rot und pulsiert, die Balken zeigen den Pegel, die Zeit läuft mit. Nochmals tippen beendet.

      00:00Zum Sprechen tippen
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Spracheingabe -->
      <div class="fx-voice" data-fx="ai-voice"><button class="fx-voice-btn" type="button" aria-pressed="false" aria-label="Spracheingabe"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19v3" />
          <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
          <rect x="9" y="2" width="6" height="13" rx="3" /></svg></button><div class="fx-voice-bars" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><span class="fx-voice-time">00:00</span><span class="fx-voice-lbl">Zum Sprechen tippen</span></div>
      EffekteBewegung für Flächen, Texte und das Scrollen. Sparsam und nur mit Grund einsetzen.19 Bausteine
      Hintergrund
      125

      Schwerkraft-Wirbel

      Der Standard-Hintergrund aller TFCZ-Seiten: ein Punkteraster, das um den Mauszeiger wirbelt und danach sanft zurückkehrt. Einschalten und mit der Maus über die ganze Seite fahren. Er kommt aus dem Baustein assets/js/bg-swirl.js, der Canvas und CSS selbst anlegt; er wird nie pro Seite kopiert. Auf dem Handy und bei «Bewegung reduzieren» ist er aus.

      Danach mit der Maus über die Seite fahren.
      Code für Entwickler

      Nur diese eine Zeile, der Baustein legt alles Weitere selbst an. Die Einbindung steht einmal im <head>.

      <!-- im <head>: Schwerkraft-Wirbel -->
      <script defer src="assets/js/bg-swirl.js"></script>
      126

      Linien-Hintergrund

      Fliessende Linien in Blau und Gold, die sich langsam weiterzeichnen. Nur innerhalb einer Fläche wie einer Hero-Kachel; der Seitenhintergrund bleibt der Schwerkraft-Wirbel.

      Clubmeisterschaft 2026Samstag, 14. November
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Linien-Hintergrund -->
      <div class="fx-panel fx-bg fx-paths" data-fx="bg-paths"><svg viewBox="-380 -60 1064 620" preserveAspectRatio="xMidYMid slice" aria-hidden="true"></svg><div class="fx-bg-txt"><b>Clubmeisterschaft 2026</b><span>Samstag, 14. November</span></div></div>
      127

      Lichtstrahlen

      Schräge Lichtstrahlen, die langsam durch die Fläche ziehen. Leise dosiert, damit der Text lesbar bleibt.

      LivestreamJeden Freitag ab 20 Uhr
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Lichtstrahlen -->
      <div class="fx-panel fx-bg" data-fx="bg-beams"><div class="fx-bg-txt"><b>Livestream</b><span>Jeden Freitag ab 20 Uhr</span></div></div>
      128

      Strömungsfeld

      Punkte folgen einem Strömungsfeld und hinterlassen Spuren, wie Bälle, die über den Tisch laufen.

      Töggele in Zürichseit 1990
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Strömungsfeld -->
      <div class="fx-panel fx-bg" data-fx="bg-flow"><div class="fx-bg-txt"><b>Töggele in Zürich</b><span>seit 1990</span></div></div>
      129

      Formen-Hero

      Durchscheinende Formen fallen beim Sichtbarwerden ein und schweben danach leicht, dann erscheint der Text.

      Komm an den TischOffenes Training jeden Dienstag
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Formen-Hero -->
      <div class="fx-panel fx-bg fx-shapes" data-fx="bg-shapes">
        <span class="fx-shape" style="--r:-14deg; width:260px; height:70px; left:-40px; top:30px; background:rgba(92,167,220,.12)"></span>
        <span class="fx-shape" style="--r:12deg; width:200px; height:56px; right:-30px; top:60px; background:rgba(205,168,87,.12)"></span>
        <span class="fx-shape" style="--r:-8deg; width:160px; height:44px; left:18%; bottom:22px; background:rgba(92,167,220,.1)"></span>
        <span class="fx-shape" style="--r:20deg; width:110px; height:34px; right:14%; bottom:36px; background:rgba(205,168,87,.1)"></span>
        <div class="fx-bg-txt fx-shape-txt"><b>Komm an den Tisch</b><span>Offenes Training jeden Dienstag</span></div>
      </div>
      Text
      130

      Wechselnder Gruss

      Ein Wort wechselt alle zwei Sekunden, hier der Gruss in den Landessprachen.

      Grüeziam Tisch

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Wechselnder Gruss -->
      <p class="fx-txt fx-dyn" data-fx="dynamic" data-words='["Grüezi","Bonjour","Buongiorno","Allegra","Hello"]' aria-live="polite"><span class="fx-dyn-w">Grüezi</span><span>am Tisch</span></p>
      131

      Glitch-Text

      Kurze Störung in Blau und Gold, alle paar Sekunden. Sparsam einsetzen, etwa für ein Event mit Technik-Thema.

      LIVESTREAM

      Code für Entwickler

      Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      
      <!-- Glitch-Text -->
      <p class="fx-txt"><span class="fx-glitch" data-text="LIVESTREAM">LIVESTREAM</span></p>
      132

      Matrix-Text

      Buchstaben laufen als Nullen und Einsen durch, bis sie stehen. Ein Klick spielt es erneut.

      Tischfussball Club Zürich

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Matrix-Text -->
      <p class="fx-txt fx-matrix" data-fx="matrix" data-text="Tischfussball Club Zürich" title="Nochmals abspielen"><span class="fx-matrix-t">Tischfussball Club Zürich</span></p>
      133

      Scroll-Wörter

      Beim Scrollen durch die Liste leuchtet das Wort in der Mitte gold auf.

      Wir sind

      • offen
      • ehrgeizig
      • herzlich
      • laut
      • fair
      • schnell
      • zusammen
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Scroll-Wörter -->
      <div class="fx-sw" data-fx="scroll-words"><p class="fx-txt" style="margin:0">Wir sind</p><ul class="fx-sw-box" tabindex="0" aria-label="Wörter"><li>offen</li><li>ehrgeizig</li><li>herzlich</li><li>laut</li><li>fair</li><li>schnell</li><li>zusammen</li></ul></div>
      134

      Glanz-Text

      Ein goldener Glanz läuft in Schleife über den Text. Für eine einzelne Überschrift, nie für Fliesstext.

      Clubmeisterschaft 2026

      Code für Entwickler

      Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      
      <!-- Glanz-Text -->
      <p class="fx-txt"><span class="fx-shim-txt">Clubmeisterschaft 2026</span></p>
      135

      Geteilter Text

      Der Text liegt in zwei versetzten Hälften und fügt sich beim Überfahren zusammen.

      Komm an den Tisch

      Code für Entwickler

      Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      
      <!-- Geteilter Text -->
      <p class="fx-txt"><span class="fx-slice" data-text="Komm an den Tisch">Komm an den Tisch</span></p>
      136

      Schatten-Text

      Mehrere farbige Schatten in Blau und Gold, die beim Überfahren flach zusammenschnappen.

      TÖGGELE!

      Code für Entwickler

      Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      
      <!-- Schatten-Text -->
      <p class="fx-txt"><span class="fx-swoosh">TÖGGELE!</span></p>
      137

      Schreibmaschine

      Schreibt Wörter nacheinander, löscht sie wieder und beginnt von vorn, mit blinkendem Cursor.

      Bei uns:

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Schreibmaschine -->
      <p class="fx-txt" data-fx="typewriter" data-words='["Offenes Training","Plausch-Turniere","Kindertraining","Livestream"]'>Bei uns: <span class="fx-tw-t" style="color:var(--gold-lt)"></span><span class="fx-tw-c"></span></p>
      137b

      Invertierender Text

      Weisse Schrift, die sich über mix-blend-mode: difference aus dem Hintergrund rechnet: über Dunklem bleibt sie hell, über Hellem wird sie dunkel, und an einer Kante kippt sie mitten im Wort. Eine Option für grosse Überschriften über Fotos, sparsam eingesetzt.

      Die Farbe des Textes entsteht aus dem Hintergrund und steht darum nicht im Brand. Erlaubt ist er nur in Weiss, über Fotos oder über Flächen, die klar hell oder klar dunkel sind. Nicht auf Blau, Gold oder den Marken-Verläufen und nicht über Mitteltönen, dort wird er unlesbar. Den Kontrast immer am echten Bild prüfen.

      Wo Zürich töggelt

      Töggele

      SoKlar hell und klar dunkel. Der Text kippt an der Kante sauber um.

      Töggele

      SoÜber einem Foto. Kontrast am echten Bild prüfen.

      Töggele

      Nicht soAuf Marken-Blau wird Weiss zu Braun, einer Farbe ohne Platz im Brand.

      Töggele

      Nicht soÜber Mittelgrau verschwindet der Text: Weiss minus Mittelgrau ist wieder Mittelgrau.
      Code für Entwickler

      Nur CSS, kein JavaScript. isolation: isolate am Behälter sorgt dafür, dass nur mit der eigenen Fläche gerechnet wird. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      
      <!-- Invertierender Text · Hintergrund über style oder eigene Klasse -->
      <div class="fx-inv fx-inv-hero" style="background-image:url(assets/fotos/galerie/full/dsc_6815.webp)"><p class="fx-inv-t">Wo Zürich töggelt</p></div>
      Scroll & Bewegung
      138

      Scroll-Zeitstrahl

      Die Linie füllt sich im Takt des Scrollens, erreichte Punkte werden gold. Für Abläufe und Geschichte. Scrollen bleibt frei, die Seite reagiert nur darauf.

      1. 18:30TüröffnungAnmeldung an der Kasse
      2. 19:00AuslosungDie Teams stehen fest
      3. 19:15VorrundeSchweizer System, vier Runden
      4. 21:30KO-PhaseDie besten acht spielen weiter
      5. 23:00SiegerehrungPreise und ein Bier an der Bar
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Scroll-Zeitstrahl -->
      <div class="fx-stl" data-fx="scroll-timeline"><div class="fx-stl-track"><span class="fx-stl-fill"></span></div><ol>
        <li><span class="t">18:30</span><b>Türöffnung</b>Anmeldung an der Kasse</li><li><span class="t">19:00</span><b>Auslosung</b>Die Teams stehen fest</li><li><span class="t">19:15</span><b>Vorrunde</b>Schweizer System, vier Runden</li><li><span class="t">21:30</span><b>KO-Phase</b>Die besten acht spielen weiter</li><li><span class="t">23:00</span><b>Siegerehrung</b>Preise und ein Bier an der Bar</li>
      </ol></div>
      139

      Angeheftete Szene

      Die Bühne bleibt stehen, während man scrollt, und wechselt Schritt für Schritt. Hier im Kasten; auf einer Seite läuft es über die ganze Höhe.

      01AnmeldenAn der Kasse oder online, bis 19:00.
      02AuslosenDie Software lost die Paarungen der ersten Runde.
      03SpielenAufgerufen wird per Push, direkt an deinen Tisch.

      Im Kasten scrollen.

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Angeheftete Szene -->
      <div class="fx-pin" data-fx="pin-scene"><div class="fx-pin-box" tabindex="0" aria-label="Scroll durch die Schritte"><div class="fx-pin-track"><div class="fx-pin-stage">
        <div class="fx-pin-step on"><span class="n">01</span><b>Anmelden</b><span class="k-muted">An der Kasse oder online, bis 19:00.</span></div><div class="fx-pin-step"><span class="n">02</span><b>Auslosen</b><span class="k-muted">Die Software lost die Paarungen der ersten Runde.</span></div><div class="fx-pin-step"><span class="n">03</span><b>Spielen</b><span class="k-muted">Aufgerufen wird per Push, direkt an deinen Tisch.</span></div>
        <div class="fx-pin-dots" aria-hidden="true"><i class="on"></i><i></i><i></i></div>
      </div></div></div><div class="fx-pin-bar" aria-hidden="true"><i></i></div><p class="fx-pin-hint">Im Kasten scrollen.</p></div>
      140

      Linie zeichnet sich

      Eine Linie zeichnet sich beim Sichtbarwerden, am Ziel springt die Markierung auf. Für Wege, Verbindungen und Abläufe.

      Bahnhof Wipkingen ClublokalLandenbergstrasse 10
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Linie zeichnet sich -->
      <div class="fx-map" data-fx="draw-line"><button class="kc-replay fx-replay" type="button" aria-label="Neu abspielen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
          <path d="M3 3v5h5" /></svg></button>
        <svg viewBox="0 0 520 260" role="img" aria-label="Weg vom Bahnhof Wipkingen zum Clublokal">
          <path class="street" d="M-10 200 L530 150"/><path class="street" d="M150 -10 L190 270"/><path class="street" d="M-10 70 L530 40"/><path class="street" d="M380 -10 L360 270"/>
          <path class="rail" d="M-10 232 L530 186"/>
          <circle class="stn" cx="120" cy="214" r="9"/><text x="68" y="248">Bahnhof Wipkingen</text>
          <path class="fx-route" d="M120 214 C 150 205, 170 190, 178 160 S 190 90, 250 72 S 350 60, 368 110"/>
          <g class="fx-route-pin"><path class="pin-body" d="M368 110 c-10 -14 -14 -20 -14 -27 a14 14 0 0 1 28 0 c0 7 -4 13 -14 27z"/><circle cx="368" cy="83" r="5" fill="#0d273d"/></g>
          <text x="392" y="90">Clublokal</text><text x="392" y="106" style="font-weight:700; fill:#c3d2e0; font-size:11.5px">Landenbergstrasse 10</text>
        </svg>
      </div>
      141

      Ball auf der Bahn

      Ein Element folgt einer vorgezeichneten Kurve: der Ball läuft vom Mittelfeld über die Stürmer ins Tor.

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Ball auf der Bahn -->
      <div class="fx-table" data-fx="motion-path" role="img" aria-label="Ball läuft über den Tisch ins Tor"><button class="kc-replay fx-replay" type="button" aria-label="Neu abspielen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
          <path d="M3 3v5h5" /></svg></button>
        <span class="fx-rod" style="left:18%"><span class="fx-man g" style="top:calc(30% - 11px)"></span><span class="fx-man g" style="top:calc(50% - 11px)"></span><span class="fx-man g" style="top:calc(70% - 11px)"></span></span><span class="fx-rod" style="left:32%"><span class="fx-man " style="top:calc(25% - 11px)"></span><span class="fx-man " style="top:calc(45% - 11px)"></span><span class="fx-man " style="top:calc(65% - 11px)"></span><span class="fx-man " style="top:calc(85% - 11px)"></span></span><span class="fx-rod" style="left:50%"><span class="fx-man g" style="top:calc(20% - 11px)"></span><span class="fx-man g" style="top:calc(35% - 11px)"></span><span class="fx-man g" style="top:calc(50% - 11px)"></span><span class="fx-man g" style="top:calc(65% - 11px)"></span><span class="fx-man g" style="top:calc(80% - 11px)"></span></span><span class="fx-rod" style="left:68%"><span class="fx-man " style="top:calc(30% - 11px)"></span><span class="fx-man " style="top:calc(70% - 11px)"></span></span><span class="fx-rod" style="left:82%"><span class="fx-man g" style="top:calc(50% - 11px)"></span></span>
        <span class="fx-goal-mouth"></span><span class="fx-ball"></span><span class="fx-goal">Tor!</span>
      </div>
      142

      Sortierbare Liste

      Einträge am Griff ziehen, die anderen gleiten an ihren Platz. Mit der Tastatur: Griff fokussieren und Pfeil hoch oder runter.

      1. 1Punkte
      2. 2Buchholz
      3. 3Torverhältnis
      4. 4Siege
      5. 5Erzielte Tore

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Sortierbare Liste -->
      <div class="fx-sort" data-fx="sortable"><ol><li><span class="fx-sort-n">1</span><b>Punkte</b><button class="fx-sort-h" type="button" aria-label="Punkte verschieben"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="12" r="1" />
          <circle cx="9" cy="5" r="1" />
          <circle cx="9" cy="19" r="1" />
          <circle cx="15" cy="12" r="1" />
          <circle cx="15" cy="5" r="1" />
          <circle cx="15" cy="19" r="1" /></svg></button></li><li><span class="fx-sort-n">2</span><b>Buchholz</b><button class="fx-sort-h" type="button" aria-label="Buchholz verschieben"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="12" r="1" />
          <circle cx="9" cy="5" r="1" />
          <circle cx="9" cy="19" r="1" />
          <circle cx="15" cy="12" r="1" />
          <circle cx="15" cy="5" r="1" />
          <circle cx="15" cy="19" r="1" /></svg></button></li><li><span class="fx-sort-n">3</span><b>Torverhältnis</b><button class="fx-sort-h" type="button" aria-label="Torverhältnis verschieben"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="12" r="1" />
          <circle cx="9" cy="5" r="1" />
          <circle cx="9" cy="19" r="1" />
          <circle cx="15" cy="12" r="1" />
          <circle cx="15" cy="5" r="1" />
          <circle cx="15" cy="19" r="1" /></svg></button></li><li><span class="fx-sort-n">4</span><b>Siege</b><button class="fx-sort-h" type="button" aria-label="Siege verschieben"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="12" r="1" />
          <circle cx="9" cy="5" r="1" />
          <circle cx="9" cy="19" r="1" />
          <circle cx="15" cy="12" r="1" />
          <circle cx="15" cy="5" r="1" />
          <circle cx="15" cy="19" r="1" /></svg></button></li><li><span class="fx-sort-n">5</span><b>Erzielte Tore</b><button class="fx-sort-h" type="button" aria-label="Erzielte Tore verschieben"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="12" r="1" />
          <circle cx="9" cy="5" r="1" />
          <circle cx="9" cy="19" r="1" />
          <circle cx="15" cy="12" r="1" />
          <circle cx="15" cy="5" r="1" />
          <circle cx="15" cy="19" r="1" /></svg></button></li></ol><p class="sr-only fx-sort-live" aria-live="polite"></p></div>
      143

      Wort-Reveal

      Die Überschrift steigt Wort für Wort aus einer unsichtbaren Linie auf, sobald sie ins Bild kommt. Für eine Hauptüberschrift je Seite.

      Der grösste Tischfussball-Verein der Schweiz

      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Wort-Reveal -->
      <div class="fx-wr" data-fx="word-reveal"><button class="kc-replay fx-replay" type="button" aria-label="Neu abspielen"><svg class="ki" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
          <path d="M3 3v5h5" /></svg></button><p class="fx-wr-t">Der grösste Tischfussball-Verein der Schweiz</p></div>
      143b

      Einblenden beim Scrollen

      Ein Element steigt auf und wird sichtbar, während es ins Bild scrollt, und steht fest, sobald es ein knappes Drittel weit drin ist. Reines CSS über animation-timeline: view(), ohne JavaScript. Anwählen mit der Klasse fx-rise, gestaffelt mit d1 und d2.

      Sparsam einsetzen: am Anfang eines Abschnitts oder für eine Reihe Karten. Steigt jedes Element einzeln auf, wirkt die Seite nach Vorlage. Browser ohne Unterstützung und Geräte mit «Bewegung reduzieren» zeigen den Inhalt sofort und ohne Bewegung, es geht nie etwas verloren. Im Kasten unten rechnet die Animation mit dem Kasten, auf einer Seite mit dem Fenster.

      Im Kasten nach unten scrollen

      Unsere Turnierformate

      KäsekickLetzter Freitag im Monat
      Plausch-TurnierJeden Mittwoch
      Regio TourEinzel und Doppel
      Zürich OpenViermal im Jahr, samstags
      Code für Entwickler

      Nur CSS, kein JavaScript. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      
      <!-- Einblenden beim Scrollen · Klasse fx-rise, gestaffelt mit d1 und d2 -->
      <h2 class="fx-rise">Unsere Turnierformate</h2>
      <div class="raster">
        <article class="fx-rise">…</article>
        <article class="fx-rise d1">…</article>
        <article class="fx-rise d2">…</article>
      </div>
      143c

      Gestaffelt erscheinen

      Die Elemente einer Liste oder Kartenreihe erscheinen nacheinander, sobald sie ins Bild kommt. Die Reihenfolge rechnet CSS mit sibling-index(), darum braucht kein Element eine eigene Nummer. Den Takt setzt --fx-step, Standard sind 60 ms.

      Für kurze Listen bis etwa zwölf Einträge, einmal pro Abschnitt. Browser ohne sibling-index() staffeln über einen Rückfall bis 16 Einträge. Ohne JavaScript und bei «Bewegung reduzieren» steht alles sofort da.

      • Fr2
        Regio Tour DoppelClublokal · 19:30
        Pro
      • Sa10
        Swiss Open P4PUitikon Waldegg · 10:00
        Elite
      • Fr30
        KäsekickClublokal · 19:00
        Fun
      • Sa14
        Zürich Open 4/4Clublokal · 10:00
        Pro
      • Fr27
        KäsekickClublokal · 19:00
        Fun
      Code für Entwickler

      Braucht die drei Stylesheets und effects.js. Die Einbindung steht einmal im <head>.

      <!-- im <head> -->
      <link rel="stylesheet" href="system/tokens.css">
      <link rel="stylesheet" href="system/buttons.css">
      <link rel="stylesheet" href="system/ui.css">
      <script defer src="system/components/effects.js"></script>
      
      <!-- Gestaffelt erscheinen · Abstand je Kind über --fx-step (Standard 60ms) -->
      <ul class="fx-stagger" data-fx="stagger" style="--fx-step:60ms">
        <li>…</li>
        <li>…</li>
        <li>…</li>
      </ul>
      Teil 4 · Komposition

      Wie eine Seite entsteht

      Die Bausteine sagen, woraus eine Seite besteht. Dieser Teil zeigt, wie man sie zusammensetzt, damit das Ergebnis nach TFCZ aussieht und nicht nach Vorlage. Jede Regel steht mit einem richtigen und einem falschen Beispiel da.

      Richtung
      144

      Zuerst die Richtung

      Bevor die erste Zeile entsteht, steht fest, wie ruhig oder wie mutig die Seite wird. Die Stufe folgt aus dem Zweck der Seite. Farben, Schrift und Bausteine bleiben auf jeder Stufe dieselben, es ändern sich Aufbau, Bildanteil und Bewegung.

      Stufe 1 bis 3 · Ruhig

      Für Seiten, auf denen jemand etwas nachschlägt: Anfahrt, Preise, Statuten, Kontakt. Eine Spalte, kaum Bilder, keine Bewegung ausser dem Hover.

      Stufe 4 bis 6 · Ausgewogen

      Der Normalfall für Angebotsseiten und Landingpages. Text und Foto teilen sich den Platz, jeder Abschnitt hat eine eigene Form.

      Stufe 7 bis 10 · Mutig

      Für Kampagnen und grosse Events mit Ablaufdatum. Grosses Foto, wenig Text, mehr Bewegung beim Scrollen. Diese Stufe braucht einen Anlass.

      Aufbau
      145

      Der Einstieg: Text links, Foto rechts

      Der erste Bildschirm teilt sich in einen Textblock und ein echtes Foto aus dem Club. Links steht, worum es geht und was man tun kann, rechts sieht man, wie es sich anfühlt. Auf dem Handy steht der Text zuerst.

      Richtig

      Lesbar in einem Blick. Das Foto zeigt den Ort und die Menschen, der Knopf steht direkt unter dem Satz, auf den er antwortet.

      Falsch als Standard

      Zentrierter Titel auf leerer Fläche sieht aus wie jede zweite Vorlage. Er eignet sich für eine kurze Ankündigung oder ein Event auf Stufe 7 und höher, mit Foto im Hintergrund.

      146

      Keine drei gleichen Karten

      Drei gleich grosse Kästchen mit Icon, Titel und Satz sagen dem Auge, dass alles gleich wichtig ist. Meistens stimmt das nicht. Das Wichtigste bekommt mehr Fläche und ein Foto, der Rest ordnet sich darunter.

      Richtig

      Eine Hauptsache, zwei Nebensachen. Das Auge weiss sofort, wo es anfangen soll.

      Falsch

      Alles gleich laut, darum fällt nichts auf. Ausnahme: echte Wahlmöglichkeiten wie die Paket-Karten, dort sind gleiche Kästen gewollt, damit sich die Pakete vergleichen lassen.

      147

      Jeder Abschnitt hat seine eigene Form

      Eine Seite liest sich wie ein Gespräch, wenn die Abschnitte wechseln: Einstieg, eine Reihe mit Zahlen, Text neben Foto in der anderen Richtung, ein Zitat, zum Schluss die Aktion. Wiederholt sich dieselbe Form, blättert man durch, ohne etwas zu lesen.

      Richtig

      Fünf Abschnitte, fünf Formen. Das Foto wechselt die Seite, die Zahlen stehen frei, die Aktion kommt am Schluss noch einmal.

      Falsch

      Nach dem Einstieg dreimal dasselbe Raster. Man scrollt durch, und nichts bleibt hängen.

      148

      Abstand zeigt, was zusammengehört

      Innerhalb einer Gruppe liegen die Dinge nah beieinander, zwischen zwei Gruppen ist deutlich mehr Luft. So braucht es weniger Linien und Kästen, weil der Abstand die Ordnung schon zeigt. Die Werte kommen aus den Spacing-Tokens des Brand Guides.

      Richtig

      Zwei Gruppen, auf den ersten Blick getrennt.

      Falsch

      Überall derselbe Abstand. Man muss lesen, um zu merken, wo das eine aufhört.

      149

      Eine Seite, eine Hauptaktion

      Jede Seite hat genau eine Sache, die sie auslösen soll. Sie steht im Einstieg und am Schluss noch einmal. Alles andere ist Blau oder Ghost. Gold bleibt der Mitgliedschaft vorbehalten, wie im Abschnitt zu den Buttons beschrieben.

      Richtig

      Eine goldene Aktion, daneben eine ruhige zweite Wahl.

      Falsch

      Vier Knöpfe, drei davon golden. Wer alles hervorhebt, hebt nichts hervor.

      Wirkung
      150

      Bewegung braucht einen Grund

      Eine Animation ist erlaubt, wenn sie etwas sagt: dass ein Klick angekommen ist, dass sich ein Zustand ändert, was zuerst wichtig ist, oder wie eine Geschichte weitergeht. «Sieht lebendig aus» reicht nicht. Der Schwerkraft-Wirbel im Hintergrund ist die eine Bewegung, die einfach zur Marke gehört. Wer weniger Bewegung eingestellt hat, bekommt sie auch.

      Richtig
      Angemeldet, bis Dienstag

      Klick auf den Knopf. Die Bewegung bestätigt, dass etwas passiert ist, und hört dann auf.

      Falsch
      Neu!

      Pulsiert ohne Anlass und ohne Ende. Das Auge wird immer wieder hingezogen, auch wenn es gerade etwas anderes liest.

      151

      Kein Glühen, keine fremden Farben

      Tiefe entsteht über die Frost-Fläche, den feinen Innenrand und die Brand-Line. Neon-Schein, Glas auf allem und Lila-Türkis-Verläufe gehören zu anderen Marken. Erlaubt sind die vier Verläufe aus dem Abschnitt Farben.

      Richtig
      Plausch-Turnier

      Freitag, 9. Oktober · 16 Plätze frei

      Brand-Line oben blau, unten gold, dunkle Fläche. Die Karte gehört erkennbar zum TFCZ.

      Falsch
      Plausch-Turnier

      Freitag, 9. Oktober · 16 Plätze frei

      Leuchtet, ist aber nicht mehr unsere Karte. So sehen Vorlagen aus, die eine KI ohne Vorgaben baut.

      152

      Auf dem Handy zuerst der Satz

      Ein zweispaltiger Einstieg stapelt sich auf dem Handy. Oben stehen Titel, Satz und Knopf, darunter das Foto. So ist die Aktion sichtbar, ohne dass jemand scrollen muss.

      Richtig

      Die Aktion steht im ersten Bildschirm.

      Falsch

      Das Foto kommt zuerst und schiebt den Knopf aus dem Bild.

      Vor dem Bau
      153

      Vier Fragen vor der ersten Zeile

      Wer eine neue Seite baut, beantwortet diese vier Fragen zuerst. Zusammen mit dem Playbook im Brand Guide ergibt das den Plan für die Seite.

      1Wer liest das, und was soll passieren?

      Eine Zielgruppe aus dem Brand Guide und eine Hauptaktion. Gibt es zwei, braucht es zwei Seiten.

      2Welche Stufe?

      Ruhig, ausgewogen oder mutig. Die Stufe bestimmt Bildanteil, Aufbau und Bewegung.

      3Welche Form hat jeder Abschnitt?

      Für jeden Abschnitt eine Form wählen, bevor Text entsteht. Zweimal dieselbe Form hintereinander ist ein Zeichen, dass ein Abschnitt zu viel ist.

      4Welche Bewegung, und wozu?

      Für jede Animation den Grund aufschreiben. Findet sich keiner, fällt sie weg.

      Teil 5 · Kasse

      Regeln der Kassen-Oberfläche

      Die Kasse im Clublokal (okapi, /public/cashier) ist die einzige TFCZ-Oberfläche, die den ganzen Abend in einer Hand liegt und Geld bewegt. Sie benutzt dieselben Farben, Zeichen und Wörter wie alles andere — nur gelten dafür eigene Regeln, weil das Gerät, die Entfernung und die Eile andere sind. Wer ein neues Element der Kasse baut, liest zuerst dieses Kapitel und danach das Icon-Glossar KASSE-ICONS.md im okapi-Repo.

      Grundlagen
      154

      Ein Gerät, ein Finger, kein Zeiger

      Die Kasse läuft auf EINEM Tablet quer (11 Zoll, 1194 × 834) und wird mit dem Finger bedient, im Stehen, oft mit der anderen Hand am Glas. Alles, was eine Maus voraussetzt, gibt es hier nicht.

      Kein :hover

      Ein Finger schwebt nicht. Ein Zustand, den nur die Maus zeigt, ist auf diesem Gerät unsichtbar — die Kasse kennt nur den Ruhezustand und :active.

      Keine Aufklappmenüs

      Was gewählt werden kann, steht da. Ein Menü, das sich öffnet, verdeckt genau das, woran man sich orientiert, und schliesst beim nächsten Tipp daneben.

      Keine schwebenden Ebenen

      Tooltips, Popover, Hinweise am Zeiger: alles Dinge ohne Zeiger. Was erklärt werden muss, steht als Zeile im Fenster.

      Trefferflächen ab 34 px

      Schrittknöpfe 34 × 34, Zeilen und Knöpfe in Fenstern 40–44 px hoch. Kleiner trifft man im Betrieb nicht.

      155

      Nichts springt

      Wer an der Kasse steht, tippt auf eine Stelle, die er eine Sekunde vorher gesehen hat. Eine Fläche, die ihre Grösse ändert, verschiebt den nächsten Tipp — und der bucht dann etwas anderes.

      Flächen behalten ihr Mass

      Das Fenster «Töggeler hinzufügen» hält seine Höhe, solange darin eine Trefferliste scrollt. Ein Fenster mit einer bekannten Zahl Felder wächst dafür mit ihnen, statt sie in ein festes Mass zu pressen.

      Gesperrt statt weg

      Was gerade nicht geht, bleibt stehen und ist ausgegraut. Ein Knopf, der verschwindet, rückt alle anderen nach.

      Spalten sind fest

      Beträge stehen in einer Spalte fester Breite. Ein Minuszeichen vor der Zahl darf die Knöpfe daneben nicht verschieben.

      Die Zeile bleibt, wo sie ist

      Das «+» im Warenkorb erhöht an Ort und Stelle. Nach oben rückt eine Zeile nur, wenn sie von der Artikelseite kommt — dort sucht man sie danach.

      156

      Vier Farben, vier Bedeutungen

      Die Kasse läuft im Navy-Modus. Jede Farbe sagt genau eine Sache, und sie sagt sie überall gleich.

      Gold = Auswahl

      Favorit, gewählter Filter, aktives Eingabefeld, Fenstertitel und Abschnittsüberschrift. Gewählt ist ein goldener RAND, nie eine goldene Füllung — dieselbe Regel wie beim Toggle.

      Blau = Marke

      Der Rahmen oben, das Logo, der Lichtpunkt im Glas. Blau führt, es wählt nicht aus.

      Grün = bezahlt und dazu

      Bezahlknöpfe, die Marke an einer Kachel im Korb, die bezahlte Namenskarte. Der Ton ist der der Bezahlknöpfe (Farbton 142–149°), nie Türkis.

      Rot = weg und Schuld

      Entfernen, Ausloggen, Gefahr, dazu der Rosé-Ton #e4c9c9 für «Offen von früher»: dort spricht die GANZE Zeile in dieser Farbe, nicht nur der Betrag.

      Die eine Ausnahme

      Der goldene VERLAUF ist der Hauptaktion eines Fensters vorbehalten («Hinzufügen», die beiden Hälften der Umschaltleiste «Suchen / Neu erfassen»). Das ist keine Auswahl, sondern der Weg hinaus — darum darf er füllen.

      157

      Kontrast wird gemessen, nicht geschätzt

      Das Tablet steht unter Deckenlicht, oft schräg. Jede Farbentscheidung nennt ihren gemessenen Wert im Kommentar daneben — wer sie später anfasst, sieht, woran er sie messen muss.

      Code für Entwickler
      /* RICHTIG — der Wert steht dabei, die Regel ist nachprüfbar */
      /* Glas: Name 6.0:1, Zeichen 3.8:1 gemessen. */
      .kasse-tile { background: radial-gradient(...), linear-gradient(155deg, rgba(26,52,78,.80), rgba(7,17,28,.70)); }
      
      /* FALSCH — «sieht hell genug aus» ist keine Messung */
      .kasse-tile .nm { color: rgba(255,255,255,.55); }
      Text ab 4.5:1

      Namen, Beschriftungen, Zusatzzeilen. Ein Zusatz darf leiser sein als der Name, aber nicht unter diesen Wert.

      Grosse Zahlen ab 3:1

      Totalbeträge und Kennzahlen ab 22 px.

      Farbton prüfen, nicht nur Helligkeit

      Ein «Grün», das in Wahrheit Türkis ist, fällt beim Messen der Helligkeit nicht auf. Der Farbton gehört mitgeprüft.

      Flächen
      158

      Karten und Kacheln sind Glas

      Jede Fläche, die etwas trägt — eine Namenskarte, eine Artikelkachel — ist dieselbe Glasfläche: ein Lichtpunkt oben links, ein Verlauf nach unten rechts, ein feiner Rahmen. Kein Schatten, keine zweite Sorte Karte.

      Code für Entwickler
      background:
        radial-gradient(260px circle at 30% 40%, rgba(92,167,220,.22), transparent 60%),
        linear-gradient(155deg, rgba(26,52,78,.80), rgba(7,17,28,.70));
      border: 1px solid rgba(38,80,118,.9);
      border-radius: 8px;
      box-shadow: none;
      Radius 8 px

      Karten, Kacheln und Zeilen. Fenster sind runder (18 px), kleine Knöpfe eckiger (10 px) — dazwischen gibt es nichts.

      Bezahlt ist dieselbe Karte in Grün

      Gleicher Verlauf, gleicher Winkel, nur die Farbe der Bezahlknöpfe. Eine eigene Gestaltung für «bezahlt» wäre eine zweite Karte.

      Kein backdrop-filter neben Kacheln

      Die Unschärfe liest Pixel ausserhalb des Elements: an der Buchstabenleiste zog sie die Kanten der Kacheln als Striche herein. Wo Flächen aneinanderstossen, wird nicht geblurrt.

      159

      Die Artikelkachel und ihre Zustände

      Eine Kachel ist immer gleich gebaut: Bild oben, Name, Zusatz, unten Preis und Menge. Ihre Zustände kommen dazu, sie ersetzen nichts.

      Im Korb

      Eine grüne Marke am unteren Rand (inset 0 -4px 0) und die Menge im Fuss. Die Kachel selbst bleibt, wie sie ist.

      Gesperrt

      Halbe Deckkraft, wenn die Kasse nichts mehr annimmt. Der Grund steht als Zeile über den Kacheln, nicht an jeder einzelnen.

      Gebucht ist nicht gesperrt

      Was einmal und nur einmal gebucht wird (Schnuppertraining), nimmt danach nichts mehr an — trägt aber die grüne Marke in voller Helligkeit. Halb weggeblendet sähe es aus wie kaputt.

      Sonderfälle sind Kacheln wie jede andere

      Eigener Artikel, Gutschein, Schnuppertraining laufen durch dieselbe Vorlage (im Code über negative Ids). Dadurch tragen sie denselben Stern und dieselben Zustände, ohne dass irgendetwas zweimal gebaut wird.

      160

      Favoriten: der Stern überall, das Gold nur in der Sektion

      Favoriten sind persönlich und stehen in einer eigenen Sektion zuoberst oder zuunterst. Unter seiner Gruppe bleibt derselbe Artikel unauffällig — sonst stünde jede zweite Kachel in Gold und die Ordnung der Gruppen ginge unter.

      Stern oben links

      Gesetzt: star, gold gefüllt. Nicht gesetzt: star-plus, grau. Der Stern ist kein eigener Knopf mit Rahmen, sondern eine Fläche auf der Kachel.

      Gold nur in der Favoriten-Sektion

      Dort trägt die Kachel goldenen Rahmen UND goldenen Namen. Unter ihrer Gruppe ist nur der Stern gold.

      Umsortieren nur dort

      Drücken und halten (250 ms), dann ziehen — und nur in der Sektion, denn nur dort gibt es eine Reihenfolge. Ein sehr leiser Griff in der Ecke sagt, dass es geht.

      Gezogen wird mit Rechtecken

      elementFromPoint gibt ausserhalb des sichtbaren Bereichs null zurück. Getroffen wird über die Rechtecke der Kacheln, und die Kachel trägt touch-action: none, sonst nimmt der Browser den Finger für seinen Bildlauf.

      Warenkorb
      161

      Jede Korbzeile ist gleich gebaut

      Name links, Bedienung in der Mitte, Betrag rechts — in allen Zeilen an derselben Stelle, auch wenn eine Zeile weniger kann als die andere.

      Code für Entwickler
      /* Die Betragsspalte ist FEST: mit «auto» wuchs sie um die Breite des
         Minuszeichens, und die Knöpfe der Gutschein-Zeile standen weiter links
         als die aller anderen. 68 px fassen auch «-1234.50». */
      .kd2-l { display: grid; grid-template-columns: minmax(0,1fr) auto 68px; gap: 10px; }
      Dieselbe Dreiergruppe

      «−», Zahl, «+». Was nicht mehr werden kann (ein Gutschein), zeigt den «+» ausgeschaltet — nicht ein anderes Bedienelement an anderer Stelle.

      Zahlen tabellarisch

      font-variant-numeric: tabular-nums, rechtsbündig. Untereinander stehende Beträge sollen sich vergleichen lassen.

      Der Name bricht nicht um

      Eine Zeile ist eine Zeile. Was nicht passt, wird abgeschnitten; der Zusatz darunter trägt nur Sorte, Menge und «à Preis».

      Von Hand Getipptes bleibt, wie es getippt wurde

      Die Aufteilung eines Namens in Titel und Zusatz gilt dem Sortiment. Ein eigener Artikel «test 2» ist ein Name, kein Titel mit Zusatz.

      162

      Meldungen stehen IM Korb, nicht darüber

      Was den Korb betrifft, erscheint an der Stelle, an der es passiert ist — so breit wie die Zeilen, in der Farbe der Sache, und es verschwindet von selbst.

      «Entfernt» mit Rückweg

      Dezentes Rot, Balken links, ein Icon-Knopf undo-2 ohne Wort, 5 Sekunden. Die Leiste steht an der Stelle der gelöschten Zeile.

      Zustände erklären sich einmal

      «Bezahlt und abgeschlossen» steht als eine Zeile über den Kacheln, mit dem Weg zurück. Nicht an jeder Kachel.

      Keine Erklärung dessen, was der Knopf tut

      Ein Hinweis, der nur wiederholt, was gleich passiert, wird nicht gelesen und kostet die Höhe, die der Inhalt braucht.

      Fenster
      163

      Ein Mass für jedes Fenster

      Alle Fenster der Kasse sind gleich breit: so breit, wie es das breiteste braucht. Unterschiedlich breite Fenster lasen sich wie ein Versehen.

      480 px, Kopf und Fuss bleiben stehen

      Was länger ist, scrollt im Fenster. Die Leiste oben und die Fusszeile unten bleiben immer frei, dazu 16 px Luft.

      Titel in Gold

      Der Fenstertitel trägt dieselbe Farbe wie die Abschnittsüberschriften darunter. Darüber links das Zeichen der Sache in seinem Feld.

      Markenrahmen

      Blaue Linie oben, goldene unten — wie überall bei TFCZ.

      Abbrechen links, Handlung rechts

      Der Weg hinaus ist ein stiller Knopf, die Handlung trägt Gold (oder Rot, wenn sie zerstört).

      164

      Wählen im Fenster

      Eine Auswahl aus wenigen Möglichkeiten ist eine Liste, keine Wolke aus Pillen — eine Zeile trifft man mit dem Finger sicherer, und untereinander liest sie sich schneller.

      Code für Entwickler
      /* Gewählt = goldener RAND, nie goldene Füllung. Haken 6.9:1, Rand 5.1:1 gemessen. */
      .kd-check .box { width:18px; height:18px; border-radius:5px;
        border:1.5px solid rgba(195,210,224,.45); background:rgba(0,0,0,.2); color:transparent; }
      .kd-check[aria-checked="true"] .box { border-color:var(--gold);
        background:rgba(205,168,87,.16); color:var(--gold-lt); }
      Mehrfachwahl: Kästchen

      Zeilen zu 36 px, zweispaltig, ohne Kasten je Antwort. Sieben Kacheln untereinander sind mehr Rahmen als Inhalt.

      Einfachwahl: Punkt

      Zeile zu 44 px mit rundem Punkt. Gewählt ist der PUNKT, nicht die Zeile: ein goldener Rahmen um das ganze Feld las sich wie ein aktives Eingabefeld.

      Freiwillig wird angeschrieben

      Nur was freiwillig ist, trägt die Marke «freiwillig». Pflicht sagt sich anders: der Knopf bleibt gesperrt, bis die Angabe da ist.

      Zeichen & Bewegung
      165

      Ein Zeichen, eine Bedeutung

      Jedes Icon der Kasse ist genau einer Sache zugeordnet. Das Verzeichnis ist KASSE-ICONS.md im okapi-Repo — nicht der Code: wer ein Zeichen neu vergibt, trägt es dort ein, wer eines braucht, das schon vergeben ist, nimmt ein anderes. Dasselbe Bild an zwei Orten hiesse am Tresen zweierlei.

      Code für Entwickler
      /* Die Strichstärke folgt der Grösse: 34px-Icon auf einer Kachel = 34/24 = 1.4px */
      svg { stroke-width: calc(var(--ih, 17px) / var(--iw, 15)); vector-effect: non-scaling-stroke; }
      .kd2-tile .pic { --ih: 34px; --iw: 24; }   /* gross und trotzdem dünn */
      
      /* Ein «fettes» Icon ist immer ein Fehler im Zuschnitt, nie Absicht. */
      Lucide, zugeschnitten

      Die Pfade kommen aus dem Icon-Satz des Guides, die viewBox wird auf den gezeichneten Bereich zugeschnitten. So sind gleich grosse Icons auch optisch gleich gross.

      Artikel-Zeichen werden abgeleitet

      Welches Bild ein Artikel oder eine Gruppe trägt, entscheidet eine Tabelle aus dem Namen — nicht die Hand. Ein neues Getränk bekommt sein Zeichen von selbst.

      Keine Emojis

      Gilt in der Kasse wie überall.

      166

      Bewegung hat einen Grund

      Die Kasse bewegt sich nur dort, wo Bewegung etwas bestätigt. Alles andere steht still.

      Partikel beim Hinzufügen

      Der Partikel-Knopf des Guides, 18 Punkte, blau und gold zu gleichen Teilen. Sie steigen IMMER am «+» der Zeile auf, die gerade gewachsen ist — auch wenn ein gleichnamiger Artikel dazugezählt wurde.

      Rollende Beträge

      Total und Trinkgeld rollen beim Ändern. Der Betrag ist die Zahl, auf die alle schauen.

      Halten statt bestätigen

      Ausloggen läuft über Hold to Confirm — ein Fenster mit «Sicher?» kostet am Tresen mehr Zeit als das Halten.

      prefers-reduced-motion

      Schaltet jede dieser Bewegungen ab. Keine Ausnahme.

      Sprache & Arbeitsweise
      167

      Wie die Kasse spricht

      Kurz, konkret, in der Sprache des Clublokals. Die Kasse redet nicht über sich selbst.

      «Töggeler»

      Die männliche Form, überall, ohne Gendern. So heissen die Leute im Club.

      Gesagt wird, was ist

      «Artikel entfernt», «Bezahlt und abgeschlossen», «geht aufs Haus». Nicht: «Der Vorgang wurde erfolgreich durchgeführt».

      Überschriften sind Grossbuchstaben in Gold

      11.5 px, weit gesperrt, mit dem kurzen goldenen Strich davor — dasselbe Mass über Artikelgruppen wie in Fenstern.

      Fehler sagen den Weg

      Was schiefging UND was jetzt hilft. Ein Code aus der Schnittstelle ist kein Satz für den Tresen.

      168

      Bevor etwas an die Kasse kommt

      Die Kasse ist die einzige Oberfläche, bei der ein Fehler Geld kostet und niemand Zeit hat, ihn zu suchen. Darum gelten für sie zwei zusätzliche Regeln der Arbeitsweise.

      Eine Regel steht an EINER Stelle

      Dieselbe Kachel, dieselbe Zeile, dasselbe Fenster werden einmal gebaut und von allen benutzt. Zwei Fassungen laufen auseinander — und die Kasse zeigt dann zweierlei für dieselbe Sache.

      Gemessen, nicht angeschaut

      Kontrast, Geometrie, Abstände: nachrechnen, bevor es «passt» heisst. Die Werte gehören als Kommentar neben die Regel.

      Keine Abkehr von der Sache

      Änderungen an der Gestaltung ändern nichts an dem, was gebucht wird. Was die Buchung betrifft, wird vorher besprochen.

      Totes geht mit

      Wer eine Regel ersetzt, nimmt die alte mit — die verwaiste Klasse, das nicht mehr vergebene Icon, die Kopie im Mockup.

      Kopiert