Pelote
FR
Rules6 min

The behaviour rules, valid in M3 and in the retro variants. Each one comes from a real screen in the repos.

Hierarchy and actions

  • One primary action per screen, where the thumb or the eye expects it: FAB bottom right on mobile ("Meal" in Stabilité), btn-filled button to the right of the page title on the web.
  • Urgency gets its own colour, not its own size. The "Crash SOS" FAB is ec / on-ec next to the "Meal" FAB in pc: same size, different colour. Never enlarge a button to draw attention.
  • Dialog button order: secondary action (text) on the left, primary action on the right. The destructive action is btn-danger, never btn-filled.
  • No useless blank space in a card. A short element (chip, status, action, value) never sits alone on its line if it fits next to the title or another element: status and action in the header, metadata grouped on one line ("Port 443 · 3 d"). A card takes the height of its content, with no stretched empty area.
  • A disabled button says why: "Pick at least one symptom" replaces the label while the condition is unmet, instead of a silent grey button.

Feedback

  • Every network action has three visible states: in progress (button waiting, label kept), succeeded (snackbar in the past tense), failed (snackbar error with the API message). That is what run() in lib/ui.ts does.
  • Snackbar for what is done, dialog for what is about to be done. A confirmation only appears before an irreversible or costly action (delete, restart, stop a container).
  • Undo instead of confirm when the action is reversible: snackbar with an "Undo" action or "Detail the snack".
  • One snackbar at a time, bottom centre, 4 to 6 seconds, above the navigation bar on mobile.

Empty states, loading, errors

  • An empty state says what to do and how long it takes: "Nothing logged yet. The Meal button takes less than 5 seconds."
  • Never the text "Loading…" alone: always a loader. Skeleton at final dimensions for content, an 18px indicator inside the working button, an M3E indicator for an isolated wait, a determinate bar with a percentage for a long task. Text may accompany a loader beyond 3 s to say what is happening, never replace it.
  • Loading keeps the layout: the previous value stays visible during the update, no full-screen spinner.
  • An error stays close to its cause: under the field (field-help error), in the affected card, or in an error snackbar if it is global.

Forms

  • Label always visible above the field, never only as a placeholder.
  • Numeric fields with inputmode="numeric", no arrows, tabular figures.
  • Validate on blur, not on every keystroke. The submit button stays active and explains what is missing.
  • Short choices (symptoms, place, meal type) as filter chips, not a dropdown.
  • 3 to 5 destinations in the bottom bar or rail. Beyond that, a "More" entry opens a sheet.
  • The active item combines three signals: secc pill, filled icon, weight 600 label. Never colour alone.
  • aria-current="page" on the active destination. Escape closes sheets and dialogs, focus returns to the trigger.

Accessibility

  • 4.5:1 contrast for text, 3:1 for control outlines, meaningful icons and the focus ring, in both themes.
  • Status never by colour alone: StatusDot with the word ("Running", "Stopped"), or an icon in the chip.
  • A 40px minimum touch target, 48px for primary mobile actions.
  • Chart components and gauges with role="meter" and ARIA values (as MetricCard does).
  • Reduced motion respected everywhere.

Data and legibility

  • Summary before detail: MetricCards at the top of a dashboard, detailed list below.
  • Main value large (metric), context below in small ("3.1 / 8 GB").
  • Relative durations and dates when recent ("3 h 05 ago"), absolute beyond a week.

Mobile web and PWA

  • No blue tap highlight. On mobile the browser colours the touched element: remove it everywhere with -webkit-tap-highlight-color: transparent (on * or html). Visual feedback comes from the .st state layer (12% on press) and the ripple, and the keyboard focus stays visible with :focus-visible.
  • Always a PWA. Every site or web app using Pelote is installable:
    • manifest.webmanifest with name, short_name, description, lang, start_url, scope, display: standalone, background_color and theme_color (the theme surface), and the AppIcon files: favicon as any 192 and 512, maskable 192 and 512;
    • <link rel="manifest">, <meta name="theme-color"> (one value per theme with media), apple-touch-icon 180 and the SVG favicon;
    • a service worker that caches the app shell and shows a useful offline screen (like Stabilité's SOS, which works without a network);
    • shortcuts to the most used screens when there are any (PoGo Search: 100 IV, Shiny).

Languages and localisation

  • French and English at minimum in every app, other languages next. The language follows the device, with a choice in settings; correct lang on the page or Locale in Flutter.
  • No hard-coded text: everything goes through translation files (ARB in Flutter, message files on the web). No sentence built by concatenation: one complete string with variables, and plurals handled by the language (ICU: "1 day", "3 days").
  • Allow +35% length (French and German are longer than English) and test every screen with a lengthened pseudo-translation before shipping.
  • Buttons (illustrated in the Localisation spec):
    • width follows content, never fixed; never truncated text or a two-line label in a button;
    • if two buttons no longer fit side by side, they stack, full width, the primary action on top on mobile;
    • if a label is still too long, shorten it in that language (one verb and one object), do not reduce the font size.
  • Chips, tabs, navigation: short labels in every language; a chip that is too long wraps in the group, a tab is never truncated (the group scrolls). Navigation labels fit in one word.
  • Text: no fixed height on a text block, wrapping allowed everywhere; headings use text-wrap: balance.
  • Formats: dates, times, numbers, units and currencies through Intl (web) or intl (Flutter), never formatted by hand ("3 h 05" in French, "3:05 h" or "3 h 5 min" depending on the language).
  • Accessibility: aria-label, tooltips and alternative texts are translated too. Lay out with logical properties (margin-inline, padding-inline-start) to stay ready for right-to-left languages.

Theme personalisation

  • Always let people choose, in every app, site or tool:
    • the mode: Light, Dark or System (default: System);
    • the theme colour: 4 to 6 source colours as swatches, plus a free choice; every role is regenerated from that colour (material-color-utilities, ColorScheme.fromSeed).
  • Android: Material You always available. A "Wallpaper colours" switch (dynamic colour), on by default on Android 12+; when it is on, the colour choice is greyed out with a sentence explaining why.
  • The choice applies immediately (no Save button), is remembered, and follows the person across devices when the app has an account.
  • The ok, warn, bad statuses do not change with the theme.
  • The setting lives in Settings, "Appearance" section, with a live preview; the Plaza and Chrome variants are added there as a "Style" choice.

More

TokensAssetsLicense GitHub

Appearance

The choice applies immediately and stays on this device.

Mode
Theme colour Every role is regenerated from this source colour.
Style The material changes, components and roles stay the same.
Material YouOn the web, the wallpaper colour is not available. Pick a source colour above; in Android 12+ apps, Material You follows the wallpaper.