feat: charte graphique des boutons (primary/secondary/danger)

Override CSS scopé aux classes .btn-* (jamais les variables --bs-* racine,
donc alertes/badges inchangés) pour remplacer les couleurs dérivées du thème
Simplex (danger mauve, secondary gris illisible). Trois rôles : la couleur
encode la fonction, le remplissage l'emphase.

- primary terracotta : action principale validante
- secondary gris ardoise : action neutre/alternative (lisible)
- danger rouge #c0392b : destructif (outline par défaut, plein en confirmation)

Audit + conformation des usages :
- compte_admin « Supprimer mon compte » : primary outline -> danger outline
- compte_abonnement « Me désabonner » : outline-primary -> outline-danger
- compte_abonnement « Je suis sûr » : outline-primary -> danger plein

Tests : ajout tests/test_boutons.py (garde de non-régression du primary).
conftest : implémente le hook pytest_setup_options (--headless=new) ; la
fixture chrome_options n'était jamais utilisée par dash.testing, d'où les
fenêtres Chrome qui s'ouvraient pendant les tests.

Specs et plan : docs/superpowers/{specs,plans}/2026-06-30-charte-boutons*.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Colin Maudry
2026-06-30 13:21:24 +02:00
parent d436fd4d9e
commit 0cfafedeef
7 changed files with 567 additions and 25 deletions
+25
View File
@@ -0,0 +1,25 @@
from dash.testing.composite import DashComposite
def _rgb_tuple(css_color: str) -> tuple[int, int, int]:
"""Parse 'rgb(r, g, b)' ou 'rgba(r, g, b, a)' en (r, g, b)."""
inner = css_color[css_color.index("(") + 1 : css_color.index(")")]
parts = [p.strip() for p in inner.split(",")]
return (int(parts[0]), int(parts[1]), int(parts[2]))
def test_btn_primary_is_terracotta(dash_duo: DashComposite):
"""Garde de non-régression : le bouton primaire de l'accueil conserve son
dégradé terracotta et son texte blanc après l'override de la charte."""
from src.app import app
dash_duo.start_server(app)
dash_duo.wait_for_element("a.btn.btn-primary, button.btn.btn-primary", timeout=10)
btn = dash_duo.find_element("a.btn.btn-primary, button.btn.btn-primary")
# Le dégradé terracotta est posé via background-image ; le texte est blanc.
bg_image = btn.value_of_css_property("background-image")
color = btn.value_of_css_property("color")
assert "gradient" in bg_image # dégradé terracotta appliqué
assert _rgb_tuple(color) == (255, 255, 255) # texte blanc