@zevra/ui 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DECISIONS.md +123 -0
- package/dist/content.d.ts +24 -5
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +21 -5
- package/dist/content.js.map +1 -1
- package/dist/css-inline.d.ts.map +1 -1
- package/dist/css-inline.js +1 -1
- package/dist/css-inline.js.map +1 -1
- package/dist/email-tokens.d.ts +56 -0
- package/dist/email-tokens.d.ts.map +1 -0
- package/dist/email-tokens.js +58 -0
- package/dist/email-tokens.js.map +1 -0
- package/dist/email.d.ts +77 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +166 -0
- package/dist/email.js.map +1 -0
- package/package.json +6 -2
- package/react/content.tsx +48 -8
- package/react/css-inline.ts +1 -1
- package/react/email-tokens.ts +60 -0
- package/react/email.ts +219 -0
- package/src/components.css +50 -3
- package/src/layout.css +28 -5
package/dist/css-inline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css-inline.js","sourceRoot":"","sources":["../react/css-inline.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,0EAA0E;AAE1E;;;iEAGiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAW,
|
|
1
|
+
{"version":3,"file":"css-inline.js","sourceRoot":"","sources":["../react/css-inline.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,0EAA0E;AAE1E;;;iEAGiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAW,osiQAAosiQ,CAAA;AAEvuiQ;;;yBAGyB;AACzB,MAAM,CAAC,MAAM,WAAW,GAAW,03BAA03B,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare const COULEURS: {
|
|
2
|
+
readonly ink: "#0b1020";
|
|
3
|
+
readonly ink2: "#4a5468";
|
|
4
|
+
readonly muted: "#5a637a";
|
|
5
|
+
readonly label: "#8a93a8";
|
|
6
|
+
readonly line: "#e3e6ef";
|
|
7
|
+
readonly hairline: "#eef1f8";
|
|
8
|
+
readonly paper: "#f5f6fa";
|
|
9
|
+
readonly panel: "#fbfcfe";
|
|
10
|
+
readonly white: "#ffffff";
|
|
11
|
+
readonly accentDeep: "#1e1b8c";
|
|
12
|
+
readonly accent: "#2a2aa8";
|
|
13
|
+
readonly accentLine: "#c9d6f5";
|
|
14
|
+
readonly accentSoft: "#eaf1ff";
|
|
15
|
+
readonly violet: "#8a3af8";
|
|
16
|
+
readonly iris: "#5848f8";
|
|
17
|
+
readonly bleu: "#2f6ef8";
|
|
18
|
+
readonly cyan: "#14c2f8";
|
|
19
|
+
readonly lavande: "#b8a8f8";
|
|
20
|
+
readonly accentOnDark: "#a5b4fc";
|
|
21
|
+
readonly ok: "#1c6b4a";
|
|
22
|
+
readonly okSoft: "#e6f3ec";
|
|
23
|
+
readonly okLine: "#bfdccb";
|
|
24
|
+
readonly warn: "#b98a1f";
|
|
25
|
+
readonly warnSoft: "#faf3e2";
|
|
26
|
+
readonly warnLine: "#ebdcb4";
|
|
27
|
+
readonly warnInk: "#8a6d1e";
|
|
28
|
+
readonly err: "#b5202c";
|
|
29
|
+
readonly errSoft: "#fdece9";
|
|
30
|
+
readonly errLine: "#efc9cd";
|
|
31
|
+
readonly fmtPdf: "#c4362b";
|
|
32
|
+
readonly fmtDocx: "#2a5ba8";
|
|
33
|
+
};
|
|
34
|
+
export declare const POLICES: {
|
|
35
|
+
readonly display: "\"Outfit\", \"Helvetica Neue\", Arial, sans-serif";
|
|
36
|
+
readonly texte: "\"Public Sans\", \"Helvetica Neue\", Arial, sans-serif";
|
|
37
|
+
readonly mono: "\"Space Grotesk\", \"Menlo\", monospace";
|
|
38
|
+
readonly code: "ui-monospace, \"SF Mono\", \"Menlo\", \"Consolas\", monospace";
|
|
39
|
+
};
|
|
40
|
+
export declare const TAILLES: {
|
|
41
|
+
readonly h1: "76px";
|
|
42
|
+
readonly h2: "56px";
|
|
43
|
+
readonly h3: "38px";
|
|
44
|
+
readonly cardTitle: "24px";
|
|
45
|
+
readonly lead: "18px";
|
|
46
|
+
readonly body: "15px";
|
|
47
|
+
readonly appLg: "14px";
|
|
48
|
+
readonly app: "13px";
|
|
49
|
+
readonly appSm: "12px";
|
|
50
|
+
readonly appH2: "20px";
|
|
51
|
+
readonly appH3: "16px";
|
|
52
|
+
readonly monoLg: "11px";
|
|
53
|
+
readonly mono: "10px";
|
|
54
|
+
readonly monoSm: "9px";
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=email-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-tokens.d.ts","sourceRoot":"","sources":["../react/email-tokens.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ;aACnB,GAAK,EAAE,SAAS;aAChB,IAAM,EAAE,SAAS;aACjB,KAAO,EAAE,SAAS;aAClB,KAAO,EAAE,SAAS;aAClB,IAAM,EAAE,SAAS;aACjB,QAAU,EAAE,SAAS;aACrB,KAAO,EAAE,SAAS;aAClB,KAAO,EAAE,SAAS;aAClB,KAAO,EAAE,SAAS;aAClB,UAAY,EAAE,SAAS;aACvB,MAAQ,EAAE,SAAS;aACnB,UAAY,EAAE,SAAS;aACvB,UAAY,EAAE,SAAS;aACvB,MAAQ,EAAE,SAAS;aACnB,IAAM,EAAE,SAAS;aACjB,IAAM,EAAE,SAAS;aACjB,IAAM,EAAE,SAAS;aACjB,OAAS,EAAE,SAAS;aACpB,YAAc,EAAE,SAAS;aACzB,EAAI,EAAE,SAAS;aACf,MAAQ,EAAE,SAAS;aACnB,MAAQ,EAAE,SAAS;aACnB,IAAM,EAAE,SAAS;aACjB,QAAU,EAAE,SAAS;aACrB,QAAU,EAAE,SAAS;aACrB,OAAS,EAAE,SAAS;aACpB,GAAK,EAAE,SAAS;aAChB,OAAS,EAAE,SAAS;aACpB,OAAS,EAAE,SAAS;aACpB,MAAQ,EAAE,SAAS;aACnB,OAAS,EAAE,SAAS;CACZ,CAAA;AAEV,eAAO,MAAM,OAAO;aAClB,OAAS,EAAE,mDAAmD;aAC9D,KAAO,EAAE,wDAAwD;aACjE,IAAM,EAAE,yCAAyC;aACjD,IAAM,EAAE,+DAA+D;CAC/D,CAAA;AAEV,eAAO,MAAM,OAAO;aAClB,EAAI,EAAE,MAAM;aACZ,EAAI,EAAE,MAAM;aACZ,EAAI,EAAE,MAAM;aACZ,SAAW,EAAE,MAAM;aACnB,IAAM,EAAE,MAAM;aACd,IAAM,EAAE,MAAM;aACd,KAAO,EAAE,MAAM;aACf,GAAK,EAAE,MAAM;aACb,KAAO,EAAE,MAAM;aACf,KAAO,EAAE,MAAM;aACf,KAAO,EAAE,MAAM;aACf,MAAQ,EAAE,MAAM;aAChB,IAAM,EAAE,MAAM;aACd,MAAQ,EAAE,KAAK;CACP,CAAA"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// GÉNÉRÉ par scripts/build-email-tokens.mjs — ne pas éditer à la main.
|
|
2
|
+
// Source de vérité : tokens/index.js, lui-même miroir de src/tokens.css.
|
|
3
|
+
export const COULEURS = {
|
|
4
|
+
"ink": "#0b1020",
|
|
5
|
+
"ink2": "#4a5468",
|
|
6
|
+
"muted": "#5a637a",
|
|
7
|
+
"label": "#8a93a8",
|
|
8
|
+
"line": "#e3e6ef",
|
|
9
|
+
"hairline": "#eef1f8",
|
|
10
|
+
"paper": "#f5f6fa",
|
|
11
|
+
"panel": "#fbfcfe",
|
|
12
|
+
"white": "#ffffff",
|
|
13
|
+
"accentDeep": "#1e1b8c",
|
|
14
|
+
"accent": "#2a2aa8",
|
|
15
|
+
"accentLine": "#c9d6f5",
|
|
16
|
+
"accentSoft": "#eaf1ff",
|
|
17
|
+
"violet": "#8a3af8",
|
|
18
|
+
"iris": "#5848f8",
|
|
19
|
+
"bleu": "#2f6ef8",
|
|
20
|
+
"cyan": "#14c2f8",
|
|
21
|
+
"lavande": "#b8a8f8",
|
|
22
|
+
"accentOnDark": "#a5b4fc",
|
|
23
|
+
"ok": "#1c6b4a",
|
|
24
|
+
"okSoft": "#e6f3ec",
|
|
25
|
+
"okLine": "#bfdccb",
|
|
26
|
+
"warn": "#b98a1f",
|
|
27
|
+
"warnSoft": "#faf3e2",
|
|
28
|
+
"warnLine": "#ebdcb4",
|
|
29
|
+
"warnInk": "#8a6d1e",
|
|
30
|
+
"err": "#b5202c",
|
|
31
|
+
"errSoft": "#fdece9",
|
|
32
|
+
"errLine": "#efc9cd",
|
|
33
|
+
"fmtPdf": "#c4362b",
|
|
34
|
+
"fmtDocx": "#2a5ba8"
|
|
35
|
+
};
|
|
36
|
+
export const POLICES = {
|
|
37
|
+
"display": "\"Outfit\", \"Helvetica Neue\", Arial, sans-serif",
|
|
38
|
+
"texte": "\"Public Sans\", \"Helvetica Neue\", Arial, sans-serif",
|
|
39
|
+
"mono": "\"Space Grotesk\", \"Menlo\", monospace",
|
|
40
|
+
"code": "ui-monospace, \"SF Mono\", \"Menlo\", \"Consolas\", monospace"
|
|
41
|
+
};
|
|
42
|
+
export const TAILLES = {
|
|
43
|
+
"h1": "76px",
|
|
44
|
+
"h2": "56px",
|
|
45
|
+
"h3": "38px",
|
|
46
|
+
"cardTitle": "24px",
|
|
47
|
+
"lead": "18px",
|
|
48
|
+
"body": "15px",
|
|
49
|
+
"appLg": "14px",
|
|
50
|
+
"app": "13px",
|
|
51
|
+
"appSm": "12px",
|
|
52
|
+
"appH2": "20px",
|
|
53
|
+
"appH3": "16px",
|
|
54
|
+
"monoLg": "11px",
|
|
55
|
+
"mono": "10px",
|
|
56
|
+
"monoSm": "9px"
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=email-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-tokens.js","sourceRoot":"","sources":["../react/email-tokens.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yEAAyE;AAEzE,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;IACjB,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,SAAS;IACvB,QAAQ,EAAE,SAAS;IACnB,YAAY,EAAE,SAAS;IACvB,YAAY,EAAE,SAAS;IACvB,QAAQ,EAAE,SAAS;IACnB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,SAAS;IACpB,cAAc,EAAE,SAAS;IACzB,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;IACnB,MAAM,EAAE,SAAS;IACjB,UAAU,EAAE,SAAS;IACrB,UAAU,EAAE,SAAS;IACrB,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,SAAS;IAChB,SAAS,EAAE,SAAS;IACpB,SAAS,EAAE,SAAS;IACpB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,SAAS;CACZ,CAAA;AAEV,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,SAAS,EAAE,mDAAmD;IAC9D,OAAO,EAAE,wDAAwD;IACjE,MAAM,EAAE,yCAAyC;IACjD,MAAM,EAAE,+DAA+D;CAC/D,CAAA;AAEV,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,MAAM;IACnB,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,MAAM;IACf,KAAK,EAAE,MAAM;IACb,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,MAAM;IAChB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,KAAK;CACP,CAAA"}
|
package/dist/email.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** Échappe ce qui entre dans le HTML. Un objet d'e-mail vient souvent d'une
|
|
2
|
+
* base de données : un nom de client avec une esperluette casserait le
|
|
3
|
+
* document, et un `<` bien placé y injecterait du balisage. */
|
|
4
|
+
export declare function echappe(texte: string): string;
|
|
5
|
+
/** Sur-titre : le filet de spectre suivi du label en capitales.
|
|
6
|
+
* PREMIER des trois emplois autorisés du spectre. Le dégradé est rendu en
|
|
7
|
+
* TROIS APLATS de 19px (violet, bleu, cyan) : un `linear-gradient` ne se
|
|
8
|
+
* rend pas dans Outlook, qui afficherait une bande vide. */
|
|
9
|
+
export declare function emailKicker(texte: string): string;
|
|
10
|
+
/** Le titre du message. Un seul par e-mail : c'est la promesse de l'objet,
|
|
11
|
+
* tenue à l'ouverture. */
|
|
12
|
+
export declare function emailTitre(texte: string): string;
|
|
13
|
+
/** Un paragraphe. Le HTML d'enrichissement (`<strong>`, `<a>`) est admis :
|
|
14
|
+
* c'est du texte rédigé, pas une saisie utilisateur — l'échapper
|
|
15
|
+
* afficherait des chevrons. Passer par `echappe` toute valeur qui vient
|
|
16
|
+
* d'une base. */
|
|
17
|
+
export declare function emailTexte(html: string): string;
|
|
18
|
+
/** L'action. UN SEUL bouton plein par e-mail, comme par page — c'est ce qui
|
|
19
|
+
* fait qu'on sait où cliquer.
|
|
20
|
+
* Construit en table : le `padding` d'un `<a>` est ignoré par Outlook, qui
|
|
21
|
+
* rendrait un lien nu au milieu du message. Aplat indigo et non dégradé,
|
|
22
|
+
* pour la même raison. Angle vif, comme partout. */
|
|
23
|
+
export declare function emailBouton({ href, libelle }: {
|
|
24
|
+
href: string;
|
|
25
|
+
libelle: string;
|
|
26
|
+
}): string;
|
|
27
|
+
/** Le lien secondaire, sous le bouton : l'autre chemin, pour qui ne veut pas
|
|
28
|
+
* du premier. Souligné — dans un e-mail, la couleur seule ne suffit pas à
|
|
29
|
+
* annoncer un lien. */
|
|
30
|
+
export declare function emailLienSecondaire({ href, libelle }: {
|
|
31
|
+
href: string;
|
|
32
|
+
libelle: string;
|
|
33
|
+
}): string;
|
|
34
|
+
/** La mention discrète : ce qu'on lit après avoir décidé. */
|
|
35
|
+
export declare function emailNote(html: string): string;
|
|
36
|
+
/** L'encart : un extrait, un récapitulatif de commande, un code. */
|
|
37
|
+
export declare function emailEncart(html: string): string;
|
|
38
|
+
/** Le filet de séparation. */
|
|
39
|
+
export declare function emailFilet(): string;
|
|
40
|
+
export interface EmailOptions {
|
|
41
|
+
/** Repris en `<title>`. Ce n'est PAS l'objet du message : l'objet se pose
|
|
42
|
+
* à l'envoi, dans l'en-tête SMTP. */
|
|
43
|
+
titre: string;
|
|
44
|
+
/** Le texte gris qui suit l'objet dans la liste des messages. Sans lui,
|
|
45
|
+
* le client y affiche les premiers mots du HTML — souvent « Voir cet
|
|
46
|
+
* e-mail dans votre navigateur », ce qui gâche la seule ligne dont on
|
|
47
|
+
* dispose pour convaincre d'ouvrir. */
|
|
48
|
+
preheader?: string;
|
|
49
|
+
/** Le nom de la marque, écrit en toutes lettres. ⚠️ OBLIGATOIRE même avec
|
|
50
|
+
* un logo : une image sur deux est bloquée par défaut, et un e-mail qui
|
|
51
|
+
* ne dit pas de qui il vient part à la corbeille. */
|
|
52
|
+
marque: string;
|
|
53
|
+
/** L'URL ABSOLUE du logo — une image d'e-mail ne se résout pas depuis un
|
|
54
|
+
* chemin relatif, et les data-URI sont bloquées par Gmail. */
|
|
55
|
+
logo?: {
|
|
56
|
+
src: string;
|
|
57
|
+
hauteur?: number;
|
|
58
|
+
};
|
|
59
|
+
/** Les blocs, concaténés. */
|
|
60
|
+
contenu: string;
|
|
61
|
+
/** Sous la carte : mentions légales, désinscription. */
|
|
62
|
+
pied?: string;
|
|
63
|
+
lang?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Assemble un e-mail complet.
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ Le mode sombre est REFUSÉ explicitement (`color-scheme: light`) : la
|
|
69
|
+
* charte est claire par nature, et l'inversion automatique d'Apple Mail
|
|
70
|
+
* retourne les neutres sans toucher aux aplats — l'indigo du bouton
|
|
71
|
+
* resterait sur un fond devenu noir, et les contrastes réglés dans un sens
|
|
72
|
+
* partiraient dans l'autre. C'est la même règle que pour les surfaces
|
|
73
|
+
* sombres du système : elles se font avec la bande manifeste, jamais en
|
|
74
|
+
* inversant les neutres.
|
|
75
|
+
*/
|
|
76
|
+
export declare function email({ titre, preheader, marque, logo, contenu, pied, lang, }: EmailOptions): string;
|
|
77
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../react/email.ts"],"names":[],"mappings":"AAmDA;;gEAEgE;AAChE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C;AAID;;;6DAG6D;AAC7D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;2BAC2B;AAC3B,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;kBAGkB;AAClB,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;qDAIqD;AACrD,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAKxF;AAED;;wBAEwB;AACxB,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEhG;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,8BAA8B;AAC9B,wBAAgB,UAAU,IAAI,MAAM,CAGnC;AAID,MAAM,WAAW,YAAY;IAC3B;0CACsC;IACtC,KAAK,EAAE,MAAM,CAAA;IACb;;;4CAGwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;0DAEsD;IACtD,MAAM,EAAE,MAAM,CAAA;IACd;mEAC+D;IAC/D,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,SAAS,EACT,MAAM,EACN,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,IAAW,GACZ,EAAE,YAAY,GAAG,MAAM,CA8CvB"}
|
package/dist/email.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Gabarits d'e-mail « Encre & Papier ».
|
|
2
|
+
//
|
|
3
|
+
// ⚠️ CE MODULE N'EST PAS DU REACT ET N'EMPLOIE PAS LE CSS DU PAQUET. Il rend
|
|
4
|
+
// des chaînes de HTML. C'est la seule dérogation à la règle première du dépôt
|
|
5
|
+
// (« le CSS est la source de vérité, les composants n'en sont qu'une
|
|
6
|
+
// façade »), et elle est imposée par le support, pas choisie :
|
|
7
|
+
//
|
|
8
|
+
// · il n'y a pas de feuille de style à charger dans un e-mail ;
|
|
9
|
+
// · Outlook et Gmail SUPPRIMENT les propriétés personnalisées : un
|
|
10
|
+
// `var(--accent)` s'y rend en couleur nulle, donc en texte noir sur fond
|
|
11
|
+
// transparent. Les valeurs sont donc écrites en clair — mais tirées du
|
|
12
|
+
// miroir JS des tokens, que tests/tokens.test.js tient aligné sur le CSS ;
|
|
13
|
+
// · ni flexbox ni grid ne sont fiables : la mise en page est en TABLES ;
|
|
14
|
+
// · les dégradés ne se rendent pas dans Outlook — le bouton plein perd le
|
|
15
|
+
// sien et devient un aplat, le filet de spectre devient trois aplats.
|
|
16
|
+
//
|
|
17
|
+
// Il vit dans react/ parce que c'est la racine de compilation du paquet
|
|
18
|
+
// (`rootDir`), pas parce qu'il en dépend : il n'importe pas une ligne de
|
|
19
|
+
// React et n'émet aucune classe `zv-`.
|
|
20
|
+
//
|
|
21
|
+
// ⚠️ Ce module ÉCHAPPE à ta feuille de style : ce qui n'est pas ici ne
|
|
22
|
+
// s'affichera pas. Ne pas y ajouter de classes en espérant qu'elles soient
|
|
23
|
+
// stylées.
|
|
24
|
+
import { COULEURS, POLICES as PILES } from './email-tokens.js';
|
|
25
|
+
/** ⚠️ Les piles de polices du paquet contiennent des GUILLEMETS DOUBLES
|
|
26
|
+
* (`"Public Sans", …`), et tout ici s'écrit dans un attribut `style="…"`
|
|
27
|
+
* délimité par ces mêmes guillemets : l'attribut se refermerait au premier
|
|
28
|
+
* guillemet interne, et TOUT le style du nœud serait perdu. En HTML, un
|
|
29
|
+
* guillemet simple est équivalent à l'intérieur d'une valeur de police.
|
|
30
|
+
* Cette faute ne se voit pas à la relecture — elle se voit au rendu, où
|
|
31
|
+
* l'e-mail entier repasse en Times sans style. */
|
|
32
|
+
const pourAttribut = (pile) => pile.replace(/"/g, "'");
|
|
33
|
+
const POLICES = {
|
|
34
|
+
display: pourAttribut(PILES.display),
|
|
35
|
+
texte: pourAttribut(PILES.texte),
|
|
36
|
+
mono: pourAttribut(PILES.mono),
|
|
37
|
+
};
|
|
38
|
+
/* ─── Réglages communs ─────────────────────────────────────────────── */
|
|
39
|
+
/** 600px : la largeur qui traverse tous les clients depuis vingt ans, dont
|
|
40
|
+
* le volet de lecture d'Outlook. Ce n'est pas --page-max, et ça n'a rien à
|
|
41
|
+
* voir : c'est une contrainte du support. */
|
|
42
|
+
const LARGEUR = 600;
|
|
43
|
+
const CORPS = `font-family:${POLICES.texte};font-size:15px;line-height:1.6;color:${COULEURS.muted};`;
|
|
44
|
+
const TITRE = `font-family:${POLICES.display};font-weight:300;letter-spacing:-0.02em;color:${COULEURS.ink};`;
|
|
45
|
+
/** Échappe ce qui entre dans le HTML. Un objet d'e-mail vient souvent d'une
|
|
46
|
+
* base de données : un nom de client avec une esperluette casserait le
|
|
47
|
+
* document, et un `<` bien placé y injecterait du balisage. */
|
|
48
|
+
export function echappe(texte) {
|
|
49
|
+
return texte
|
|
50
|
+
.replace(/&/g, '&')
|
|
51
|
+
.replace(/</g, '<')
|
|
52
|
+
.replace(/>/g, '>')
|
|
53
|
+
.replace(/"/g, '"');
|
|
54
|
+
}
|
|
55
|
+
/* ─── Blocs ────────────────────────────────────────────────────────── */
|
|
56
|
+
/** Sur-titre : le filet de spectre suivi du label en capitales.
|
|
57
|
+
* PREMIER des trois emplois autorisés du spectre. Le dégradé est rendu en
|
|
58
|
+
* TROIS APLATS de 19px (violet, bleu, cyan) : un `linear-gradient` ne se
|
|
59
|
+
* rend pas dans Outlook, qui afficherait une bande vide. */
|
|
60
|
+
export function emailKicker(texte) {
|
|
61
|
+
const brin = (couleur) => `<td width="19" height="2" style="background:${couleur};font-size:0;line-height:0;"> </td>`;
|
|
62
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:0 0 14px;"><tr>
|
|
63
|
+
<td><table role="presentation" cellpadding="0" cellspacing="0" border="0"><tr>${brin(COULEURS.violet)}${brin(COULEURS.bleu)}${brin(COULEURS.cyan)}</tr></table></td>
|
|
64
|
+
<td style="padding-left:12px;font-family:${POLICES.mono};font-size:11px;font-weight:500;letter-spacing:0.2em;text-transform:uppercase;color:${COULEURS.accent};">${echappe(texte)}</td>
|
|
65
|
+
</tr></table>`;
|
|
66
|
+
}
|
|
67
|
+
/** Le titre du message. Un seul par e-mail : c'est la promesse de l'objet,
|
|
68
|
+
* tenue à l'ouverture. */
|
|
69
|
+
export function emailTitre(texte) {
|
|
70
|
+
return `<h1 style="${TITRE}font-size:26px;line-height:1.2;margin:0 0 18px;">${echappe(texte)}</h1>`;
|
|
71
|
+
}
|
|
72
|
+
/** Un paragraphe. Le HTML d'enrichissement (`<strong>`, `<a>`) est admis :
|
|
73
|
+
* c'est du texte rédigé, pas une saisie utilisateur — l'échapper
|
|
74
|
+
* afficherait des chevrons. Passer par `echappe` toute valeur qui vient
|
|
75
|
+
* d'une base. */
|
|
76
|
+
export function emailTexte(html) {
|
|
77
|
+
return `<p style="${CORPS}margin:0 0 16px;">${html}</p>`;
|
|
78
|
+
}
|
|
79
|
+
/** L'action. UN SEUL bouton plein par e-mail, comme par page — c'est ce qui
|
|
80
|
+
* fait qu'on sait où cliquer.
|
|
81
|
+
* Construit en table : le `padding` d'un `<a>` est ignoré par Outlook, qui
|
|
82
|
+
* rendrait un lien nu au milieu du message. Aplat indigo et non dégradé,
|
|
83
|
+
* pour la même raison. Angle vif, comme partout. */
|
|
84
|
+
export function emailBouton({ href, libelle }) {
|
|
85
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:26px 0;"><tr>
|
|
86
|
+
<td align="center" bgcolor="${COULEURS.accentDeep}" style="background:${COULEURS.accentDeep};">
|
|
87
|
+
<a href="${echappe(href)}" style="display:inline-block;padding:15px 30px;font-family:${POLICES.texte};font-size:15px;font-weight:600;color:${COULEURS.white};text-decoration:none;">${echappe(libelle)}</a>
|
|
88
|
+
</td></tr></table>`;
|
|
89
|
+
}
|
|
90
|
+
/** Le lien secondaire, sous le bouton : l'autre chemin, pour qui ne veut pas
|
|
91
|
+
* du premier. Souligné — dans un e-mail, la couleur seule ne suffit pas à
|
|
92
|
+
* annoncer un lien. */
|
|
93
|
+
export function emailLienSecondaire({ href, libelle }) {
|
|
94
|
+
return `<p style="${CORPS}font-size:14px;margin:0 0 16px;"><a href="${echappe(href)}" style="color:${COULEURS.accent};text-decoration:underline;">${echappe(libelle)}</a></p>`;
|
|
95
|
+
}
|
|
96
|
+
/** La mention discrète : ce qu'on lit après avoir décidé. */
|
|
97
|
+
export function emailNote(html) {
|
|
98
|
+
return `<p style="font-family:${POLICES.texte};font-size:13px;line-height:1.55;color:${COULEURS.label};margin:0 0 12px;">${html}</p>`;
|
|
99
|
+
}
|
|
100
|
+
/** L'encart : un extrait, un récapitulatif de commande, un code. */
|
|
101
|
+
export function emailEncart(html) {
|
|
102
|
+
return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:0 0 18px;"><tr>
|
|
103
|
+
<td style="background:${COULEURS.panel};border:1px solid ${COULEURS.hairline};border-left:2px solid ${COULEURS.accentLine};padding:14px 18px;font-family:${POLICES.texte};font-size:13px;line-height:1.6;color:${COULEURS.ink};">${html}</td>
|
|
104
|
+
</tr></table>`;
|
|
105
|
+
}
|
|
106
|
+
/** Le filet de séparation. */
|
|
107
|
+
export function emailFilet() {
|
|
108
|
+
return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:22px 0;"><tr>
|
|
109
|
+
<td height="1" style="background:${COULEURS.line};font-size:0;line-height:0;"> </td></tr></table>`;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Assemble un e-mail complet.
|
|
113
|
+
*
|
|
114
|
+
* ⚠️ Le mode sombre est REFUSÉ explicitement (`color-scheme: light`) : la
|
|
115
|
+
* charte est claire par nature, et l'inversion automatique d'Apple Mail
|
|
116
|
+
* retourne les neutres sans toucher aux aplats — l'indigo du bouton
|
|
117
|
+
* resterait sur un fond devenu noir, et les contrastes réglés dans un sens
|
|
118
|
+
* partiraient dans l'autre. C'est la même règle que pour les surfaces
|
|
119
|
+
* sombres du système : elles se font avec la bande manifeste, jamais en
|
|
120
|
+
* inversant les neutres.
|
|
121
|
+
*/
|
|
122
|
+
export function email({ titre, preheader, marque, logo, contenu, pied, lang = 'fr', }) {
|
|
123
|
+
const enTete = logo
|
|
124
|
+
? `<img src="${echappe(logo.src)}" height="${logo.hauteur ?? 28}" alt="${echappe(marque)}" style="display:block;border:0;height:${logo.hauteur ?? 28}px;width:auto;">`
|
|
125
|
+
: `<span style="font-family:${POLICES.display};font-weight:600;font-size:20px;letter-spacing:-0.02em;color:${COULEURS.ink};">${echappe(marque)}</span>`;
|
|
126
|
+
return `<!doctype html>
|
|
127
|
+
<html lang="${lang}">
|
|
128
|
+
<head>
|
|
129
|
+
<meta charset="utf-8">
|
|
130
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
131
|
+
<meta name="color-scheme" content="light">
|
|
132
|
+
<meta name="supported-color-schemes" content="light">
|
|
133
|
+
<title>${echappe(titre)}</title>
|
|
134
|
+
<style>
|
|
135
|
+
/* Le seul <style> du document, et il ne porte QUE du responsive : ce que
|
|
136
|
+
Gmail en supprime ne fait rien perdre, tout le reste est en ligne. */
|
|
137
|
+
@media (max-width:620px) {
|
|
138
|
+
.zv-carte { padding:26px 20px !important; }
|
|
139
|
+
.zv-marge { padding:20px 12px !important; }
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
142
|
+
</head>
|
|
143
|
+
<body style="margin:0;padding:0;width:100%;background:${COULEURS.paper};-webkit-text-size-adjust:100%;">
|
|
144
|
+
<div style="display:none;max-height:0;overflow:hidden;opacity:0;">${preheader ? echappe(preheader) : ''}</div>
|
|
145
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:${COULEURS.paper};">
|
|
146
|
+
<tr><td align="center" class="zv-marge" style="padding:32px 16px;">
|
|
147
|
+
<table role="presentation" width="${LARGEUR}" cellpadding="0" cellspacing="0" border="0" style="width:100%;max-width:${LARGEUR}px;">
|
|
148
|
+
|
|
149
|
+
<tr><td style="padding:0 0 20px;">${enTete}</td></tr>
|
|
150
|
+
|
|
151
|
+
<tr><td class="zv-carte" bgcolor="${COULEURS.white}" style="background:${COULEURS.white};border:1px solid ${COULEURS.line};padding:34px 36px;">
|
|
152
|
+
${contenu}
|
|
153
|
+
</td></tr>
|
|
154
|
+
|
|
155
|
+
${pied
|
|
156
|
+
? `<tr><td style="padding:20px 4px 0;font-family:${POLICES.texte};font-size:12px;line-height:1.6;color:${COULEURS.label};">${pied}</td></tr>`
|
|
157
|
+
: ''}
|
|
158
|
+
|
|
159
|
+
</table>
|
|
160
|
+
</td></tr>
|
|
161
|
+
</table>
|
|
162
|
+
</body>
|
|
163
|
+
</html>
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../react/email.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zevra/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Design system Zevra « Encre & Papier » — tokens, CSS des composants et logos officiels.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
"types": "./dist/css-inline.d.ts",
|
|
38
38
|
"default": "./dist/css-inline.js"
|
|
39
39
|
},
|
|
40
|
+
"./email": {
|
|
41
|
+
"types": "./dist/email.d.ts",
|
|
42
|
+
"default": "./dist/email.js"
|
|
43
|
+
},
|
|
40
44
|
"./react/table": {
|
|
41
45
|
"types": "./dist/data-table.d.ts",
|
|
42
46
|
"default": "./dist/data-table.js"
|
|
@@ -74,7 +78,7 @@
|
|
|
74
78
|
"DECISIONS.md"
|
|
75
79
|
],
|
|
76
80
|
"scripts": {
|
|
77
|
-
"build": "node scripts/build-icon-react.mjs && node scripts/build-css-bundle.mjs && tsc -p tsconfig.json",
|
|
81
|
+
"build": "node scripts/build-icon-react.mjs && node scripts/build-css-bundle.mjs && node scripts/build-email-tokens.mjs && tsc -p tsconfig.json",
|
|
78
82
|
"test": "npm run build && vitest run",
|
|
79
83
|
"prepublishOnly": "npm run build",
|
|
80
84
|
"storybook": "npm run build && node scripts/build-stories.mjs && storybook dev -p 6006 --no-open",
|
package/react/content.tsx
CHANGED
|
@@ -247,21 +247,61 @@ export function Faq({ className, ...reste }: Div & { className?: string }) {
|
|
|
247
247
|
return <div className={cx('zv-faq', className)} {...reste} />
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
export interface FaqItemProps
|
|
250
|
+
export interface FaqItemProps
|
|
251
|
+
extends Omit<HTMLAttributes<HTMLDetailsElement>, 'className' | 'title'> {
|
|
251
252
|
question: ReactNode
|
|
253
|
+
/** Dépliée au chargement. Une par page au plus : si toutes le sont, le
|
|
254
|
+
* repli ne sert plus à rien et la page redevient un mur. */
|
|
255
|
+
ouvert?: boolean
|
|
252
256
|
className?: string
|
|
253
257
|
children?: ReactNode
|
|
254
258
|
}
|
|
255
259
|
|
|
256
|
-
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
|
|
260
|
+
/** Une question qui se replie. `<details>`, donc AUCUN JavaScript : elle
|
|
261
|
+
* s'ouvre au clic, à Entrée et à Espace, elle est annoncée comme repliable,
|
|
262
|
+
* et la recherche du navigateur (⌘F) l'ouvre toute seule pour montrer ce
|
|
263
|
+
* qu'elle a trouvé.
|
|
264
|
+
*
|
|
265
|
+
* ⚠️ La réponse reste DANS LE DOM quand la question est fermée — `details`
|
|
266
|
+
* la masque, il ne la retire pas. C'est ce qui permet à une page de porter
|
|
267
|
+
* un balisage `FAQPage` honnête : ce que le moteur lit est bien ce que le
|
|
268
|
+
* visiteur peut ouvrir. Un accordéon qui démonte son contenu annoncerait
|
|
269
|
+
* des réponses absentes.
|
|
270
|
+
*
|
|
271
|
+
* Le chevron fait partie du composant, comme celui de `Fold` et la flèche
|
|
272
|
+
* de `LinkArrow` : recopié à la main, il finit par diverger.
|
|
273
|
+
*
|
|
274
|
+
* `question` accepte un TITRE (`<h3>Ma question</h3>`) : une FAQ de vitrine
|
|
275
|
+
* veut ses questions dans le plan du document — c'est ce que lisent les
|
|
276
|
+
* moteurs, et ce que parcourt un lecteur d'écran qui saute de titre en
|
|
277
|
+
* titre. Le CSS lui retire ce que le navigateur lui met : le niveau reste
|
|
278
|
+
* au balisage, l'apparence au système. */
|
|
279
|
+
export function FaqItem({
|
|
280
|
+
question,
|
|
281
|
+
ouvert,
|
|
282
|
+
className,
|
|
283
|
+
children,
|
|
284
|
+
...reste
|
|
285
|
+
}: FaqItemProps) {
|
|
260
286
|
return (
|
|
261
|
-
<
|
|
262
|
-
<
|
|
287
|
+
<details className={cx('zv-faq__item', className)} open={ouvert} {...reste}>
|
|
288
|
+
<summary className="zv-faq__q">
|
|
289
|
+
{question}
|
|
290
|
+
<svg
|
|
291
|
+
className="zv-faq__chevron"
|
|
292
|
+
viewBox="0 0 24 24"
|
|
293
|
+
fill="none"
|
|
294
|
+
stroke="currentColor"
|
|
295
|
+
strokeWidth="2"
|
|
296
|
+
strokeLinecap="square"
|
|
297
|
+
strokeLinejoin="miter"
|
|
298
|
+
aria-hidden="true"
|
|
299
|
+
>
|
|
300
|
+
<path d="M6.67 9.33 L12 14.67 L17.33 9.33" />
|
|
301
|
+
</svg>
|
|
302
|
+
</summary>
|
|
263
303
|
<p className="zv-faq__a">{children}</p>
|
|
264
|
-
</
|
|
304
|
+
</details>
|
|
265
305
|
)
|
|
266
306
|
}
|
|
267
307
|
|