generated from knso/webklar-preview-template
Apple-natives Design: SF Symbols statt Emojis, iOS-System-Optik
- Neue Ikone-Komponente (expo-symbols): echte SF Symbols auf iOS, Material Symbols auf Android — alle Emojis in Buttons/Kacheln/ Checkboxen/Ampel/Chevrons ersetzt - Theme: iOS-Systemgrau (#F2F2F7) statt Papier-Ton, weiße Karten ohne Rahmen und Schatten (wie Einstellungen/Health), "graue Taste" für Sekundär-Buttons statt Umrandung - Kernaussage-Karte: dezenter Marken-Tint statt Textmarker-Gelb - Frist/Termin-Zeilen ohne Emoji-Präfixe - Layout unverändert (Kacheln, Ampel-Streifen, großer Titel) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
3
App.tsx
3
App.tsx
@@ -49,8 +49,7 @@ export default function App() {
|
|||||||
options={{
|
options={{
|
||||||
title: 'BehördenKlar',
|
title: 'BehördenKlar',
|
||||||
headerLargeTitle: true,
|
headerLargeTitle: true,
|
||||||
headerLargeTitleStyle: { color: farben.primaer, fontWeight: '800' },
|
headerLargeTitleStyle: { color: farben.primaer },
|
||||||
headerLargeStyle: { backgroundColor: farben.hintergrund },
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen name="Consent" component={ConsentScreen} options={{ title: 'Datenschutz' }} />
|
<Stack.Screen name="Consent" component={ConsentScreen} options={{ title: 'Datenschutz' }} />
|
||||||
|
|||||||
51
package-lock.json
generated
51
package-lock.json
generated
@@ -28,6 +28,7 @@
|
|||||||
"expo-speech": "~57.0.0",
|
"expo-speech": "~57.0.0",
|
||||||
"expo-splash-screen": "~57.0.2",
|
"expo-splash-screen": "~57.0.2",
|
||||||
"expo-status-bar": "~57.0.0",
|
"expo-status-bar": "~57.0.0",
|
||||||
|
"expo-symbols": "~57.0.1",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-native": "0.86.0",
|
"react-native": "0.86.0",
|
||||||
"react-native-safe-area-context": "~5.7.0",
|
"react-native-safe-area-context": "~5.7.0",
|
||||||
@@ -1275,6 +1276,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@expo-google-fonts/material-symbols": {
|
||||||
|
"version": "0.4.41",
|
||||||
|
"resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.41.tgz",
|
||||||
|
"integrity": "sha512-BKNSJ6oFGWi+aXu6PF+1EdI43J8Ag+Sm7Ste1SXYmC2eYOJasyjXHkKjgkIieBB1eDZQJSPWuPv421Vg6N5tCQ==",
|
||||||
|
"license": "MIT AND Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@expo/code-signing-certificates": {
|
"node_modules/@expo/code-signing-certificates": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz",
|
||||||
@@ -4254,6 +4261,20 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-font": {
|
||||||
|
"version": "57.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-57.0.1.tgz",
|
||||||
|
"integrity": "sha512-QyS9L1Kh9sKJg4gfU6rdbpxpmH+DyzBX8z6jVvXMUDoqLr1GqmkO/Wu379KCXjL///kWbhpNlbi7AgBuj4VdIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fontfaceobserver": "^2.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-image-loader": {
|
"node_modules/expo-image-loader": {
|
||||||
"version": "57.0.0",
|
"version": "57.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-57.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-57.0.0.tgz",
|
||||||
@@ -4450,6 +4471,22 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-symbols": {
|
||||||
|
"version": "57.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-57.0.1.tgz",
|
||||||
|
"integrity": "sha512-8Zf+a83OywV0vf1NUtSKpNqKcULmO0GTI+zfFnGYl7SLDH9FjL5RcEZoy6CHvCgq2KDrQF21pl3r7Tb4ItPscw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@expo-google-fonts/material-symbols": "^0.4.1",
|
||||||
|
"sf-symbols-typescript": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"expo-font": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo/node_modules/@expo/cli": {
|
"node_modules/expo/node_modules/@expo/cli": {
|
||||||
"version": "57.0.6",
|
"version": "57.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-57.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-57.0.6.tgz",
|
||||||
@@ -4761,20 +4798,6 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo/node_modules/expo-font": {
|
|
||||||
"version": "57.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-57.0.0.tgz",
|
|
||||||
"integrity": "sha512-zF+J7WrNFjqyAADwdvDgkFEoIQv9DqcjJ57HVstNEH7/7Tx1ThPEhKraodEQOwSjMYWirP+4BYsVbdb+/Zr4QQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fontfaceobserver": "^2.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"expo": "*",
|
|
||||||
"react": "*",
|
|
||||||
"react-native": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/expo/node_modules/expo-keep-awake": {
|
"node_modules/expo/node_modules/expo-keep-awake": {
|
||||||
"version": "57.0.0",
|
"version": "57.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-57.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-57.0.0.tgz",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"expo-speech": "~57.0.0",
|
"expo-speech": "~57.0.0",
|
||||||
"expo-splash-screen": "~57.0.2",
|
"expo-splash-screen": "~57.0.2",
|
||||||
"expo-status-bar": "~57.0.0",
|
"expo-status-bar": "~57.0.0",
|
||||||
|
"expo-symbols": "~57.0.1",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-native": "0.86.0",
|
"react-native": "0.86.0",
|
||||||
"react-native-safe-area-context": "~5.7.0",
|
"react-native-safe-area-context": "~5.7.0",
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
/**
|
/**
|
||||||
* Dringlichkeits-Ampel als gut sichtbares Banner.
|
* Dringlichkeits-Ampel als gut sichtbares Banner.
|
||||||
* Farbe + Symbol + Text — nicht nur Farbe (Barrierefreiheit: Farbenblindheit).
|
* Farbe + Icon + Text — nicht nur Farbe (Barrierefreiheit: Farbenblindheit).
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import { AmpelStatus } from '../utils/ampel';
|
import { AmpelStatus } from '../utils/ampel';
|
||||||
import { abstand, schrift } from '../theme';
|
import { abstand, schrift } from '../theme';
|
||||||
|
import { Ikone, IkonenName } from './Ikone';
|
||||||
|
|
||||||
const SYMBOL: Record<string, string> = { rot: '⚠️', gelb: '⏰', gruen: 'ℹ️' };
|
const IKONE: Record<string, IkonenName> = { rot: 'warnung', gelb: 'wecker', gruen: 'info' };
|
||||||
|
|
||||||
export function Ampel({ status }: { status: AmpelStatus }) {
|
export function Ampel({ status }: { status: AmpelStatus }) {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.banner, { backgroundColor: status.hintergrund, borderColor: status.farbe }]}
|
style={[styles.banner, { backgroundColor: status.hintergrund, borderLeftColor: status.farbe }]}
|
||||||
accessibilityRole="alert"
|
accessibilityRole="alert"
|
||||||
accessibilityLabel={`Dringlichkeit: ${status.text}`}
|
accessibilityLabel={`Dringlichkeit: ${status.text}`}
|
||||||
>
|
>
|
||||||
<Text style={styles.symbol}>{SYMBOL[status.stufe]}</Text>
|
<Ikone name={IKONE[status.stufe]} groesse={26} farbe={status.farbe} />
|
||||||
<Text style={[styles.text, { color: status.farbe }]}>{status.text}</Text>
|
<Text style={[styles.text, { color: status.farbe }]}>{status.text}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -26,12 +27,10 @@ const styles = StyleSheet.create({
|
|||||||
banner: {
|
banner: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderWidth: 1,
|
|
||||||
borderLeftWidth: 6,
|
borderLeftWidth: 6,
|
||||||
borderRadius: 14,
|
borderRadius: 12,
|
||||||
padding: abstand.m,
|
padding: abstand.m,
|
||||||
gap: abstand.s,
|
gap: abstand.s,
|
||||||
},
|
},
|
||||||
symbol: { fontSize: 28 },
|
|
||||||
text: { flex: 1, fontSize: schrift.gross, fontWeight: '700' },
|
text: { flex: 1, fontSize: schrift.gross, fontWeight: '700' },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
* Großer, barrierefreier Button: min. 56px hoch, große Schrift,
|
* Großer, barrierefreier Button nach iOS-Vorbild:
|
||||||
* hoher Kontrast. Varianten: primär (gefüllt) und sekundär (Umriss).
|
* - primär: gefüllt mit der Marken-Tintfarbe (wie "prominente" iOS-Buttons)
|
||||||
|
* - sekundär: hellgraue Fläche mit Tint-Text (wie Apples "graue Taste" —
|
||||||
|
* bewusst keine Umrandung, das wirkt nativer)
|
||||||
|
* - gefahr: rot gefüllt
|
||||||
|
* Min. 56px hoch, große Schrift, System-Icons statt Emojis.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, Pressable, StyleSheet, Text } from 'react-native';
|
import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
|
||||||
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
|
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
|
||||||
|
import { Ikone, IkonenName } from './Ikone';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
titel: string;
|
titel: string;
|
||||||
@@ -12,7 +17,7 @@ interface Props {
|
|||||||
variante?: 'primaer' | 'sekundaer' | 'gefahr';
|
variante?: 'primaer' | 'sekundaer' | 'gefahr';
|
||||||
deaktiviert?: boolean;
|
deaktiviert?: boolean;
|
||||||
laedt?: boolean;
|
laedt?: boolean;
|
||||||
symbol?: string;
|
ikone?: IkonenName;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GrossButton({
|
export function GrossButton({
|
||||||
@@ -21,10 +26,11 @@ export function GrossButton({
|
|||||||
variante = 'primaer',
|
variante = 'primaer',
|
||||||
deaktiviert = false,
|
deaktiviert = false,
|
||||||
laedt = false,
|
laedt = false,
|
||||||
symbol,
|
ikone,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const istPrimaer = variante === 'primaer';
|
const istPrimaer = variante === 'primaer';
|
||||||
const istGefahr = variante === 'gefahr';
|
const istGefahr = variante === 'gefahr';
|
||||||
|
const inhaltsFarbe = istPrimaer || istGefahr ? farben.primaerText : farben.primaer;
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
@@ -41,18 +47,12 @@ export function GrossButton({
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{laedt ? (
|
{laedt ? (
|
||||||
<ActivityIndicator color={istPrimaer || istGefahr ? farben.primaerText : farben.primaer} />
|
<ActivityIndicator color={inhaltsFarbe} />
|
||||||
) : (
|
) : (
|
||||||
<Text
|
<View style={styles.zeile}>
|
||||||
style={[
|
{ikone ? <Ikone name={ikone} farbe={inhaltsFarbe} /> : null}
|
||||||
styles.text,
|
<Text style={[styles.text, { color: inhaltsFarbe }]}>{titel}</Text>
|
||||||
(istPrimaer || istGefahr) && { color: farben.primaerText },
|
</View>
|
||||||
variante === 'sekundaer' && { color: farben.primaer },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{symbol ? `${symbol} ` : ''}
|
|
||||||
{titel}
|
|
||||||
</Text>
|
|
||||||
)}
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
);
|
);
|
||||||
@@ -61,24 +61,17 @@ export function GrossButton({
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
minHeight: TOUCH_TARGET,
|
minHeight: TOUCH_TARGET,
|
||||||
borderRadius: 16,
|
borderRadius: 14,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingHorizontal: abstand.l,
|
paddingHorizontal: abstand.l,
|
||||||
paddingVertical: abstand.s,
|
paddingVertical: abstand.s,
|
||||||
},
|
},
|
||||||
primaer: {
|
zeile: { flexDirection: 'row', alignItems: 'center', gap: abstand.xs + 2 },
|
||||||
backgroundColor: farben.primaer,
|
primaer: { backgroundColor: farben.primaer },
|
||||||
// Tiefe fürs wichtigste Element auf dem Bildschirm
|
sekundaer: { backgroundColor: farben.flaecheSekundaer },
|
||||||
shadowColor: farben.primaer,
|
|
||||||
shadowOffset: { width: 0, height: 4 },
|
|
||||||
shadowOpacity: 0.28,
|
|
||||||
shadowRadius: 6,
|
|
||||||
elevation: 4,
|
|
||||||
},
|
|
||||||
sekundaer: { backgroundColor: farben.flaeche, borderWidth: 2, borderColor: farben.primaer },
|
|
||||||
gefahr: { backgroundColor: farben.fehler },
|
gefahr: { backgroundColor: farben.fehler },
|
||||||
deaktiviert: { opacity: 0.5 },
|
deaktiviert: { opacity: 0.4 },
|
||||||
gedrueckt: { opacity: 0.8, transform: [{ translateY: 1 }] },
|
gedrueckt: { opacity: 0.65 },
|
||||||
text: { fontSize: schrift.gross, fontWeight: '700', textAlign: 'center' },
|
text: { fontSize: schrift.gross, fontWeight: '600', textAlign: 'center' },
|
||||||
});
|
});
|
||||||
|
|||||||
72
src/components/Ikone.tsx
Normal file
72
src/components/Ikone.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Zentrale Icon-Komponente — echte System-Icons statt Emojis:
|
||||||
|
* SF Symbols auf iOS (Apple-Look), Material Symbols auf Android.
|
||||||
|
* Alle in der App verwendeten Icons sind hier benannt, damit die
|
||||||
|
* Plattform-Zuordnung an einer Stelle liegt.
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { SymbolView } from 'expo-symbols';
|
||||||
|
|
||||||
|
export type IkonenName =
|
||||||
|
| 'kamera'
|
||||||
|
| 'galerie'
|
||||||
|
| 'dokument'
|
||||||
|
| 'buch'
|
||||||
|
| 'zahnrad'
|
||||||
|
| 'brief'
|
||||||
|
| 'pfeilRechts'
|
||||||
|
| 'vorlesen'
|
||||||
|
| 'stopp'
|
||||||
|
| 'kalender'
|
||||||
|
| 'stift'
|
||||||
|
| 'teilen'
|
||||||
|
| 'muell'
|
||||||
|
| 'globus'
|
||||||
|
| 'checkAn'
|
||||||
|
| 'checkAus'
|
||||||
|
| 'warnung'
|
||||||
|
| 'wecker'
|
||||||
|
| 'info'
|
||||||
|
| 'funken';
|
||||||
|
|
||||||
|
// `as const` hält die Namen als Literal-Typen — so prüft TypeScript gegen
|
||||||
|
// die offizielle SF-Symbol-/Material-Namensliste von expo-symbols
|
||||||
|
const NAMEN = {
|
||||||
|
kamera: { ios: 'camera.fill', android: 'photo_camera' },
|
||||||
|
galerie: { ios: 'photo.on.rectangle', android: 'image' },
|
||||||
|
dokument: { ios: 'doc.fill', android: 'description' },
|
||||||
|
buch: { ios: 'book.fill', android: 'menu_book' },
|
||||||
|
zahnrad: { ios: 'gearshape.fill', android: 'settings' },
|
||||||
|
brief: { ios: 'envelope.open.fill', android: 'mail' },
|
||||||
|
pfeilRechts: { ios: 'chevron.right', android: 'chevron_right' },
|
||||||
|
vorlesen: { ios: 'speaker.wave.2.fill', android: 'volume_up' },
|
||||||
|
stopp: { ios: 'stop.fill', android: 'stop' },
|
||||||
|
kalender: { ios: 'calendar.badge.plus', android: 'event' },
|
||||||
|
stift: { ios: 'square.and.pencil', android: 'edit' },
|
||||||
|
teilen: { ios: 'square.and.arrow.up', android: 'share' },
|
||||||
|
muell: { ios: 'trash.fill', android: 'delete' },
|
||||||
|
globus: { ios: 'globe', android: 'language' },
|
||||||
|
checkAn: { ios: 'checkmark.square.fill', android: 'check_box' },
|
||||||
|
checkAus: { ios: 'square', android: 'check_box_outline_blank' },
|
||||||
|
warnung: { ios: 'exclamationmark.triangle.fill', android: 'warning' },
|
||||||
|
wecker: { ios: 'alarm.fill', android: 'alarm' },
|
||||||
|
info: { ios: 'info.circle.fill', android: 'info' },
|
||||||
|
funken: { ios: 'wand.and.stars', android: 'auto_awesome' },
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: IkonenName;
|
||||||
|
groesse?: number;
|
||||||
|
farbe: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Ikone({ name, groesse = 22, farbe }: Props) {
|
||||||
|
const eintrag = NAMEN[name];
|
||||||
|
return (
|
||||||
|
<SymbolView
|
||||||
|
name={{ ios: eintrag.ios, android: eintrag.android }}
|
||||||
|
size={groesse}
|
||||||
|
tintColor={farbe}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -23,7 +23,8 @@ import { formatiereDatum } from '../services/erinnerungen';
|
|||||||
import { berechneAmpel } from '../utils/ampel';
|
import { berechneAmpel } from '../utils/ampel';
|
||||||
import { Ampel } from '../components/Ampel';
|
import { Ampel } from '../components/Ampel';
|
||||||
import { GrossButton } from '../components/GrossButton';
|
import { GrossButton } from '../components/GrossButton';
|
||||||
import { farben, schrift, abstand, kartenSchatten, TOUCH_TARGET } from '../theme';
|
import { Ikone } from '../components/Ikone';
|
||||||
|
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<RootStackParamList, 'Analyse'>;
|
type Props = NativeStackScreenProps<RootStackParamList, 'Analyse'>;
|
||||||
|
|
||||||
@@ -133,9 +134,13 @@ export function AnalyseScreen({ navigation, route }: Props) {
|
|||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={`Sprache ändern. Aktuell: ${sprache.name}`}
|
accessibilityLabel={`Sprache ändern. Aktuell: ${sprache.name}`}
|
||||||
>
|
>
|
||||||
<Text style={styles.sprachButtonText}>
|
<View style={styles.sprachZeileInhalt}>
|
||||||
🌐 {sprache.eigenname} {uebersetzt ? ' (übersetzt…)' : ''}
|
<Ikone name="globus" groesse={20} farbe={farben.primaer} />
|
||||||
</Text>
|
<Text style={styles.sprachButtonText}>
|
||||||
|
{sprache.eigenname} {uebersetzt ? ' (übersetzt…)' : ''}
|
||||||
|
</Text>
|
||||||
|
<Ikone name="pfeilRechts" groesse={16} farbe={farben.textTertiaer} />
|
||||||
|
</View>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
{/* Kernaussage: Was will das Amt von mir? */}
|
{/* Kernaussage: Was will das Amt von mir? */}
|
||||||
@@ -145,7 +150,7 @@ export function AnalyseScreen({ navigation, route }: Props) {
|
|||||||
<View style={{ marginTop: abstand.s }}>
|
<View style={{ marginTop: abstand.s }}>
|
||||||
<GrossButton
|
<GrossButton
|
||||||
titel={liest ? 'Vorlesen stoppen' : 'Vorlesen'}
|
titel={liest ? 'Vorlesen stoppen' : 'Vorlesen'}
|
||||||
symbol={liest ? '⏹️' : '🔊'}
|
ikone={liest ? 'stopp' : 'vorlesen'}
|
||||||
variante="sekundaer"
|
variante="sekundaer"
|
||||||
onPress={vorlesen}
|
onPress={vorlesen}
|
||||||
/>
|
/>
|
||||||
@@ -162,7 +167,7 @@ export function AnalyseScreen({ navigation, route }: Props) {
|
|||||||
>
|
>
|
||||||
{analyse.frist && (
|
{analyse.frist && (
|
||||||
<Text style={styles.fristText}>
|
<Text style={styles.fristText}>
|
||||||
📅 Frist: <Text style={styles.fett}>{formatiereDatum(analyse.frist.datum)}</Text>
|
Frist: <Text style={styles.fett}>{formatiereDatum(analyse.frist.datum)}</Text>
|
||||||
{'\n'}
|
{'\n'}
|
||||||
{analyse.frist.aktion}
|
{analyse.frist.aktion}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -170,13 +175,13 @@ export function AnalyseScreen({ navigation, route }: Props) {
|
|||||||
{analyse.termin && (
|
{analyse.termin && (
|
||||||
<>
|
<>
|
||||||
<Text style={styles.fristText}>
|
<Text style={styles.fristText}>
|
||||||
🕐 Termin: <Text style={styles.fett}>{formatiereDatum(analyse.termin.datum)}</Text>
|
Termin: <Text style={styles.fett}>{formatiereDatum(analyse.termin.datum)}</Text>
|
||||||
{analyse.termin.uhrzeit ? ` um ${analyse.termin.uhrzeit} Uhr` : ''}
|
{analyse.termin.uhrzeit ? ` um ${analyse.termin.uhrzeit} Uhr` : ''}
|
||||||
{analyse.termin.ort ? `\n📍 ${analyse.termin.ort}` : ''}
|
{analyse.termin.ort ? `\nOrt: ${analyse.termin.ort}` : ''}
|
||||||
</Text>
|
</Text>
|
||||||
<GrossButton
|
<GrossButton
|
||||||
titel="Termin zum Kalender hinzufügen"
|
titel="Termin zum Kalender hinzufügen"
|
||||||
symbol="🗓️"
|
ikone="kalender"
|
||||||
variante="sekundaer"
|
variante="sekundaer"
|
||||||
onPress={kalenderExport}
|
onPress={kalenderExport}
|
||||||
/>
|
/>
|
||||||
@@ -203,7 +208,11 @@ export function AnalyseScreen({ navigation, route }: Props) {
|
|||||||
accessibilityRole="checkbox"
|
accessibilityRole="checkbox"
|
||||||
accessibilityState={{ checked: !!erledigt[i] }}
|
accessibilityState={{ checked: !!erledigt[i] }}
|
||||||
>
|
>
|
||||||
<Text style={styles.checkBox}>{erledigt[i] ? '☑️' : '⬜'}</Text>
|
<Ikone
|
||||||
|
name={erledigt[i] ? 'checkAn' : 'checkAus'}
|
||||||
|
groesse={26}
|
||||||
|
farbe={erledigt[i] ? farben.ampelGruen : farben.textTertiaer}
|
||||||
|
/>
|
||||||
<Text style={[styles.text, { flex: 1 }, erledigt[i] && styles.durchgestrichen]}>
|
<Text style={[styles.text, { flex: 1 }, erledigt[i] && styles.durchgestrichen]}>
|
||||||
{punkt}
|
{punkt}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -229,13 +238,13 @@ export function AnalyseScreen({ navigation, route }: Props) {
|
|||||||
{analyse.antwort_noetig && (
|
{analyse.antwort_noetig && (
|
||||||
<GrossButton
|
<GrossButton
|
||||||
titel="Antwort erstellen"
|
titel="Antwort erstellen"
|
||||||
symbol="✍️"
|
ikone="stift"
|
||||||
onPress={() => navigation.navigate('Antwort', { briefId: brief.id })}
|
onPress={() => navigation.navigate('Antwort', { briefId: brief.id })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View style={{ height: abstand.m }} />
|
<View style={{ height: abstand.m }} />
|
||||||
<GrossButton titel="Brief löschen" symbol="🗑️" variante="gefahr" onPress={loeschen} />
|
<GrossButton titel="Brief löschen" ikone="muell" variante="gefahr" onPress={loeschen} />
|
||||||
<View style={{ height: abstand.xl }} />
|
<View style={{ height: abstand.xl }} />
|
||||||
|
|
||||||
{/* Sprach-Auswahl-Dialog */}
|
{/* Sprach-Auswahl-Dialog */}
|
||||||
@@ -275,23 +284,19 @@ const styles = StyleSheet.create({
|
|||||||
zentriert: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
zentriert: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
karte: {
|
karte: {
|
||||||
backgroundColor: farben.flaeche,
|
backgroundColor: farben.flaeche,
|
||||||
borderRadius: 16,
|
borderRadius: 12,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: farben.rand,
|
|
||||||
padding: abstand.m,
|
padding: abstand.m,
|
||||||
marginTop: abstand.m,
|
marginTop: abstand.m,
|
||||||
gap: abstand.s,
|
gap: abstand.s,
|
||||||
...kartenSchatten,
|
|
||||||
},
|
},
|
||||||
// DIE Antwort der App — als "Textmarker"-Karte hervorgehoben (Markenzeichen)
|
// DIE Antwort der App — dezent im Marken-Tint hervorgehoben
|
||||||
karteWichtig: {
|
karteWichtig: {
|
||||||
backgroundColor: farben.markerHintergrund,
|
backgroundColor: farben.hervorhebung,
|
||||||
borderRadius: 16,
|
borderRadius: 12,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: farben.markerRand,
|
borderColor: farben.hervorhebungRand,
|
||||||
padding: abstand.m,
|
padding: abstand.m,
|
||||||
marginTop: abstand.m,
|
marginTop: abstand.m,
|
||||||
...kartenSchatten,
|
|
||||||
},
|
},
|
||||||
abschnittTitel: {
|
abschnittTitel: {
|
||||||
fontSize: schrift.gross,
|
fontSize: schrift.gross,
|
||||||
@@ -310,22 +315,19 @@ const styles = StyleSheet.create({
|
|||||||
minHeight: 44,
|
minHeight: 44,
|
||||||
paddingVertical: 4,
|
paddingVertical: 4,
|
||||||
},
|
},
|
||||||
checkBox: { fontSize: 24 },
|
|
||||||
durchgestrichen: { textDecorationLine: 'line-through', color: farben.textSekundaer },
|
durchgestrichen: { textDecorationLine: 'line-through', color: farben.textSekundaer },
|
||||||
begriffBlock: { marginBottom: abstand.s },
|
begriffBlock: { marginBottom: abstand.s },
|
||||||
begriff: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
|
begriff: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
|
||||||
sprachButton: {
|
sprachButton: {
|
||||||
marginTop: abstand.m,
|
marginTop: abstand.m,
|
||||||
minHeight: TOUCH_TARGET,
|
minHeight: TOUCH_TARGET,
|
||||||
borderRadius: 16,
|
borderRadius: 12,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: farben.rand,
|
|
||||||
backgroundColor: farben.flaeche,
|
backgroundColor: farben.flaeche,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingHorizontal: abstand.m,
|
paddingHorizontal: abstand.m,
|
||||||
...kartenSchatten,
|
|
||||||
},
|
},
|
||||||
sprachButtonText: { fontSize: schrift.basis, color: farben.text, fontWeight: '600' },
|
sprachZeileInhalt: { flexDirection: 'row', alignItems: 'center', gap: abstand.s },
|
||||||
|
sprachButtonText: { flex: 1, fontSize: schrift.basis, color: farben.text, fontWeight: '600' },
|
||||||
modalHintergrund: {
|
modalHintergrund: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||||
@@ -333,7 +335,7 @@ const styles = StyleSheet.create({
|
|||||||
padding: abstand.l,
|
padding: abstand.l,
|
||||||
},
|
},
|
||||||
modalKarte: {
|
modalKarte: {
|
||||||
backgroundColor: farben.hintergrund,
|
backgroundColor: farben.flaeche,
|
||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
padding: abstand.m,
|
padding: abstand.m,
|
||||||
maxHeight: '75%',
|
maxHeight: '75%',
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export function AntwortScreen({ route }: Props) {
|
|||||||
|
|
||||||
<GrossButton
|
<GrossButton
|
||||||
titel={text ? 'Neuen Entwurf erstellen' : 'Entwurf erstellen'}
|
titel={text ? 'Neuen Entwurf erstellen' : 'Entwurf erstellen'}
|
||||||
symbol="✨"
|
ikone="funken"
|
||||||
onPress={erzeugen}
|
onPress={erzeugen}
|
||||||
deaktiviert={!typ}
|
deaktiviert={!typ}
|
||||||
laedt={laedt}
|
laedt={laedt}
|
||||||
@@ -173,8 +173,8 @@ export function AntwortScreen({ route }: Props) {
|
|||||||
accessibilityLabel="Brieftext"
|
accessibilityLabel="Brieftext"
|
||||||
/>
|
/>
|
||||||
<View style={{ gap: abstand.s }}>
|
<View style={{ gap: abstand.s }}>
|
||||||
<GrossButton titel="Als PDF exportieren" symbol="📄" onPress={alsPdf} />
|
<GrossButton titel="Als PDF exportieren" ikone="dokument" onPress={alsPdf} />
|
||||||
<GrossButton titel="Als Text teilen / kopieren" symbol="📤" variante="sekundaer" onPress={alsText} />
|
<GrossButton titel="Als Text teilen / kopieren" ikone="teilen" variante="sekundaer" onPress={alsText} />
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ export function EinstellungenScreen({ navigation }: Props) {
|
|||||||
<View style={styles.trenner} />
|
<View style={styles.trenner} />
|
||||||
|
|
||||||
<Text style={styles.abschnittTitel}>Daten löschen</Text>
|
<Text style={styles.abschnittTitel}>Daten löschen</Text>
|
||||||
<GrossButton titel="Alle Daten löschen" symbol="🗑️" variante="gefahr" onPress={allesLoeschen} />
|
<GrossButton titel="Alle Daten löschen" ikone="muell" variante="gefahr" onPress={allesLoeschen} />
|
||||||
<View style={{ height: abstand.xl }} />
|
<View style={{ height: abstand.xl }} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,29 +18,30 @@ import { holeConsent } from '../services/storage';
|
|||||||
import { berechneAmpel } from '../utils/ampel';
|
import { berechneAmpel } from '../utils/ampel';
|
||||||
import { formatiereDatum } from '../services/erinnerungen';
|
import { formatiereDatum } from '../services/erinnerungen';
|
||||||
import { GrossButton } from '../components/GrossButton';
|
import { GrossButton } from '../components/GrossButton';
|
||||||
import { farben, schrift, abstand, kartenSchatten, TOUCH_TARGET } from '../theme';
|
import { Ikone, IkonenName } from '../components/Ikone';
|
||||||
|
import { farben, schrift, abstand, TOUCH_TARGET } from '../theme';
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;
|
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;
|
||||||
|
|
||||||
/** Quadratische Navigations-Kachel: Symbol oben, Beschriftung darunter.
|
/** Quadratische Navigations-Kachel: Icon oben, Beschriftung darunter.
|
||||||
* (Löst das Platzproblem nebeneinanderstehender Text-Buttons.) */
|
* (Löst das Platzproblem nebeneinanderstehender Text-Buttons.) */
|
||||||
function Kachel({
|
function Kachel({
|
||||||
symbol,
|
ikone,
|
||||||
titel,
|
titel,
|
||||||
onPress,
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
symbol: string;
|
ikone: IkonenName;
|
||||||
titel: string;
|
titel: string;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={({ pressed }) => [styles.kachel, pressed && { opacity: 0.7 }]}
|
style={({ pressed }) => [styles.kachel, pressed && { opacity: 0.65 }]}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={titel}
|
accessibilityLabel={titel}
|
||||||
>
|
>
|
||||||
<Text style={styles.kachelSymbol}>{symbol}</Text>
|
<Ikone name={ikone} groesse={26} farbe={farben.primaer} />
|
||||||
<Text style={styles.kachelTitel} numberOfLines={1}>
|
<Text style={styles.kachelTitel} numberOfLines={1}>
|
||||||
{titel}
|
{titel}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -105,7 +106,7 @@ export function HomeScreen({ navigation }: Props) {
|
|||||||
{ampel.text}
|
{ampel.text}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.pfeil}>›</Text>
|
<Ikone name="pfeilRechts" groesse={18} farbe={farben.textTertiaer} />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -122,11 +123,11 @@ export function HomeScreen({ navigation }: Props) {
|
|||||||
contentContainerStyle={styles.inhalt}
|
contentContainerStyle={styles.inhalt}
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
<View style={styles.kopf}>
|
<View style={styles.kopf}>
|
||||||
<GrossButton titel="Brief scannen" symbol="📷" onPress={scanStarten} />
|
<GrossButton titel="Brief scannen" ikone="kamera" onPress={scanStarten} />
|
||||||
<View style={styles.reihe}>
|
<View style={styles.reihe}>
|
||||||
<Kachel symbol="📖" titel="Glossar" onPress={() => navigation.navigate('Glossar')} />
|
<Kachel ikone="buch" titel="Glossar" onPress={() => navigation.navigate('Glossar')} />
|
||||||
<Kachel
|
<Kachel
|
||||||
symbol="⚙️"
|
ikone="zahnrad"
|
||||||
titel="Einstellungen"
|
titel="Einstellungen"
|
||||||
onPress={() => navigation.navigate('Einstellungen')}
|
onPress={() => navigation.navigate('Einstellungen')}
|
||||||
/>
|
/>
|
||||||
@@ -149,7 +150,7 @@ export function HomeScreen({ navigation }: Props) {
|
|||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
briefe.length === 0 ? (
|
briefe.length === 0 ? (
|
||||||
<View style={styles.leerBox}>
|
<View style={styles.leerBox}>
|
||||||
<Text style={styles.leerSymbol}>📬</Text>
|
<Ikone name="brief" groesse={48} farbe={farben.textTertiaer} />
|
||||||
<Text style={styles.leerTitel}>Noch keine Briefe</Text>
|
<Text style={styles.leerTitel}>Noch keine Briefe</Text>
|
||||||
<Text style={styles.leer}>
|
<Text style={styles.leer}>
|
||||||
Scannen Sie Ihren ersten Behördenbrief —{'\n'}in einer Minute
|
Scannen Sie Ihren ersten Behördenbrief —{'\n'}in einer Minute
|
||||||
@@ -173,16 +174,12 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
minHeight: TOUCH_TARGET + 22,
|
minHeight: TOUCH_TARGET + 22,
|
||||||
backgroundColor: farben.flaeche,
|
backgroundColor: farben.flaeche,
|
||||||
borderRadius: 16,
|
borderRadius: 12,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: farben.rand,
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingVertical: abstand.s,
|
paddingVertical: abstand.s,
|
||||||
gap: 4,
|
gap: 6,
|
||||||
...kartenSchatten,
|
|
||||||
},
|
},
|
||||||
kachelSymbol: { fontSize: 26 },
|
|
||||||
kachelTitel: { fontSize: schrift.klein + 1, fontWeight: '600', color: farben.primaer },
|
kachelTitel: { fontSize: schrift.klein + 1, fontWeight: '600', color: farben.primaer },
|
||||||
abschnittsLabel: {
|
abschnittsLabel: {
|
||||||
marginTop: abstand.m,
|
marginTop: abstand.m,
|
||||||
@@ -194,8 +191,6 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
suche: {
|
suche: {
|
||||||
backgroundColor: farben.flaeche,
|
backgroundColor: farben.flaeche,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: farben.rand,
|
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: abstand.s,
|
padding: abstand.s,
|
||||||
fontSize: schrift.basis,
|
fontSize: schrift.basis,
|
||||||
@@ -206,21 +201,16 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: farben.flaeche,
|
backgroundColor: farben.flaeche,
|
||||||
borderRadius: 14,
|
borderRadius: 12,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: farben.rand,
|
|
||||||
borderLeftWidth: 6,
|
borderLeftWidth: 6,
|
||||||
padding: abstand.m,
|
padding: abstand.m,
|
||||||
marginBottom: abstand.s,
|
marginBottom: abstand.s,
|
||||||
gap: abstand.s,
|
gap: abstand.s,
|
||||||
...kartenSchatten,
|
|
||||||
},
|
},
|
||||||
kartenTitel: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
|
kartenTitel: { fontSize: schrift.basis, fontWeight: '700', color: farben.text },
|
||||||
kartenUntertitel: { fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
|
kartenUntertitel: { fontSize: schrift.klein, color: farben.textSekundaer, marginTop: 2 },
|
||||||
kartenAmpelText: { fontSize: schrift.klein, fontWeight: '600', marginTop: 2 },
|
kartenAmpelText: { fontSize: schrift.klein, fontWeight: '600', marginTop: 2 },
|
||||||
pfeil: { fontSize: 32, color: farben.textSekundaer },
|
leerBox: { alignItems: 'center', marginTop: abstand.xl, gap: abstand.s },
|
||||||
leerBox: { alignItems: 'center', marginTop: abstand.xl, gap: abstand.xs },
|
|
||||||
leerSymbol: { fontSize: 52 },
|
|
||||||
leerTitel: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
|
leerTitel: { fontSize: schrift.gross, fontWeight: '700', color: farben.text },
|
||||||
leer: {
|
leer: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
|||||||
@@ -182,9 +182,9 @@ export function ScanScreen({ navigation }: Props) {
|
|||||||
Fotografieren Sie den Brief gut beleuchtet und von oben. Der ganze Text
|
Fotografieren Sie den Brief gut beleuchtet und von oben. Der ganze Text
|
||||||
muss auf dem Foto sein.
|
muss auf dem Foto sein.
|
||||||
</Text>
|
</Text>
|
||||||
<GrossButton titel="Foto aufnehmen" symbol="📷" onPress={fotoAufnehmen} />
|
<GrossButton titel="Foto aufnehmen" ikone="kamera" onPress={fotoAufnehmen} />
|
||||||
<GrossButton titel="Aus Galerie wählen" symbol="🖼️" variante="sekundaer" onPress={ausGalerie} />
|
<GrossButton titel="Aus Galerie wählen" ikone="galerie" variante="sekundaer" onPress={ausGalerie} />
|
||||||
<GrossButton titel="PDF hochladen" symbol="📄" variante="sekundaer" onPress={pdfWaehlen} />
|
<GrossButton titel="PDF hochladen" ikone="dokument" variante="sekundaer" onPress={pdfWaehlen} />
|
||||||
{NUTZT_PROXY && (
|
{NUTZT_PROXY && (
|
||||||
<Text style={styles.kontingent}>
|
<Text style={styles.kontingent}>
|
||||||
{kontingentLeer
|
{kontingentLeer
|
||||||
|
|||||||
32
src/theme.ts
32
src/theme.ts
@@ -5,18 +5,20 @@
|
|||||||
* - Große Touch-Targets (min. 56px Höhe)
|
* - Große Touch-Targets (min. 56px Höhe)
|
||||||
*/
|
*/
|
||||||
export const farben = {
|
export const farben = {
|
||||||
// Grundfarben — warmer Papier-Ton als Markenzeichen (wie die Webseite):
|
// Grundfarben — natives iOS-Vorbild: gruppierter System-Hintergrund,
|
||||||
// Inhalte liegen als weiße "Blätter" auf Papier
|
// Inhalte als weiße Karten ohne Rahmen und Schatten (wie Einstellungen/Health)
|
||||||
hintergrund: '#FAF7F0',
|
hintergrund: '#F2F2F7',
|
||||||
flaeche: '#FFFFFF',
|
flaeche: '#FFFFFF',
|
||||||
primaer: '#1A365D', // dunkles, ruhiges Blau
|
flaecheSekundaer: '#E9EDF3', // "graue Taste" nach Apple-Art (statt Umrandung)
|
||||||
|
primaer: '#1A365D', // dunkles, ruhiges Blau (Marken-Tint)
|
||||||
primaerText: '#FFFFFF',
|
primaerText: '#FFFFFF',
|
||||||
text: '#1A202C',
|
text: '#1A202C',
|
||||||
textSekundaer: '#4A5568',
|
textSekundaer: '#4A5568',
|
||||||
rand: '#E4DECF',
|
textTertiaer: '#AEAEB2', // z. B. Chevrons (iOS systemGray2)
|
||||||
// Akzent: Textmarker-Gelb (sparsam einsetzen — nur für DIE Kernaussage)
|
rand: '#E2E2E7',
|
||||||
markerHintergrund: '#FFF6D2',
|
// Dezente Hervorhebung für DIE Kernaussage (leichter Marken-Tint)
|
||||||
markerRand: '#EBDD9A',
|
hervorhebung: '#EDF2F9',
|
||||||
|
hervorhebungRand: '#DCE5F1',
|
||||||
|
|
||||||
// Dringlichkeits-Ampel (kräftig, gut unterscheidbar)
|
// Dringlichkeits-Ampel (kräftig, gut unterscheidbar)
|
||||||
ampelRot: '#C0392B',
|
ampelRot: '#C0392B',
|
||||||
@@ -48,11 +50,9 @@ export const abstand = {
|
|||||||
/** Mindesthöhe für alle interaktiven Elemente (Barrierefreiheit). */
|
/** Mindesthöhe für alle interaktiven Elemente (Barrierefreiheit). */
|
||||||
export const TOUCH_TARGET = 56;
|
export const TOUCH_TARGET = 56;
|
||||||
|
|
||||||
/** Dezenter Schatten, damit weiße Karten sich vom Papier-Grund abheben. */
|
/**
|
||||||
export const kartenSchatten = {
|
* Bewusst leer: Nach Apple-Vorbild heben sich weiße Karten allein durch
|
||||||
shadowColor: '#1A202C',
|
* den grauen System-Hintergrund ab — ohne Schatten und Rahmen.
|
||||||
shadowOffset: { width: 0, height: 2 },
|
* (Als No-Op behalten, damit bestehende Spreads harmlos bleiben.)
|
||||||
shadowOpacity: 0.07,
|
*/
|
||||||
shadowRadius: 6,
|
export const kartenSchatten = {} as const;
|
||||||
elevation: 2,
|
|
||||||
} as const;
|
|
||||||
|
|||||||
Reference in New Issue
Block a user