# Vertrag 6: Platform Standards (Pflicht) Was die V2-Plattform allen Modulen **liefert** und was sie von ihnen **verlangt** — über die reine Schnittstellen-Definition hinaus. Damit GeoGraSim als ein zusammenhängendes Produkt wirkt und nicht wie eine Sammlung loser Apps. Diese Standards sind nicht verhandelbar. Bei Konformitäts-Check prüft Atlas auch die Einhaltung dieser Regeln (Bildstil-Eyeballing, Touch-Target-Messung, Lexikon-Scan). --- ## 1. Design-Prinzipien (didaktisch) ### Wygotski-Lernarbeit, kein Spiel GeoGraSim ist **Lernarbeit mit Simulationen**, kein Spiel. Wir sprechen Schüler*innen als ernsthafte Bearbeiter*innen an, nicht als Spieler*innen. Theoriebezug: Wygotski („Zone der nächsten Entwicklung"). **Verbindliches Lexikon** für alle UI-Texte: | NICHT verwenden | STATTDESSEN | |---|---| | spielen, Spiel | arbeiten mit, Simulation, bearbeiten | | Weiterspielen | Fortsetzen, weiterarbeiten | | „Jetzt spielen" | „Simulation starten", „Los arbeiten" | | Spielstand | Arbeitsstand, Fortschritt | | Spieler*in | Lernende, Bearbeiter*in | | Spielrunde | Durchgang, Arbeitsphase | | Gameplay | Simulationsablauf, Arbeitsweise | | „verloren", „Game Over" | „Ziel verfehlt", „Durchgang beendet" | | Highscore | Beste Leistung, Bestwert | | Level | OK, aber lieber „Schwierigkeit" oder „Stufe" | **Ausnahmen**: Im Code (Variablennamen wie `gameState`, `gameTick`) ok, da unsichtbar. Tabellennamen wie `game_levels`, `game_saves` ok. Nur **sichtbare UI-Texte** und **Lehrer-/Schüler-Dokumentation** folgen dem Lexikon. ### Inklusion: Easy-Sprache ist Pflicht Jedes Modul muss alle UI-Texte in zwei Varianten anbieten: - `-standard` — Standardsprache - `-easy` — Leichte Sprache nach Netzwerk-Leichte-Sprache- Regeln, A1–A2-Niveau, kurze Sätze, keine Fachbegriffe ohne Erklärung Easy-Variante ist **kein nettes Add-on**, sondern Pflicht für die Inklusion (Schüler*innen mit Lese-/Lernschwächen, Deutsch-als- Zweitsprache, kognitive Behinderung). ### Keine KI im fertigen Produkt **Auswertungen, Bewertungen, Insight-Cards** im Lehrer-Dashboard nutzen **parametrische Regeln**, keine KI-API-Calls. ``` ✅ if classScore < 40% then showWarning("Klasse hat Schwierigkeiten") ❌ if AI.summarize(class.results) then ... ``` Begründung: Lehrpersonen müssen sich auf deterministische, nachvollziehbare Aussagen verlassen können. Keine Halluzinationen, keine Datenschutz-Probleme durch externe LLM-Calls. **Erlaubt**: KI-Generierung von Assets (Bilder, Audio) **vor** dem Deployment — siehe Punkt 5 unten. --- ## 2. iPad als Hauptreferenzgerät GeoGraSim wird primär auf **iPad Landscape (1180 × 820 CSS-Pixel)** genutzt — Schulen in AT/DACH setzen iPads ein. ### Layout-Breakpoints - Desktop ≥ 1200 px: dreispaltig (Sidebar + Canvas + Sidebar) - iPad Landscape 900–1199 px: dreispaltig (kompakter) - iPad Portrait / Mobile < 900 px: einspaltig ### Touch-Pattern (Pflicht) ```html ``` `viewport-fit=cover` ist **Pflicht** in jeder Modul-HTML, sonst funktioniert `env(safe-area-inset-*)` nicht. **CSS-Pflichten** im Modul: ```css body { touch-action: manipulation; /* kein Double-Tap-Zoom */ -webkit-tap-highlight-color: transparent; } button, .interactive { min-width: 36px; min-height: 36px; /* Touch-Ziel-Minimum */ } /* Hover NUR auf Geräten mit echtem Hover */ @media (hover: hover) { button:hover { ... } } /* Touch-Feedback statt Hover */ button:active { ... } /* Form-Inputs mind. 16px font-size, sonst zoomt iOS */ input, textarea, select { font-size: 16px; } /* Drag-Elemente brauchen touch-action: none als Kind */ .canvas-draggable { touch-action: none; } ``` ### Modal-/Overlay-Pattern (Pflicht!) **Lerngeschichte 2026-04-28**: Park-Mini-Spiel hatte am iPad einen unerreichbaren „Schließen"-Button. Stundenlanges Warten ohne Abbrechen. Daraus die vier Pflicht-Regeln: 1. **Höhen mit `dvh` statt `vh`**: ```css .modal { max-height: 94dvh; } ``` 2. **Safe-area-padding-bottom**: ```css .modal { padding-bottom: max(16px, env(safe-area-inset-bottom)); } ``` 3. **Aktions-Buttons sticky am unteren Modal-Rand**: ```css .modal-actions { position: sticky; bottom: 0; background: linear-gradient(to top, var(--ggs-white) 80%, transparent); } ``` 4. **Float-✕ rechts oben** als Backup: ```css .modal-close { position: absolute; top: 8px; right: 8px; width: 36px; height: 36px; } ``` Bei kritischen Buttons (Schließen, Abbrechen, Bestätigen) Touch-Ziel ≥ 44 px. **Globale Klassen** in `v2-platform/assets/css/design-system.css`: `.ggs-endscreen-overlay`, `.ggs-endscreen`, `.ggs-endscreen-actions` haben das alles zentral. Module sollen diese Klassen verwenden, nicht eigene Modale neu bauen. --- ## 3. UI-Design-Tokens Module nutzen **CSS-Variablen** aus `design-system.css`, keine hardcodierten Hex-Werte. ### Farben ```css /* Akzent (Fjord) */ --ggs-fjord: #4a7c8a; --ggs-fjord-dark: #1f4e5a; --ggs-fjord-light: #dae8ec; /* Erfolg (Moss) */ --ggs-moss: #5a8a5e; --ggs-moss-dark: #3a6b3e; --ggs-moss-light: #dcead0; /* Neutral (Sand) */ --ggs-sand: #e8d5b5; --ggs-sand-dark: #c4a97a; /* Gefahr (Coral) */ --ggs-coral: #c85c4a; --ggs-coral-light: #f0d0ca; /* Warnung (Orange) */ --ggs-orange: #e8833a; /* Hintergrund */ --ggs-bg: #f5f2eb; --ggs-bg-dark: #e8e4d8; --ggs-white: #ffffff; /* Text */ --ggs-text: #2a2a2a; --ggs-text-muted: #8a8a8a; --ggs-text-light: #b0b0b0; /* Sonstige */ --ggs-border: #e0ddd4; --ggs-success: #5a8a5e; --ggs-warning: #e8833a; --ggs-danger: #c85c4a; --ggs-info: #4a7c8a; ``` ### Spacing & Layout | Token | Wert | Verwendung | |---|---|---| | `--ggs-gap-xs` | 4 px | Inline-Abstände | | `--ggs-gap-sm` | 8 px | Form-Felder | | `--ggs-gap-md` | 16 px | Standard | | `--ggs-gap-lg` | 24 px | Sektionen | | `--ggs-gap-xl` | 32 px | Hero | | `--ggs-radius-sm` | 6 px | Pills | | `--ggs-radius-md` | 12 px | Karten | | `--ggs-radius-lg` | 16 px | Hero-Cards | | `--ggs-radius-pill` | 999 px | Avatar-Kreise | | `--ggs-shadow-sm` | `0 1px 3px rgba(0,0,0,.08)` | Karten | | `--ggs-shadow-md` | `0 2px 8px rgba(0,0,0,.12)` | Modale | | `--ggs-shadow-lg` | `0 4px 16px rgba(0,0,0,.16)` | Tooltips | ### Typografie ```css --ggs-font: 'Inter', system-ui, -apple-system, sans-serif; --ggs-font-mono: 'JetBrains Mono', 'Fira Code', monospace; ``` **Wichtig**: Modul-eigene Tokens (z.B. für besondere Stimmungen wie Wintertag) als `---*`-Variablen lokal definieren, aber GGS- Tokens als Fallback referenzieren. **Wichtig 2**: UI-Palette ≠ Bild-Palette. UI = Fjord/Moss/Sand. Bilder = Scandinavian Flat (siehe Punkt 5). --- ## 4. Avatar-Pool 30 vorgenerierte Schüler-Avatare in 3 thematischen Gruppen. Module dürfen den Avatar der Schüler*in aus `/api/student/me` beziehen und für Begrüßungen / Belohnungen anzeigen. **Pfad**: `/v2beta/assets/avatars/avatar-.png` **Auflösung**: 1024×1024 PNG, kreisrund maskiert (`border-radius: 50%`) ### Geo-Welt `pilotin` · `pilot` · `kartografin` · `geologe` · `meteorologin` · `stadtplaner` · `forscherin` · `forscher` · `taucherin` · `astronaut` ### Cool & Lustig `coole-girl` · `coole-boy` · `lustige-girl` · `lustige-boy` · `nerd-girl` · `sportlich-boy` · `kuenstlerin` · `musiker` · `gaertnerin` · `abenteurer` ### Tier-Begleiter `fuchs-explorer` · `eule-prof` · `baer-foerster` · `pinguin-polar` · `affe-tropen` · `delfin-meer` · `adler-pilot` · `schildkroete-w` · `katze-stadt` · `biene-natur` Bei Bedarf für neue Avatare: Plattform pflegt das, **Module generieren keine eigenen Avatare**. --- ## 5. Bildstil — Flat Scandinavian Illustration (verbindlich) Module-Sims stehen auf der Schülerseite nebeneinander. Ein Stilbruch fällt sofort auf. Daher: **alle generierten Bilder** (Splash, Cockpit-Card, Spielfeld-Hintergrund, Sprites, Marker, Glossar) folgen demselben Stil. ### Offizieller Prompt-Block (verbatim, NICHT umformulieren) > Flat Scandinavian illustration, wide panoramic landscape filling the > entire 16:9 frame edge-to-edge, painted style with clean vector > shapes, dark forest green (#1f4b37) and sage green (#4a7c4e) tones, > yellow-mustard (#e8c547) accents, beige (#e8e4d8) buildings and > valleys, white highlights. No text, no watermark, no logos, no > borders, no frame. Bei jedem Bild diesen Block voranstellen, danach nur den Szenen-Teil ergänzen. ### Bildtypen und Auflösungen | Bildtyp | Auflösung | Quality | |---|---|---| | Splash (Sim-Intro) | 1792 × 1024 (16:9) | standard | | Cockpit-Card | ≈ 800 × 450 (16:9) | standard | | Glossar-Bild | 1792 × 1024 | standard | | Avatar | 1024 × 1024 | standard | ### Tabu - Keine Schrift im Bild - Keine Logos / Wasserzeichen - Keine Rahmen / Borders - Keine Stockphoto-Übernahmen — bei Bedarf neu generieren --- ## 6. Zentrale API-Keys und Plattform-Dienste API-Keys für externe Dienste werden **zentral von der Plattform gehalten**, nicht von einzelnen Modulen. Modul-Instanzen rufen sie über Plattform-Proxy-Endpoints auf, damit Keys nicht in Modul-Code/Repos landen. | Dienst | Zweck | Plattform-Endpoint | API-Key liegt in | |---|---|---|---| | **DALL-E 3** | Bild-Generierung (Splash, Cards, Glossar, Sprites) | `POST /v2beta/api/asset-gen/image` | `v2-platform/.env.local` → `OPENAI_API_KEY` | | **ElevenLabs** | Sprach-Synthese (TTS für Aufgaben, Audio-Erzähler) | `POST /v2beta/api/asset-gen/voice` | `v2-platform/.env.local` → `ELEVENLABS_API_KEY` | | **Suno** | Hintergrundmusik | manuell durch Thomas | (Suno hat kein klassisches API) | | **OpenStreetMap-Tile-Proxy** | Karten-Tiles (Leaflet, Maplibre) | `GET /v2beta/proxy/tile///.png` | – (User-Agent gesetzt) | | **OSRM** | Routing für Logistik-/Bus-Module | `GET /v2beta/proxy/osrm/route/...` | – (öffentlich) | ### Wichtige Regeln (entschieden 2026-05-17) - **API-Keys liegen NUR LOKAL**, niemals auf dem Server. Schlüssel- Diebstahl-Risiko bei kompromittiertem Server ist real, Live-Kosten wären unkontrollierbar. Server hat keine `OPENAI_API_KEY` / `ELEVENLABS_API_KEY` in der `.env.local`. - **Asset-Generierung läuft VOR Deployment**, lokal durch Atlas oder Modul-Instanz. Keine Live-Generation zur Laufzeit. - Modul speichert generierte Assets in `/public/assets/` und committed sie ins Repo. - Live-Aufrufe an OpenAI/ElevenLabs **aus dem Schüler-Browser sind verboten** (siehe „Keine KI im Produkt" in Punkt 1). - Live-Aufrufe an Tile-Proxy / OSRM sind ok (das ist Karten-Lieferung, keine KI). ### Plattform-Proxy-Beispiel (asset-gen, im Build-Prozess) ```bash # Modul-Instanz ruft das Plattform-Tool auf cd v2-modules/ php ../../v2-platform/tools/asset-gen/image.php \ --prompt "" \ --output public/assets/splash-level1.png \ --resolution 1792x1024 ``` Das Tool liest den OpenAI-Key aus `v2-platform/.env.local`, schickt DALL-E den Prompt-Block + Suffix, speichert das Ergebnis am gewünschten Pfad. Modul committed das PNG. --- ## 7. Multi-Locale-Strategie GeoGraSim startet AT-only, ist aber Spec-seitig multi-locale-fähig. ### Locale-Code-Format `--`, z.B.: - `de-AT-standard`, `de-AT-easy` (Pflicht heute) - `de-DE-standard`, `de-DE-easy` (später) - `de-CH-standard`, `de-CH-easy` (später) ### Lehrplan-Codes mit Land-Prefix - `AT-GW-1.4.2` — Österreichischer GW-Lehrplan (Sek I) - `DE-NRW-GEO-7-3` — NRW-Erdkunde, Klasse 7 (später) - `CH-LP21-RZG-2.2` — Schweizer Lehrplan 21, Räume Zeiten Gesellschaften (später) ### Lokalisierte Beispiele Geographische Beispiele in Modulen (z.B. „Wien als Beobachtungsort" im Sonnensystem) sollen **konfigurierbar** sein: - `country: "AT"` → Default-Beispielort Wien - `country: "DE"` → Default-Beispielort Berlin (oder Bundesland- Hauptstadt) - `country: "CH"` → Default-Beispielort Zürich Modul liest `country` aus `/api/student/me` und wählt entsprechende Beispiele. Wenn das Modul keine geografischen Beispiele hat (z.B. Logistik mit ganz Europa), spielt das keine Rolle. ### Was heute zu tun ist - **Sprache**: alle Texte in i18n-Files mit `de-AT-standard` und `de-AT-easy` - **Lehrplan**: alle Codes mit `AT-`-Prefix - **Beispiele**: AT-zentriert, aber nicht „nur AT funktioniert" hardcoden (z.B. nicht nur österreichische Stadtnamen erlauben) ### Was kommt wenn DE/CH dazu sollen - **Inhaltsarbeit** (Übersetzungen, neue Lehrplan-Anker einpflegen) — nicht Code-Refactor - Neue i18n-Files anlegen - Neue Beispielort-Mappings - Neue `country`-Werte in der Plattform freischalten --- ## 9. Barrierefreiheit (Pflicht — Stufe 1 WCAG 2.1 AA) A11y ist **kein Add-on**, sondern Pflicht für jedes V2-Modul ab Tag 1. Begründung: EU Web Accessibility Directive verlangt für öffentlich finanzierte Bildung WCAG 2.1 AA. Außerdem ist nachgereicht-A11y bei 12+ Modulen ein Refactor-Albtraum — heute eingebaut kostet ~5% Mehraufwand pro Modul, später nachgerüstet ein Vielfaches. ### 9.1 Kontraste (WCAG 2.1 AA) - **Body-Text auf Hintergrund**: mind. 4.5:1 - **Großer Text (≥ 18px) auf Hintergrund**: mind. 3:1 - **UI-Komponenten (Buttons, Form-Elemente, Icons)**: mind. 3:1 gegen umgebenden Hintergrund - **Focus-Indikatoren**: mind. 3:1 gegen unfokussierten Zustand Standard-Tokens müssen das erfüllen — Atlas prüft `--ggs-text` auf `--ggs-bg` mit axe-core o.ä. im Konformitäts-Check. ### 9.2 High-Contrast-Mode — zwei Varianten (Tri-State) Plattform liefert **zwei** Token-Sets für High-Contrast: - **hell** (`data-high-contrast="light"`) — schwarz auf weiß, dunkel- saturierte Farben (#001a4d, #003300, #800000). Default für `preferences.highContrast === true`, weil visuell ruhiger und für die meisten Schüler*innen mit Sehschwäche besser. - **dunkel** (`data-high-contrast="dark"`) — gelb/grün auf schwarz. Für Schüler*innen die ein dunkles Schema explizit bevorzugen. ```css body[data-high-contrast="light"] { --ggs-bg: #ffffff; --ggs-white: #ffffff; --ggs-text: #000000; --ggs-text-muted: #000000; --ggs-fjord: #003366; --ggs-fjord-dark: #001a4d; --ggs-moss: #1a4d1a; --ggs-coral: #800000; --ggs-border: #000000; } body[data-high-contrast="dark"] { --ggs-bg: #000000; --ggs-white: #000000; --ggs-text: #ffffff; --ggs-fjord: #ffff00; --ggs-moss: #00ff00; --ggs-coral: #ff6060; --ggs-border: #ffffff; } ``` Aktiviert via `preferences.highContrast` aus `/api/student/me`. Bei `true` setzt das Modul den Default `data-high-contrast="light"` und bietet im Topbar einen **Tri-State-Toggle** an: aus → hell → dunkel → aus. Schüler*in kann zwischen den beiden HC-Varianten wechseln. ### 9.3 Reduced Motion (Pflicht) ```css @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } ``` Plus: **alle Auto-Play-Animationen** (Sonnensystem-Drohnenflug, Klima-Zeitraffer, Logistik-Tag-Ablauf) brauchen einen sichtbaren Stop/Pause-Button. Bei `preferences.reducedMotion === true` aus dem Profil: keine Auto-Play, manueller Start nötig. ### 9.4 Epilepsie-Trigger vermeiden - Keine Blitze > 3 Hz (3 Wechsel pro Sekunde) - Keine Vollbild-Farbänderungen (Rot-Schwarz-Wechsel etc.) - Keine Stroboskop-Effekte - Bei Explosions-/Crash-Animationen (Heli, Logistik): max. 1 Sekunde intensives Effekt-Frame, dann Beruhigung ### 9.5 Keyboard-Navigation Jedes interaktive Element ist per Tastatur erreichbar: - **Tab/Shift+Tab**: Fokus-Wechsel in logischer Reihenfolge - **Enter / Space**: aktiviert Buttons und Links - **Esc**: schließt Modale und Overlays - **Pfeiltasten**: navigieren in Listen, Tabs, Slider - **Home/End**: erste/letzte Position in Listen Kein `tabindex` > 0 (zerstört natürliche Reihenfolge). `tabindex="0"` für Custom-Widgets erlaubt, `tabindex="-1"` für programmatischen Fokus. ### 9.6 Focus-Indikatoren ```css :focus-visible { outline: 3px solid var(--ggs-fjord-dark); outline-offset: 2px; } /* High-Contrast-Variante */ body[data-high-contrast="true"] :focus-visible { outline: 3px solid #ffff00; outline-offset: 2px; } ``` **Niemals** `outline: none` ohne sichtbaren Ersatz. ### 9.7 Information nicht nur durch Farbe ❌ Nur grüne Markierung für „richtig", nur rote für „falsch" ✅ Grün **plus** Häkchen-Icon, Rot **plus** Kreuz-Icon Gilt auch für Charts, Karten-Marker, Status-Pills. Daltonismus (8% der männlichen Bevölkerung) verlangt das. ### 9.8 ARIA-Pattern (Mindeststandard) - **Buttons**: `