--- name: frontend description: >- Conventions front (Slick, Preact, CSS structurel, islands). A utiliser des que l'on ecrit, modifie ou review du HTML, CSS, JSX/TSX, pages, templates, islands, components ou styles. --- # Front Appliquer ces regles a chaque tache front. Lire aussi [css-order.md](css-order.md) avant d'ecrire du CSS. ## Langue et prose - Tout le contenu UI, commentaires utiles, labels, titres, messages et textes visibles en francais. - Ne jamais utiliser le tiret cadratin (`—`). Preferer la virgule, les deux-points, les parentheses ou un tiret simple `-`. ## Stack et dossiers Stack: Deno, `@webtools/slick-server`, Preact, CSS dans `static/styles/`. | Dossier | Role | | ---------------- | ---------------------------------------------------- | | `pages/` | Routes SSR | | `templates/` | Layouts | | `islands/` | UI interactive hydratee | | `components/` | UI statique ou logique pure, sans hydratation propre | | `static/styles/` | CSS (tokens, reset, pages, templates) | Reutiliser `tokens.css` (variables) et `reset.css`. Ne pas reinventer couleurs, rayons, espaces ou typos deja tokens. ## Polices et assets - Importer les polices via `@import` dans le CSS (`tokens.css` ou fichier de zone), pas via `` ou `preconnect` dans `head`. - Laisser `head: null` sauf besoin exceptionnel (meta specifique, script tiers, etc.). ## HTML structurel ### Slick et `#root` Le body rendu d'un template est deja injecte dans `div#root` par Slick. Inutile d'ajouter une div principale de coque (`div#auth-shell`, etc.) : utiliser un fragment `<>...` quand plusieurs racines suffisent. Le slot de page est `div#app` (pas un `main`) : Slick y injecte le body de la page. Les templates portent deja `header` (et `nav` si besoin). Les pages injectees dans `#app` n'utilisent **pas** `main`, `header`, `footer`, `nav` : `section` / `aside` / `article` / `div`. `header` / `footer` restent valides **dans un ``**. Styler la coque via `#root` dans le CSS du template concerne. ### Semantique et wrappers 1. `main`, `header`, `footer`, `nav` : seulement dans le template (une fois). Pas de doublon dans la page injectee. Le slot du template est `div#app`. 2. Les pages injectent leur contenu dans `div#app` avec `section`, `aside`, `article`, puis elements semantiques ou `div` / `span` si besoin. 3. Preferer une hierarchie claire et semantique plutot que des wrappers inutiles. 4. Eviter les conteneurs sans but precis : pas de `nav > ol` si `ol` seul suffit (choisir l'un ou l'autre), pas de `div` autour d'un `h1` + `p` deja adjacents, etc. 5. Factoriser: pas de duplication de markup; extraire un component ou une island selon le cas. Exemple template (slot Slick) : ```tsx
{/* contenu page */}
; ``` Exemple page avec aside : ```tsx
...
; ``` Exemple page simple : ```tsx
...
; ``` ## CSS structurel ### Selecteurs - **Conteneur de page ou zone** : un `id` sur la coque unique (`#register`, `#billing`). - **Balise unique dans le template** : pas besoin d'`id` si l'element n'existe qu'une fois (`aside`, `nav`, `header` dans ce template). Utiliser le selecteur de type directement (`aside > h1`, `nav > a`). - **Motif reutilisable** : `class` sur le conteneur racine du motif (`.field`, `.btn`, `.brand`). - **Enfants d'un conteneur identifie** : eviter les `id` enfants. Preferer le markup semantique (`h1`, `p`, `form`, `fieldset`) et des selecteurs structurels. - A l'interieur d'un conteneur, **pas** de classes partout. Styler via cascade et selecteurs structurels: ```css #register > article > form > fieldset > label > span { ... } aside > h1 { ... } aside > ol > li[aria-current="step"] > p { ... } .field > input { ... } ``` - Factoriser les styles partages en classes courtes; eviter les utilitaires type Tailwind. - Un fichier CSS par page ou zone, declare dans `styles` du template/page. ### Fichiers et specificite L'ordre dans `styles` compte: a specificite egale, le fichier charge **plus tard** gagne. Pour masquer une zone, le selecteur doit battre le `display` du fichier de zone. Preferer un enfant de la coque: ```css #page > #panel { display: none; } ``` pas `#panel { display: none }` si `#panel { display: flex }` est declare plus loin. ### Layout - Preferer **flexbox** par defaut. - Utiliser **grid** seulement quand c'est clairement mieux (grilles 2D, alignements complexes). - Eviter `float`, et les proprietes layout exotiques ou fragiles (`position` absolu partout, hacks, etc.) sauf besoin reel. ### Ordre des proprietes Toujours le meme ordre de groupes. Mieux vaut declarer explicitement (ex. `flex-direction: row`) que d'omettre. Details et liste complete: [css-order.md](css-order.md). ```css .box { width: 100%; height: auto; max-width: 28rem; color: var(--color-text); font-size: var(--text-md); font-weight: 400; font-family: var(--font-sans); line-height: 1.5; text-align: left; text-transform: none; display: flex; flex-direction: row; justify-content: flex-start; align-items: center; flex-wrap: nowrap; gap: var(--space-3); margin: 0; padding: var(--space-4); overflow: visible; background: var(--color-bg-elevated); border: 1px solid var(--color-border); border-radius: var(--radius-md); } ``` ## Islands vs components | Besoin | Emplacement | | --------------------------------------------------------------- | ------------- | | Element interactif (etat, events, formulaires client, signals) | `islands/` | | Markup / UI non hydratee, ou logique UI sans hydratation propre | `components/` | Regles strictes: 1. Une island = racine d'interactivite. Export default Preact depuis `islands/`. 2. Les components peuvent etre dynamiques (props, rendu conditionnel) mais **doivent etre importes depuis une island** (ou depuis une page/template s'ils restent 100% statiques SSR). 3. **Interdit**: importer une island depuis une autre island. 4. Pages et templates importent les islands; les islands importent des components, jamais l'inverse pour l'hydratation. 5. Minimiser le JS client: ne pas hydrater ce qui peut rester HTML statique. ```txt page / template -> island (interactive) -> component (sous-UI, factorisee) -> component (statique SSR ok) ``` ## Navigation Interdit: `location.reload()`, `location.assign()`, `location.href =`. - Changer de page: `Slick.redirect(url)` (`@webtools/slick-client`). - Rester sur la page apres une mutation: mettre a jour le DOM et les signals (conserver media, scroll, etat local). - Lien interne: `` suffit (Slick intercepte). ## UX - Liste / catalogue: la liste est le contenu principal. Creation et edition dans un ``, pas un formulaire toujours visible a cote. - Champs de saisie vides: `placeholder` (exemples courts). - "Reinitialiser" applique tout de suite. Pas besoin d'une seconde confirmation du type Valider. - Un filtre ou etat actif doit se voir (compteur, pastille, `aria-pressed`). - Ne pas garder de controle UI sans usage reel. ## Factorisation - Extraire des qu'un motif se repete (markup, CSS, logique). - Preferer un component/island clair plutot que copier-coller. Deux ecrans identiques = une island parametree, pas deux. - CSS: classes partagees pour les motifs; ids pour les coques de page. - Props et API internes courtes; pas d'abstractions prematurees inutiles. - Reutiliser tokens et styles existants avant d'en ajouter. ## Checklist avant de livrer - [ ] Textes FR, aucun tiret cadratin - [ ] Polices via `@import` CSS, pas de `` font dans `head` - [ ] Pas de div coque inutile dans le template (`#root` suffit) - [ ] `div#app` dans le template (slot Slick), pas de `main` sur le slot - [ ] Pas de `main` / `header` / `footer` / `nav` dans la page injectee (deja dans le template) - [ ] Balises uniques sans `id` superflu; pas d'`id` sur les enfants d'une coque - [ ] Markup semantique (`h1`, `p`, `ol`) plutot que `id` descriptifs - [ ] Pas de wrappers inutiles (`nav > ol`, `div` autour de `h1` + `p`, etc.) - [ ] Ids sur conteneurs de page, classes sur motifs reutilisables - [ ] CSS structurel dans les sous-arbres (pas de classes sur chaque enfant) - [ ] Proprietes CSS dans l'ordre defini, flex explicite - [ ] Interactif = island; pas d'import island -> island - [ ] Factorise, tokens reutilises, peu de JS client - [ ] Pas de reload hard; mutation sur place ou `Slick.redirect` - [ ] Placeholders; reinitialiser applique sans etape de plus - [ ] Responsive: palier intermediaire