Compare commits

...
2 Commits
Author SHA1 Message Date
JUSNandClaude Opus 5.5 a2748957ac Praesentation MBE: Ablaufplan / Spickzettel (2 Seiten A4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:27:02 +02:00
JUSNandClaude Opus 5.5 0571fc01eb App-Design wie die Webseite: Papier, Tinte, Amtsblau, Textmarker, Georgia
- theme.ts nimmt die Werte der Webseite (--papier, --tinte, --amtsblau,
  --marker, --stempelrot) statt Gold/Bronze; neue Bausteine schriftart
  (Georgia / Courier), ecke, kartenSchatten, formularRahmen, etikett
- alle Texte in Georgia, Versal-Etiketten in Schreibmaschinenschrift (grau
  wie "BETREFF"), Karten als Papierblaetter mit feiner Linie + flachem Schatten
- Knoepfe wie .cta: Amtsblau bzw. weisses Blatt, Tinten-Rahmen, harter
  Versatz-Schatten, beim Druecken rutscht der Knopf in den Schatten
- Startseite: Briefkopf "BehoerdenKlar by Webklar" mit Tinten-Linie,
  Textmarker auf "Fristen", dringendste Frist als Formular
- Tab-Leiste: Papier mit Tinten-Linie, Scan-Knopf mit hartem Schatten

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:27:02 +02:00
16 changed files with 400 additions and 198 deletions

No files matched your search

+10 -12
View File
@@ -18,7 +18,7 @@ import { initialisiereBenachrichtigungen } from './src/services/erinnerungen';
import { holeConsent } from './src/services/storage';
import { AppSchutz } from './src/components/AppSchutz';
import { Ikone } from './src/components/Ikone';
import { farben, schrift } from './src/theme';
import { farben, schrift, schriftart } from './src/theme';
import { FristenScreen } from './src/screens/FristenScreen';
import { ArchivScreen } from './src/screens/ArchivScreen';
import { ConsentScreen } from './src/screens/ConsentScreen';
@@ -67,10 +67,11 @@ function HauptTabs() {
headerShown: false,
tabBarActiveTintColor: farben.primaer,
tabBarInactiveTintColor: farben.textTertiaer,
tabBarLabelStyle: { fontSize: 11, fontWeight: '600' },
tabBarLabelStyle: { fontFamily: schriftart.serif, fontSize: 12, fontWeight: '700' },
tabBarStyle: {
backgroundColor: farben.flaeche,
borderTopColor: farben.rand,
backgroundColor: farben.hintergrund,
borderTopColor: farben.tinte,
borderTopWidth: 2,
height: 56 + insets.bottom,
paddingTop: 6,
paddingBottom: insets.bottom,
@@ -120,7 +121,7 @@ export default function App() {
headerStyle: { backgroundColor: farben.hintergrund },
headerShadowVisible: false,
headerTintColor: farben.primaer,
headerTitleStyle: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
headerTitleStyle: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text },
headerBackTitle: 'Zurück',
contentStyle: { backgroundColor: farben.hintergrund },
}}
@@ -149,12 +150,9 @@ const styles = StyleSheet.create({
backgroundColor: farben.primaerFuellung,
alignItems: 'center',
justifyContent: 'center',
borderWidth: 3,
borderColor: farben.flaeche,
shadowColor: '#000',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.2,
shadowRadius: 5,
elevation: 5,
// Wie die Knöpfe der Webseite: Amtsblau, Tinten-Rahmen, harter Schatten
borderWidth: 2,
borderColor: farben.tinte,
boxShadow: '3px 4px 0px #21211D',
},
});
+156
View File
@@ -0,0 +1,156 @@
<!DOCTYPE html>
<!--
Ablaufplan / Spickzettel für die Präsentation bei der MBE Kirchheimbolanden
(Do 29.10.2026). Nur für Justin, nicht zum Dalassen. 2 Seiten A4.
PDF: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new \
--no-pdf-header-footer --print-to-pdf=ablaufplan.pdf "file://$PWD/ablaufplan.html"
-->
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Ablaufplan Präsentation MBE</title>
<style>
@page { size: A4; margin: 0; }
:root { --papier: #faf7f0; --tinte: #21211d; --grau: #6b675e; --amtsblau: #1d4e89; --rot: #b3372b; --marker: #ffe66b; --gruen: #2e7d4f; }
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body { font-family: Georgia, 'Times New Roman', serif; color: var(--tinte); }
.seite { width: 210mm; height: 297mm; padding: 11mm 14mm; background: var(--papier); overflow: hidden; position: relative; page-break-after: always; font-size: 9.6pt; line-height: 1.4; }
.seite:last-child { page-break-after: auto; }
.mono { font-family: 'Courier New', monospace; }
.kopf { display: flex; justify-content: space-between; align-items: baseline; border-bottom: 0.6mm solid var(--tinte); padding-bottom: 2mm; }
.kopf b { font-size: 12.5pt; }
.kopf span { font-family: 'Courier New', monospace; font-size: 8pt; color: var(--grau); }
h1 { font-size: 20pt; line-height: 1.15; margin-top: 4mm; }
.markiert { background: linear-gradient(104deg, transparent 2%, var(--marker) 4%, var(--marker) 96%, transparent 98%); padding: 0 1.5mm; }
.unter { color: var(--grau); margin-top: 1.5mm; font-size: 9.5pt; }
h2 { font-family: 'Courier New', monospace; font-weight: 400; font-size: 8pt; text-transform: uppercase; letter-spacing: .14em; color: var(--grau);
margin: 5mm 0 2.5mm; display: flex; gap: 3mm; align-items: center; }
h2::after { content: ''; flex: 1; border-top: 0.25mm solid rgba(33,33,29,.25); }
.schritt { display: grid; grid-template-columns: 17mm 1fr; gap: 3mm; padding: 2.4mm 0; border-bottom: 0.25mm dotted rgba(33,33,29,.35); }
.zeit { font-family: 'Courier New', monospace; font-weight: 700; color: var(--amtsblau); font-size: 9pt; }
.schritt h3 { font-size: 10.5pt; margin-bottom: .8mm; }
.sag { border-left: 0.8mm solid var(--amtsblau); background: #fff; padding: 1.2mm 2.5mm; margin: 1mm 0; font-style: italic; }
ol, ul { padding-left: 5mm; }
li { margin-bottom: .6mm; }
.ziel { margin-top: 4mm; border: 0.6mm solid var(--tinte); box-shadow: 1.5mm 1.8mm 0 rgba(33,33,29,.9); background: #fff; padding: 3mm 4mm; }
.ziel b { color: var(--rot); }
.zwei { display: grid; grid-template-columns: 1fr 1fr; gap: 5mm; }
.kasten { background: #fff; border: 0.3mm solid rgba(33,33,29,.3); padding: 2.5mm 3.5mm; }
.check li { list-style: none; position: relative; padding-left: 5mm; }
.check li::before { content: '☐'; position: absolute; left: 0; color: var(--amtsblau); }
.frage { margin-bottom: 2mm; }
.frage b { display: block; }
.nicht { border-left: 0.8mm solid var(--rot); background: #fdf1ef; padding: 1.5mm 2.5mm; }
.klein { font-size: 8.3pt; color: var(--grau); }
</style>
</head>
<body>
<div class="seite">
<div class="kopf"><b>📬 BehördenKlar · Ablaufplan</b><span>MBE Kirchheimbolanden · Do 29.10.2026 · nur für mich</span></div>
<h1>10–15 Minuten. <span class="markiert">Zeigen, zuhören, Testzugang dalassen.</span></h1>
<p class="unter">Die Webseite ist die Präsentation. Alles läuft im Browser, ohne App. Die App nur kurz erwähnen.</p>
<h2>Ablauf</h2>
<div class="schritt"><span class="zeit">0–1'</span><div>
<h3>Begrüßung und wer ich bin</h3>
<p class="sag">„Ich bin Justin Klein aus Winnweiler. Meine Mutter ist selbst nach Deutschland gekommen. Briefe vom Amt kenne ich von klein auf, deshalb habe ich BehördenKlar gebaut.“</p>
</div></div>
<div class="schritt"><span class="zeit">1–3'</span><div>
<h3>Das Problem aus ihrer Sicht: erst fragen, dann zuhören</h3>
<p class="sag">„Wie oft kommt jemand zu Ihnen, bei dem die Frist schon fast vorbei ist? Oder bei dem der Termin erst mal nur fürs Vorlesen draufgeht?“</p>
<p>Kurz zuhören und ihre Antwort aufgreifen. Das ist der Aufhänger für alles Weitere.</p>
</div></div>
<div class="schritt"><span class="zeit">3–8'</span><div>
<h3>Live-Vorführung mit dem Musterbrief</h3>
<ol>
<li>Laptop: <b>behoerdenklar.de/demo</b> ist schon offen, Sprache <b>Arabisch</b> gewählt, Vorführ-Code eingelöst.</li>
<li>„Lieber mit dem Handy fotografieren“ antippen, mit dem Handy den QR-Code scannen.</li>
<li>Ausgedruckten Musterbrief fotografieren. <i>„Das dauert etwa eine Minute.“</i></li>
<li>Laptop zeigt die Erklärung <b>auf Deutsch</b>, das Handy <b>auf Arabisch</b>. Kurz umschalten.</li>
<li>Zeigen: <b>rote Ampel</b> (Termin in 7 Tagen), <b>„In meinen Kalender“</b>, <b>Anruf-Hilfe</b> mit Aktenzeichen, Fachbegriff „Fiktionsbescheinigung“ bleibt deutsch.</li>
<li>„Ergebnis aufs Handy mitnehmen“: So nimmt jemand die Erklärung nach der Beratung mit nach Hause.</li>
</ol>
<p class="sag">„Alles, was Sie sehen, geht ohne App, im Browser, auf jedem Handy, ohne Konto. Eine App kommt später dazu, zum Beispiel für Erinnerungen.“</p>
</div></div>
<div class="schritt"><span class="zeit">8–9'</span><div>
<h3>Datenschutz ehrlich (bevor sie fragen)</h3>
<p class="sag">„Kein Konto, wir speichern keine Briefe. Zwischen PC und Handy ist alles Ende-zu-Ende verschlüsselt. Die Erklärung macht ein KI-Dienst in den USA, Anthropic. Der löscht nach spätestens 30 Tagen und trainiert nicht damit. Das steht auch vor dem Hochladen auf der Seite.“</p>
</div></div>
<div class="schritt"><span class="zeit">9–11'</span><div>
<h3>Das Angebot: Handzettel übergeben</h3>
<p class="sag">„Ich möchte Ihnen einen Testzugang dalassen: einen Monat, bis zu 100 Briefe, kostenlos. Ihre Ratsuchenden zahlen nichts. Der Code steht hier auf dem Zettel.“</p>
<p><b>Gleich den Auswertungstermin ausmachen:</b> „Darf ich in vier Wochen wiederkommen, am <b>Do 26.11.</b>, und hören, was es gebracht hat?“</p>
</div></div>
<div class="schritt" style="border-bottom: none;"><span class="zeit">11–15'</span><div>
<h3>Zuhören (das ist der eigentliche Gewinn)</h3>
<ol>
<li>Welche Briefe bringen Ihre Ratsuchenden am häufigsten mit?</li>
<li>Was fehlt in dem, was ich gezeigt habe?</li>
<li>Würden Sie es empfehlen? Wenn nicht, warum nicht?</li>
<li>Darf ich Ihre Stelle auf meiner Seite nennen, und würden Sie auf BehördenKlar verlinken?</li>
</ol>
</div></div>
<div class="ziel">
<b>Ziel des Termins:</b> Code übergeben <i>und</i> Termin am 26.11. im Kalender. Ein ehrliches „das fehlt noch“ ist auch ein Erfolg. Mitschreiben!
</div>
</div>
<div class="seite">
<div class="kopf"><b>📬 BehördenKlar · Vorbereitung &amp; Fragen</b><span>Seite 2</span></div>
<div class="zwei" style="margin-top: 4mm;">
<div>
<h2 style="margin-top: 0;">Am Vorabend</h2>
<ul class="check kasten">
<li><b>Deploy erledigt</b>, <span class="mono">npm run pruefung</span> alles grün</li>
<li>MBE-Code angelegt, im Handzettel eingetragen</li>
<li>Handzettel <b>5×</b> gedruckt, Musterbrief <b>2×</b> gedruckt</li>
<li>Einmal komplett durchgespielt, mit Stoppuhr</li>
<li>Screenshots und kurzes Video als Notfall offline auf dem Laptop</li>
<li>Laptop und Handy laden, Ladekabel einpacken</li>
</ul>
<h2>Kurz vor dem Termin</h2>
<ul class="check kasten">
<li><b>Eigener Handy-Hotspot</b> an (nicht ihr WLAN)</li>
<li>Demo-Seite offen, <b>Vorführ-Code</b> eingelöst, Arabisch gewählt</li>
<li>Handy entsperrt, Kamera-App bereit, Ton aus</li>
<li>Notizblock und Stift</li>
</ul>
<h2>Notfallplan</h2>
<div class="kasten">
<p><b>Kein Internet:</b> Hotspot. Wenn das auch nicht geht: Screenshots zeigen, <i>„Ich lasse Ihnen den Link da, probieren Sie es selbst.“</i></p>
<p style="margin-top: 1.5mm;"><b>Die KI braucht lange:</b> ruhig weitererzählen (Datenschutz-Teil vorziehen).</p>
<p style="margin-top: 1.5mm;"><b>Fehlermeldung:</b> nicht nervös werden, zweiten Musterbrief nehmen oder Screenshots.</p>
</div>
</div>
<div>
<h2 style="margin-top: 0;">Typische Fragen</h2>
<div class="frage"><b>„Was passiert mit den Daten?“</b>Kein Konto. Wir speichern den Brief nicht. Die KI (Anthropic, USA) löscht nach spätestens 30 Tagen, kein Training. PC ↔ Handy Ende-zu-Ende verschlüsselt.</div>
<div class="frage"><b>„Ist das nicht einfach ChatGPT?“</b>ChatGPT braucht ein Konto und eine gute Frage, und danach ist der Brief vergessen. Hier reicht ein Foto: Frist in den Kalender, Spickzettel fürs Telefon. Wir erfinden keine Nummern oder Daten.</div>
<div class="frage"><b>„Ersetzt das unsere Beratung?“</b>Nein. Jede Erklärung verweist bei wichtigen Entscheidungen auf eine Beratungsstelle. Die einfachen Fälle werden abgefangen, die schweren kommen vorbereitet.</div>
<div class="frage"><b>„Was kostet das nach dem Test?“</b>Ein festes Briefkontingent für die Einrichtung, Ratsuchende zahlen nichts. <span class="klein">Nur auf Nachfrage Zahlen: 100 Briefe 20 €, 500 Briefe 75 €. Untergrenze 10 ct pro Brief. Keine USt. (§ 19).</span></div>
<div class="frage"><b>„Was kostet Sie ein Brief?“</b>Ehrlich: ein paar Cent für die KI, dazu Server, Zahlungsgebühren und Arbeitszeit.</div>
<div class="frage"><b>„Gibt es eine App?“</b>Sie ist in Arbeit. Alles Gezeigte geht ohne App. <i>Keinen Termin versprechen.</i></div>
<div class="frage"><b>„Wer steckt dahinter?“</b>Ich, mit der Webklar GbR in Winnweiler. Aus dem Donnersbergkreis.</div>
<h2>Nicht sagen</h2>
<div class="nicht">
<p>✗ „Alles bleibt auf dem Handy“: Das stimmt für die Webseite nicht.</p>
<p>✗ „Unbegrenzt“: immer eine feste Briefzahl.</p>
<p>✗ Ein Datum für die App oder neue Funktionen versprechen.</p>
</div>
</div>
</div>
</div>
</body>
</html>
Binary file not shown.
+3 -3
View File
@@ -5,7 +5,7 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { AmpelStatus } from '../utils/ampel';
import { abstand, schrift } from '../theme';
import { abstand, schrift, schriftart, ecke } from '../theme';
import { Ikone, IkonenName } from './Ikone';
const IKONE: Record<string, IkonenName> = { rot: 'warnung', gelb: 'wecker', gruen: 'info' };
@@ -28,9 +28,9 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
borderLeftWidth: 6,
borderRadius: 12,
borderRadius: ecke,
padding: abstand.m,
gap: abstand.s,
},
text: { flex: 1, fontSize: schrift.gross, fontWeight: '700' },
text: { fontFamily: schriftart.serif, flex: 1, fontSize: schrift.gross, fontWeight: '700' },
});
+4 -4
View File
@@ -14,7 +14,7 @@ import { AppState, AppStateStatus, StyleSheet, Text, View } from 'react-native';
import * as LocalAuthentication from 'expo-local-authentication';
import { holeAppSperre } from '../services/storage';
import { GrossButton } from './GrossButton';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart } from '../theme';
export function AppSchutz({ children }: { children: React.ReactNode }) {
const [entsperrt, setEntsperrt] = useState(false);
@@ -97,14 +97,14 @@ const styles = StyleSheet.create({
gap: abstand.m,
},
sichtschutz: { justifyContent: 'center', alignItems: 'center' },
symbol: { fontSize: 56, textAlign: 'center' },
titel: {
symbol: { fontFamily: schriftart.serif, fontSize: 56, textAlign: 'center' },
titel: { fontFamily: schriftart.serif,
fontSize: schrift.titel,
fontWeight: '700',
color: farben.text,
textAlign: 'center',
},
text: {
text: { fontFamily: schriftart.serif,
fontSize: schrift.basis,
color: farben.textSekundaer,
textAlign: 'center',
+13 -11
View File
@@ -8,7 +8,7 @@
*/
import React from 'react';
import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
import { farben, schrift, abstand, TOUCH_TARGET, schriftart, ecke } from '../theme';
import { Ikone, IkonenName } from './Ikone';
interface Props {
@@ -30,8 +30,8 @@ export function GrossButton({
}: Props) {
const istPrimaer = variante === 'primaer';
const istGefahr = variante === 'gefahr';
// Sekundär: dunkles Bronze statt hellem Gold — auf grauer Fläche gut lesbar
const inhaltsFarbe = istPrimaer || istGefahr ? farben.primaerText : farben.primaerFuellung;
// Wie `.cta` der Webseite: Amtsblau gefüllt bzw. weißes Blatt mit blauer Schrift
const inhaltsFarbe = istPrimaer ? farben.primaerText : istGefahr ? farben.fehler : farben.primaer;
return (
<Pressable
onPress={onPress}
@@ -44,7 +44,7 @@ export function GrossButton({
variante === 'sekundaer' && styles.sekundaer,
istGefahr && styles.gefahr,
(deaktiviert || laedt) && styles.deaktiviert,
pressed && styles.gedrueckt,
pressed && !(deaktiviert || laedt) && styles.gedrueckt,
]}
>
{laedt ? (
@@ -62,17 +62,19 @@ export function GrossButton({
const styles = StyleSheet.create({
button: {
minHeight: TOUCH_TARGET,
borderRadius: 14,
borderRadius: ecke,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: abstand.l,
paddingVertical: abstand.s,
},
zeile: { flexDirection: 'row', alignItems: 'center', gap: abstand.xs + 2 },
primaer: { backgroundColor: farben.primaerFuellung },
sekundaer: { backgroundColor: farben.flaecheSekundaer },
gefahr: { backgroundColor: farben.fehler },
deaktiviert: { opacity: 0.4 },
gedrueckt: { opacity: 0.65 },
text: { fontSize: schrift.gross, fontWeight: '600', textAlign: 'center' },
// Tinten-Rahmen + harter Versatz-Schatten wie `.cta` auf der Webseite;
// beim Drücken rutscht der Knopf in den Schatten (wie dort beim Hover)
primaer: { backgroundColor: farben.primaerFuellung, borderWidth: 2, borderColor: farben.tinte, boxShadow: '4px 4px 0px #21211D' },
sekundaer: { backgroundColor: farben.flaeche, borderWidth: 2, borderColor: farben.tinte, boxShadow: '4px 4px 0px #21211D' },
gefahr: { backgroundColor: farben.flaeche, borderWidth: 2, borderColor: farben.fehler },
deaktiviert: { opacity: 0.4, boxShadow: 'none' },
gedrueckt: { transform: [{ translateX: 2 }, { translateY: 2 }], boxShadow: '2px 2px 0px #21211D' },
text: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', textAlign: 'center' },
});
+29 -29
View File
@@ -24,7 +24,7 @@ import { terminZumKalender } from '../services/kalender';
import { berechneAmpel, naechsteFrist, formatiereLangDatum } from '../utils/ampel';
import { GrossButton } from '../components/GrossButton';
import { Ikone } from '../components/Ikone';
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
import { farben, schrift, abstand, TOUCH_TARGET, schriftart, ecke, kartenSchatten } from '../theme';
type Props = NativeStackScreenProps<RootStackParamList, 'Analyse'>;
@@ -343,66 +343,66 @@ const styles = StyleSheet.create({
zentriert: { flex: 1, justifyContent: 'center', alignItems: 'center' },
// Frist-Karte
fristKarte: { borderRadius: 16, borderWidth: 1, padding: abstand.l },
fristKarte: { borderRadius: ecke, borderWidth: 1, padding: abstand.l },
fristKopf: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 10 },
fristKicker: { fontSize: 12, letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: '700' },
grosseZahl: { fontSize: 64, lineHeight: 64, fontWeight: '800', fontVariant: ['tabular-nums'] },
zahlEinheit: { fontSize: 22, fontWeight: '700', lineHeight: 24 },
zahlUnter: { fontSize: 13, color: farben.textSekundaer },
fristKicker: { fontFamily: schriftart.mono, fontSize: 12, letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: '700' },
grosseZahl: { fontFamily: schriftart.serif, fontSize: 64, lineHeight: 64, fontWeight: '800', fontVariant: ['tabular-nums'] },
zahlEinheit: { fontFamily: schriftart.serif, fontSize: 22, fontWeight: '700', lineHeight: 24 },
zahlUnter: { fontFamily: schriftart.serif, fontSize: 13, color: farben.textSekundaer },
trenner: { height: 1, backgroundColor: farben.rand, marginVertical: 14 },
datenZeile: { flexDirection: 'row', justifyContent: 'space-between', gap: 12, marginTop: 5 },
datenLabel: { fontSize: schrift.klein, color: farben.textSekundaer },
datenWert: { fontSize: schrift.klein, fontWeight: '700', color: farben.text },
datenLabel: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer },
datenWert: { fontFamily: schriftart.serif, fontSize: schrift.klein, fontWeight: '700', color: farben.text },
keineFrist: {
flexDirection: 'row', gap: abstand.s, alignItems: 'center',
backgroundColor: farben.ampelGruenHintergrund, borderRadius: 12,
backgroundColor: farben.ampelGruenHintergrund, borderRadius: ecke,
borderLeftWidth: 4, borderLeftColor: farben.ampelGruen, padding: abstand.m,
},
keineFristTitel: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
keineFristText: { fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
keineFristTitel: { fontFamily: schriftart.serif, fontSize: schrift.basis, fontWeight: '700', color: farben.text },
keineFristText: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
// Werkzeuge
werkzeugZeile: { flexDirection: 'row', gap: abstand.s, marginTop: abstand.m },
werkzeug: {
werkzeug: { ...kartenSchatten,
flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 7,
backgroundColor: farben.flaeche, borderRadius: 12, minHeight: 48, paddingHorizontal: abstand.s,
backgroundColor: farben.flaeche, borderRadius: ecke, minHeight: 48, paddingHorizontal: abstand.s,
},
werkzeugText: { fontSize: schrift.klein, fontWeight: '600', color: farben.primaerFuellung },
werkzeugText: { fontFamily: schriftart.serif, fontSize: schrift.klein, fontWeight: '600', color: farben.primaerFuellung },
kicker: {
kicker: { fontFamily: schriftart.mono,
fontSize: 12, letterSpacing: 1.1, textTransform: 'uppercase',
color: farben.primaer, fontWeight: '700', marginTop: abstand.l, marginBottom: abstand.xs,
color: farben.textSekundaer, fontWeight: '700', marginTop: abstand.l, marginBottom: abstand.xs,
},
kernaussage: { fontSize: schrift.gross, color: farben.text, lineHeight: 29, fontWeight: '600' },
erklaerung: { fontSize: schrift.basis, color: farben.textSekundaer, lineHeight: 27, marginTop: abstand.s },
kernaussage: { fontFamily: schriftart.serif, fontSize: schrift.gross, color: farben.text, lineHeight: 29, fontWeight: '600' },
erklaerung: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.textSekundaer, lineHeight: 27, marginTop: abstand.s },
dünnerTrenner: { height: 1, backgroundColor: farben.rand, marginTop: abstand.l },
// To-do
todoZeile: { flexDirection: 'row', gap: abstand.s, alignItems: 'flex-start', minHeight: 40 },
todoKreis: {
width: 28, height: 28, borderRadius: 14, borderWidth: 1.5, borderColor: farben.primaer,
width: 28, height: 28, borderRadius: ecke, borderWidth: 1.5, borderColor: farben.primaer,
alignItems: 'center', justifyContent: 'center', marginTop: 1,
},
todoKreisFertig: { backgroundColor: farben.ampelGruen, borderColor: farben.ampelGruen },
todoNummer: { fontSize: schrift.klein, fontWeight: '700', color: farben.primaerFuellung, fontVariant: ['tabular-nums'] },
todoText: { flex: 1, fontSize: schrift.basis, color: farben.text, lineHeight: 25, paddingTop: 3 },
todoNummer: { fontFamily: schriftart.serif, fontSize: schrift.klein, fontWeight: '700', color: farben.primaerFuellung, fontVariant: ['tabular-nums'] },
todoText: { fontFamily: schriftart.serif, flex: 1, fontSize: schrift.basis, color: farben.text, lineHeight: 25, paddingTop: 3 },
durchgestrichen: { textDecorationLine: 'line-through', color: farben.textSekundaer },
// Fachbegriffe
begriffKarte: { backgroundColor: farben.flaeche, borderRadius: 12, padding: abstand.m },
begriff: { fontSize: schrift.basis, fontWeight: '700', color: farben.text, marginBottom: 3 },
begriffText: { fontSize: schrift.klein, color: farben.textSekundaer, lineHeight: 23 },
begriffKarte: { ...kartenSchatten, backgroundColor: farben.flaeche, borderRadius: ecke, padding: abstand.m },
begriff: { fontFamily: schriftart.serif, fontSize: schrift.basis, fontWeight: '700', color: farben.text, marginBottom: 3 },
begriffText: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, lineHeight: 23 },
schutzZeile: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, marginTop: abstand.l },
schutzText: { fontSize: 11.5, color: farben.textTertiaer },
schutzText: { fontFamily: schriftart.serif, fontSize: 11.5, color: farben.textTertiaer },
text: { fontSize: schrift.basis, color: farben.text, lineHeight: 27 },
text: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.text, lineHeight: 27 },
// Modal
modalHintergrund: { flex: 1, backgroundColor: 'rgba(0,0,0,0.45)', justifyContent: 'center', padding: abstand.l },
modalKarte: { backgroundColor: farben.flaeche, borderRadius: 14, padding: abstand.m, maxHeight: '75%' },
modalTitel: { fontSize: schrift.gross, fontWeight: '700', color: farben.text, marginBottom: abstand.xs },
modalKarte: { ...kartenSchatten, backgroundColor: farben.flaeche, borderRadius: ecke, padding: abstand.m, maxHeight: '75%' },
modalTitel: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text, marginBottom: abstand.xs },
sprachZeile: { minHeight: TOUCH_TARGET, justifyContent: 'center' },
sprachZeileText: { fontSize: schrift.gross, color: farben.text },
sprachZeileText: { fontFamily: schriftart.serif, fontSize: schrift.gross, color: farben.text },
});
+18 -18
View File
@@ -26,7 +26,7 @@ import { generiereAntwort } from '../services/antwort';
import { ClaudeFehler } from '../services/claudeClient';
import { GrossButton } from '../components/GrossButton';
import { Ikone, IkonenName } from '../components/Ikone';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart, ecke, kartenSchatten } from '../theme';
type Props = NativeStackScreenProps<RootStackParamList, 'Antwort'>;
@@ -258,28 +258,28 @@ function escapeHtml(s: string): string {
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: farben.hintergrund },
zentriert: { flex: 1, justifyContent: 'center', alignItems: 'center' },
text: { fontSize: schrift.basis, color: farben.text },
text: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.text },
schrittLabel: { fontSize: schrift.klein, color: farben.textSekundaer, fontWeight: '600', marginBottom: abstand.xs },
schrittLabel: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, fontWeight: '600', marginBottom: abstand.xs },
fortschritt: { flexDirection: 'row', gap: 6, marginBottom: abstand.l },
balken: { flex: 1, height: 5, borderRadius: 3 },
titel: { fontSize: schrift.titel, fontWeight: '800', color: farben.text, marginBottom: 4 },
unterTitel: { fontSize: schrift.klein, color: farben.textSekundaer, lineHeight: 22, marginBottom: abstand.s },
titel: { fontFamily: schriftart.serif, fontSize: schrift.titel, fontWeight: '800', color: farben.text, marginBottom: 4 },
unterTitel: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, lineHeight: 22, marginBottom: abstand.s },
// Schritt 1 — Karten
typKarte: {
typKarte: { ...kartenSchatten,
flexDirection: 'row', alignItems: 'center', gap: abstand.s,
backgroundColor: farben.flaeche, borderRadius: 14, padding: abstand.m,
backgroundColor: farben.flaeche, borderRadius: ecke, padding: abstand.m,
},
typIkone: {
width: 44, height: 44, borderRadius: 12, borderWidth: 1, borderColor: farben.primaer,
width: 44, height: 44, borderRadius: ecke, borderWidth: 1, borderColor: farben.primaer,
alignItems: 'center', justifyContent: 'center', flexShrink: 0,
},
typTitel: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
typBeschreibung: { fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2, lineHeight: 20 },
typTitel: { fontFamily: schriftart.serif, fontSize: schrift.basis, fontWeight: '700', color: farben.text },
typBeschreibung: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2, lineHeight: 20 },
empfohlenTag: { backgroundColor: farben.hervorhebung, borderRadius: 6, paddingHorizontal: 8, paddingVertical: 2 },
empfohlenText: { fontSize: 11, fontWeight: '700', color: farben.primaerFuellung },
empfohlenText: { fontFamily: schriftart.serif, fontSize: 11, fontWeight: '700', color: farben.primaerFuellung },
// Schritt 2/3
wahlPill: {
@@ -287,19 +287,19 @@ const styles = StyleSheet.create({
borderWidth: 1, borderColor: farben.primaer, borderRadius: 9999,
paddingHorizontal: 12, paddingVertical: 6, marginBottom: abstand.m,
},
wahlPillText: { fontSize: schrift.klein, color: farben.primaerFuellung, fontWeight: '600' },
feldLabel: { fontSize: schrift.klein, color: farben.textSekundaer, fontWeight: '600', marginTop: abstand.s, marginBottom: 6 },
eingabe: {
borderWidth: 1, borderColor: farben.rand, borderRadius: 12, padding: abstand.s,
wahlPillText: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.primaerFuellung, fontWeight: '600' },
feldLabel: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, fontWeight: '600', marginTop: abstand.s, marginBottom: 6 },
eingabe: { fontFamily: schriftart.serif,
borderWidth: 1, borderColor: farben.rand, borderRadius: ecke, padding: abstand.s,
fontSize: schrift.basis, color: farben.text, backgroundColor: farben.flaeche, minHeight: 52,
},
mehrzeilig: { minHeight: 110 },
briefText: { minHeight: 300, lineHeight: 26 },
infoBox: {
flexDirection: 'row', gap: abstand.s, alignItems: 'flex-start',
backgroundColor: farben.hervorhebung, borderRadius: 12, padding: abstand.m, marginTop: abstand.m,
backgroundColor: farben.hervorhebung, borderRadius: ecke, padding: abstand.m, marginTop: abstand.m,
},
infoBoxText: { flex: 1, fontSize: schrift.klein, color: farben.text, lineHeight: 21 },
infoBoxText: { fontFamily: schriftart.serif, flex: 1, fontSize: schrift.klein, color: farben.text, lineHeight: 21 },
zurueck: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, marginTop: abstand.m, paddingVertical: abstand.s },
zurueckText: { fontSize: schrift.klein, color: farben.primaerFuellung, fontWeight: '600' },
zurueckText: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.primaerFuellung, fontWeight: '600' },
});
+17 -17
View File
@@ -13,7 +13,7 @@ import { berechneAmpel } from '../utils/ampel';
import { formatiereDatum } from '../services/erinnerungen';
import { Ikone } from '../components/Ikone';
import { GrossButton } from '../components/GrossButton';
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
import { farben, schrift, abstand, TOUCH_TARGET, schriftart, ecke, kartenSchatten } from '../theme';
type Nav = NativeStackNavigationProp<RootStackParamList>;
@@ -161,39 +161,39 @@ export function ArchivScreen() {
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: farben.hintergrund },
kicker: {
kicker: { fontFamily: schriftart.mono,
fontSize: 12, letterSpacing: 1.3, textTransform: 'uppercase',
color: farben.primaer, fontWeight: '600', marginBottom: 4,
color: farben.textSekundaer, fontWeight: '600', marginBottom: 4,
},
titel: { fontSize: schrift.riesig, fontWeight: '800', color: farben.text, marginBottom: abstand.m },
sucheBox: {
titel: { fontFamily: schriftart.serif, fontSize: schrift.riesig, fontWeight: '800', color: farben.text, marginBottom: abstand.m },
sucheBox: { ...kartenSchatten,
flexDirection: 'row', alignItems: 'center', gap: 8,
backgroundColor: farben.flaeche, borderRadius: 12,
backgroundColor: farben.flaeche, borderRadius: ecke,
paddingHorizontal: abstand.s, marginBottom: abstand.s, minHeight: 48,
},
sucheFeld: { flex: 1, fontSize: schrift.basis, color: farben.text, paddingVertical: abstand.s },
sucheFeld: { fontFamily: schriftart.serif, flex: 1, fontSize: schrift.basis, color: farben.text, paddingVertical: abstand.s },
chip: {
borderRadius: 9999, borderWidth: 1, borderColor: farben.rand,
backgroundColor: farben.flaeche, paddingHorizontal: 14, paddingVertical: 8,
},
chipAktiv: { borderColor: farben.primaer, backgroundColor: farben.hervorhebung },
chipText: { fontSize: schrift.klein, color: farben.textSekundaer },
chipText: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer },
chipTextAktiv: { color: farben.primaer, fontWeight: '600' },
zahlZeile: {
zahlZeile: { fontFamily: schriftart.mono,
fontSize: 12, letterSpacing: 1, textTransform: 'uppercase',
color: farben.textTertiaer, marginBottom: abstand.s,
},
zeile: {
zeile: { ...kartenSchatten,
flexDirection: 'row', alignItems: 'center', gap: abstand.s,
backgroundColor: farben.flaeche, borderRadius: 12,
backgroundColor: farben.flaeche, borderRadius: ecke,
padding: abstand.m, marginBottom: abstand.s, minHeight: TOUCH_TARGET,
},
punkt: { width: 10, height: 10, borderRadius: 5 },
zeileTyp: { flex: 1, fontSize: schrift.basis, fontWeight: '700', color: farben.text },
datum: { fontSize: schrift.klein, color: farben.textTertiaer, fontVariant: ['tabular-nums'] },
zeileMeta: { fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
ampelText: { fontSize: schrift.klein, fontWeight: '600', marginTop: 2 },
zeileTyp: { fontFamily: schriftart.serif, flex: 1, fontSize: schrift.basis, fontWeight: '700', color: farben.text },
datum: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textTertiaer, fontVariant: ['tabular-nums'] },
zeileMeta: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
ampelText: { fontFamily: schriftart.serif, fontSize: schrift.klein, fontWeight: '600', marginTop: 2 },
leerBox: { alignItems: 'center', gap: abstand.s, marginTop: abstand.xl },
leerTitel: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
leerText: { fontSize: schrift.basis, color: farben.textSekundaer, textAlign: 'center', lineHeight: 26 },
leerTitel: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text },
leerText: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.textSekundaer, textAlign: 'center', lineHeight: 26 },
});
+5 -5
View File
@@ -8,7 +8,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { RootStackParamList } from '../types';
import { speichereConsent } from '../services/storage';
import { GrossButton } from '../components/GrossButton';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart, ecke, kartenSchatten } from '../theme';
type Props = NativeStackScreenProps<RootStackParamList, 'Consent'>;
@@ -60,18 +60,18 @@ export function ConsentScreen({ navigation }: Props) {
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: farben.hintergrund },
inhalt: { padding: abstand.l },
titel: {
titel: { fontFamily: schriftart.serif,
fontSize: schrift.titel,
fontWeight: '700',
color: farben.text,
marginBottom: abstand.l,
},
karte: {
karte: { ...kartenSchatten,
backgroundColor: farben.flaeche,
borderRadius: 14,
borderRadius: ecke,
padding: abstand.m,
marginBottom: abstand.xl,
gap: abstand.m,
},
punkt: { fontSize: schrift.basis, color: farben.text, lineHeight: 27 },
punkt: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.text, lineHeight: 27 },
});
+8 -8
View File
@@ -33,7 +33,7 @@ import {
import { NUTZT_PROXY } from '../services/claudeClient';
import { useAppStore } from '../store/useAppStore';
import { GrossButton } from '../components/GrossButton';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart, ecke, kartenSchatten } from '../theme';
type Props = NativeStackScreenProps<RootStackParamList, 'Einstellungen'>;
@@ -268,30 +268,30 @@ export function EinstellungenScreen({ navigation }: Props) {
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: farben.hintergrund },
abschnittTitel: {
abschnittTitel: { fontFamily: schriftart.serif,
fontSize: schrift.gross,
fontWeight: '700',
color: farben.primaer,
marginBottom: abstand.s,
},
text: { fontSize: schrift.basis, color: farben.text, lineHeight: 27 },
hinweis: {
text: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.text, lineHeight: 27 },
hinweis: { fontFamily: schriftart.serif,
fontSize: schrift.klein,
color: farben.textSekundaer,
lineHeight: 22,
marginVertical: abstand.s,
},
eingabe: {
eingabe: { fontFamily: schriftart.serif,
borderWidth: 1,
borderColor: farben.rand,
borderRadius: 12,
borderRadius: ecke,
padding: abstand.s,
fontSize: schrift.basis,
color: farben.text,
minHeight: 56,
marginBottom: abstand.s,
},
karte: { backgroundColor: farben.flaeche, borderRadius: 14, padding: abstand.m },
karte: { ...kartenSchatten, backgroundColor: farben.flaeche, borderRadius: ecke, padding: abstand.m },
trenner: { height: 1, backgroundColor: farben.rand, marginVertical: abstand.l },
schalterZeile: {
flexDirection: 'row',
@@ -301,7 +301,7 @@ const styles = StyleSheet.create({
option: {
borderWidth: 1,
borderColor: farben.rand,
borderRadius: 12,
borderRadius: ecke,
padding: abstand.s,
minHeight: 56,
justifyContent: 'center',
+50 -35
View File
@@ -21,7 +21,7 @@ import {
NaechsteFrist,
} from '../utils/ampel';
import { Ikone } from '../components/Ikone';
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
import { farben, schrift, abstand, TOUCH_TARGET, schriftart, ecke, kartenSchatten, formularRahmen } from '../theme';
type Nav = NativeStackNavigationProp<RootStackParamList>;
@@ -93,8 +93,10 @@ export function FristenScreen() {
{/* Kopf */}
<View style={styles.kopf}>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<Ikone name="uhr" groesse={22} farbe={farben.primaer} />
<Text style={styles.marke}>BehördenKlar</Text>
<Ikone name="brief" groesse={22} farbe={farben.primaer} />
<Text style={styles.marke}>
BehördenKlar <Text style={styles.markeZusatz}>by Webklar</Text>
</Text>
</View>
<View style={{ flexDirection: 'row', gap: 8 }}>
<KopfKnopf ikone="buch" label="Glossar" onPress={() => navigation.navigate('Glossar')} />
@@ -104,7 +106,9 @@ export function FristenScreen() {
<View style={{ paddingHorizontal: abstand.m }}>
<Text style={styles.kicker}>Fristen-Übersicht</Text>
<Text style={styles.titel}>Ihre Fristen</Text>
<Text style={styles.titel}>
Ihre <Text style={styles.markiert}> Fristen </Text>
</Text>
<Text style={styles.unterTitel}>
Nach Dringlichkeit geordnet. Kümmern Sie sich zuerst um das Oberste.
</Text>
@@ -238,44 +242,55 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: abstand.m,
marginBottom: abstand.m,
marginHorizontal: abstand.m,
paddingBottom: abstand.s,
marginBottom: abstand.l,
// Briefkopf wie auf der Webseite: Tinten-Linie unter Absender
borderBottomWidth: 2,
borderBottomColor: farben.tinte,
},
marke: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
markeZusatz: { fontFamily: schriftart.serif, fontSize: 12, fontWeight: '400', color: farben.textSekundaer },
// gelber Textmarker — das Erkennungszeichen der Webseite
markiert: { backgroundColor: farben.marker },
marke: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text },
kopfKnopf: {
width: 40,
height: 40,
borderRadius: 20,
borderRadius: ecke,
backgroundColor: farben.flaeche,
borderWidth: 1.5,
borderColor: farben.tinte,
alignItems: 'center',
justifyContent: 'center',
},
kicker: {
kicker: { fontFamily: schriftart.mono,
fontSize: 12,
letterSpacing: 1.3,
textTransform: 'uppercase',
color: farben.primaer,
color: farben.textSekundaer,
fontWeight: '600',
marginBottom: 4,
},
titel: { fontSize: schrift.riesig, fontWeight: '800', color: farben.text, marginBottom: 4 },
unterTitel: { fontSize: schrift.klein, color: farben.textSekundaer, marginBottom: abstand.l },
titel: { fontFamily: schriftart.serif, fontSize: schrift.riesig, fontWeight: '800', color: farben.text, marginBottom: 4 },
unterTitel: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, marginBottom: abstand.l },
// wichtigste Frist wie ein Formular der Webseite: Tinten-Rahmen, harter Schatten
heldKarte: {
borderRadius: 16,
borderLeftWidth: 5,
...formularRahmen,
borderRadius: ecke,
borderLeftWidth: 7,
padding: abstand.l,
marginBottom: abstand.l,
},
heldKopf: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 12 },
punkt: { width: 9, height: 9, borderRadius: 5 },
heldKicker: { fontSize: 12, letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: '700' },
grosseZahl: { fontSize: 68, lineHeight: 68, fontWeight: '800', fontVariant: ['tabular-nums'] },
zahlEinheit: { fontSize: 22, fontWeight: '700', lineHeight: 24 },
zahlDatum: { fontSize: 13, color: farben.textSekundaer, marginTop: 2 },
heldKicker: { fontFamily: schriftart.mono, fontSize: 12, letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: '700' },
grosseZahl: { fontFamily: schriftart.serif, fontSize: 68, lineHeight: 68, fontWeight: '800', fontVariant: ['tabular-nums'] },
zahlEinheit: { fontFamily: schriftart.serif, fontSize: 22, fontWeight: '700', lineHeight: 24 },
zahlDatum: { fontFamily: schriftart.serif, fontSize: 13, color: farben.textSekundaer, marginTop: 2 },
trenner: { height: 1, backgroundColor: farben.rand, marginVertical: 15 },
briefTyp: { fontSize: schrift.gross, fontWeight: '700', color: farben.text, marginBottom: 3 },
briefMeta: { fontSize: schrift.klein, color: farben.textSekundaer },
abschnitt: {
briefTyp: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text, marginBottom: 3 },
briefMeta: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer },
abschnitt: { fontFamily: schriftart.mono,
fontSize: 12,
letterSpacing: 1.1,
textTransform: 'uppercase',
@@ -284,42 +299,42 @@ const styles = StyleSheet.create({
marginTop: abstand.l,
marginBottom: abstand.s,
},
zeile: {
zeile: { ...kartenSchatten,
flexDirection: 'row',
alignItems: 'center',
gap: abstand.m,
backgroundColor: farben.flaeche,
borderRadius: 12,
borderRadius: ecke,
borderLeftWidth: 4,
padding: abstand.m,
},
zahlBox: { width: 54, alignItems: 'center' },
zahlKlein: { fontSize: 30, fontWeight: '800', fontVariant: ['tabular-nums'], lineHeight: 32 },
zahlBoxLabel: { fontSize: 10, letterSpacing: 0.5, textTransform: 'uppercase', color: farben.textTertiaer },
zeileTyp: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
zeileMeta: { fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
zahlKlein: { fontFamily: schriftart.serif, fontSize: 30, fontWeight: '800', fontVariant: ['tabular-nums'], lineHeight: 32 },
zahlBoxLabel: { fontFamily: schriftart.mono, fontSize: 10, letterSpacing: 0.5, textTransform: 'uppercase', color: farben.textTertiaer },
zeileTyp: { fontFamily: schriftart.serif, fontSize: schrift.basis, fontWeight: '700', color: farben.text },
zeileMeta: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
tag: { alignSelf: 'flex-start', borderRadius: 5, paddingHorizontal: 9, paddingVertical: 3, marginTop: 7 },
tagText: { fontSize: 11, fontWeight: '600' },
infoZeile: {
tagText: { fontFamily: schriftart.serif, fontSize: 11, fontWeight: '600' },
infoZeile: { ...kartenSchatten,
flexDirection: 'row',
alignItems: 'center',
gap: abstand.s,
backgroundColor: farben.flaeche,
borderRadius: 12,
borderRadius: ecke,
padding: abstand.m,
minHeight: TOUCH_TARGET,
},
schutz: {
schutz: { ...kartenSchatten,
flexDirection: 'row',
gap: abstand.s,
alignItems: 'flex-start',
backgroundColor: farben.flaeche,
borderRadius: 12,
borderRadius: ecke,
padding: abstand.m,
marginTop: abstand.l,
},
schutzText: { flex: 1, fontSize: 12.5, lineHeight: 19, color: farben.textSekundaer },
schutzText: { fontFamily: schriftart.serif, flex: 1, fontSize: 12.5, lineHeight: 19, color: farben.textSekundaer },
leerBox: { alignItems: 'center', gap: abstand.s, marginTop: abstand.xl, marginBottom: abstand.l },
leerTitel: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
leerText: { fontSize: schrift.basis, color: farben.textSekundaer, textAlign: 'center', lineHeight: 26 },
leerTitel: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text },
leerText: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.textSekundaer, textAlign: 'center', lineHeight: 26 },
});
+8 -8
View File
@@ -4,7 +4,7 @@
import React, { useState } from 'react';
import { FlatList, StyleSheet, Text, TextInput, View } from 'react-native';
import { GLOSSAR } from '../data/glossar';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart, ecke, kartenSchatten } from '../theme';
export function GlossarScreen() {
const [suche, setSuche] = useState('');
@@ -44,30 +44,30 @@ export function GlossarScreen() {
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: farben.hintergrund },
suche: {
suche: { fontFamily: schriftart.serif,
margin: abstand.m,
borderWidth: 1,
borderColor: farben.rand,
borderRadius: 12,
borderRadius: ecke,
padding: abstand.s,
fontSize: schrift.basis,
color: farben.text,
minHeight: 48,
},
karte: {
karte: { ...kartenSchatten,
backgroundColor: farben.flaeche,
borderRadius: 14,
borderRadius: ecke,
padding: abstand.m,
marginBottom: abstand.s,
},
begriff: { fontSize: schrift.gross, fontWeight: '700', color: farben.primaer },
erklaerung: {
begriff: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.primaer },
erklaerung: { fontFamily: schriftart.serif,
fontSize: schrift.basis,
color: farben.text,
lineHeight: 27,
marginTop: abstand.xs,
},
leer: {
leer: { fontFamily: schriftart.serif,
textAlign: 'center',
fontSize: schrift.basis,
color: farben.textSekundaer,
+5 -5
View File
@@ -22,7 +22,7 @@ import {
import { planeErinnerungen } from '../services/erinnerungen';
import { useAppStore, neueId } from '../store/useAppStore';
import { GrossButton } from '../components/GrossButton';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart } from '../theme';
type Props = NativeStackScreenProps<RootStackParamList, 'Scan'>;
@@ -204,7 +204,7 @@ const styles = StyleSheet.create({
gap: abstand.m,
justifyContent: 'center',
},
anleitung: {
anleitung: { fontFamily: schriftart.serif,
fontSize: schrift.basis,
color: farben.textSekundaer,
textAlign: 'center',
@@ -219,14 +219,14 @@ const styles = StyleSheet.create({
padding: abstand.xl,
gap: abstand.m,
},
kontingent: {
kontingent: { fontFamily: schriftart.serif,
fontSize: schrift.klein,
color: farben.textSekundaer,
textAlign: 'center',
marginTop: abstand.m,
},
ladeText: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
ladeHinweis: {
ladeText: { fontFamily: schriftart.serif, fontSize: schrift.gross, fontWeight: '700', color: farben.text },
ladeHinweis: { fontFamily: schriftart.serif,
fontSize: schrift.basis,
color: farben.textSekundaer,
textAlign: 'center',
+9 -9
View File
@@ -20,7 +20,7 @@ import { holeAusUebergabe, loescheUebergabe, oeffneUebergabe, sendeAnUebergabe }
import { briefZuWebPaket, leseUebergabeLink, webPaketZuBrief, WebPaket } from '../utils/uebergabeFormat';
import { GrossButton } from '../components/GrossButton';
import { Ikone } from '../components/Ikone';
import { farben, schrift, abstand } from '../theme';
import { farben, schrift, abstand, schriftart, ecke, kartenSchatten } from '../theme';
type Props = NativeStackScreenProps<RootStackParamList, 'Verbinden'>;
@@ -173,19 +173,19 @@ export function VerbindenScreen({ navigation, route }: Props) {
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: farben.hintergrund },
titel: { fontSize: schrift.titel, fontWeight: '700', color: farben.text, marginBottom: abstand.s },
text: { fontSize: schrift.basis, color: farben.text, lineHeight: 26 },
klein: { fontSize: schrift.klein, color: farben.textSekundaer, marginBottom: abstand.s },
titel: { fontFamily: schriftart.serif, fontSize: schrift.titel, fontWeight: '700', color: farben.text, marginBottom: abstand.s },
text: { fontFamily: schriftart.serif, fontSize: schrift.basis, color: farben.text, lineHeight: 26 },
klein: { fontFamily: schriftart.serif, fontSize: schrift.klein, color: farben.textSekundaer, marginBottom: abstand.s },
kameraRahmen: {
marginTop: abstand.m, height: 300, borderRadius: 16, overflow: 'hidden',
marginTop: abstand.m, height: 300, borderRadius: ecke, overflow: 'hidden',
backgroundColor: farben.flaecheSekundaer,
},
kameraLeer: { flex: 1, justifyContent: 'center', padding: abstand.m, gap: abstand.m },
meldung: { marginTop: abstand.m, gap: abstand.m, padding: abstand.m, borderRadius: 16, backgroundColor: farben.flaeche },
meldung: { ...kartenSchatten, marginTop: abstand.m, gap: abstand.m, padding: abstand.m, borderRadius: ecke, backgroundColor: farben.flaeche },
schutzZeile: { flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: abstand.m },
schutzText: { fontSize: 13, color: farben.textTertiaer },
feld: {
backgroundColor: farben.flaeche, borderRadius: 12, padding: 14, fontSize: schrift.klein,
schutzText: { fontFamily: schriftart.serif, fontSize: 13, color: farben.textTertiaer },
feld: { fontFamily: schriftart.serif,
backgroundColor: farben.flaeche, borderRadius: ecke, padding: 14, fontSize: schrift.klein,
color: farben.text, borderWidth: 1, borderColor: farben.rand, marginBottom: abstand.s,
},
});
+65 -34
View File
@@ -1,38 +1,51 @@
/**
* Design-System: ruhig, vertrauenswürdig, barrierefrei.
* Design-System — wie die Webseite behoerdenklar.de: ein Amtsbrief, der
* endlich verständlich ist. Papier, Tinte, Amtsblau, gelber Textmarker,
* Georgia-Serifenschrift, Etiketten in Schreibmaschinenschrift.
*
* - Große Schrift (Basis 18) für die Zielgruppe
* - Hoher Kontrast (dunkles Blau auf Weiß, WCAG AA)
* - Hoher Kontrast (Tinte auf Papier, WCAG AA)
* - Große Touch-Targets (min. 56px Höhe)
*
* Die Werte entsprechen den CSS-Variablen der Webseite (--papier, --tinte,
* --amtsblau, --marker, --stempelrot …). Ändert sich dort etwas, hier mitziehen.
*/
import { Platform, TextStyle, ViewStyle } from 'react-native';
export const farben = {
// Warme, ruhige Palette nach dem Claude-Design-Entwurf ("Postklar/Classical")
// und passend zur Website: Papier-Beige, Gold-Akzent, dunkle Tinte.
// Gut lesbar (hoher Kontrast bei Fließtext), nur die Schrift bleibt
// die klare System-Schrift statt der dünnen Serifenschrift.
hintergrund: '#F1EFE9', // warmes Off-White (Grund)
flaeche: '#FBFAF6', // warme, fast weiße Karten
flaecheSekundaer: '#EAE7E0', // gedämpfte Fläche (graue Taste)
primaer: '#B68235', // Gold-Akzent (Icons, aktive Tabs, Rahmen, Punkte)
primaerFuellung: '#7D5411', // dunkles Bronze — Hintergrund gefüllter Buttons (weiße Schrift lesbar)
primaerText: '#FBF7EF', // warmes Weiß (Text auf gefüllten Buttons)
text: '#201F1D', // dunkle Tinte
textSekundaer: '#6B675E', // warmes Grau (wie --grau der Website)
textTertiaer: '#A8A29A', // helles warmes Grau (Chevrons)
rand: '#E2DED6', // warme Trennlinie
// Dezente Gold-Hervorhebung (z. B. aktive Filter-Chips)
hervorhebung: '#F7EFE0',
hervorhebungRand: '#E9D8BC',
hintergrund: '#FAF7F0', // Papier (--papier)
flaeche: '#FFFFFF', // weißes Formularblatt (Karten, Felder)
flaecheSekundaer: '#F1ECDF', // dunkleres Papier (--papier-dunkel)
primaer: '#1D4E89', // Amtsblau (Icons, aktive Tabs, Links)
primaerFuellung: '#1D4E89', // Amtsblau — gefüllte Knöpfe
primaerText: '#FFFFFF', // Text auf gefüllten Knöpfen
text: '#21211D', // Tinte
textSekundaer: '#6B675E', // Grau (--grau)
textTertiaer: '#9A958A', // helles Grau (Platzhalter, Chevrons)
rand: 'rgba(33,33,29,0.22)', // dünne Linie wie auf der Webseite
tinte: '#21211D', // harte Rahmen und Schatten
marker: '#FFE66B', // gelber Textmarker (Signatur der Marke)
stempelrot: '#B3372B',
// Hervorhebung = Textmarker (z. B. aktive Filter)
hervorhebung: '#FFE66B',
hervorhebungRand: '#21211D',
// Dringlichkeits-Ampel in den warmen Design-Tönen — als Text auf hellem
// Tint gut lesbar (WCAG): Backstein-Rot, Bronze-Gold, Salbei-Grün.
ampelRot: '#A23B2B',
ampelGelb: '#8A5E14',
ampelGruen: '#3F5A4C',
ampelRotHintergrund: '#F5E9E5',
ampelGelbHintergrund: '#F5EEDF',
ampelGruenHintergrund: '#E8EFEA',
// Dringlichkeits-Ampel wie auf der Webseite (--rot, --gelb, --gruen),
// Gelb etwas dunkler, damit es als Text auf hellem Grund WCAG AA erreicht.
ampelRot: '#B3372B',
ampelGelb: '#8A5A00',
ampelGruen: '#2E7D4F',
ampelRotHintergrund: '#FDF1EF',
ampelGelbHintergrund: '#FFF6D6',
ampelGruenHintergrund: '#E7F4EA',
fehler: '#A23B2B',
fehler: '#B3372B',
};
/** Schriften der Webseite: Georgia (Text) und Courier (Etiketten). */
export const schriftart = {
serif: Platform.select({ ios: 'Georgia', android: 'serif', default: 'Georgia' }) as string,
mono: Platform.select({ ios: 'Courier New', android: 'monospace', default: 'Courier New' }) as string,
};
export const schrift = {
@@ -54,9 +67,27 @@ export const abstand = {
/** Mindesthöhe für alle interaktiven Elemente (Barrierefreiheit). */
export const TOUCH_TARGET = 56;
/**
* Bewusst leer: Nach Apple-Vorbild heben sich weiße Karten allein durch
* den grauen System-Hintergrund ab — ohne Schatten und Rahmen.
* (Als No-Op behalten, damit bestehende Spreads harmlos bleiben.)
*/
export const kartenSchatten = {} as const;
/** Papier hat (fast) keine runden Ecken. */
export const ecke = 2;
/** Karte wie `.brief-karte` der Webseite: weißes Blatt, feine Linie, flacher Schatten. */
export const kartenSchatten: ViewStyle = {
borderWidth: 1,
borderColor: 'rgba(33,33,29,0.28)',
boxShadow: '4px 5px 0px rgba(33,33,29,0.08)',
};
/** Formular wie `.antrag`: Tinten-Rahmen mit hartem Versatz-Schatten. */
export const formularRahmen: ViewStyle = {
borderWidth: 2,
borderColor: farben.tinte,
boxShadow: '6px 7px 0px rgba(33,33,29,0.9)',
};
/** Etikett wie `h2`/`.karten-label` der Webseite: Schreibmaschine, Versalien. */
export const etikett: TextStyle = {
fontFamily: schriftart.mono,
fontSize: 13,
letterSpacing: 1.4,
textTransform: 'uppercase',
};