Files
wordpress-kratos-yang/assets/css/lightbox.css
T
laoyang 7125fdd3ce feat(lightbox): 正文图片点击在当前页放大、多图轮播、键盘切换
新增主题自带的图片灯箱,替代「点图片跳新标签页」的旧行为,
不再依赖第三方灯箱插件。

主要能力
· 正文里的图片链接改写成「在当前页覆盖展示原图」,不跳转
· 原图优先按附件 ID 取 full size(wp_get_attachment_image_src),
  取不到才退回 href 自己 —— 不在前端从缩略图文件名反推原图地址
· 原图地址写在 href 里而不是 data-full:主题的 CDN 是整页输出缓冲
  改写,只认 src= / href= 与 CSS url(),写 data-* 会绕回源站
· 多图左右箭头轮播(到头循环),键盘 ←↑ / →↓ 切换、Esc 关闭、
  Home / End 跳首尾,点空白关闭,打开时锁滚动并在关闭后归还焦点
· 两套遮罩配色(白色默认 / 深色)与九档不透明度(默认 80%)
· 图片本身始终保持不透明:透明度只作用在遮罩底色上

兼容性处理
· 剥掉第三方灯箱插件留下的触发属性(data-rel / data-imagelightbox /
  data-rl_title 等)。线上实测 100 篇文章 112 张图**全部**带这些属性,
  若只「让给插件」则主题灯箱永不生效;同时关掉开关时一律不动,
  插件可照常工作
· 移除 imgnofollow() 给图片链接加的 target="_blank" 与 rel,
  链接不再承担跳转职责
· 无 JS 时 href 仍在,右键「在新标签页打开图片」照常可用

可读性
· 半透明遮罩下底部说明条自带更实的底(≥0.9),并按「遮罩底下是
  纯黑」的最坏情况验证文字对比度 ≥ 4.5:1
· 行内不透明度样式用 [data-theme] 选择器,避免被样式表默认值压掉
  (裸类名优先级不足,会让深色配色下透明度静默失效)

验证
· 服务端正文改写探针 51 通过 / 0 失败(PHP 8.4 与 8.5)
· 浏览器真实交互探针 200 通过 / 0 失败(两配色 × 80%,含 50%/100%)
· 上述两者的反向验证 21 项注入全部按预期捕获
· 三个改动文件 php -l 通过

Refs #10
2026-10-25 22:05:24 +08:00

