Cible UX
État. Les phases 1B-bis et 1C sont terminées : coquille, sélecteur, persistance de la famille active, routes par fonctionnalité, écran de première utilisation, accueil familial avec ses trois blocs, listes de courses et de tâches unifiées, et réordonnancement au glisser. Calendar, Meals and Recipes are also implemented; the sidebar shown below is now in use.
Le schéma ne comporte plus d'entrée « Activité » : elle décrivait le fil social que « Ce qu'on ne reprend pas » exclut déjà, et plus aucune entrée « Tâches », celles-ci vivant désormais sous « Listes ».
Le pied de la barre latérale porte une seule entrée, « Paramètres », qui ouvre une page déroulant quatre registres : les paramètres de la famille, les notifications personnelles, les données de la famille, et — pour un administrateur — l'administration de l'installation.
Le principe qui commande tout le reste
Une seule famille est le cas nominal. La grande majorité des utilisateurs n'en aura jamais qu'une. Le multi-familles existe — familles recomposées, aidants, colocation — mais c'est l'exception, et il ne doit rien coûter à ceux qui ne l'utilisent pas.
Conséquence directe : l'application ne montre jamais une liste de familles comme écran d'accueil. Elle affiche une famille active, et un sélecteur discret permet d'en changer. Quelqu'un qui n'a qu'une famille ne doit jamais avoir conscience que le concept de « plusieurs familles » existe.
The former Families view required a detour through family selection before opening lists. The current shell replaced it with an active-family context and switcher. See D8 in Architecture.
Structure visée
┌──────────────┬──────────────────────────────────────────┐
│ Famille ▾ │ │
│ 4 membres │ Contenu de la fonctionnalité │
├──────────────┤ sélectionnée, pleine largeur │
│ ▸ Accueil │ │
│ ▸ Agenda │ │
│ ▸ Listes │ │
│ ▸ Repas │ │
│ ▸ Recettes │ │
├──────────────┤ │
│ Paramètres │ │
│ Utilisateur │ │
└──────────────┴──────────────────────────────────────────┘
En haut à gauche, l'identité de la famille : emoji sur pastille colorée, nom, nombre de membres, et un chevron. Le chevron ouvre le sélecteur — la liste des familles avec leur nombre de membres, plus « Créer une famille ». C'est le seul endroit d'où l'on change de contexte, et le seul endroit d'où l'on en crée une.
La navigation est par fonctionnalité, pas par famille. On choisit « Listes », pas « ma famille puis ses listes ». La famille est un contexte ambiant, comme l'utilisateur connecté dans Nextcloud.
One word per object, everywhere (#17). The calendar screen is « Agenda » in the navigation, on the home and on its cards; a list says « 3 articles restants » or « 2 tâches restantes », on the index and on the home alike; the read-only card of a deadline or a meal says what it is to the family — « Échéance d'une tâche », « Repas prévu » — rather than « agenda dérivé ». A field's example reads « ex. : Piscine », paler than a value, so a grey word in a labelled field is not taken for its content.
Les vues de fonctionnalité partagent le même en-tête. Accueil, agenda, listes, repas, recettes et paramètres emploient la même hiérarchie de titre, le même rythme vertical et le même emplacement pour leurs actions. Le retrait à gauche réserve la place du bouton de navigation de Nextcloud : le titre ne passe jamais dessous, y compris sur un écran étroit. Sur ces écrans, choisir une destination referme aussitôt la barre latérale superposée afin de révéler le contenu demandé sans second geste.
Native to Nextcloud, with one role for the family's colour (#17). The interface keeps Nextcloud's look — its components, its Material Design icons on every navigation entry and on the list kinds (a cart, a checklist; a ✅ read as "done") — and gives the family's own colour one job: marking where you are and what is today. The active navigation entry, the home banner and today's column in the calendar and the meals wear a tint of it with a stripe of the colour itself; text stays in the theme's colour, since a family's colour may be too light to write on. The banner is flat, about half its former height, without the decorative circles. Card titles carry no overline repeating them; the view header's overline stays where it says something (the date, a recipe's servings and times), in ordinary case. In the calendar's toolbar the settings share one outlined pill shape, and creating is the one filled action.
L'accueil répond à « qu'est-ce qui arrive à la famille ? ». La route / et la sortie
de l'assistant mènent à /home, placé en premier dans la navigation. Son bandeau porte
l'identité et la couleur de la famille ; ses trois cartes présentent les rendez-vous des
trente prochains jours, les tâches dues et les listes récemment utilisées. Elles donnent
assez d'information pour choisir où aller, puis renvoient vers la fonctionnalité complète.
The banner sums the day up, and a phone shows what is due first (#17). « 1 tâche pour aujourd'hui. Ensuite : Dentiste, Demain · 10:00. » replaces a line that described the page to someone already on it. Below 980 px the tasks card comes before « À venir ». Each task there has a real checkbox: checking goes through the lists API, like a check made in the list, and the home is read again. A task's link, like a deadline's card on the grid, opens its list scrolled to that task and outlined for a moment.
La carte « À venir » montre tous les agendas de la famille sauf les projections : son agenda d'événements et les abonnements externes qu'un membre a ajoutés. Les échéances de tâches et les repas en sont absents parce que la page les affiche déjà dans leurs propres cartes ; un abonnement, lui, n'apparaît nulle part ailleurs. Un événement déjà commencé et pas encore terminé y figure aussi — une quinzaine de vacances scolaires est un seul événement, et l'accueil n'aurait rien annoncé pendant toute sa durée ; il porte alors la mention « En cours » plutôt que la date à laquelle il a commencé. Chaque événement porte la couleur de son agenda, comme sur la grille — un événement venu d'un flux externe n'est pas un événement de la famille, et rien d'autre sur cette page ne le dirait. Masquer un agenda depuis le panneau « Agendas » ne change rien ici : c'est une préférence de l'agenda, pas de l'accueil.
« Listes » couvre les courses et les tâches. Ce sont les mêmes objets — une ligne qu'on coche, dans une liste ordonnée — et deux entrées de navigation pour ça donneraient deux modules jumeaux qu'il faudrait choisir avant de savoir ce qu'on cherche. Le type est une propriété de la liste : il décide des champs proposés à l'ajout et de l'icône, pas de l'endroit où l'on va. Voir D9 dans Architecture.
La synthèse ne suit plus partout. Une colonne permanente répétait l'accueil et retirait de la place à chaque fonctionnalité sur grand écran. Elle disparaît donc du shell : agenda, listes, repas et recettes occupent tout le contenu disponible. La synthèse vit uniquement sur l'accueil et chaque bloc renvoie vers sa fonctionnalité.
Elle annonce des listes, pas leur contenu. Montrer les derniers éléments ajoutés revenait à répéter ce qui est déjà à l'écran juste à côté ; annoncer les listes donne accès à ce qui n'y est pas, et permet d'en proposer plusieurs d'un coup.
Ce qu'on retient de FamilyWall
La référence a été regardée pour ses bonnes idées, pas pour être copiée.
| Idée | Pourquoi on la garde |
|---|---|
| Sélecteur de cercle en haut à gauche | Rend le multi-famille invisible quand il n'y en a qu'une |
| Navigation par fonctionnalité | La famille est un contexte, pas une étape |
| Synthèse des prochaines actions | Donne une vue d'ensemble avant de choisir une fonctionnalité |
| Listes épinglées vs autres | Deux ou trois listes servent tous les jours, les autres dorment |
| Compteur d'éléments par liste | Se lit d'un coup d'œil, évite d'ouvrir pour savoir |
| Ajout en ligne (« Nouvelle tâche ») | Le geste le plus fréquent doit être le plus court |
| Réordonnancement au doigt | Les courses se font sur téléphone, à une main, dans un magasin |
| Bandeau coloré par liste | Repère visuel immédiat entre plusieurs listes |
| Sélecteur de calendriers avec cases | L'utilisateur choisit ce qu'il superpose |
| Filtre par membre (avatars) | Utile dès que la famille dépasse deux personnes |
| Fiche recette en deux cartes | Ingrédients et étapes côte à côte : on cuisine en lisant les deux |
| États vides porteurs d'action | « Ajouter des ingrédients » est un lien, pas un constat |
| Envoyer les ingrédients vers une liste | Depuis la recette, sans passer par le planning — fait, avant même le planning |
Ce qu'on ne reprend pas
- Le fil d'activité social (« Exprimez-vous », commentaires, cœurs). Hors périmètre : Nextcloud Talk couvre la conversation, et Scope exclut la messagerie.
- Partage de position, Galerie, Répertoire, Messages. Déjà couverts par Nextcloud (Photos, Contacts, Talk) et exclus définitivement du périmètre.
- Budget. Exclu définitivement.
- Premium / paywall. Le projet est libre et sans monétisation.
Conséquences concrètes
Persistance de la famille active. Le choix doit survivre au rechargement et suivre
l'utilisateur d'un appareil à l'autre : à stocker via l'API Preferences OCS d'AppAPI
(par utilisateur), avec localStorage comme cache de démarrage pour éviter un écran vide
le temps de l'appel. Repli si rien n'est enregistré : la première famille, ou l'écran de
création s'il n'y en a aucune.
Routes name the feature, in English. /home, /calendar, /lists/:listId,
/meals, /recipes/:recipeId, /settings/:section (family, notifications, data,
administration) and /welcome. The active family lives in a store and appears in the URL
only as an optional ?family=, so a shared link opens the right context. The French paths
they replaced are gone without redirects: the application had not shipped, so no link
needed preserving (#17).
An address that leads nowhere says so. An unknown path shows « Page introuvable » with a way back home, and a list or recipe id that names nothing shows « Liste introuvable » or « Recette introuvable » in its own view, its navigation entry still highlighted. Showing the first list instead, as the lists did, answers a question nobody asked. The one exception is a family switch: the id left in the URL then belongs to the family just left, and the new family's first list opens.
La langue est celle du compte Nextcloud, et elle ne se règle pas ici. L'interface, les messages de l'API, les notifications, la page hors ligne et le manifeste suivent la langue du compte, prise en compte au rechargement suivant. Il n'y a pas de sélecteur : un réglage propre à l'application serait un deuxième endroit où dire la même chose, et le premier est déjà dans les paramètres Nextcloud. Une langue non prise en charge lit l'anglais, qui est la langue source (D38).
Le format des dates est un réglage distinct de la langue. Une interface anglaise avec des dates françaises est un cas valide, et l'un ne suit pas l'autre : les deux viennent de Nextcloud, séparément. La semaine commence toujours le lundi.
Dates keep the case of their language. No capitalisation is forced on day or month names:
French writes « jeudi 24 septembre », and Intl already writes each language as it should be
written (#17).
The event form reads dates the way Nextcloud writes them. Its fields are
NcDateTimePicker, a day and a time for each bound, so they follow the account's date
format rather than the browser's language. Moving the start carries the end along by the
event's length; an end moved before the start pulls the start back by that length. The form
never holds an event that ends before it begins (#17).
Ce que les familles écrivent n'est jamais traduit — noms de familles, de listes, d'articles, de recettes, d'agendas. Ils traversent les phrases comme paramètres, ce qui laisse une traduction les placer là où sa grammaire les veut.
La page hors ligne parle la langue de l'instantané. Elle n'a ni réseau ni sous-ressource (D35), donc elle emporte son catalogue ; un cache enregistré avant que cette page sache traduire quoi que ce soit se lit en français, la seule langue que l'application avait alors.
L'API ne bouge pas. Elle est déjà découpée par famille (/api/families/:id/lists), ce
qui est correct : c'est la couche de présentation qui doit cesser d'exposer ce découpage.
Premier écran quand il n'y a aucune famille. Un assistant de création, pas une liste vide. C'est le seul moment où l'on parle de « famille » comme d'un objet à créer.
A new family is not empty. It starts with an empty shopping list and an empty task list, named in the founder's language (D41), so the home and the Lists screen have something to open from the first minute instead of a blank to guess past. They are ordinary lists: renamed or deleted like any other, and never put back.
Une seule exception à « aucune famille → l'assistant » : l'administration. Une
installation neuve n'a pas de famille, et ce qu'un administrateur y fait — mettre en place
le compte qui détiendra les agendas — précède forcément la première. L'y envoyer par
l'assistant rendrait la mise en place atteignable seulement après la chose qu'elle
conditionne. L'assistant porte donc un lien direct vers /settings/administration,
visible pour un administrateur seulement.
L'administration est un registre à côté du familial, du personnel et des données.
« Paramètres de la famille » concerne une famille, « Mes notifications » concerne une
personne, « Données de la famille » concerne ce que l'application garde en propre — listes,
recettes, planning, agenda familial et abonnements externes — et son export,
« Administration » concerne l'installation — une fois,
par quelqu'un qui en a le droit. Cette dernière entrée n'apparaît que pour un
administrateur, et c'est le serveur qui dit qui en est un : le navigateur ne décide pas
de ses propres droits. Ces quatre registres sont réunis sous une seule entrée « Paramètres »
et une seule vue /settings/:section. La page les déroule simplement dans l'ordre —
famille, notifications, données, administration — avec un séparateur discret et un scroll
naturel, sur ordinateur comme sur téléphone. Les anciennes URLs redirigent et positionnent
la page près du contenu attendu afin de préserver les favoris.
Modifier l'identité de la famille est une action directe au crayon — elle couvre le nom,
l'icône et la couleur. La suppression, destructive et bien plus rare, vit tout en bas dans
l'administration : conséquences écrites en rouge, puis confirmation explicite. Cette zone
reste accessible au propriétaire de la famille sans lui donner les réglages de l'installation.
Un écran qui transporte des données dit ce qu'il transporte, et ce qu'il n'a pas pu prendre. « Données de la famille » porte trois avertissements qu'aucun autre endroit ne peut porter, parce que le fichier est parti dès que l'utilisateur a cliqué :
- l'adresse des agendas externes est dans le fichier, et l'adresse d'un flux peut contenir un identifiant d'accès — c'est la raison pour laquelle le panneau « Agendas » n'affiche que l'hôte. La phrase est au-dessus du bouton, pas après.
- un agenda illisible, ou tronqué, ressemble sinon exactement à une famille sans événements. L'écran l'annonce en avertissement après l'export, en nommant ce qui manque.
- les événements sont rejoués et non dupliqués à un second import, contrairement aux listes, aux recettes et aux repas. C'est la seule asymétrie du fichier, et se tromper dans un sens fait supprimer des doublons à la main, dans l'autre fait renoncer à relancer un import interrompu.
Le compte-rendu d'import suit la même règle que le reste de l'écran : il ne nomme que ce qui s'est produit. Une famille qui n'importe aucun repas ne lit pas « 0 repas importés », elle ne lit rien sur les repas.
L'écran qui nomme la panne est celui qui la répare. Le compte de service détient les agendas de toutes les familles, et le supprimer les emporte. L'administration le détectait déjà ; elle le défait désormais — le recréer remet les collections en place et refait le plein des tâches et des repas, qui sont des projections. Le bouton dit ce qu'il a fait, et ce qui ne revient pas : les événements de l'agenda familial n'ont d'autre source que CalDAV. Un bouton « Vérifier les agendas » offre la même passe quand le compte est là — une collection peut disparaître sans lui. Une passe qui n'a rien trouvé et une passe qui n'a pas pu tourner annoncent toutes deux zéro, et l'écran les distingue : rassurer un administrateur au moment précis où son installation est cassée est la seule chose que cette page ne doit pas faire. Voir D32. L'invitation conserve l'identifiant Nextcloud comme valeur sans ambiguïté, mais le champ recherche dès deux caractères et présente le nom affiché avec l'identifiant. Une saisie manuelle reste possible pour ne pas rendre un compte introuvable impossible à inviter.
Une recette porte une photo, et la galerie la montre. C'est la seule image de l'application, et elle sert à reconnaître un plat d'un coup d'œil plutôt qu'à lire un titre. Vignette de hauteur fixe sur les cartes pour que la rangée garde son rythme quelles que soient les proportions ; l'image entière sur la fiche. La photo s'ajoute et se retire depuis la fiche, par le sélecteur de fichiers du système — sur un téléphone, il propose déjà l'appareil photo, ce qu'aucune zone de dépôt ne saurait faire.
Un agenda injoignable le dit, il ne se fait pas passer pour vide. Une liste vide veut dire « rien de prévu » partout ailleurs ; ici elle peut aussi vouloir dire « on n'a pas pu regarder ». L'accueil et la grille l'écrivent chacun dans ses mots plutôt que d'annoncer sereinement une semaine libre. Inventer la seule chose qu'on ne sait pas est la faute que l'application s'interdit.
Ni l'un ni l'autre ne nomme l'agenda fautif : la conséquence est la même quel qu'il soit, et le nommer deviendrait faux dès qu'un second tombe. Les deux écrans lisent désormais plusieurs agendas, donc un échec partiel coexiste avec des événements bien lus : l'avertissement se place au-dessus de ce qui a pu être affiché et ne le remplace que lorsqu'il n'y a rien à montrer.
L'agenda adapte sa lecture au besoin du moment. Un sélecteur donne accès au jour, au jour réparti par membre, à la semaine, au mois et au planning chronologique. La semaine est la vue initiale sur un grand écran, le planning sur un téléphone (#17) : une grille horaire répartit les sept jours en colonnes et place les rendez-vous selon leur heure et leur durée. Les événements qui se chevauchent ne se partagent pas la largeur à parts égales : ce qui se déroule pendant un autre se dessine à l'intérieur de lui, sur sa moitié droite, tandis que deux événements qui se débordent mutuellement deviennent voisins, décalés, en gardant chacun les deux tiers de la place. Un rendez-vous d'une heure glissé dans une journée de correction ne réduit donc pas la journée de moitié. Un événement qui passe minuit se poursuit dans la colonne suivante, coupé à chaque jour. La hauteur dit la durée : sous une demi-heure, l'heure et le titre tiennent sur une seule ligne, et les membres n'apparaissent que lorsqu'il reste une ligne pour eux. Seule la zone des horaires défile verticalement ; les jours et les événements « toute la journée » restent fixés en haut pour préserver le contexte — cette bande montre deux rangs et propose le reste d'un clic, plutôt que de repousser les horaires hors de l'écran. La grille prend exactement la place que la page lui laisse : en vue semaine, l'écran ne défile pas, seule la grille le fait à l'intérieur d'elle-même — deux barres de défilement côte à côte pour un même geste sont une hésitation, pas un choix. Le mois et le planning grandissent au contraire avec ce que la famille a prévu et font défiler la page, ce pour quoi la règle ne les concerne pas. Sur téléphone, la grille conserve ses sept colonnes et défile horizontalement. Elle s'ouvre sur aujourd'hui, juste après l'axe des heures, plutôt que sur lundi (#17). Le jour par membre duplique volontairement un événement commun dans chaque colonne concernée ; le mois privilégie le repérage, et le planning la lecture linéaire. Le filtre accepte plusieurs membres et se remet à zéro d'un geste ; il est retenu par famille, parce qu'il appartient à la personne qui regarde et non à la famille — filtrer sur les enfants pendant que quelqu'un d'autre consulte la semaine entière est le cas normal.
La modale de filtre montre les visages et se valide en bloc. Chaque membre y est une ligne : coche, avatar, prénom, poignée. Rien ne bouge derrière la modale tant qu'on n'a pas enregistré — cocher membre par membre redessinait l'agenda sous la modale ouverte, et une sélection commencée par erreur n'avait pas de retour en arrière. La poignée fait glisser les lignes pour fixer l'ordre des colonnes de la vue « jour par membre » : cet ordre est une préférence de lecture, retenue comme le filtre et par famille. La poignée est un bouton et les flèches du clavier la déplacent, parce que le glisser n'existe ni au clavier ni au lecteur d'écran.
Sur téléphone, les contrôles de l'agenda tiennent en deux lignes. La période consultée vit entre les flèches de navigation ; le sélecteur de vue et le filtre partagent la ligne suivante. La création devient un FAB « + », tandis que les événements mettent heure, titre et membres sur une ligne compacte dès que la largeur le permet. Le menu des vues porte une icône propre à chacune : calendrier du jour, membres, semaine, mois et liste chronologique.
The calendar and the meals walk periods with one control (#17). The range sits between the arrows and wraps rather than being cut on a phone; « Aujourd'hui » is a button of its own, disabled when the period already holds today.
Un événement sans attribution disparaît dès qu'un filtre est actif. Le filtre répond à « ce qui concerne ces personnes », et ce qui ne dit rien de qui il concerne n'en fait pas partie. C'est assez surprenant pour que la vue l'écrive au-dessus de la grille : un événement qui s'évapore sans explication vaut un bug.
Un événement est attribué à quelqu'un, sinon c'est une tâche. À la création, tous les membres sont cochés — c'est ce qu'est un événement familial, et décocher une personne va plus vite que d'en cocher quatre. Ce qui ne concerne personne en particulier a déjà sa place dans les listes.
« Tout le monde » chooses the whole family in one press, and clears it when everyone is already chosen. A chosen member is a filled chip with a check mark. The earlier tint almost disappeared in dark mode, where the selection read as the opposite of the truth (#17).
Les réglages de notification sont personnels, et ils ont leur écran. Ils suivent la personne dans toutes ses familles ; les glisser dans les paramètres de la famille mentirait sur leur portée. Deux interrupteurs seulement — ce que fait la famille, et les rappels — parce qu'une liste plus fine serait un écran que personne n'ouvre. Voir D17.
Le formulaire demande d'abord ce qu'on vise : cette occurrence, ou toute la série. Le choix est posé avant les champs, parce que la portée doit être réglée avant qu'on lise ce qu'on modifie — et les libellés des boutons la répètent, « Enregistrer cette occurrence » contre « Enregistrer la série ».
Le défaut est l'occurrence. Les deux défauts peuvent se tromper, mais pas également : déplacer un cours de piscine et déplacer tous les lundis se ressemblent jusqu'à la semaine suivante, alors que renommer une seule semaine quand on visait la série se voit tout de suite et se répare aussi vite.
Une série qui porte des exceptions ne s'enregistre pas ici. Décaler une seule séance crée une exception dans l'objet de l'agenda ; notre formulaire réécrit l'objet en entier et les effacerait. Il refuse donc, et le dit. La supprimer reste possible — retirer la série emporte ses exceptions, ce qui est justement ce qu'on demande.
Une répétition définie ailleurs n'est pas réécrite. Un événement venu de Nextcloud Calendar peut porter une règle que ce formulaire ne sait pas exprimer. Elle est affichée, laissée telle quelle, et conservée à l'enregistrement plutôt que reconstruite en plus pauvre. Voir D15.
Les recettes se parcourent en galerie, pas en onglets. « Listes » présente ses listes
en onglets parce qu'on en a trois ou quatre et qu'on passe de l'une à l'autre. Une boîte à
recettes en compte des dizaines, on n'en consulte qu'une à la fois, et on y arrive en
cherchant plutôt qu'en parcourant. D'où une grille de cartes, et une fiche qui occupe tout
l'écran une fois ouverte — adressée par son URL (/recipes/:id), pour qu'un lien envoyé
à quelqu'un ouvre bien la recette et pas la galerie.
A recipe is written on a page, and its sheet offers the shopping list first (#17). Creating or
editing opens /recipes/new or /recipes/:id/edit rather than a dialog in which a long recipe
scrolled inside a box; its steps are numbered once, by their label; a photo can be chosen while
creating. On the sheet, « Ajouter les ingrédients à une liste » is a visible button — it is what a
recipe is opened for most often — and the same words name it in Meals and in the dialog it opens.
La recherche porte sur le titre et les ingrédients. Chercher « pommes » pour savoir quoi faire de celles qui ramollissent est le geste qui justifie une boîte à recettes ; n'indexer que les titres obligerait à se souvenir du nom du plat, c'est-à-dire à connaître déjà la réponse. Le filtrage est local, la galerie renvoyant les recettes entières.
Le planning se remplit en ouvrant un créneau, pas en glissant. C'est l'exception à la règle du glisser posée en D10, et elle vient de la même contrainte : le téléphone. Ordonner une liste, c'est déplacer une ligne parmi d'autres au même endroit — le glisser y est le geste évident. Remplir un planning, c'est amener une recette depuis un autre écran jusqu'à l'une de quatorze cellules ; sur un téléphone la liste source et la grille ne tiennent pas ensemble. On ouvre le créneau, on choisit une recette ou on note « restes ». Le glisser reste souhaitable sur grand écran, par-dessus.
Un créneau accepte du texte libre autant qu'une recette. « Restes », « chez mamie », « pizza » font la moitié d'une semaine réelle. Un planning qui n'accepterait que des recettes obligerait à créer une fiche « Restes » pour être utilisable, ce qui salit la boîte à recettes pour satisfaire la grille.
A slot opens on the recipes, and the week invites planning (#17). The dialog lists the recipes first, the one already planned marked, and the free text follows as « Autre chose ». In the grid an empty slot reads « + Planifier » rather than « — », today carries a badge, past days are faded but still usable, and a phone scrolls to today.
On ordonne en glissant, pas avec des flèches. Deux boutons par ligne encombrent l'écran là où le geste est évident. Le glisser doit fonctionner au doigt : les courses se font sur téléphone, à une main, dans un magasin — ce qui exclut l'API HTML5 native. Les actions « Monter » et « Descendre » ne disparaissent pas pour autant, elles rejoignent le menu de l'élément : sans elles, réordonner deviendrait impossible au clavier. Voir D10.
Lists use a master-detail layout on wide screens and two screens on phones. The list index stays visible beside the active list on a wide screen, with card-like entries that are quick to scan. On a phone, opening an entry replaces the index with the list detail; an explicit Back action returns to the index. This gives both screens the full viewport width instead of squeezing them side by side.
List creation is an explicit, occasional action. A floating “+” button belongs to the list index on both layouts and opens the name/type form in a dialog. The form no longer occupies the list panel permanently, while creation remains reachable with one obvious action.
Quick add asks for the item name only. Quantity for shopping items and the task-specific fields stay in the item editing dialog, reachable from the row menu. This keeps the family's most frequent list gesture to one field without making the details unavailable.
Deleting an item is one tap, and can be taken back for eight seconds. A notice « « Lait » n'est plus dans la liste. » offers « Annuler », which puts the item back where it was. No confirmation: deletion is the most frequent correction in a shop, done one-handed. The menu entry is red. The name of an item is also its checkbox's label — announced with it, and touching the name checks it (#17, D42).
A dialog closes on success and stays open on failure. Whatever was typed stays there, ready to be corrected, and the error appears in the dialog rather than behind it. Closing on failure asks someone to find their way back and type it all again, which is how a single transient error turns into abandoned work. The parent decides when to close, because only the parent knows whether the call succeeded. Every form dialog does it — event, family, item, recipe, meal slot, list creation. And each opens clean: the previous failure is cleared before the dialog shows, in the same tick, so it neither lingers in a new form nor flashes and vanishes.
Escape closes a form dialog from inside a field. NcModal ignores keys typed in a field, so that shortcuts never steal typing, and every form opens with its first field focused — Escape used to do nothing at all. It now closes the dialog from a field too, except from a picker whose list is open: there the first Escape closes the list, the second the dialog (#17). A click beside a dialog closes it as well, as everywhere else in Nextcloud; a click in a date or time picker's menu does not, although that menu sits outside the dialog.
An edit refused because someone else changed the item says what changed, and keeps what was typed. The item dialog names each field both sides changed, with the value the other member left, and keeps the member's own entry in the field; saving again overwrites knowingly, « Use their version » takes theirs. A field only the other side touched is updated in the form without a word, and an item deleted meanwhile is named as such with a « Recreate » action instead of Save. A refusal that loses the member's entry would cost more than the overwrite it prevents. It names what, not who. This dialog is the one exception to "the parent decides when to close": it saves through the store itself, because what it shows after a refusal is read from the list the store has just fetched again. See D40.
Never announce an absence that has not been verified yet. An empty collection and a collection still travelling look identical, and the interface must not read the first into the second: a picker announced "no shopping list in this family" for the whole duration of its own fetch, which is long enough to be read and believed before the real answer replaces it. Every screen that can say "there is nothing" therefore tracks loaded separately from empty — the same rule that makes an unreachable calendar say so instead of showing a free week.
Hiding checked items lives in the list's menu, with the other actions that concern it, and is remembered per list. It belongs to the list, not to the person globally: hiding what is already in the cart on a shopping list says nothing about whether a chores list should hide what is done.
Reordering is refused, and shown as refused, when the displayed order is not the real one. Filtering by assignee, sorting by due date or hiding checked items all mean the positions on screen are not the positions in the list, so a drag would persist an order that is partial or simply wrong. The controls are visibly disabled rather than accepting a gesture with no effect. The same applies to the keyboard fallback, which shares the rule rather than a separate one. See D10.
A shopping list's checked items go below the rest (#17). In a section of their own, « 1 article coché », open by default and folded on a tap; not draggable, since positions there are not the stored ones (D10). Task lists keep checked items in place. The list index shows the kind and what is left: « Courses · 2 articles restants ».
The timetable says which calendars it is made of, and lets you choose. A family plans three different things — its own events, the deadlines of its tasks, its meals — and each gets a calendar of its own (D31). A gear beside « Filtrer » opens a panel that lists them with their colour and a tick; the events and the deadlines are on by default, the meals are not. Whatever is hidden is named on screen, exactly as the member filter is: a week missing its meals is baffling otherwise, and the button alone does not say which agenda the absence comes from.
The gear is an icon, and what it hides is said in words below it. The button carried « Agendas (1 masqué) » and the count pushed the toolbar past a phone's width. It is the one control in that row that opens configuration rather than changing the week on the spot, so it reads as a gear; a dot marks it while something is hidden, its accessible name still carries the count, and the sentence under the bar still names the agendas themselves.
Two questions, two controls, in that order. « Agendas » decides what the week is made of and lasts; « Filtrer » asks "what concerns these people" and is dropped as soon as it has been answered. Folding them into one dialog would have been more compact today and wrong tomorrow — the panel is where adding an external calendar and removing one live, and that is configuration, not a filter. It is also why the panel lists calendars by name and colour rather than showing three checkboxes: the row was already the shape a longer list of subscriptions needed, and now is one.
Adding a subscription is membership, not a stronger right. Any member sees the form and the remove control on every row, subscriptions someone else added included — the same right that already lets any member start a shopping list. See D34.
A subscription's row names where it comes from, not the whole address. The panel shows
the source's host — calendrier.exemple.fr, say — never the full URL, which may carry a
token or a password meant for whoever pasted it, not for the rest of the family reading the
same panel.
Two things a new subscription's row says, so silence does not read as failure. It fills at the instance's next hourly refresh, not on the spot — an empty week right after adding one is not a sign anything went wrong. And it is visible in this application alone: seeing it in Nextcloud Calendar too means subscribing to the same address there as well, since there is nothing here to share it through (D33).
The refusal card names an external calendar, not a derived one, when that is what a chip is. A task deadline or a meal "reflète ce que la famille a planifié ailleurs" — that sentence would be false about a feed nobody in the app authored, so a subscription's card says instead that the item comes from an external calendar and is not edited here.
Colour says what a chip is, or which event it is, but not both. A week holds exactly one deadline calendar and one meal calendar, so a shared colour is what makes a deadline recognisable at a glance. It holds any number of events, so there the useful thing is telling them apart, and each keeps a hue derived from its own identity. On the week grid the colour fills the chip; in the day, month and planning views it is an edge, because those lists are read as text and a filled row costs the contrast that makes them legible.
A meal and a deadline are read-only on the grid, and the refusal says where to go. They are shown, coloured and filtered like anything else, but clicking one opens a card rather than the event form: what it is, when, which agenda it comes from, and a button onto Repas or Listes. A refusal that does not say where to act is a dead end, and the calendar's own name is what connects it to the row on screen.
A deadline's button opens its own list on that task rather than the lists screen (D31, #17).
That is a display decision resting on a real one: those two calendars are read-only everywhere, Nextcloud Calendar and a phone included (D31). The interface is not hiding a capability — it is telling the truth about one that does not exist.
Offline is a state of the application, not a second interface. The same principle that makes "no family" a shell state rather than an empty view (§ above) applies here: a family's lists do not get their own standalone app to maintain in parallel, read-only and inevitably drifting from the real one. What exists offline is a single page a service worker serves in place of the real one when the network is down — the family's name, its lists, each item's checked state, and when the copy was taken — with nothing to navigate to and nothing to edit, said in as many words rather than left for a broken button to discover. The banner and the slower poll the live application shows when a request it already made fails are a different thing again: not offline mode, just the ordinary screens saying the one true thing they now know. See D35.
A member wears one colour per family, and it is read where an assignment exists. Every member carries a colour — handed out on arrival, correctable by anyone from the family settings' Members panel, where the colours another member already wears are refused. It reads at a glance: a chip of the timetable naming exactly one member (day, day by member, week, month — the day-by-member view also wears a dot in its column headers), the assignee chip of a task's row, and on the home both an event of one member under « À venir » and the assignee of a task due today — the home agrees with the grid, or one person reads as two. An event naming several members is a family event and takes the family's own colour. The colour is redundant with the name, never its replacement — colour-blind members, and chips too small for a readable fill. See D39.