335 lines
8.6 KiB
TypeScript
335 lines
8.6 KiB
TypeScript
/**
|
|
* Carries each site style into email, so a digest looks like the site its
|
|
* reader chose and the other emails like the instance's style. A mail client
|
|
* takes no stylesheet of ours, so each style is spelled out here: its palettes,
|
|
* copied from its theme under `src/themes/`, and the few touches that make it
|
|
* recognisable once flattened into inline styles. `layout.ts` reads them.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import type { StyleName } from "../../shared/theme.ts";
|
|
|
|
/**
|
|
* The colors of one style in one scheme, by role. Most are the theme's own
|
|
* tokens; the optional ones carry a style's particular touches.
|
|
*/
|
|
export interface EmailPalette {
|
|
/** The page around the card (`--color-bg`). */
|
|
outer: string;
|
|
/** The card holding the content (`--color-surface`). */
|
|
card: string;
|
|
text: string;
|
|
sec: string;
|
|
muted: string;
|
|
/** Section titles, ranks and button fills (`--color-accent`). */
|
|
accent: string;
|
|
link: string;
|
|
/** Text on an accent fill. */
|
|
onAccent: string;
|
|
/** Dividers and hairlines. */
|
|
rule: string;
|
|
/** Pills and the reset link's box. */
|
|
soft: string;
|
|
/** Quoted comments and messages. */
|
|
quote: string;
|
|
/** Kanagawa's fujiGray dates; others use `muted`. */
|
|
date?: string;
|
|
/** Kanagawa's oniViolet wordmark; others use `text`. */
|
|
brand?: string;
|
|
/** GeoCities' ridged border. */
|
|
border?: string;
|
|
/** Melange's warm card shadow. */
|
|
shadow?: string;
|
|
/** Melange's spice gradient, left to right. */
|
|
spice?: string[];
|
|
}
|
|
|
|
/**
|
|
* How a style draws an email. `layout.ts` builds every block from it, so
|
|
* adding a style to `shared/theme.ts` means adding its entry here.
|
|
*/
|
|
export interface EmailStyle {
|
|
/**
|
|
* The font stack. Its first family is a web font only Apple's mail apps
|
|
* load, through {@link webFont}; other clients fall back along the stack.
|
|
*/
|
|
font: string;
|
|
/** The Google Fonts `family` parameter of the web font, if it is one. */
|
|
webFont?: string;
|
|
/** Multiplies every font size: Times sets its serif larger, Kanagawa its monospace smaller. */
|
|
scale: number;
|
|
/** Corner radii, in pixels. */
|
|
radius: {
|
|
card: number;
|
|
img: number;
|
|
btn: number;
|
|
pill: number;
|
|
avatar: number;
|
|
};
|
|
/** The heading: its size in pixels, weight, and Brutalist's uppercase and spacing. */
|
|
title: {
|
|
size: number;
|
|
weight: number;
|
|
upper?: boolean;
|
|
spacing?: string;
|
|
accent?: boolean;
|
|
};
|
|
/** The weight of titles of items, figures and names. */
|
|
heavy: number;
|
|
/** How the card stands out from the page. */
|
|
frame: "none" | "hard" | "ridge" | "hairline" | "soft";
|
|
/** The divider between sections. */
|
|
rule: "hairline" | "thick" | "rainbow";
|
|
/** A colored bar along the card's top. */
|
|
topBar?: "rainbow" | "spice";
|
|
/** Underlines links, for styles whose links are the color of the text. */
|
|
linkUnderline?: boolean;
|
|
light: EmailPalette;
|
|
dark: EmailPalette;
|
|
}
|
|
|
|
/** GeoCities' rainbow, as its theme draws its dividers. */
|
|
export const RAINBOW = [
|
|
"#ff004d",
|
|
"#ff8a00",
|
|
"#ffee00",
|
|
"#00d11a",
|
|
"#00b3ff",
|
|
"#b300ff",
|
|
];
|
|
|
|
/** Every style's email look, keyed by its name. */
|
|
export const EMAIL_STYLES: Record<StyleName, EmailStyle> = {
|
|
smooth: {
|
|
font: "Saira, 'Helvetica Neue', Helvetica, Arial, sans-serif",
|
|
webFont: "Saira:wght@400;600;700",
|
|
scale: 1,
|
|
radius: { card: 14, img: 6, btn: 8, pill: 999, avatar: 16 },
|
|
title: { size: 30, weight: 600 },
|
|
heavy: 600,
|
|
frame: "none",
|
|
rule: "hairline",
|
|
light: {
|
|
outer: "#faf5e9",
|
|
card: "#fffdf8",
|
|
text: "#0e3a42",
|
|
sec: "#35626b",
|
|
muted: "#4d7178",
|
|
accent: "#b54a41",
|
|
link: "#b54a41",
|
|
onAccent: "#fffaf0",
|
|
rule: "#ebe4d4",
|
|
soft: "#f3ecdd",
|
|
quote: "#5c7d83",
|
|
},
|
|
dark: {
|
|
outer: "#01152a",
|
|
card: "#0a2139",
|
|
text: "#e9eef2",
|
|
sec: "#a3b9c7",
|
|
muted: "#7b93a6",
|
|
accent: "#f4d58d",
|
|
link: "#f4d58d",
|
|
onAccent: "#001427",
|
|
rule: "#1b3350",
|
|
soft: "#112a46",
|
|
quote: "#a3b9c7",
|
|
},
|
|
},
|
|
brutalist: {
|
|
font: "'Space Grotesk', Arial, Helvetica, sans-serif",
|
|
webFont: "Space+Grotesk:wght@400;700",
|
|
scale: 1,
|
|
radius: { card: 0, img: 0, btn: 0, pill: 0, avatar: 0 },
|
|
title: { size: 30, weight: 900, upper: true, spacing: "0.03em" },
|
|
heavy: 700,
|
|
frame: "hard",
|
|
rule: "thick",
|
|
light: {
|
|
outer: "#f4f1de",
|
|
card: "#f8f5e8",
|
|
text: "#3d405b",
|
|
sec: "#5a5e80",
|
|
muted: "#7a7e9a",
|
|
accent: "#e07a5f",
|
|
link: "#b85a3a",
|
|
onAccent: "#f4f1de",
|
|
rule: "#8a8da8",
|
|
soft: "#ede9d4",
|
|
quote: "#5a5e80",
|
|
},
|
|
dark: {
|
|
outer: "#252838",
|
|
card: "#2e3145",
|
|
text: "#f4f1de",
|
|
sec: "#c8c5b2",
|
|
muted: "#8e8c80",
|
|
accent: "#f2cc8f",
|
|
link: "#f2cc8f",
|
|
onAccent: "#3d405b",
|
|
rule: "#4a4e6a",
|
|
soft: "#222436",
|
|
quote: "#c8c5b2",
|
|
},
|
|
},
|
|
geocities: {
|
|
font:
|
|
"'Comic Sans MS', 'Comic Sans', 'Chalkboard SE', 'Comic Neue', cursive",
|
|
scale: 1,
|
|
radius: { card: 0, img: 0, btn: 0, pill: 0, avatar: 0 },
|
|
title: { size: 30, weight: 700, accent: true },
|
|
heavy: 700,
|
|
frame: "ridge",
|
|
rule: "rainbow",
|
|
topBar: "rainbow",
|
|
linkUnderline: true,
|
|
light: {
|
|
outer: "#fff0f8",
|
|
card: "#ffffff",
|
|
text: "#2a1a4e",
|
|
sec: "#aa00aa",
|
|
muted: "#7a6a9a",
|
|
accent: "#d000d0",
|
|
link: "#0000ee",
|
|
onAccent: "#ffffff",
|
|
rule: "#f0c0f0",
|
|
soft: "#fff0fb",
|
|
quote: "#aa00aa",
|
|
border: "#d000d0",
|
|
},
|
|
dark: {
|
|
outer: "#0a0a2e",
|
|
card: "#1b1b46",
|
|
text: "#f2f0ff",
|
|
sec: "#7fdbff",
|
|
muted: "#a99ad6",
|
|
accent: "#ff00ff",
|
|
link: "#00ffff",
|
|
onAccent: "#000000",
|
|
rule: "#34346e",
|
|
soft: "#14143a",
|
|
quote: "#7fdbff",
|
|
border: "#00ffff",
|
|
},
|
|
},
|
|
nyt: {
|
|
font: "'Instrument Serif', Georgia, 'Times New Roman', serif",
|
|
webFont: "Instrument+Serif:ital@0;1",
|
|
scale: 1.12,
|
|
radius: { card: 0, img: 0, btn: 0, pill: 0, avatar: 16 },
|
|
title: { size: 38, weight: 400 },
|
|
heavy: 400,
|
|
frame: "hairline",
|
|
rule: "hairline",
|
|
linkUnderline: true,
|
|
light: {
|
|
outer: "#ffffff",
|
|
card: "#ffffff",
|
|
text: "#121212",
|
|
sec: "#363636",
|
|
muted: "#767676",
|
|
accent: "#121212",
|
|
link: "#121212",
|
|
onAccent: "#ffffff",
|
|
rule: "#e2e2e2",
|
|
soft: "#f3f3f3",
|
|
quote: "#5a5a5a",
|
|
},
|
|
dark: {
|
|
outer: "#121212",
|
|
card: "#1e1e1e",
|
|
text: "#e6e6e6",
|
|
sec: "#c4c4c4",
|
|
muted: "#8a8a8a",
|
|
accent: "#ececec",
|
|
link: "#ececec",
|
|
onAccent: "#121212",
|
|
rule: "#3a3a3a",
|
|
soft: "#2a2a2a",
|
|
quote: "#ababab",
|
|
},
|
|
},
|
|
kanagawa: {
|
|
font: "'JetBrains Mono', Menlo, Consolas, 'Courier New', monospace",
|
|
webFont: "JetBrains+Mono:ital,wght@0,400;0,600;1,400",
|
|
scale: 0.9,
|
|
radius: { card: 6, img: 4, btn: 4, pill: 3, avatar: 4 },
|
|
title: { size: 26, weight: 600 },
|
|
heavy: 600,
|
|
frame: "hairline",
|
|
rule: "hairline",
|
|
light: {
|
|
outer: "#f2ecbc",
|
|
card: "#e7dfb2",
|
|
text: "#545464",
|
|
sec: "#716e61",
|
|
muted: "#766b90",
|
|
accent: "#4d699b",
|
|
link: "#4d699b",
|
|
onAccent: "#f2ecbc",
|
|
rule: "#d0c79d",
|
|
soft: "#dcd5ac",
|
|
quote: "#6f894e",
|
|
date: "#8a8980",
|
|
brand: "#624c83",
|
|
},
|
|
dark: {
|
|
outer: "#1f1f28",
|
|
card: "#2a2a37",
|
|
text: "#dcd7ba",
|
|
sec: "#c8c093",
|
|
muted: "#938aa9",
|
|
accent: "#7e9cd8",
|
|
link: "#7e9cd8",
|
|
onAccent: "#16161d",
|
|
rule: "#43435a",
|
|
soft: "#16161d",
|
|
quote: "#98bb6c",
|
|
date: "#727169",
|
|
brand: "#957fb8",
|
|
},
|
|
},
|
|
melange: {
|
|
font: "Fraunces, Georgia, serif",
|
|
webFont: "Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700",
|
|
scale: 1,
|
|
radius: { card: 18, img: 10, btn: 12, pill: 999, avatar: 16 },
|
|
title: { size: 32, weight: 600 },
|
|
heavy: 600,
|
|
frame: "soft",
|
|
rule: "hairline",
|
|
topBar: "spice",
|
|
light: {
|
|
outer: "#e9e1db",
|
|
card: "#f6f2ef",
|
|
text: "#54433a",
|
|
sec: "#7d6658",
|
|
muted: "#a98a78",
|
|
accent: "#a06d00",
|
|
link: "#a06d00",
|
|
onAccent: "#f7f4f1",
|
|
rule: "#e0d3ca",
|
|
soft: "#ece4dd",
|
|
quote: "#7d6658",
|
|
shadow: "rgba(84,67,58,0.28)",
|
|
spice: ["#a06d00", "#bc5c00", "#c77b8b", "#be79bb", "#6e9b72"],
|
|
},
|
|
dark: {
|
|
outer: "#292522",
|
|
card: "#34302c",
|
|
text: "#ece1d7",
|
|
sec: "#c1a78e",
|
|
muted: "#867462",
|
|
accent: "#ebc06d",
|
|
link: "#ebc06d",
|
|
onAccent: "#292522",
|
|
rule: "#4a423b",
|
|
soft: "#403a36",
|
|
quote: "#c1a78e",
|
|
shadow: "rgba(0,0,0,0.5)",
|
|
spice: ["#ebc06d", "#e49b5d", "#bd8183", "#b380b0", "#78997a"],
|
|
},
|
|
},
|
|
};
|