325 lines
11 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*!
* 图片灯箱样式
* Yongquan Yang <yangyq.net>
*
* ── 配色为什么做成两套 ──────────────────────────────────────
* 深色底适合看照片:周围全黑时人眼对图里颜色的判断更准,也不会
* 被白底反射光干扰。浅色底适合看截图和文档图:黑底会让白底截图的
* 边缘发灰、像蒙了一层雾,而且深色底下一张 A4 白文档刺眼。
* 用户明确说「哪种效果好看」,所以两套都做出来、后台可选。
*
* ── 为什么用 CSS 变量 ───────────────────────────────────────
* 两套配色只差几个颜色值,用变量可以只写一遍布局。
* 注意:主题**没有**全局 CSS 变量(主色各处硬编码),所以这里的
* 变量全部**定义在 .yang-lb 自己身上**,不依赖外部。
*
* ── 层级 ────────────────────────────────────────────────────
* z-index 取 99999:主题导航栏用的是 1000 级别,layer.js 弹层是
* 19891015 以下。灯箱必须盖住**所有**内容(用户要求「把页面其他
* 内容都盖住」),所以取一个明显高于常规的整数,同时避开 2147483647
* 这类极端值(部分浏览器上会有渲染 bug)。
*/
.yang-lb {
/* ── 浅色(默认)────────────────────────────────────────────
* 用户明确选了白色底,并要求遮罩**半透明**(约 80%),
* 不把底下正文完全盖死,而是「压暗一档、突出照片」。
*
* 🔴 半透明带来的真问题(必须一起解决,否则一开就废):
* 透明度 < 1 时,遮罩颜色会与**底下透上来的正文**复合。
* 本站正文是白底黑字,透上来的是白 + 黑字。
* · 白底透上来 → 遮罩几乎还是白,底部灰字(--lb-fg-dim)对比度够;
* · 但**黑字/深色图片块透上来**时,那一片会变暗,
* 灰字压在上面就可能掉到 4.5:1 以下,看不清「3 / 4」。
* 所以不能只调 alpha 就完事:底部说明条与控件必须自己
* 带一块**更实的底**(见 --lb-ctl-bg 与 .yang-lb-foot),
* 让文字永远有确定的可读底色,不依赖底下恰好是什么。
*
* ⚠️ 透明度用「白色 80% 不透明」= rgba(255,255,255,0.82),
* 不是 `opacity: 0.82` —— 用 opacity 会让**整层**(含图片、
* 箭头、文字)一起变半透明,图片会跟着发灰、看不清,
* 这不是用户要的效果。用户要的是「底透、图清楚」。
*/
--lb-bg: rgba(255, 255, 255, 0.82);
--lb-fg: #1f2430;
--lb-fg-dim: rgba(31, 36, 48, 0.76);
/* 控件底必须**接近不透明**:它盖在图片上方,底下可能是任意颜色 */
--lb-ctl-bg: rgba(255, 255, 255, 0.94);
--lb-ctl-bg-hover: #ffffff;
--lb-ctl-bd: rgba(31, 36, 48, 0.16);
--lb-ctl-fg: #1f2430;
--lb-shadow: 0 10px 40px rgba(31, 36, 48, 0.26);
/* 底部说明条自己的底色:比遮罩更实,保证文字可读 */
--lb-foot-bg: rgba(255, 255, 255, 0.9);
position: fixed;
inset: 0;
z-index: 99999;
display: none;
align-items: center;
justify-content: center;
background-color: var(--lb-bg);
/* 背景模糊:让底下页面「退后」但不完全消失,用户仍知道自己没离开。
半透明遮罩下这一条更重要 —— 它把底下文字糊掉,
减少「字压在图上」的视觉干扰,同时保住「我还在这页」的空间感。 */
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
color: var(--lb-fg);
/* 覆盖层自己吃掉所有指针事件,底下的链接不会被误点 */
overscroll-behavior: contain;
opacity: 0;
transition: opacity 0.18s ease;
}
/* ── 深色遮罩(可选)─────────────────────────────────────────
* 看照片时用这套:周围全黑,人眼对图里颜色的判断更准,
* 不会被白底反射光干扰。透明度同样留一点,不全盖死。
*/
.yang-lb[data-theme='dark'] {
--lb-bg: rgba(12, 14, 18, 0.86);
--lb-fg: #f2f4f7;
--lb-fg-dim: rgba(242, 244, 247, 0.7);
--lb-ctl-bg: rgba(40, 44, 52, 0.94);
--lb-ctl-bg-hover: rgba(58, 63, 73, 0.98);
--lb-ctl-bd: rgba(255, 255, 255, 0.2);
--lb-ctl-fg: #f2f4f7;
/* 图片外发光:深色底上没有它,深色图片的边界会糊在背景里 */
--lb-shadow: 0 12px 48px rgba(0, 0, 0, 0.62);
--lb-foot-bg: rgba(12, 14, 18, 0.9);
}
.yang-lb.is-open {
display: flex;
opacity: 1;
}
/* 覆盖层打开时禁止底部页面滚动。
⚠️ 不用 `body { overflow: hidden }` 写死在 CSS 里:那样关闭后
恢复不可控(要和 JS 里记住的原值配合)。JS 负责设置,
这里只兜一层「万一 JS 没设」的情况。 */
body.yang-lb-lock {
overflow: hidden;
}
/* ── 图片舞台 ─────────────────────────────────────────────── */
.yang-lb-stage {
display: flex;
align-items: center;
justify-content: center;
/* 给左右箭头留出空间,避免大图被箭头压住 */
width: calc(100% - 132px);
height: calc(100% - 96px);
pointer-events: none;
}
.yang-lb-img {
display: block;
max-width: 100%;
max-height: 100%;
/* 原图可能小于屏幕:不要为了填满而放大,放大只会变糊 */
width: auto;
height: auto;
object-fit: contain;
box-shadow: var(--lb-shadow);
border-radius: 2px;
background-color: transparent;
/* 图片本身要能接收点击(点图不关闭),但舞台是 none,
所以这里必须显式改回来 */
pointer-events: auto;
/* 大图加载时的淡入,避免「啪」一下出现 */
animation: yang-lb-fade 0.2s ease;
}
@keyframes yang-lb-fade {
from { opacity: 0; }
to { opacity: 1; }
}
/* ── 左右箭头 ─────────────────────────────────────────────── */
.yang-lb-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 52px;
height: 52px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--lb-ctl-bd);
border-radius: 50%;
background-color: var(--lb-ctl-bg);
color: var(--lb-ctl-fg);
cursor: pointer;
/* 触屏上不要 300ms 延迟 */
touch-action: manipulation;
transition: background-color 0.15s ease, transform 0.15s ease;
}
.yang-lb-prev { left: 24px; }
.yang-lb-next { right: 24px; }
.yang-lb-nav:hover {
background-color: var(--lb-ctl-bg-hover);
}
.yang-lb-nav:active {
transform: translateY(-50%) scale(0.94);
}
.yang-lb-nav:focus-visible,
.yang-lb-close:focus-visible {
outline: 2px solid #00a2ff;
outline-offset: 2px;
}
/* ── 关闭按钮 ─────────────────────────────────────────────── */
.yang-lb-close {
position: absolute;
top: 18px;
right: 18px;
width: 44px;
height: 44px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--lb-ctl-bd);
border-radius: 50%;
background-color: var(--lb-ctl-bg);
color: var(--lb-ctl-fg);
cursor: pointer;
touch-action: manipulation;
transition: background-color 0.15s ease, transform 0.15s ease;
}
.yang-lb-close:hover {
background-color: var(--lb-ctl-bg-hover);
transform: rotate(90deg);
}
/* ── 底部说明与计数 ───────────────────────────────────────── */
/* 🔴 这一条是「半透明遮罩」逼出来的改动。
原来用 `linear-gradient(to top, var(--lb-bg) 30%, transparent)`:
遮罩不透明时看着没问题,但遮罩只有 80% 时,
渐变到 transparent 的那一段**完全没有底**,
说明文字会直接压在透上来的正文上 —— 底下是深色图片或粗标题时
就彻底读不清了。
改成「一块实底 + 上沿渐变过渡」,文字永远落在确定底色上。 */
.yang-lb-foot {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 14px 76px 18px;
display: flex;
align-items: baseline;
justify-content: center;
gap: 14px;
background-color: var(--lb-foot-bg);
/* 上沿做一小段渐变,避免出现一条生硬的分界线 */
box-shadow: inset 0 1px 0 var(--lb-ctl-bd);
pointer-events: none;
}
.yang-lb-cap {
font-size: 13px;
line-height: 1.5;
color: var(--lb-fg-dim);
max-width: 76%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.yang-lb-count {
font-size: 13px;
line-height: 1.5;
color: var(--lb-fg-dim);
font-variant-numeric: tabular-nums;
flex: none;
}
/* ── 正文里「可点」的视觉提示 ─────────────────────────────── */
/* 默认加一个放大光标。不用「图片上盖一个放大镜图标」那种做法:
它会在每张图角落加一个浮层,正文一多图就显得很脏。 */
.content a.yang-lb-item {
cursor: zoom-in;
display: inline-block;
/* <a> 默认是 inline,包着块级图时会让父级出现基线空隙 */
line-height: 0;
}
.content a.yang-lb-item > img {
transition: opacity 0.15s ease;
}
.content a.yang-lb-item:hover > img {
opacity: 0.9;
}
/* ── 窄屏 ─────────────────────────────────────────────────── */
@media (max-width: 720px) {
.yang-lb-stage {
/* 手机上左右箭头改成半透明压在图上,图片尽量占满宽 */
width: 100%;
height: calc(100% - 88px);
padding: 0 8px;
}
.yang-lb-nav {
width: 42px;
height: 42px;
/* 压到图上,给图片腾出宽度。
⚠️ 这里必须用**接近不透明**的底:箭头此刻直接压在图片上,
底下可能是任意颜色(白纸扫图 / 纯黑夜景都有可能)。
半透明底会随图片内容变化,对比度不可控。
用 0.88 的实底,两套配色各自保证 ≥ 3:1。 */
background-color: rgba(255, 255, 255, 0.88);
border-color: rgba(31, 36, 48, 0.18);
color: #1f2430;
}
.yang-lb[data-theme='dark'] .yang-lb-nav {
background-color: rgba(24, 27, 33, 0.88);
border-color: rgba(255, 255, 255, 0.24);
color: #f2f4f7;
}
.yang-lb-prev { left: 6px; }
.yang-lb-next { right: 6px; }
.yang-lb-foot {
padding: 10px 16px 14px;
flex-direction: column;
align-items: center;
gap: 2px;
}
.yang-lb-cap {
max-width: 100%;
font-size: 12px;
}
}
/* ── 尊重「减少动态效果」系统设置 ─────────────────────────── */
@media (prefers-reduced-motion: reduce) {
.yang-lb,
.yang-lb-img,
.yang-lb-nav,
.yang-lb-close {
transition: none;
animation: none;
}
.yang-lb-close:hover {
transform: none;
}
}