Personnalisation
Chaque écran, chaque texte et chaque couleur est une option — changez ce dont vous avez besoin sans rien dupliquer
Toute l’apparence du widget est un arbre d’options. Vous passez un objet partiel ; il est fusionné par-dessus les valeurs par défaut, puis validé. Rien n’est gabarisé, rien n’est injecté, et il n’y a aucune feuille de style à surcharger — le widget vit dans un shadow root précisément pour que le CSS de votre page ne puisse pas l’atteindre.
const instance = SaphereScan.create("#scan", {
lang: "fr",
components: {
onboarding: {
onboarding3: { ignore: false } // demander les informations personnelles
},
placement: {
start: { mode: "manual" } // un bouton de départ au lieu d'un décompte automatique
}
},
proxy: { retrieveAccessToken: { strategy: "delegate", url: "/api/saphere-token" } }
})
La forme de l’arbre
| Branche | Ce qu’elle contient |
|---|---|
lang | La langue de départ, parmi les dix. |
desiredVariables | Les variables à calculer. Absente, la mesure calcule tout ce à quoi elle a droit. |
tags | Les étiquettes sous lesquelles vous retrouverez la mesure dans la console. |
components.intro | L’animation d’ouverture. |
components.onboarding | Les sept écrans d’accueil, et les pastilles de progression. |
components.loadStream | L’écran de chargement de la caméra et ses cinq écrans d’erreur. |
components.placement | L’écran de placement du visage et la façon dont la mesure démarre. |
components.result | L’écran de résultat et les cartes par variable. |
components.error | L’écran de fin anormale. |
components.buttons | Les libellés de boutons employés tout au long du parcours. |
components.menu, components.langChoice, components.gotIt | Les panneaux de la barre d’outils. |
components.containers | Le cadre dans lequel chaque écran s’inscrit. |
`desiredVariables` est un levier de coût
Ce n’est pas une préférence d’affichage. Chaque variable demandée déclenche son propre traitement, de sorte qu’en demander moins coûte moins cher. Ne l’omettez que si vous voulez véritablement tout ce à quoi le compte client a droit.Retirer un écran
La plupart des écrans portent un drapeau ignore. Posez-le et l’écran n’est pas simplement masqué — il est absent des routes, donc inatteignable, et il ne compte pas dans la numérotation de l’onboarding.
components: {
intro: { ignore: true }, // pas d'animation d'ouverture
onboarding: {
onboarding3: { ignore: false }, // informations personnelles — désactivé par défaut
onboarding5: { ignore: true }, // passer la consigne « visage découvert »
onboarding6: { ignore: true } // passer la consigne d'éclairage
}
}
Les sept écrans d’onboarding sont, dans l’ordre : l’introduction, la porte de consentement, le formulaire d’informations personnelles, et quatre écrans de consigne (posture, visage découvert, éclairage, immobilité). Seul onboarding3 est désactivé par défaut ; les autres sont actifs.
Retirer les consignes coûte en qualité de signal
Les écrans de consigne existent parce que la posture, l’éclairage et l’immobilité déterminent si les trente secondes produisent un signal exploitable. Les retirer raccourcit le parcours et augmente le taux de mesures qui se terminent sans résultat.Atteindre un écran par son chemin est également concerné — voyez la note sur screen:enter portant name en plus de path dans la référence des événements.
Deux façons de décrire un écran
Laquelle un écran donné emploie est décidé par le fait qu’il soit un formulaire ou non.
Un arbre d’éléments, sous <écran>.content.<langue>. Les écrans d’onboarding 1, 2 et 4 à 7, les cinq erreurs de caméra, l’écran de fin anormale et l’en-tête du sélecteur de langue fonctionnent tous ainsi. Vous décrivez le balisage lui-même.
Des chaînes localisées, un champ par langue. Les libellés de boutons, le menu de la barre d’outils et l’onboarding 3 fonctionnent ainsi. Ces écrans lient leurs champs à un formulaire ou construisent leur liste depuis un service, de sorte qu’un arbre d’éléments arbitraire ne peut pas les décrire.
Modifier un arbre d’éléments
Un élément est { type, ... }, où type vaut div, p, span, text, a, image, svg-component ou projection. Les conteneurs portent un tableau children.
components: {
onboarding: {
onboarding4: {
content: {
fr: {
type: "div",
attrs: { class: "d-flex-column-center text-center" },
children: [
{ type: "image", src: "001.webp" },
{ type: "p", children: [{ type: "text", value: "Installez-vous ainsi." }] }
]
}
}
}
}
}
Seuls div, p, span et a acceptent des enfants. Un type non autorisé à cette position est refusé par la validation plutôt qu’ignoré en silence.
a est le seul élément qui porte un attribut propre : href, restreint à http(s):, mailto: et tel: par la validation, et assaini de nouveau par le moteur de rendu.
La règle de fusion des tableaux
Les options sont fusionnées par une fusion profonde qui combine les tableaux par indice. Trois conséquences :
- Remplacer un enfant fonctionne. Donnez
children[0]et le premier enfant par défaut est remplacé. - Ajouter fonctionne. Donnez plus d’entrées que n’en compte le défaut et elles sont ajoutées.
- Raccourcir ne fonctionne pas. Vous ne pouvez pas retirer un enfant par défaut en donnant un tableau plus court — les valeurs par défaut au-delà de votre dernier indice survivent en dessous.
Pour changer le deuxième enfant sans toucher au premier, vous devez laisser un trou — et les trous doivent être explicites :
children: [undefined, { type: "p", children: [{ type: "text", value: "Remplacé." }] }]
Pour retirer un nœud, masquez-le
Puisqu’un arbre ne peut pas être raccourci, la façon de supprimer un nœud par défaut est de lui donner un contenu vide — ou de retirer l’écran entier avecignore. C’est une limite connue du modèle de fusion, non un oubli du schéma.Emplacements de projection
Certaines parties d’un écran ne peuvent pas être décrites par un arbre : tout ce qui porte un clic, un signal ou une boucle. Celles-là apparaissent dans l’arbre sous forme de nœud projection, et le widget remplit l’emplacement avec la commande réelle.
{ type: "projection", slot: "toolbarMenu" }
C’est ainsi que vous déplacez le bouton du menu, les cases de consentement ou la flèche de retour à l’intérieur d’une mise en page que vous décrivez, sans avoir à réimplémenter ce qu’ils font.
La liste des emplacements est close. Ceux que vous emploierez vraisemblablement :
| Emplacement | Ce que le widget y place |
|---|---|
check1, check2 | Les deux cases de consentement |
termsOfUse, privacy | Les deux liens de document de l’écran de consentement |
toolbar, layers | La barre d’outils, et les panneaux ouverts par-dessus le parcours |
toolbarPrevious, toolbarMenu | La flèche de retour et le bouton du menu |
gotIt, gotItDismiss | L’infobulle pointant vers le menu, et son lien de fermeture |
menuClose, menuItems | La croix de fermeture du panneau de menu et sa liste |
menuItemIcon, menuItemLabel | L’icône et le libellé d’une entrée de menu |
Un nom d’emplacement absent de la liste est une erreur de validation, non un trou silencieux à l’écran.
Couleurs
Chaque variable de résultat porte un dégradé à deux arrêts, et ce sont les couleurs que vous voudrez le plus vraisemblablement aligner sur une marque :
components: {
result: {
hr: { color01: "#22C55E", color02: "#84CC16" },
healthScore: { color01: "#8B5CF6", color02: "#A855F7" }
}
}
| Variable | Dégradé par défaut |
|---|---|
hr — fréquence cardiaque | #22C55E → #84CC16 |
br — fréquence respiratoire | #F59E0B → #F97316 |
hrv — variabilité cardiaque | #06B6D4 → #0EA5E9 |
strs — stress | #F59E0B → #EAB308 |
bp — pression artérielle | #3B82F6 → #06B6D4 |
bpClass — risque tensionnel | #10B981 → #14B8A6 |
physicalAge — âge apparent | #14B8A6 → #10B981 |
bmi — indice de masse corporelle | #6366F1 → #8B5CF6 |
faceBmi — IMC estimé du visage | #EC4899 → #D946EF |
healthScore — score cardiovasculaire | #8B5CF6 → #A855F7 |
Les pastilles de progression de l’onboarding ont leur propre paire :
components: { onboarding: { slider: { colors: { activated: "#3B82F6", inactive: "#D1D5DB" } } } }
Le score de santé n'est délibérément pas coloré selon sa valeur
Il emploie une seule teinte, non un feu tricolore. Le score est une estimation qui ne remplace pas un examen médical, et le teinter selon sa valeur le présenterait comme un verdict.Langues, et sens de lecture
Dix sont intégrées : arabe (ar), allemand (de), anglais (en), espagnol (es), français (fr), italien (it), polonais (pl), portugais (pt), portugais du Brésil (pt_BR) et turc (tr).
lang fixe la langue de départ. Absente, le widget lit celle du navigateur, avec l’anglais en repli. L’utilisateur peut en changer en cours de parcours depuis la barre d’outils, ce qui émet ui:language-changed.
Chaque option de texte est un objet par langue, de sorte qu’une personnalisation s’écrit une fois par langue que vous prenez en charge :
components: {
buttons: {
title: { en: "Continue", fr: "Continuer", de: "Weiter" }
}
}
Les langues que vous ne surchargez pas conservent leurs valeurs par défaut, ce qui vous permet de ne traduire que les chaînes que vous avez réellement changées.
De droite à gauche
L’arabe s’affiche de droite à gauche, et toute la mise en page se retourne — la barre d’outils, les chevrons de retour, les espacements. Cela découle d’un unique attribut dir sur l’hôte du shadow root, de sorte que cela s’applique à tout d’un coup.
Deux conséquences pour une personnalisation :
Les espacements sont logiques, non physiques. Les options de style exposent marginStart / marginEnd / paddingStart / paddingEnd, jamais marginLeft. C’est délibéré : une personnalisation doit décrire une mise en page valable dans les dix langues, et un côté physique casserait silencieusement l’arabe.
Les animations décoratives restent de gauche à droite. Le balayage de l’ECG conserve son sens dans toutes les langues, parce qu’un tracé d’ECG se lit de gauche à droite par convention médicale.
Testez en arabe
Si vous personnalisez la mise en page si peu que ce soit, chargez le widget une fois aveclang: "ar". C’est le moyen le plus rapide d’attraper un côté codé en dur.Comment la fusion est validée
L’objet partiel que vous passez est fusionné par-dessus les valeurs par défaut, puis l’arbre entier est validé au moment du create().
- Une clé mal orthographiée est ignorée silencieusement. Elle ne lèvera pas, et elle ne prendra pas effet — si une personnalisation semble ignorée, vérifiez d’abord l’orthographe.
- Une valeur de forme incorrecte lève, avec le chemin du champ fautif.
- Un type d’élément non autorisé à sa position est une erreur de validation.
La validation se fait de façon synchrone dans create(), avant que quoi que ce soit ne soit monté, de sorte que vous l’apprenez sur votre propre machine plutôt que dans le parcours d’un utilisateur.