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

BrancheCe qu’elle contient
langLa langue de départ, parmi les dix.
desiredVariablesLes variables à calculer. Absente, la mesure calcule tout ce à quoi elle a droit.
tagsLes étiquettes sous lesquelles vous retrouverez la mesure dans la console.
components.introL’animation d’ouverture.
components.onboardingLes sept écrans d’accueil, et les pastilles de progression.
components.loadStreamL’écran de chargement de la caméra et ses cinq écrans d’erreur.
components.placementL’écran de placement du visage et la façon dont la mesure démarre.
components.resultL’écran de résultat et les cartes par variable.
components.errorL’écran de fin anormale.
components.buttonsLes libellés de boutons employés tout au long du parcours.
components.menu, components.langChoice, components.gotItLes panneaux de la barre d’outils.
components.containersLe 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 avec ignore. 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 :

EmplacementCe que le widget y place
check1, check2Les deux cases de consentement
termsOfUse, privacyLes deux liens de document de l’écran de consentement
toolbar, layersLa barre d’outils, et les panneaux ouverts par-dessus le parcours
toolbarPrevious, toolbarMenuLa flèche de retour et le bouton du menu
gotIt, gotItDismissL’infobulle pointant vers le menu, et son lien de fermeture
menuClose, menuItemsLa croix de fermeture du panneau de menu et sa liste
menuItemIcon, menuItemLabelL’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" }
    }
}
VariableDé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 avec lang: "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.