版本号 3.2.7 -> 3.2.8(邮件配色整封同色系化 + 对比度修复)
邮件模板原来只有品牌条一个色块,其余大面积底色全是中性灰, 主色稍朴素(主题默认 #24292e)整封信看起来就是黑白的。 - 新增 yang_mail_palette():底色/描边/强调文字全部从主色同色系派生 - 新增 yang_mail_chroma()/hsl()/hsl_hex()/ensure_chroma()/has_hue() - 修复深浅判定:白字对比度 = 黑字对比度的交叉点在 L≈0.179,不是 0.5。 用 0.5 会把亮蓝 #00a2ff(L=0.331)判成深色配白字,只有 2.76:1 - 新增 yang_mail_fg_on():在白与深之间取对比度更高者, 中灰 #808080 上从 4.41 提到 5.32(AA 达标) - 修复:与白混合是同比压缩色度,0.94 档压 16 倍 ⇒ 近中性主色 派生出的浅底只剩 0.002,物理不可见。加彩度兜底,按各自亮度补足 - 修复:页脚改浅底后 12px 说明文字降到 4.26:1,muted 改为按背景推导 - 品牌条/按钮仍严格用用户填的原色,补彩度只作用于派生色
This commit is contained in:
+361
-27
@@ -69,21 +69,343 @@ function yang_mail_shade($hex, $ratio = 0.82)
|
|||||||
*
|
*
|
||||||
* 用相对亮度公式(WCAG),不是简单取平均 —— 人眼对绿最敏感、蓝最不敏感,
|
* 用相对亮度公式(WCAG),不是简单取平均 —— 人眼对绿最敏感、蓝最不敏感,
|
||||||
* 直接平均会把 #00ff00 判成中等亮度。
|
* 直接平均会把 #00ff00 判成中等亮度。
|
||||||
|
*
|
||||||
|
* ── 阈值为什么是 0.179 而不是 0.5 ────────────────────────────
|
||||||
|
* 「白字对比度 = 黑字对比度」的那个点,可以解出来:
|
||||||
|
* 白字对比度 = 1.05 / (L + 0.05)
|
||||||
|
* 黑字对比度 = (L + 0.05) / 0.05
|
||||||
|
* 两者相等 ⇒ (L + 0.05)² = 0.0525 ⇒ L ≈ 0.179
|
||||||
|
* 所以在 L = 0.179 处,白字与黑字一样清楚;低于它白字更好,高于它黑字更好。
|
||||||
|
* 之前写 0.5,把一整段"其实该用深色字"的中间亮度全划成了"深色",
|
||||||
|
* 典型受害者就是亮蓝 #00a2ff(L=0.331):被判成深色 ⇒ 压白字 ⇒ 只有 2.76:1,
|
||||||
|
* 远低于 AA 的 4.5,品牌条上的站名其实很难看清。按 0.179 判定后走深色字,6.31:1。
|
||||||
|
*
|
||||||
|
* ⚠️ 这个函数只回答"深还是浅",**不负责保证对比度**。中灰 #808080 落在
|
||||||
|
* 交叉点附近,白字 3.95、深字 4.41 —— 两个都不到 4.5,但必须选一个。
|
||||||
|
* 所以配字时不要直接用它,用 yang_mail_fg_on():那里是"黑白里取更优",
|
||||||
|
* 顺带在都不达标时退回纯黑/纯白,能多榨出一两档。
|
||||||
*/
|
*/
|
||||||
function yang_mail_is_dark($hex)
|
function yang_mail_is_dark($hex)
|
||||||
|
{
|
||||||
|
return yang_mail_lum($hex) < 0.179;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 相对亮度(WCAG,0=纯黑 1=纯白)
|
||||||
|
*/
|
||||||
|
function yang_mail_lum($hex)
|
||||||
{
|
{
|
||||||
$hex = ltrim((string) $hex, '#');
|
$hex = ltrim((string) $hex, '#');
|
||||||
if (strlen($hex) !== 6) {
|
if (!preg_match('/^[0-9a-fA-F]{6}$/', $hex)) {
|
||||||
return true;
|
return 0.0;
|
||||||
|
}
|
||||||
|
$lin = function ($c) {
|
||||||
|
$c /= 255;
|
||||||
|
return $c <= 0.03928 ? $c / 12.92 : pow(($c + 0.055) / 1.055, 2.4);
|
||||||
|
};
|
||||||
|
return 0.2126 * $lin(hexdec(substr($hex, 0, 2)))
|
||||||
|
+ 0.7152 * $lin(hexdec(substr($hex, 2, 2)))
|
||||||
|
+ 0.0722 * $lin(hexdec(substr($hex, 4, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给某个底色配文字色:在白与深之间取**对比度更高**的那个
|
||||||
|
*
|
||||||
|
* 为什么不是"深色就配白、浅色就配深":中灰区(如 #808080)两边都不达标
|
||||||
|
* (白字 3.95、深字 4.41),二元判定的答案取决于阈值落在哪一侧,
|
||||||
|
* 而阈值本身对两个都不达标的情况毫无意义。取更优的那个至少是当前最好的选择,
|
||||||
|
* 而且这个判据跟"阈值该定多少"完全解耦 —— 换阈值也不会选错。
|
||||||
|
*/
|
||||||
|
function yang_mail_fg_on($bg)
|
||||||
|
{
|
||||||
|
$white = yang_mail_contrast('#ffffff', $bg);
|
||||||
|
$dark = yang_mail_contrast('#1a1a1a', $bg);
|
||||||
|
if ($white >= $dark) {
|
||||||
|
return '#ffffff';
|
||||||
|
}
|
||||||
|
// 深字够不着 AA 时,纯黑还能再挤一点(#808080 上 4.41 → 5.32)
|
||||||
|
return yang_mail_contrast('#000000', $bg) > $dark ? '#000000' : '#1a1a1a';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取一个颜色的「色度」(chroma)—— 三个通道的最大差
|
||||||
|
*
|
||||||
|
* ── 为什么不用 HSL 饱和度判断"有没有颜色" ────────────────────
|
||||||
|
* HSL 的 S 在高亮度区会虚高。实测派生底 #f0f9ff(几乎纯白带一点点蓝)
|
||||||
|
* 的 HSL 饱和度是 **1.00**,而 #e5e5e6(纯灰)是 0.005 —— 看起来判据很灵。
|
||||||
|
* 但 #e9eaec 这种"肉眼就是灰"的颜色,HSL 饱和度也有 0.12,
|
||||||
|
* 会被判成"有颜色"。真正决定人眼能否看出颜色的,是通道极差(色度):
|
||||||
|
* #f0f9ff → 0.059(看得出是极浅蓝)
|
||||||
|
* #e9eaec → 0.012(看不出,就是灰)
|
||||||
|
* 两者 HSL 饱和度一个是 1.00、一个 0.12,顺序跟人的感受**正好相反**。
|
||||||
|
* 所以这里一律用色度,不用 S。
|
||||||
|
*/
|
||||||
|
function yang_mail_chroma($hex)
|
||||||
|
{
|
||||||
|
$hex = ltrim((string) $hex, '#');
|
||||||
|
if (!preg_match('/^[0-9a-fA-F]{6}$/', $hex)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
$v = array(
|
||||||
|
hexdec(substr($hex, 0, 2)),
|
||||||
|
hexdec(substr($hex, 2, 2)),
|
||||||
|
hexdec(substr($hex, 4, 2)),
|
||||||
|
);
|
||||||
|
return (max($v) - min($v)) / 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把「有色相但太淡」的颜色沿**它自己的色相**加饱和,直到色度达到下限
|
||||||
|
*
|
||||||
|
* ── 为什么需要这一步 ────────────────────────────────────────
|
||||||
|
* 主题默认主色是 #24292e,色度只有 0.039(几乎是中性深灰)。
|
||||||
|
* 往白里兑出浅底后色度只剩 0.002 —— 物理上不可见,整封邮件仍旧是黑白的,
|
||||||
|
* 用户看到的还是「模板没做好」。但「从灰里调出彩色」在数学上不可能,
|
||||||
|
* 而主题默认值就是它,所以必须有一个补饱和的环节。
|
||||||
|
*
|
||||||
|
* 只动饱和度,**色相与亮度一律不动** —— 派生色仍属同一色系,品牌识别没丢。
|
||||||
|
*/
|
||||||
|
function yang_mail_ensure_chroma($hex, $minChroma)
|
||||||
|
{
|
||||||
|
if (yang_mail_chroma($hex) >= $minChroma) {
|
||||||
|
return $hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($hue, $l) = yang_mail_hsl($hex);
|
||||||
|
$den = 1 - abs(2 * $l - 1);
|
||||||
|
if ($den < 1e-6) {
|
||||||
|
// 纯白或纯黑:没有色相可言
|
||||||
|
return $hex;
|
||||||
|
}
|
||||||
|
$s0 = yang_mail_chroma($hex) / $den;
|
||||||
|
|
||||||
|
/* 逐档搜索而不是解析求解:色度是 max-min,跟 S 的关系在 l>0.5 与
|
||||||
|
* l<0.5 两侧公式不同,硬推容易在边界写错;这里步数只有几十。 */
|
||||||
|
for ($s = $s0; $s <= 0.95; $s += 0.02) {
|
||||||
|
$out = yang_mail_hsl_hex($hue, $s, $l);
|
||||||
|
if (yang_mail_chroma($out) >= $minChroma) {
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yang_mail_hsl_hex($hue, 0.95, $l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个颜色有没有「真实色相」(可以放心拿它的色相去染色)
|
||||||
|
*
|
||||||
|
* ── 为什么判据放在源头,而不是放在每个派生色上 ──────────────────
|
||||||
|
* 与白色混合是**同比压缩**色度:0.94 那一档压 16 倍。
|
||||||
|
* 所以"派生出的浅底色度很低"是**必然现象**,不是"这个颜色没有色相"。
|
||||||
|
* 早先的实现在 yang_mail_ensure_chroma() 里对入参做 0.02 死区判断,
|
||||||
|
* 于是极浅底(混合后 0.008)被当成"没有色相"直接跳过,
|
||||||
|
* 兜底等于没做 —— 恰恰是最需要兜底的那一档失效了。
|
||||||
|
*
|
||||||
|
* 真正该判断的是**种子**:种子的色度 ≥ 0.02 说明它有一个真实可辨的色相
|
||||||
|
* (#24292e 是 0.039,属"有一点点真实的蓝",可以放大);
|
||||||
|
* 低于 0.02 的(#808080 这种纯中性灰)色相是浮点噪声,按噪声染色
|
||||||
|
* 等于随机配色,还会让「主色本来就没有色相」变得不可观测 —— 那就不染。
|
||||||
|
*/
|
||||||
|
function yang_mail_has_hue($hex)
|
||||||
|
{
|
||||||
|
return yang_mail_chroma($hex) >= 0.02;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #rrggbb → array(色相 0~1, 亮度 0~1)
|
||||||
|
*/
|
||||||
|
function yang_mail_hsl($hex)
|
||||||
|
{
|
||||||
|
$hex = ltrim((string) $hex, '#');
|
||||||
|
if (!preg_match('/^[0-9a-fA-F]{6}$/', $hex)) {
|
||||||
|
return array(0.0, 0.0);
|
||||||
}
|
}
|
||||||
$r = hexdec(substr($hex, 0, 2)) / 255;
|
$r = hexdec(substr($hex, 0, 2)) / 255;
|
||||||
$g = hexdec(substr($hex, 2, 2)) / 255;
|
$g = hexdec(substr($hex, 2, 2)) / 255;
|
||||||
$b = hexdec(substr($hex, 4, 2)) / 255;
|
$b = hexdec(substr($hex, 4, 2)) / 255;
|
||||||
|
$mx = max($r, $g, $b);
|
||||||
|
$mn = min($r, $g, $b);
|
||||||
|
$l = ($mx + $mn) / 2;
|
||||||
|
$d = $mx - $mn;
|
||||||
|
if ($d < 1e-9) {
|
||||||
|
return array(0.0, $l);
|
||||||
|
}
|
||||||
|
if ($mx === $r) {
|
||||||
|
$h = (($g - $b) / $d + ($g < $b ? 6 : 0)) / 6;
|
||||||
|
} elseif ($mx === $g) {
|
||||||
|
$h = (($b - $r) / $d + 2) / 6;
|
||||||
|
} else {
|
||||||
|
$h = (($r - $g) / $d + 4) / 6;
|
||||||
|
}
|
||||||
|
return array($h, $l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HSL → #rrggbb(h 为 0~1)
|
||||||
|
*/
|
||||||
|
function yang_mail_hsl_hex($h, $s, $l)
|
||||||
|
{
|
||||||
|
$hue2rgb = function ($p, $q, $t) {
|
||||||
|
if ($t < 0) {
|
||||||
|
$t += 1;
|
||||||
|
}
|
||||||
|
if ($t > 1) {
|
||||||
|
$t -= 1;
|
||||||
|
}
|
||||||
|
if ($t < 1 / 6) {
|
||||||
|
return $p + ($q - $p) * 6 * $t;
|
||||||
|
}
|
||||||
|
if ($t < 1 / 2) {
|
||||||
|
return $q;
|
||||||
|
}
|
||||||
|
if ($t < 2 / 3) {
|
||||||
|
return $p + ($q - $p) * (2 / 3 - $t) * 6;
|
||||||
|
}
|
||||||
|
return $p;
|
||||||
|
};
|
||||||
|
if ($s <= 0) {
|
||||||
|
$v = (int) round($l * 255);
|
||||||
|
return sprintf('#%02x%02x%02x', $v, $v, $v);
|
||||||
|
}
|
||||||
|
$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
|
||||||
|
$p = 2 * $l - $q;
|
||||||
|
return sprintf(
|
||||||
|
'#%02x%02x%02x',
|
||||||
|
(int) round(255 * $hue2rgb($p, $q, $h + 1 / 3)),
|
||||||
|
(int) round(255 * $hue2rgb($p, $q, $h)),
|
||||||
|
(int) round(255 * $hue2rgb($p, $q, $h - 1 / 3))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成整封邮件用的配色表(全部从主色派生,同色系)
|
||||||
|
*
|
||||||
|
* ── 为什么整封邮件不能是灰的 ──────────────────────────────────
|
||||||
|
* 早先模板里只有品牌条、徽标、按钮、最后一块引用块的竖线用主色,
|
||||||
|
* 其余 90% 面积(标题、正文、引用块底色、信息表分隔线、页脚)全是中性灰
|
||||||
|
* (#1a2028 / #5f6b78 / #c2cbd4 / #edf0f3)。主色若本身不够艳
|
||||||
|
* (比如主题默认的 #24292e 是接近中性的深灰蓝),整封信看起来就是黑白的。
|
||||||
|
* 「彩色」不是靠一个大色块,而是让**底色、描边、强调文字**都在同一色系里。
|
||||||
|
*
|
||||||
|
* ── 为什么要算而不是写死 ──────────────────────────────────────
|
||||||
|
* 主色是用户在主题选项里改的,写死一套蓝就没有意义了。
|
||||||
|
* 这里从主色派生出:极浅底(区块背景)、浅底(页脚背景)、
|
||||||
|
* 中档描边、深档文字(保证对浅底 ≥4.5:1)。
|
||||||
|
*
|
||||||
|
* ── 派生为什么不能只是「与白混合」────────────────────────────
|
||||||
|
* 混合是同比压缩色度:0.94 那一档把色度压 16 倍。主色是 #24292e 这种
|
||||||
|
* 近中性深灰(色度 0.039)时,兑完只剩 0.002,肉眼与纯灰无异 ——
|
||||||
|
* 这正是「换成默认主色后邮件又变黑白」的机制。
|
||||||
|
* 所以浅底在混合之后还要过一道 yang_mail_ensure_chroma(),按**它自己所在
|
||||||
|
* 亮度**补一个可见的色度下限(浅底用 MIN_CHROMA_TINT,中档用 MIN_CHROMA_MID)。
|
||||||
|
* 这一步只动饱和度、不动色相和亮度,颜色仍是主色的同一色系。
|
||||||
|
*
|
||||||
|
* ── 品牌条为什么仍用原色 ────────────────────────────────────
|
||||||
|
* 用户直接看到主色的地方(品牌条、按钮、徽标)一律用他填的原色。
|
||||||
|
* 他选了深灰蓝,就不该在标题栏上看到它被换成亮蓝。补饱和只用于
|
||||||
|
* 「主色的稀释/加深」,那是他自己没指定过的颜色。
|
||||||
|
*
|
||||||
|
* 全部输出**实色**,不用 rgba:Outlook(Word 引擎)不认 rgba,会整条丢掉背景。
|
||||||
|
*/
|
||||||
|
function yang_mail_palette($brand = null)
|
||||||
|
{
|
||||||
|
if ($brand === null) {
|
||||||
|
$brand = yang_mail_brand_color();
|
||||||
|
}
|
||||||
|
$brand = ltrim((string) $brand, '#');
|
||||||
|
if (!preg_match('/^[0-9a-fA-F]{6}$/', $brand)) {
|
||||||
|
$brand = '24292e';
|
||||||
|
}
|
||||||
|
$brand = '#' . $brand;
|
||||||
|
|
||||||
|
/* 派生用的种子:中性一点的先提饱和,否则这一整套派生色都将是灰的。
|
||||||
|
* 只在主色有**真实色相**时才放大(见 yang_mail_has_hue);
|
||||||
|
* 零色相的纯灰宁可保持灰,也不要凭空编一个色相。 */
|
||||||
|
$seed = yang_mail_has_hue($brand) ? yang_mail_ensure_chroma($brand, 0.30) : $brand;
|
||||||
|
|
||||||
|
$onBrand = yang_mail_fg_on($brand);
|
||||||
|
|
||||||
|
/* 浅底:混合后按自身亮度再补一次色度。
|
||||||
|
* 极浅底(ratio 0.94)本身亮度很高,能容纳的色度有限,
|
||||||
|
* 所以下限分两档 —— 用同一个值会让极浅底被迫变深、不再是"浅底"。
|
||||||
|
* 主色零色相时不补:那套灰是事实,硬染就是编造。 */
|
||||||
|
$keep = yang_mail_has_hue($brand);
|
||||||
|
$tintXX = $keep ? yang_mail_ensure_chroma(yang_mail_tint($seed, 0.94), 0.045)
|
||||||
|
: yang_mail_tint($seed, 0.94);
|
||||||
|
$tintX = $keep ? yang_mail_ensure_chroma(yang_mail_tint($seed, 0.88), 0.075)
|
||||||
|
: yang_mail_tint($seed, 0.88);
|
||||||
|
$tintLine = $keep ? yang_mail_ensure_chroma(yang_mail_tint($seed, 0.80), 0.110)
|
||||||
|
: yang_mail_tint($seed, 0.80);
|
||||||
|
$tintMid = $keep ? yang_mail_ensure_chroma(yang_mail_tint($seed, 0.55), 0.220)
|
||||||
|
: yang_mail_tint($seed, 0.55);
|
||||||
|
$pageBg = $keep ? yang_mail_ensure_chroma(yang_mail_tint($seed, 0.90), 0.065)
|
||||||
|
: yang_mail_tint($seed, 0.90);
|
||||||
|
|
||||||
|
/* 强调文字色:从种子一路压暗,取第一个"在白底和极浅底上都 ≥4.5"的档位。
|
||||||
|
* 不能直接拿主色当文字色 —— 亮蓝 #00a2ff 在白底只有 2.76:1。
|
||||||
|
* 也不能无脑取很深,那样就看不出是"主色的深版"了,所以要取**最亮的达标档**。 */
|
||||||
|
$textOnTint = $seed;
|
||||||
|
for ($s = 0.20; $s <= 0.90; $s += 0.02) {
|
||||||
|
$c = yang_mail_shade($seed, $s);
|
||||||
|
if (yang_mail_contrast($c, '#ffffff') >= 4.5
|
||||||
|
&& yang_mail_contrast($c, $tintXX) >= 4.5) {
|
||||||
|
$textOnTint = $c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 次要文字色:原来是固定的 #5c6b7a,但页脚底色改成了主色浅底之后,
|
||||||
|
* 这个固定值在浅蓝上只有 4.26:1 —— 12px 小字不达 AA。
|
||||||
|
* 「文字色写死、背景色随主色变」这个组合本身就是错的:背景一变浅,
|
||||||
|
* 固定灰就压不住。所以改成从基准灰往下压,压到在**它实际会遇到的
|
||||||
|
* 两种背景**(页脚浅底、邮件最外层)上都达标为止。 */
|
||||||
|
$muted = '#5c6b7a';
|
||||||
|
for ($t = 1.0; $t >= 0.55; $t -= 0.02) {
|
||||||
|
$cand = $t >= 0.999 ? $muted : yang_mail_shade($muted, $t);
|
||||||
|
if (yang_mail_contrast($cand, $tintX) >= 4.5
|
||||||
|
&& yang_mail_contrast($cand, $pageBg) >= 4.5) {
|
||||||
|
$muted = $cand;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'brand' => $brand, // 主色(品牌条、按钮)
|
||||||
|
'brand_dark' => yang_mail_shade($brand, 0.78), // 品牌条下沿深色带
|
||||||
|
'on_brand' => $onBrand, // 压在品牌色上的字
|
||||||
|
'text' => $textOnTint, // 同色系的强调文字(标题/标签)
|
||||||
|
'tint_xx' => $tintXX, // 极浅底:引用块、信息表
|
||||||
|
'tint_x' => $tintX, // 稍深一点的浅底:页脚
|
||||||
|
'tint_line' => $tintLine, // 浅底上的描边/分隔线
|
||||||
|
'tint_mid' => $tintMid, // 中档:弱化竖线、次要描边
|
||||||
|
'page_bg' => $pageBg, // 邮件最外层背景
|
||||||
|
'body' => '#243040', // 正文(保持深灰蓝,长文好读)
|
||||||
|
'muted' => $muted, // 次要文字(时间、页脚),按背景调过
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两色的 WCAG 对比度
|
||||||
|
*
|
||||||
|
* 给配色表自检用:派生颜色是否达标,靠算而不是靠眼睛看。
|
||||||
|
*/
|
||||||
|
function yang_mail_contrast($fg, $bg)
|
||||||
|
{
|
||||||
|
$lumOf = function ($hex) {
|
||||||
|
$hex = ltrim((string) $hex, '#');
|
||||||
|
if (strlen($hex) !== 6) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
$lin = function ($c) {
|
$lin = function ($c) {
|
||||||
|
$c /= 255;
|
||||||
return $c <= 0.03928 ? $c / 12.92 : pow(($c + 0.055) / 1.055, 2.4);
|
return $c <= 0.03928 ? $c / 12.92 : pow(($c + 0.055) / 1.055, 2.4);
|
||||||
};
|
};
|
||||||
$L = 0.2126 * $lin($r) + 0.7152 * $lin($g) + 0.0722 * $lin($b);
|
return 0.2126 * $lin(hexdec(substr($hex, 0, 2)))
|
||||||
return $L < 0.5;
|
+ 0.7152 * $lin(hexdec(substr($hex, 2, 2)))
|
||||||
|
+ 0.0722 * $lin(hexdec(substr($hex, 4, 2)));
|
||||||
|
};
|
||||||
|
$a = $lumOf($fg);
|
||||||
|
$b = $lumOf($bg);
|
||||||
|
return (max($a, $b) + 0.05) / (min($a, $b) + 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -218,9 +540,21 @@ function yang_mail_render($args)
|
|||||||
$quoteList = array($d['quote']);
|
$quoteList = array($d['quote']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$brand = yang_mail_brand_color();
|
/* 整套配色从主色派生一次,后面所有颜色都从这里取。
|
||||||
$brandD = yang_mail_shade($brand, 0.78);
|
* 不在这里散落写死 #xxxxxx —— 那样主色一改,「彩色」就断了,
|
||||||
$onBrand = yang_mail_is_dark($brand) ? '#ffffff' : '#1a1a1a';
|
* 又退回成"一个色块 + 一堆灰」。 */
|
||||||
|
$pal = yang_mail_palette();
|
||||||
|
$brand = $pal['brand'];
|
||||||
|
$brandD = $pal['brand_dark'];
|
||||||
|
$onBrand = $pal['on_brand'];
|
||||||
|
$cText = $pal['text'];
|
||||||
|
$cBody = $pal['body'];
|
||||||
|
$cMuted = $pal['muted'];
|
||||||
|
$cTintXX = $pal['tint_xx'];
|
||||||
|
$cTintX = $pal['tint_x'];
|
||||||
|
$cTintLine = $pal['tint_line'];
|
||||||
|
$cTintMid = $pal['tint_mid'];
|
||||||
|
$cPageBg = $pal['page_bg'];
|
||||||
|
|
||||||
// badge 的背景:把主色往白色方向提亮一档,得到比主色浅、但同色相的实色。
|
// badge 的背景:把主色往白色方向提亮一档,得到比主色浅、但同色相的实色。
|
||||||
// 主色本身是深色时提亮 22%,浅色时压暗 —— 否则浅主色上叠浅 badge 会看不见。
|
// 主色本身是深色时提亮 22%,浅色时压暗 —— 否则浅主色上叠浅 badge 会看不见。
|
||||||
@@ -256,22 +590,22 @@ function yang_mail_render($args)
|
|||||||
</style>
|
</style>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
</head>
|
</head>
|
||||||
<body style="margin:0;padding:0;width:100%;background-color:#eef1f4;">
|
<body style="margin:0;padding:0;width:100%;background-color:<?php echo esc_attr($cPageBg); ?>;">
|
||||||
|
|
||||||
<?php /* 预读文字:显示在收件箱列表里标题的后面。藏起来但必须真实存在,且要足够长,
|
<?php /* 预读文字:显示在收件箱列表里标题的后面。藏起来但必须真实存在,且要足够长,
|
||||||
否则客户端会把正文第一句抓上去当摘要。 */ ?>
|
否则客户端会把正文第一句抓上去当摘要。 */ ?>
|
||||||
<div style="display:none;font-size:1px;color:#eef1f4;line-height:1px;max-height:0;max-width:0;opacity:0;overflow:hidden;">
|
<div style="display:none;font-size:1px;color:<?php echo esc_attr($cPageBg); ?>;line-height:1px;max-height:0;max-width:0;opacity:0;overflow:hidden;">
|
||||||
<?php echo esc_html($d['preheader'] ? $d['preheader'] : $d['heading']); ?>
|
<?php echo esc_html($d['preheader'] ? $d['preheader'] : $d['heading']); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="background-color:#eef1f4;">
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="background-color:<?php echo esc_attr($cPageBg); ?>;">
|
||||||
<tr><td align="center" style="padding:32px 12px;">
|
<tr><td align="center" style="padding:32px 12px;">
|
||||||
|
|
||||||
<?php /* 宽度写法是「混合式」:
|
<?php /* 宽度写法是「混合式」:
|
||||||
· Outlook(Word 引擎)只认 HTML 属性 width="600",CSS 的 max-width 它当没看见;
|
· Outlook(Word 引擎)只认 HTML 属性 width="600",CSS 的 max-width 它当没看见;
|
||||||
· 其它客户端认 CSS,于是 width:100% + max-width:600px 让手机窄屏自动收窄。
|
· 其它客户端认 CSS,于是 width:100% + max-width:600px 让手机窄屏自动收窄。
|
||||||
两套并存,各取所需。只写死 600px 会让手机横向滚动——实测过。 */ ?>
|
两套并存,各取所需。只写死 600px 会让手机横向滚动——实测过。 */ ?>
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="600" role="presentation" style="width:100%;max-width:600px;background-color:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e3e7ec;">
|
<table border="0" cellpadding="0" cellspacing="0" width="600" role="presentation" style="width:100%;max-width:600px;background-color:#ffffff;border-radius:12px;overflow:hidden;border:1px solid <?php echo esc_attr($cTintLine); ?>;">
|
||||||
|
|
||||||
<?php /* ── 顶部品牌条 ── */ ?>
|
<?php /* ── 顶部品牌条 ── */ ?>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -309,13 +643,13 @@ function yang_mail_render($args)
|
|||||||
<td style="padding:34px 32px 8px 32px;">
|
<td style="padding:34px 32px 8px 32px;">
|
||||||
|
|
||||||
<?php if ($d['heading'] !== ''): ?>
|
<?php if ($d['heading'] !== ''): ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:21px;font-weight:600;color:#1a2028;line-height:1.45;margin:0 0 12px 0;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:21px;font-weight:600;color:<?php echo esc_attr($cText); ?>;line-height:1.45;margin:0 0 12px 0;">
|
||||||
<?php echo esc_html($d['heading']); ?>
|
<?php echo esc_html($d['heading']); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($d['intro'] !== ''): ?>
|
<?php if ($d['intro'] !== ''): ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:15px;color:#4a5560;line-height:1.7;margin:0 0 22px 0;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:15px;color:<?php echo esc_attr($cBody); ?>;line-height:1.7;margin:0 0 22px 0;">
|
||||||
<?php echo esc_html($d['intro']); ?>
|
<?php echo esc_html($d['intro']); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -330,23 +664,23 @@ function yang_mail_render($args)
|
|||||||
* 只有一个块时它既是第一也是最后 ⇒ 拿到主色,与旧版行为一致。
|
* 只有一个块时它既是第一也是最后 ⇒ 拿到主色,与旧版行为一致。
|
||||||
*/
|
*/
|
||||||
$isLast = ($qi === count($quoteList) - 1);
|
$isLast = ($qi === count($quoteList) - 1);
|
||||||
$barColor = $isLast ? $brand : '#c2cbd4';
|
$barColor = $isLast ? $brand : $cTintMid;
|
||||||
$barBg = $isLast ? '#f6f8fa' : '#fafbfc';
|
$barBg = $isLast ? $cTintXX : $cTintX;
|
||||||
$barMargin = $isLast ? '22px' : '14px';
|
$barMargin = $isLast ? '22px' : '14px';
|
||||||
?>
|
?>
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="margin:0 0 <?php echo $barMargin; ?> 0;">
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="margin:0 0 <?php echo $barMargin; ?> 0;">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="border-left:3px solid <?php echo esc_attr($barColor); ?>;background-color:<?php echo esc_attr($barBg); ?>;padding:16px 18px;border-radius:0 8px 8px 0;">
|
<td style="border-left:3px solid <?php echo esc_attr($barColor); ?>;background-color:<?php echo esc_attr($barBg); ?>;padding:16px 18px;border-radius:0 8px 8px 0;">
|
||||||
<?php if (!empty($q['label'])): ?>
|
<?php if (!empty($q['label'])): ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:12px;font-weight:600;color:#5f6b78;letter-spacing:0.6px;text-transform:uppercase;margin:0 0 8px 0;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:12px;font-weight:600;color:<?php echo esc_attr($cText); ?>;letter-spacing:0.6px;text-transform:uppercase;margin:0 0 8px 0;">
|
||||||
<?php echo esc_html($q['label']); ?>
|
<?php echo esc_html($q['label']); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:15px;color:#2d3740;line-height:1.7;word-break:break-word;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:15px;color:<?php echo esc_attr($cBody); ?>;line-height:1.7;word-break:break-word;">
|
||||||
<?php echo nl2br(esc_html(isset($q['content']) ? $q['content'] : '')); ?>
|
<?php echo nl2br(esc_html(isset($q['content']) ? $q['content'] : '')); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php if (!empty($q['meta'])): ?>
|
<?php if (!empty($q['meta'])): ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:13px;color:#5f6b78;line-height:1.6;margin:9px 0 0 0;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:13px;color:<?php echo esc_attr($cMuted); ?>;line-height:1.6;margin:9px 0 0 0;">
|
||||||
<?php echo esc_html($q['meta']); ?>
|
<?php echo esc_html($q['meta']); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -358,13 +692,13 @@ function yang_mail_render($args)
|
|||||||
<?php if (!empty($d['rows'])): ?>
|
<?php if (!empty($d['rows'])): ?>
|
||||||
<?php /* 信息表:两列。label 列定宽 88px 并右对齐,value 左对齐,视觉上最稳。
|
<?php /* 信息表:两列。label 列定宽 88px 并右对齐,value 左对齐,视觉上最稳。
|
||||||
不用 table-layout:fixed 之外的花样,Outlook 只认这个。 */ ?>
|
不用 table-layout:fixed 之外的花样,Outlook 只认这个。 */ ?>
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="margin:0 0 24px 0;border-top:1px solid #edf0f3;">
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation" style="margin:0 0 24px 0;border-top:1px solid <?php echo esc_attr($cTintLine); ?>;">
|
||||||
<?php foreach ($d['rows'] as $row): ?>
|
<?php foreach ($d['rows'] as $row): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="88" valign="top" style="width:88px;padding:11px 0;border-bottom:1px solid #edf0f3;font-family:<?php echo $sans; ?>;font-size:13px;color:#5f6b78;line-height:1.6;white-space:nowrap;">
|
<td width="88" valign="top" style="width:88px;padding:11px 0;border-bottom:1px solid <?php echo esc_attr($cTintLine); ?>;font-family:<?php echo $sans; ?>;font-size:13px;color:<?php echo esc_attr($cMuted); ?>;line-height:1.6;white-space:nowrap;">
|
||||||
<?php echo esc_html($row['label']); ?>
|
<?php echo esc_html($row['label']); ?>
|
||||||
</td>
|
</td>
|
||||||
<td valign="top" style="padding:11px 0 11px 16px;border-bottom:1px solid #edf0f3;font-family:<?php echo $sans; ?>;font-size:14px;color:#2d3740;line-height:1.6;word-break:break-word;">
|
<td valign="top" style="padding:11px 0 11px 16px;border-bottom:1px solid <?php echo esc_attr($cTintLine); ?>;font-family:<?php echo $sans; ?>;font-size:14px;color:<?php echo esc_attr($cBody); ?>;line-height:1.6;word-break:break-word;">
|
||||||
<?php
|
<?php
|
||||||
$v = isset($row['value']) ? $row['value'] : '';
|
$v = isset($row['value']) ? $row['value'] : '';
|
||||||
if (!empty($row['strong'])) {
|
if (!empty($row['strong'])) {
|
||||||
@@ -386,7 +720,7 @@ function yang_mail_render($args)
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($d['note'] !== ''): ?>
|
<?php if ($d['note'] !== ''): ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:13px;color:#5f6b78;line-height:1.7;padding:16px 0 0 0;border-top:1px solid #edf0f3;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:13px;color:<?php echo esc_attr($cMuted); ?>;line-height:1.7;padding:16px 0 0 0;border-top:1px solid <?php echo esc_attr($cTintLine); ?>;">
|
||||||
<?php echo esc_html($d['note']); ?>
|
<?php echo esc_html($d['note']); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -396,13 +730,13 @@ function yang_mail_render($args)
|
|||||||
|
|
||||||
<?php /* ── 页脚 ── */ ?>
|
<?php /* ── 页脚 ── */ ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:24px 32px 30px 32px;">
|
<td bgcolor="<?php echo esc_attr($cTintX); ?>" style="background-color:<?php echo esc_attr($cTintX); ?>;padding:24px 32px 30px 32px;">
|
||||||
<div style="border-top:1px solid #edf0f3;padding-top:20px;font-family:<?php echo $sans; ?>;font-size:12px;color:#5f6b78;line-height:1.9;">
|
<div style="border-top:1px solid <?php echo esc_attr($cTintLine); ?>;padding-top:20px;font-family:<?php echo $sans; ?>;font-size:12px;color:<?php echo esc_attr($cMuted); ?>;line-height:1.9;">
|
||||||
<div style="margin:0 0 6px 0;">
|
<div style="margin:0 0 6px 0;">
|
||||||
<a href="<?php echo esc_url($home); ?>" target="_blank" rel="noopener" style="color:#3d4752;text-decoration:none;font-weight:600;"><?php echo esc_html($site); ?></a>
|
<a href="<?php echo esc_url($home); ?>" target="_blank" rel="noopener" style="color:<?php echo esc_attr($cText); ?>;text-decoration:none;font-weight:600;"><?php echo esc_html($site); ?></a>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin:0;">此邮件由系统自动发出,请勿直接回复。</div>
|
<div style="margin:0;">此邮件由系统自动发出,请勿直接回复。</div>
|
||||||
<div style="margin:6px 0 0 0;color:#6b7684;">© <?php echo esc_html($year); ?> <?php echo esc_html($site); ?></div>
|
<div style="margin:6px 0 0 0;color:<?php echo esc_attr($cMuted); ?>;">© <?php echo esc_html($year); ?> <?php echo esc_html($site); ?></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -410,7 +744,7 @@ function yang_mail_render($args)
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<?php /* 底部说明:为什么收到这封邮件。放在卡片外面,视觉上属于系统信息 */ ?>
|
<?php /* 底部说明:为什么收到这封邮件。放在卡片外面,视觉上属于系统信息 */ ?>
|
||||||
<div style="font-family:<?php echo $sans; ?>;font-size:12px;color:#5f6b78;line-height:1.7;padding:18px 8px 0 8px;max-width:600px;">
|
<div style="font-family:<?php echo $sans; ?>;font-size:12px;color:<?php echo esc_attr($cMuted); ?>;line-height:1.7;padding:18px 8px 0 8px;max-width:600px;">
|
||||||
您收到这封邮件,是因为本站记录了与您邮箱相关的订阅或互动。
|
您收到这封邮件,是因为本站记录了与您邮箱相关的订阅或互动。
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Theme Name: Kratos_Yang
|
Theme Name: Kratos_Yang
|
||||||
Text Domain: kratos_Yang
|
Text Domain: kratos_Yang
|
||||||
Version: 3.2.7
|
Version: 3.2.8
|
||||||
Requires PHP: 7.0
|
Requires PHP: 7.0
|
||||||
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
||||||
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
||||||
|
|||||||
Reference in New Issue
Block a user