All checks were successful
Build & Deploy API / build-and-deploy (push) Successful in 1m8s
Avant : body crème + container blanc avec border-radius + bordure → effet "card flottante" qui laissait des marges crème en haut/bas et un fond blanc pour le contenu. Sur iPhone Mail, cette structure créait un gap mal rendu entre la frame Mail.app et le header rubis-deep. Maintenant : - bodyStyle.padding 24px 0 → 0 (rubis-deep colle au haut de la zone mail) - containerStyle background blanc → crème (toute la zone email est crème, cohérente avec la palette) - containerStyle borderRadius + border supprimés (edge-to-edge) - invoiceCardStyle (checkin) + summaryCardStyle (relance) passent en blanc pour se détacher du nouveau fond crème - Dark mode CSS : .rubis-container override aussi mis à crème Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
205 lines
5.9 KiB
TypeScript
205 lines
5.9 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.cream} !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: 0,
|
|
color: BRAND.ink,
|
|
}
|
|
|
|
const containerStyle: React.CSSProperties = {
|
|
// Fond crème pour que toute la zone du mail soit dans la palette
|
|
// (au lieu d'un container blanc flottant sur cream). Le header
|
|
// rubis-deep et le footer cream2 gardent leur couleur dédiée.
|
|
backgroundColor: BRAND.cream,
|
|
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',
|
|
}
|