polish(mobile): audit design DSFR multi-agents — tokens, a11y, états, finitions

- tokens centralisés (Tint, IconSize, LineHeight, LetterSpacing), zéro hex en dur
- accessibilité (rôles, labels, live regions, afficher/masquer mot de passe)
- états vides/erreur complétés (ex. saisie introuvable), cibles tactiles >=44px
- suppression du composant mort RepubliqueHeader

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Azmog
2026-06-19 15:29:40 +02:00
parent 7fd27d8dc2
commit 91ceca2be5
29 changed files with 433 additions and 224 deletions
+59 -26
View File
@@ -1,5 +1,6 @@
import { useState } from 'react';
import {
Pressable,
StyleSheet,
Text,
TextInput,
@@ -11,7 +12,7 @@ import {
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '@/lib/colors';
import { FontSize, FontWeight, Radius, Spacing } from '@/lib/theme';
import { FontSize, FontWeight, HIT_SLOP, IconSize, MIN_TOUCH, Radius, Spacing } from '@/lib/theme';
interface TextFieldProps {
label: string;
@@ -53,7 +54,9 @@ export default function TextField({
style,
}: TextFieldProps) {
const [focused, setFocused] = useState(false);
const [revealed, setRevealed] = useState(false);
const borderColor = error ? Colors.error : focused ? Colors.primary : Colors.gray[600];
const isSecure = secureTextEntry && !revealed;
return (
<View style={[styles.container, style]}>
@@ -61,31 +64,53 @@ export default function TextField({
{label}
{required && <Text style={styles.required}> *</Text>}
</Text>
<TextInput
value={value}
onChangeText={onChangeText}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
placeholder={placeholder}
placeholderTextColor={Colors.gray[400]}
secureTextEntry={secureTextEntry}
keyboardType={keyboardType}
autoCapitalize={autoCapitalize}
autoComplete={autoComplete}
multiline={multiline}
numberOfLines={numberOfLines}
editable={editable}
style={[
styles.input,
{ borderBottomColor: borderColor },
multiline && styles.multiline,
!editable && styles.readonly,
]}
/>
<View style={styles.inputRow}>
<TextInput
value={value}
onChangeText={onChangeText}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
placeholder={placeholder}
placeholderTextColor={Colors.gray[400]}
secureTextEntry={isSecure}
keyboardType={keyboardType}
autoCapitalize={autoCapitalize}
autoComplete={autoComplete}
multiline={multiline}
numberOfLines={numberOfLines}
editable={editable}
accessibilityLabel={label}
accessibilityState={{ disabled: !editable }}
style={[
styles.input,
{ borderBottomColor: borderColor },
secureTextEntry && styles.inputWithIcon,
multiline && styles.multiline,
!editable && styles.readonly,
]}
/>
{secureTextEntry ? (
<Pressable
onPress={() => setRevealed((r) => !r)}
hitSlop={HIT_SLOP}
style={styles.toggle}
accessibilityRole="button"
accessibilityLabel={revealed ? 'Masquer le mot de passe' : 'Afficher le mot de passe'}
>
<Ionicons
name={revealed ? 'eye-off-outline' : 'eye-outline'}
size={IconSize.lg}
color={Colors.gray[500]}
/>
</Pressable>
) : null}
</View>
{error ? (
<View style={styles.messageRow}>
<Ionicons name="alert-circle" size={14} color={Colors.error} />
<Text style={styles.errorText}>{error}</Text>
<Ionicons name="alert-circle" size={IconSize.sm} color={Colors.error} />
<Text style={styles.errorText} accessibilityLiveRegion="polite">
{error}
</Text>
</View>
) : helper ? (
<Text style={styles.helperText}>{helper}</Text>
@@ -103,6 +128,7 @@ const styles = StyleSheet.create({
marginBottom: Spacing.xs,
},
required: { color: Colors.error },
inputRow: { position: 'relative', justifyContent: 'center' },
input: {
backgroundColor: Colors.gray[50],
borderTopLeftRadius: Radius.sm,
@@ -112,9 +138,16 @@ const styles = StyleSheet.create({
paddingVertical: Spacing.md,
fontSize: FontSize.body,
color: Colors.black,
minHeight: 44,
minHeight: MIN_TOUCH,
},
multiline: { minHeight: 110, textAlignVertical: 'top' },
inputWithIcon: { paddingRight: Spacing.xxl + Spacing.md },
toggle: {
position: 'absolute',
right: Spacing.md,
height: MIN_TOUCH,
justifyContent: 'center',
},
multiline: { minHeight: MIN_TOUCH * 2.5, textAlignVertical: 'top' },
readonly: { color: Colors.gray[500], backgroundColor: Colors.gray[100] },
messageRow: { flexDirection: 'row', alignItems: 'center', gap: Spacing.xs, marginTop: Spacing.xs },
errorText: { fontSize: FontSize.xs, color: Colors.error },