Proto HTML/CSS → Uniwind¶
Procédure issue de la modale d'intention Levée 2026 (modules/fundraising-v2). Le proto est la
source de vérité visuelle ; le code du monorepo (API, providers, Modal uimmo) reste intouché.
1. Extraire les règles, ne pas les deviner¶
Le CSS du proto est minifié sur quelques lignes de 20 000+ caractères : un Read ne suffit pas.
Extraire par sélecteur avec un script, en gardant toutes les règles d'un sélecteur (la
dernière déclarée gagne, et les overrides .v2-responsive … / .device .sheet … sont fréquents) :
python3 - <<'PY'
import re; from pathlib import Path
text = Path('<proto>/prototype/index.html').read_text(errors='ignore')
for sel in ['.amount-wrap', '.quick-amounts', '.field-help']:
for m in re.finditer(r'[^{}]*' + re.escape(sel) + r'[^{}]*\{[^{}]*\}', text):
print(re.sub(r'\s+', ' ', m.group(0)).strip())
PY
- Le markup vit dans les fonctions JS (
amountHTML,renderV2IntentConfirmation…) : chercherfunction <nom>et lire le template literal. Les overrides V2 (design/participation-v2.js) patchent ce HTML par.replace(...)— lire les deux. - Une couleur absente sur l'élément est héritée : remonter jusqu'à
.sheet{color:var(--sheet-text)}oup{color:var(--muted)}, puis résoudre les variables (--sheet-text:#f3f7fc,--sheet-muted:#b8cbdc). - Les fichiers
design/*.css/design/*.jssont la version la plus récente ;prototype/index.htmlles inline. En cas de doute, ledesign/gagne.
2. Convertir les unités — le piège n° 1¶
Uniwind ≠ CSS. Une valeur arbitraire sans unité n'est pas un pixel partout :
| CSS proto | Uniwind | Pourquoi |
|---|---|---|
font-size:13px |
text-[13px] (ou variant-text-sm) |
text-[13] est écrasé par cn() — tailwind-merge le confond avec text-[#hex] |
line-height:1.6 |
leading-[21px] (13 × 1.6, arrondi) |
leading-[21] = multiplicateur sur web → 21 × 14 px = 294 px |
letter-spacing:-1px |
tracking-[-1px] |
idem |
margin:12px 0 20px |
mt-lg mb-2xl |
mb-[18] sort sur l'échelle Tailwind (énorme). Tokens : 3xs2 2xs4 xs6 sm8 md10 lg12 xl16 2xl20 3xl24 4xl32 |
width:65px / border-radius:12px |
size-[65px] / rounded-[12px] |
sans unité marche, mais garder px pour la cohérence |
#93a6be5c (hex alpha) |
border-[#93a6be5c] |
supporté tel quel, ne pas convertir en rgba |
Variants typographiques (variant-text-sm = 12/18.5, variant-text-md = 14/20.9,
variant-heading-2xs = 14 Bold, variant-heading-xs = 16 Bold) : projects/common/front/theme/src/fonts.ts.
Les préférer quand la valeur proto est à ±1 px.
3. Éléments qui n'ont pas d'équivalent direct¶
<input>numérique : ne pas recréer un champ custom (focus ring RN-web, etc.). RéutiliserNumberInputWithControllersuimmo (@bricks-common/uimmo/components/inputs), comme dansFundraisingV2IntentionModal/steps/IntentionAmountStep.tsx.- Bouton proto (
.primary,.secondary,.link-btn) :Buttonuimmo (@bricks-common/uimmo/components/buttons), pas unPressabledédié — cf. les steps d'intention et le dock. - Animation
ambient-glow(bande lumineuse 9 s) : Reanimated, pas leShineEffectuimmo du solde boosté.Easing.bezierFn(pasEasing.bezier, qui n'est pas appelable dans un worklet). Modèle :fundraising-v2/components/atoms/AmbientGlow.tsx. - Rangée de chips
flex:1 0 auto+overflow-x:auto:ScrollView horizontalaveccontentContainerClassName="flex-grow gap-xs"et chaque item enshrink-0 grow+whitespace-nowrap.flex-1seul rétrécit et tronque. :focus-withinsur le wrapper : stateisFocusedlocal, classe conditionnelle sur la bordure.
4. Vérifier dans Storybook, pas à l'œil¶
- Story sans
playpour l'état manuel, flows avecplaypour chaque branche métier. - Ouvrir l'iframe (
/iframe.html?id=<story>&viewMode=story) en viewport desktop (Emulation.setDeviceMetricsOverride1280 px) — le canvas par défaut est mobile. - Comparer les valeurs calculées, pas le screenshot :
getComputedStyle(el).fontSize / lineHeight / marginTop. UnlineHeightà 3 chiffres = unité oubliée. - « No Preview » après HMR = Fast Refresh cassé (export non-composant dans un fichier de composant,
ex.
INTENTION_AMOUNT_SUGGESTIONS). Recharger la page, pas chercher un bug.
5. Ce qui ne se copie pas¶
- Les textes proto passent par
fr.json(modules.fundraisingV2.*), jamais en dur. - Les CTA de sortie (
data-action="hub",v2-topup-start) deviennent des callbacks props ; la modale se ferme avant d'ouvrir la suivante ou de naviguer. - Les flags de scénario du proto (
kycReady(),state.wallet) deviennent des props booléennes /Centscalculées dans le container (needsKyc,currentBalance).