From 27ae0285a0f57cdbda4cf4fd4a6e79c6045f5bb1 Mon Sep 17 00:00:00 2001 From: Pierre MARTIN Date: Tue, 3 Nov 2026 01:58:16 +0100 Subject: [PATCH] =?UTF-8?q?Charte=20appliqu=C3=A9e=20=C3=A0=20l'interface?= =?UTF-8?q?=20:=20couleurs,=20composants,=20mises=20en=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- web/app.css | 504 ++++++++++++++++++++++++++++++++++++++----------- web/index.html | 22 ++- 2 files changed, 412 insertions(+), 114 deletions(-) diff --git a/web/app.css b/web/app.css index 0ed68b4..216bc94 100644 --- a/web/app.css +++ b/web/app.css @@ -1,81 +1,191 @@ -/* Style de l'application. Palette provisoire et sobre (bois, ivoire, un - * accent vert), en attendant l'identité définitive : toutes les couleurs et - * tailles sont des variables, redéfinies pour le thème sombre. */ +/* Style de l'application, selon la charte graphique (ADR 0010, + * docs/identite/charte/) : les couleurs, tailles et rayons sont les + * variables de tokens.json, sous le même nom, redéfinies pour le thème + * sombre ; les composants reprennent bundle.css. */ + +/* ------------------------------------------------------------ Police */ + +/* Alegreya Sans (SIL Open Font License 1.1, voir fonts/OFL.txt), réduite + * aux caractères latins et convertie en woff2 (voir docs/identite/README.md). + * Embarquée dans le site, donc dans le cache du service worker : elle + * fonctionne hors ligne. */ +@font-face { + font-family: "Alegreya Sans"; + src: url("fonts/AlegreyaSans-Regular.woff2") format("woff2"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Alegreya Sans"; + src: url("fonts/AlegreyaSans-Italic.woff2") format("woff2"); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: "Alegreya Sans"; + src: url("fonts/AlegreyaSans-Medium.woff2") format("woff2"); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Alegreya Sans"; + src: url("fonts/AlegreyaSans-Bold.woff2") format("woff2"); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Alegreya Sans"; + src: url("fonts/AlegreyaSans-ExtraBold.woff2") format("woff2"); + font-weight: 800; + font-style: normal; + font-display: swap; +} + +/* ------------------------------------------------------------ Couleurs */ :root { - --text: #2b211a; - --muted: #6b5d50; - --background: #f7f2e8; - --accent: #2e6b4f; + --background: #f5efe5; + --surface: #fffcf6; + --surface-sunken: #ece3d4; + --text: #2b1d15; + --muted: #6b5c50; + --border: #ded2c0; + --border-strong: #8e7d6a; + --accent: #a8432b; --on-accent: #ffffff; - --border: #cbbfae; - --error: #a3261b; + --accent-soft: #f1d9cf; + --focus: #a8432b; + --error: #8c1c3a; + --verdict-good: #2f6b4f; + --verdict-doubtful: #7e5a0f; + --verdict-bad: #8c1c3a; + --brand-noyer: #4a2e1f; + --brand-ivoire: #f3ead8; + --brand-laiton: #8f6a1c; - --frame: #6b4426; - --bed: #e6cfa6; - --bar: #5c3a20; - --tray: #57371e; - --point-a: #9a6439; - --point-b: #c79b69; - --point-number: #f3e7d0; - --checker-player: #fbf6ea; - --checker-player-edge: #7d6a52; - --checker-computer: #2b221c; - --checker-computer-edge: #0d0a08; - --count-player: #2b211a; - --count-computer: #fbf6ea; - --die-player: #fbf6ea; - --pip-player: #2b211a; - --die-computer: #2b221c; - --pip-computer: #fbf6ea; - --cube: #fbf6ea; - --cube-text: #2b211a; - --highlight: #2e6b4f; - --target: rgba(46, 107, 79, 0.35); - --last-move: #d9822b; + --frame: #4a2e1f; + --bed: #e9dcc4; + --bar: #3f2619; + --tray: #3a2418; + --point-a: #6b4430; + --point-b: #c9ae88; + --point-number: #f3ead8; + --checker-player: #f7f0e2; + --checker-player-edge: #8c7a62; + --checker-computer: #2e1c12; + --checker-computer-edge: #120a05; + --count-player: #2b1d15; + --count-computer: #f3ead8; + --die-player: #f7f0e2; + --pip-player: #2b1d15; + --die-computer: #2e1c12; + --pip-computer: #f3ead8; + --cube: #f7f0e2; + --cube-text: #2b1d15; + --highlight: #a8432b; + --target: rgba(168, 67, 43, 0.32); + /* Dernier coup joué (marques et traces) : le laiton, rien d'autre. */ + --last-move: var(--brand-laiton); - --font-size: 17px; + /* Numéros des flèches. La charte les pose sur le cadre (point-number, + * ivoire) ; notre plateau les pose sur le fond, où l'ivoire ne se lirait + * pas : texte secondaire en thème clair (4,7:1 sur bed), point-number en + * thème sombre (6,3:1). */ + --point-label: var(--muted); + + --shadow-raised: 0 1px 2px rgba(43, 29, 21, 0.1); + + --font-sans: "Alegreya Sans", "Gill Sans", "Segoe UI", system-ui, sans-serif; + + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-6: 24px; + --space-8: 32px; --button-height: 48px; - --radius: 8px; --page-width: 40rem; + --radius-sm: 6px; + --radius-md: 12px; + --radius-pill: 999px; + + /* Hauteur de la barre haute : boutons de 44 px et marges. */ + --topbar-height: 60px; } @media (prefers-color-scheme: dark) { :root { - --text: #ece4d6; - --muted: #b3a796; - --background: #1b1714; - --accent: #6fc3a0; - --on-accent: #10221a; - --border: #4d443b; - --error: #f08a7e; + --background: #17110d; + --surface: #221913; + --surface-sunken: #2c211a; + --text: #efe5d5; + --muted: #a99886; + --border: #3a2d24; + --border-strong: #7d6b5b; + --accent: #d0694b; + --on-accent: #1a110c; + --accent-soft: #4a2519; + --focus: #e58a6e; + --error: #f08fa6; + --verdict-good: #7ccba4; + --verdict-doubtful: #e0b04f; + --verdict-bad: #f08fa6; + --brand-laiton: #d6a64a; - --frame: #3a2616; - --bed: #6d5136; - --bar: #2f1f12; - --tray: #2c1d11; - --point-a: #4a3120; - --point-b: #8b6943; - --point-number: #d9cbb4; - --checker-player: #efe6d2; + --frame: #2a1a10; + --bed: #4a3626; + --bar: #24170e; + --tray: #22150d; + --point-a: #3a2519; + --point-b: #7e6142; + --point-number: #cdbfa8; + --checker-player: #ede3cf; --checker-player-edge: #8e7c64; - --checker-computer: #17110d; + --checker-computer: #140d09; --checker-computer-edge: #a8957d; - --highlight: #7fd6b0; - --target: rgba(127, 214, 176, 0.35); - --last-move: #f0a050; + --count-player: #2b1d15; + --count-computer: #f3ead8; + --die-player: #ede3cf; + --pip-player: #2b1d15; + --die-computer: #140d09; + --pip-computer: #ede3cf; + --cube: #ede3cf; + --cube-text: #2b1d15; + --highlight: #e07a5a; + --target: rgba(224, 122, 90, 0.38); + + --point-label: var(--point-number); + + --shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.4); } } +/* -------------------------------------------------------------- Page */ + * { box-sizing: border-box; } +html { + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; +} + body { margin: 0 auto; max-width: var(--page-width); - padding: 0 0.5rem 1rem; - font: var(--font-size) / 1.45 system-ui, sans-serif; + padding: 0 var(--space-4) var(--space-6); + font-family: var(--font-sans); + font-size: 17px; + line-height: 25px; color: var(--text); background: var(--background); } @@ -84,78 +194,148 @@ body { display: none !important; } +a { + color: var(--accent); + text-underline-offset: 3px; +} + +/* Anneau de focus clavier, sur tout élément interactif. */ +:focus-visible { + outline: 3px solid var(--focus); + outline-offset: 2px; +} + +/* -------------------------------------------------------- Barre haute */ + .topbar { display: flex; - gap: 0.5rem; + gap: var(--space-2); align-items: center; - padding: 0.5rem 0; + min-height: var(--topbar-height); + padding-block: var(--space-2); } -.topbar h1 { +.topbar .brand { flex: 1; + display: flex; + align-items: center; + min-width: 0; margin: 0; - font-size: 1.25rem; } +.topbar .brand img { + display: block; + width: auto; + height: 32px; +} + +/* ------------------------------------------------------------ Boutons */ + +/* Ronds comme des pions ; un seul bouton principal (brique) par écran, + * les autres en contour. */ button { min-height: var(--button-height); - padding: 0 1rem; + padding: 0 20px; font: inherit; - font-weight: 600; + font-weight: 700; color: var(--on-accent); background: var(--accent); - border: 1px solid var(--accent); - border-radius: var(--radius); + border: 2px solid var(--accent); + border-radius: var(--radius-pill); + cursor: pointer; + /* Pas de délai ni de zoom sur un double toucher. */ + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; } button.secondary { color: var(--text); background: transparent; - border-color: var(--border); + border-color: var(--border-strong); } -.topbar button { - min-height: 40px; - padding: 0 0.75rem; - font-size: 0.9rem; +@media (hover: hover) { + button.secondary:hover:not(:disabled) { + background: var(--surface-sunken); + } +} + +/* Retour visuel au toucher. */ +button:active:not(:disabled) { + transform: translateY(1px); } button:disabled { opacity: 0.45; + cursor: default; +} + +.topbar button { + min-height: 44px; + padding: 0 14px; + font-size: 15px; + line-height: 20px; } .actions { display: flex; flex-wrap: wrap; - gap: 0.5rem; + gap: var(--space-2); } .actions button { - flex: 1; + flex: 1 1 auto; } +/* --------------------------------------------- Messages sous le plateau */ + +/* Compte de points : information secondaire, discrète. */ .pips { - margin: 0.25rem 0 0; - font-size: 0.85rem; + margin: var(--space-2) 0 0; + font-size: 15px; + line-height: 20px; color: var(--muted); text-align: center; + font-variant-numeric: tabular-nums; } -.info, -.prompt { - min-height: 1.45em; - margin: 0.4rem 0; -} - +/* Ce qui vient de se passer (coup de l'ordinateur, jet d'ouverture) : + * secondaire, mais lisible. */ .info { + min-height: 25px; + margin: var(--space-3) 0 0; color: var(--muted); } -.prompt { - font-weight: 600; +.info:empty { + margin-top: 0; } -/* L'ordinateur réfléchit : trois points qui apparaissent l'un après l'autre. */ +/* La consigne du moment : ce qui doit ressortir de l'écran. Fond d'accent + * léger quand c'est au joueur, neutre pendant que l'ordinateur réfléchit. */ +.prompt { + min-height: calc(25px + 2 * var(--space-3)); + margin: var(--space-3) 0; + padding: var(--space-3) var(--space-4); + font-weight: 700; + background: var(--accent-soft); + border-radius: var(--radius-md); +} + +.prompt.thinking, +body.busy .prompt { + font-weight: 400; + background: var(--surface-sunken); +} + +/* Fin de partie : le résultat en titre. */ +#game:has(#again:not([hidden])) .prompt { + font-size: 24px; + line-height: 30px; +} + +/* L'ordinateur réfléchit : trois points qui apparaissent l'un après + * l'autre (la seule animation en boucle). */ .prompt.thinking::after { content: ""; animation: thinking 1.2s steps(4, end) infinite; @@ -168,6 +348,17 @@ button:disabled { 75% { content: "..."; } } +@media (prefers-reduced-motion: reduce) { + .prompt.thinking::after { + animation: none; + content: "…"; + } + + button:active:not(:disabled) { + transform: none; + } +} + /* ------------------------------------------------------------ Plateau */ .board { @@ -176,7 +367,20 @@ button:disabled { height: auto; aspect-ratio: 640 / 520; user-select: none; + -webkit-user-select: none; + touch-action: manipulation; -webkit-tap-highlight-color: transparent; + font-family: var(--font-sans); +} + +/* Téléphone en portrait : le plateau, héros de l'écran, prend la largeur + * presque entière (8 px de marge au lieu de 16) ; chaque pixel compte pour + * toucher un pion. */ +@media (max-width: 40rem) and (orientation: portrait) { + .board { + width: calc(100% + 2 * (var(--space-4) - var(--space-2))); + margin-inline: calc(var(--space-2) - var(--space-4)); + } } .board-frame { fill: var(--frame); } @@ -187,8 +391,9 @@ button:disabled { .point-b { fill: var(--point-b); } .point-number { - fill: var(--muted); + fill: var(--point-label); font-size: 13px; + font-weight: 500; text-anchor: middle; } @@ -206,20 +411,31 @@ button:disabled { stroke: var(--checker-computer-edge); } +/* Filet intérieur des pions (bundle.css), prêt pour le jour où le plateau + * le dessine. */ +.checker-ring { + fill: none; + stroke-width: 1.5; +} + +.ring-player { stroke: var(--checker-player-edge); } +.ring-computer { stroke: var(--checker-computer-edge); } + .checker.movable { stroke: var(--highlight); - stroke-width: 4; + stroke-width: 3; } .checker.selected { stroke: var(--highlight); - stroke-width: 7; + stroke-width: 6; } .checker-count { font-size: 18px; font-weight: 700; text-anchor: middle; + font-variant-numeric: tabular-nums; } .count-player { fill: var(--count-player); } @@ -228,8 +444,8 @@ button:disabled { .target-zone { fill: var(--target); } .last-move { fill: var(--last-move); } -.die-player { fill: var(--die-player); stroke: var(--checker-player-edge); } -.die-computer { fill: var(--die-computer); stroke: var(--checker-computer-edge); } +.die-player { fill: var(--die-player); stroke: var(--checker-player-edge); stroke-width: 1.5; } +.die-computer { fill: var(--die-computer); stroke: var(--checker-computer-edge); stroke-width: 1.5; } .pip-player { fill: var(--pip-player); } .pip-computer { fill: var(--pip-computer); } @@ -238,7 +454,7 @@ button:disabled { .cube-value { fill: var(--cube-text); font-size: 17px; - font-weight: 700; + font-weight: 800; text-anchor: middle; } @@ -249,70 +465,138 @@ button:disabled { /* --------------------------------------------------------------- Aide */ +.help { + max-width: var(--page-width); + margin: 0 auto; +} + .help h2 { - margin-top: 1.5rem; - font-size: 1.2rem; + margin: var(--space-8) 0 var(--space-2); + font-size: 24px; + line-height: 30px; + font-weight: 700; } .help h3 { - margin-bottom: 0.25rem; - font-size: 1rem; + margin: var(--space-6) 0 var(--space-1); + font-size: 20px; + line-height: 26px; + font-weight: 700; +} + +.help p { + margin: var(--space-2) 0; } .steps { - padding-left: 1.25rem; + margin: var(--space-2) 0; + padding-left: var(--space-6); +} + +.steps li + li { + margin-top: var(--space-1); +} + +.steps li::marker { + color: var(--muted); +} + +/* Glossaire : une liste de définitions, sur une carte. */ +.glossary { + margin: var(--space-3) 0 0; + padding: var(--space-1) var(--space-4); + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-md); + box-shadow: var(--shadow-raised); } .glossary dt { - margin-top: 0.6rem; - font-weight: 600; + padding-top: var(--space-3); + font-weight: 700; } .glossary dd { margin: 0; + padding-bottom: var(--space-3); +} + +.glossary dd + dt { + border-top: 1px solid var(--border); } .about { - margin-top: 2rem; - font-size: 0.85rem; + margin-top: var(--space-8); + font-size: 15px; + line-height: 20px; color: var(--muted); } +/* Une cible tactile confortable pour le lien. */ .about a { - color: var(--accent); + display: inline-block; + padding-block: 12px; } /* ------------------------------------------------------- Mode paysage */ -/* Téléphone tourné : le plateau prend toute la hauteur disponible, et les - * messages et boutons passent dans une colonne à sa droite. */ +/* Téléphone tourné : le plateau prend toute la hauteur de l'écran, à + * gauche ; la barre haute, les messages et les boutons passent dans une + * colonne à sa droite. */ @media (orientation: landscape) and (max-height: 40rem) { body { max-width: none; + padding: 0 var(--space-4); } - .topbar { - padding: 0.25rem 0; - } - - #game { + /* Le jeu en deux colonnes : #game laisse ses enfants dans la grille de + * la page. Quand l'aide est affichée, #game est caché, la première + * colonne n'a plus de largeur et l'aide prend toute la place. */ + body { display: grid; - grid-template-columns: auto minmax(12rem, 1fr); - grid-template-rows: auto auto auto 1fr; - column-gap: 1rem; + grid-template-columns: auto minmax(13rem, 1fr); + grid-template-rows: auto auto auto auto auto 1fr; + column-gap: var(--space-4); align-items: start; } + #game { + display: contents; + } + + .topbar { + grid-column: 2; + grid-row: 1; + min-height: 0; + padding-block: var(--space-2) 0; + } + .board { - grid-row: 1 / 5; + grid-column: 1; + grid-row: 1 / -1; width: auto; - /* Hauteur de l'écran moins la barre du haut (boutons de 40 px). */ - height: calc(100vh - 3.5rem); - height: calc(100dvh - 3.5rem); + height: calc(100vh - 2 * var(--space-2)); + height: calc(100dvh - 2 * var(--space-2)); max-width: 70vw; + margin-block: var(--space-2); + } + + .pips, + .info, + .prompt, + .actions { + grid-column: 2; } .pips { text-align: left; } + + .actions { + margin-bottom: var(--space-2); + } + + .help { + grid-column: 1 / -1; + } } diff --git a/web/index.html b/web/index.html index 63ad803..25d2856 100644 --- a/web/index.html +++ b/web/index.html @@ -4,17 +4,31 @@ - + Ustagammon - + + + + + - +
-

Ustagammon

+ +

+ + + + + Ustagammon + +