fix(ui): una cornice sola, e smette di spostarsi sotto le dita

Punto 14. La segnalazione dell'operatore del 28/07 non era stata circoscritta sul
codice: «le dimensioni delle viste cambiano a seconda del menu». Sono quattro
meccanismi distinti, tutti leggibili senza avere il tablet in mano.

Ogni vista si dichiarava la propria larghezza: sette valori diversi su diciassette
template, e nessuno coincideva con quello della navbar, che sta a max-w-7xl su
tutte. Il contenuto era quindi disallineato dalla barra sopra di sé, e il
disallineamento cambiava a ogni pagina. Il percorso dell'operatore faceva
1280 → 1024 → tutto schermo → 1280 in quattro passaggi.

Il padding verticale mescolava py-8 e py-6 fra viste consecutive, così la prima
card cambiava altezza a ogni schermata.

La barra di scorrimento è classica da 8px e occupa spazio: una pagina lunga la
mostrava, una corta no, e la schermata di misura la toglie sempre. A ogni
navigazione tutto il contenuto centrato — navbar compresa — si spostava di 8px.

La schermata di misura era alta 100vh, che su Android e iOS è l'altezza con la
barra dell'indirizzo nascosta: su un tablet il piede, dove stanno «Fine ciclo
misura» e il tastierino, finiva sotto il bordo.

Ora: .tmf-page e .tmf-page-narrow in themes.css, con i valori esatti della navbar;
scrollbar-gutter: stable; 100dvh dove serve. Due larghezze in tutto il prodotto al
posto di sette, e il criterio è il tipo di pagina — liste, tabelle e tele stanno
larghe, i moduli stanno stretti.

Login e schermata di misura tengono la loro geometria, per i motivi scritti in
docs/architecture/LAYOUT.md e ripetuti in EXEMPT dentro il test: la prima non ha
navbar a cui allinearsi, la seconda non deve scorrere mentre si misura.

Il test è statico e guarda i sorgenti: la vista scritta domani copierà la cornice
dalla vicina, ed è lì che la deriva ricomincia.

Resta da chiedere all'operatore su quale schermo l'ha vista: in verticale su un
tablet quasi tutte quelle larghezze collassano e il difetto non si nota.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-07-28 21:41:16 +00:00
parent 98b1c5ae85
commit 78816dfe8c
18 changed files with 239 additions and 15 deletions
@@ -3,7 +3,7 @@
{% block title %}{{ _('Impostazioni') }} - TieMeasureFlow{% endblock %}
{% block content %}
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-6"
<div class="tmf-page tmf-page-narrow"
x-data="systemSettings()">
<!-- Header -->
@@ -8,7 +8,7 @@
window.__allRecipes = {{ all_recipes|tojson }};
</script>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"
<div class="tmf-page"
x-data="stationManagement(window.__stations, window.__allRecipes)">
<!-- Header -->
@@ -5,7 +5,7 @@
{% block content %}
<script>window.__users = {{ users|tojson }};</script>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"
<div class="tmf-page"
x-data="userManagement(window.__users)">
<!-- Header -->
@@ -2,7 +2,7 @@
{% block title %}{{ _('Profilo') }} — TieMeasureFlow{% endblock %}
{% block content %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-4xl">
<div class="tmf-page tmf-page-narrow">
<!-- Page Header -->
<div class="mb-8">
<div class="flex items-center space-x-3">
@@ -2,7 +2,7 @@
{% block title %}{{ _('Stazione non configurata') }} — TieMeasureFlow{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8 max-w-2xl">
<div class="tmf-page tmf-page-narrow">
<div class="mt-20 p-8 bg-[var(--bg-card)] rounded-xl shadow-lg text-center border border-red-200 dark:border-red-800">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full
bg-red-50 dark:bg-red-900/30 mb-6">
@@ -32,7 +32,7 @@
{% set current_version = recipe.current_version if recipe and recipe.current_version else None %}
{% set versions = recipe.versions if recipe and recipe.versions else [] %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-4xl"
<div class="tmf-page tmf-page-narrow"
x-data="recipeEditor()"
x-cloak>
@@ -3,7 +3,7 @@
{% block content %}
<script>window.__recipeListData = {{ recipes|tojson }};</script>
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-6xl"
<div class="tmf-page"
x-data="{
recipes: window.__recipeListData,
search: '{{ search or '' }}',
@@ -42,7 +42,7 @@
{% endif %}
{% endfor %}
</script>
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-5xl"
<div class="tmf-page"
x-data="recipePreview()"
x-cloak>
@@ -65,7 +65,7 @@
connectionError: {{ _("Errore di connessione al server")|tojson }}
};
</script>
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-5xl"
<div class="tmf-page"
x-data="taskDrawing()"
x-cloak>
@@ -91,7 +91,7 @@
{% endblock %}
{% block content %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-5xl"
<div class="tmf-page"
x-data="taskEditor()"
x-cloak>
@@ -53,7 +53,7 @@
{% endblock %}
{% block content %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-4xl"
<div class="tmf-page tmf-page-narrow"
x-data="versionHistory()"
x-init="loadMeasurementCounts()"
x-cloak>
@@ -3,7 +3,7 @@
{% block content %}
<script>window.__selectRecipeData = {{ recipes|tojson }};</script>
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-7xl"
<div class="tmf-page"
x-data="{
recipes: window.__selectRecipeData,
search: '{{ auto_recipe_code }}',
@@ -3,7 +3,7 @@
{% block title %}{{ _('Riepilogo') }} - {{ recipe.name }}{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="tmf-page">
{# The summary is where an operator lingers: the clock has to be here as well. #}
{% include "components/production_clock.html" %}
@@ -2,7 +2,7 @@
{% block title %}{{ recipe.name }} — {{ _('Task') }} — TieMeasureFlow{% endblock %}
{% block content %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 max-w-5xl">
<div class="tmf-page">
{# The clock follows the operator here too, and brings them back when it expires. #}
{% include "components/production_clock.html" %}
@@ -8,7 +8,7 @@
{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-6"
<div class="tmf-page"
x-data="spcDashboard()"
x-init="init()">