All checks were successful
Build & Deploy API / build-and-deploy (push) Successful in 1m7s
Sur iPhone Mail.app en dark mode, iOS inverse automatiquement les couleurs de l'email : header rubis-deep (#771328) devenait rose pâle, fond crème devenait noir, texte sombre devenait blanc. Fix appliqué dans le _layout.tsx (donc impacte checkin + relance) : - Ajoute meta `color-scheme: light only` + `supported-color-schemes` → signal aux clients mail (iOS Mail, Gmail mobile, Yahoo) qu'on ne souhaite PAS d'auto-dark-mode - Ajoute style block avec :root color-scheme + overrides Outlook.com dark mode ([data-ogsc] / [data-ogsb]) - Ajoute className sur Body / Container / header / footer pour permettre le ciblage CSS dark-mode-resistant Couvre : iOS Mail, Apple Mail macOS, Gmail mobile dark, Outlook.com. Aucun impact sur les clients qui ne font pas d'inversion (Outlook desktop, Thunderbird, etc.). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
204 lines
5.8 KiB
TypeScript
204 lines
5.8 KiB
TypeScript
/**
|
|
* Squelette commun aux 2 templates Rubis : header rubis-deep avec brand
|
|
* + container cream + footer "Émis via Rubis sur l'ongle" (cliquable
|
|
* vers la landing publique).
|
|
*
|
|
* Approche : on stylé en inline via les `style` props que React Email
|
|
* convertit en HTML inline (compatible tous mail clients y compris
|
|
* Outlook). Pas de classes Tailwind ici — risquées en mail.
|
|
*/
|
|
|
|
import * as React from 'react'
|
|
import {
|
|
Html,
|
|
Head,
|
|
Preview,
|
|
Body,
|
|
Container,
|
|
Section,
|
|
Row,
|
|
Column,
|
|
Text,
|
|
Link,
|
|
} from '@react-email/components'
|
|
|
|
import { BRAND, sp } from './_brand.js'
|
|
|
|
type LayoutProps = {
|
|
/** Aperçu dans la liste mail (Gmail preview text). */
|
|
preview: string
|
|
/** Nom commercial affiché dans le header (vendeur ou "Rubis"). */
|
|
brandName: string
|
|
/** Sous-titre header (ex: numéro de facture, date). Optionnel. */
|
|
brandSubtitle?: string | null
|
|
/** URL de la landing publique — lien dans le footer ("Rubis sur l'ongle"). */
|
|
landingUrl?: string
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export function EmailLayout({
|
|
preview,
|
|
brandName,
|
|
brandSubtitle,
|
|
landingUrl,
|
|
children,
|
|
}: LayoutProps) {
|
|
return (
|
|
<Html lang="fr">
|
|
<Head>
|
|
{/*
|
|
* Force le mode clair sur les clients qui auto-invertissent en dark mode
|
|
* (iOS Mail, Gmail mobile, Outlook.com web). Sans ces tags, les fonds
|
|
* crème/blanc deviennent noirs et le header rubis-deep devient rose pâle.
|
|
*/}
|
|
<meta name="color-scheme" content="light only" />
|
|
<meta name="supported-color-schemes" content="light only" />
|
|
<style
|
|
// eslint-disable-next-line react/no-danger -- nécessaire dans <style>
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
:root {
|
|
color-scheme: light only;
|
|
supported-color-schemes: light only;
|
|
}
|
|
/* Outlook.com / Hotmail dark mode — force nos couleurs */
|
|
[data-ogsc] body,
|
|
[data-ogsb] body {
|
|
background-color: ${BRAND.cream} !important;
|
|
}
|
|
[data-ogsc] .rubis-container,
|
|
[data-ogsb] .rubis-container {
|
|
background-color: ${BRAND.white} !important;
|
|
}
|
|
[data-ogsc] .rubis-header,
|
|
[data-ogsb] .rubis-header {
|
|
background-color: ${BRAND.rubisDeep} !important;
|
|
}
|
|
[data-ogsc] .rubis-footer,
|
|
[data-ogsb] .rubis-footer {
|
|
background-color: ${BRAND.cream2} !important;
|
|
}
|
|
`,
|
|
}}
|
|
/>
|
|
</Head>
|
|
<Preview>{preview}</Preview>
|
|
<Body style={bodyStyle} className="rubis-body">
|
|
<Container style={containerStyle} className="rubis-container">
|
|
{/* Bandeau rubis-deep avec gem ◆ + brand */}
|
|
<Section style={headerStyle} className="rubis-header">
|
|
<Row>
|
|
<Column style={{ verticalAlign: 'middle' }}>
|
|
<Text style={headerBrandStyle}>
|
|
<span style={gemStyle}>◆</span>
|
|
{brandName}
|
|
</Text>
|
|
{brandSubtitle ? (
|
|
<Text style={headerSubtitleStyle}>{brandSubtitle}</Text>
|
|
) : null}
|
|
</Column>
|
|
</Row>
|
|
</Section>
|
|
|
|
{/* Corps de l'email */}
|
|
<Section style={contentStyle}>{children}</Section>
|
|
|
|
{/* Footer Rubis — "Rubis sur l'ongle" cliquable vers la landing */}
|
|
<Section style={footerStyle} className="rubis-footer">
|
|
<Text style={footerTextStyle}>
|
|
Émis via{' '}
|
|
{landingUrl ? (
|
|
<Link href={landingUrl} style={footerLinkStyle}>
|
|
Rubis sur l'ongle
|
|
</Link>
|
|
) : (
|
|
<strong style={{ color: BRAND.ink2 }}>Rubis sur l'ongle</strong>
|
|
)}
|
|
{' — '}
|
|
<span style={{ color: BRAND.ink3 }}>
|
|
vos factures relancées toutes seules pendant que vous travaillez.
|
|
</span>
|
|
</Text>
|
|
</Section>
|
|
</Container>
|
|
</Body>
|
|
</Html>
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Styles inline
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const bodyStyle: React.CSSProperties = {
|
|
backgroundColor: BRAND.cream,
|
|
fontFamily: BRAND.fontBody,
|
|
margin: 0,
|
|
padding: `${sp.xl} 0`,
|
|
color: BRAND.ink,
|
|
}
|
|
|
|
const containerStyle: React.CSSProperties = {
|
|
backgroundColor: BRAND.white,
|
|
borderRadius: BRAND.radiusCard,
|
|
border: `1px solid ${BRAND.line}`,
|
|
margin: '0 auto',
|
|
maxWidth: '560px',
|
|
overflow: 'hidden',
|
|
}
|
|
|
|
const headerStyle: React.CSSProperties = {
|
|
backgroundColor: BRAND.rubisDeep,
|
|
padding: `${sp.xl} ${sp.xl}`,
|
|
}
|
|
|
|
const headerBrandStyle: React.CSSProperties = {
|
|
color: BRAND.white,
|
|
fontSize: '20px',
|
|
fontWeight: 800,
|
|
letterSpacing: '-0.01em',
|
|
margin: 0,
|
|
lineHeight: '1.1',
|
|
}
|
|
|
|
const gemStyle: React.CSSProperties = {
|
|
display: 'inline-block',
|
|
marginRight: sp.sm,
|
|
color: BRAND.rubisGlow,
|
|
fontSize: '18px',
|
|
verticalAlign: '-1px',
|
|
}
|
|
|
|
const headerSubtitleStyle: React.CSSProperties = {
|
|
color: BRAND.cream2,
|
|
fontSize: '12px',
|
|
margin: `${sp.xs} 0 0 0`,
|
|
letterSpacing: '0.04em',
|
|
textTransform: 'uppercase',
|
|
fontWeight: 600,
|
|
}
|
|
|
|
const contentStyle: React.CSSProperties = {
|
|
padding: `${sp.xl} ${sp.xl}`,
|
|
}
|
|
|
|
const footerStyle: React.CSSProperties = {
|
|
borderTop: `1px solid ${BRAND.line}`,
|
|
backgroundColor: BRAND.cream2,
|
|
padding: `${sp.lg} ${sp.xl}`,
|
|
}
|
|
|
|
const footerTextStyle: React.CSSProperties = {
|
|
color: BRAND.ink3,
|
|
fontSize: '11px',
|
|
lineHeight: '1.5',
|
|
margin: 0,
|
|
textAlign: 'center',
|
|
}
|
|
|
|
const footerLinkStyle: React.CSSProperties = {
|
|
color: BRAND.rubis,
|
|
fontWeight: 600,
|
|
textDecoration: 'none',
|
|
}
|