Files
wordpress-kratos-yang/assets/css/comments.css
T
laoyang cbe0f2dc59 3.2.18: 清明按节气换算、主题内 [TOC] 目录、重建评论区替代 wpDiscuz
三项功能:

1. 清明不再用固定 04-04
   清明是节气,日期在 4/4~4/6 之间浮动。硬编码 04-04 实测 1900-2100
   共 201 年里只有 76 年是对的(约 38%)。改为内嵌逐年表换算真实日期,
   超出 1900-2100 返回 null 明确放弃,不退化成近似值。
   兼容 3.2.17 及以前勾过的 '04-04' 键,升级后勾选不会被静默丢弃。

2. 主题内实现 [TOC]
   文章里写 [TOC],该位置输出本文目录,点击跳到对应标题。
   在 the_content 优先级 9 处理(早于 wpautop 10 / do_shortcode 11),
   保证"补 id"与"扫目录"用的是同一份文本,href 与 id 必然对得上。
   嵌套用深度归一化,h1→h3 跳级不会算错层级。
   scroll-margin-top 声明式让位,原生锚点/外部 #hash/后退全都生效。

3. 重建评论区
   旧实现的问题:列表渲染与 AJAX 渲染各写一份 HTML,已经分叉
   (AJAX 返回的新评论没有回复按钮);<li> 被 <div> 包着,嵌套回复错位。
   现在统一到 yang_comment_html() 单一渲染函数,列表与 AJAX 只差一个
   is-new 类。同时修掉三处真实缺陷:
     · 评论项用 flex 会把 <ol class="yang-children"> 当成第三个 flex 项,
       子评论跑到父评论右侧、正文被挤到 156px、页面横向溢出 75px —— 改 grid
     · 成功提示在 exitReplyMode() 之前设置,被其末尾的 setStatus('','')
       擦掉,用户提交成功却看不到反馈 —— 调整顺序
     · 窄屏媒体查询打不过深层选择器(媒体查询不增加特异性),
       嵌套头像反向变大 —— 补深层覆盖

   与 wpDiscuz 解耦:邮件模块不再无条件 return false(那等于永久关掉
   notify_postauthor 且不报错);插件在场时不加载主题评论资源,
   避免双份表单互抢。

验证:PHP 语法 73/73;_static_qingming 36、_test_toc 53、
_test_comments 62、_test_comments_falsify 17、
_toc_geometry 121、_toc_geometry_falsify 9、
_comments_geometry 98、_comments_geometry_falsify 23,全部 0 失败。
线上端到端 29/0(真实文章渲染目录 + 真实评论数据渲染)。
2026-10-24 01:03:36 +08:00

893 lines
18 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>
*
* ── 为什么整块重写,而不是在旧样式上改 ──────────────────────
* 旧样式的骨架是「头像 float:left + 右侧 margin-left 挤出来」,
* 这个结构在三个地方会坏:
* · 长昵称/长网址会把头部撑破(float 不定宽容器里没有约束);
* · 嵌套回复靠 .children { padding-left:0 } 实现,等于**没有缩进**,
* 三层以后完全看不出谁回谁;
* · 头像 50px 写死在 img 上,但没有 flex 居中,头像和第一行文字
* 基线对不齐(看起来"文字比头像低一点")。
*
* 所以改用 flex 两列布局:头像固定宽、主体 flex:1,
* 并把嵌套缩进做成明确的层级视觉(左侧引导线)。
*/
/*--------------------------------------------------------------
1. 区块与标题
--------------------------------------------------------------*/
.k-main .details .comments {
margin-top: 16px;
padding: 20px 22px 22px;
border-radius: 4px;
background-color: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.k-main .details .post-2 .comments {
box-shadow: none;
}
.comments-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
padding-bottom: 14px;
border-bottom: 1px solid #f0f0f0;
}
.comments-head .title {
margin: 0;
color: #1f2328;
font-weight: 600;
font-size: 19px;
line-height: 1.4;
}
.comments-jump-form {
flex: 0 0 auto;
padding: 5px 13px;
border: 1px solid #d9e4f5;
border-radius: 3px;
background-color: #f4f8ff;
color: #0b74d1;
font-size: 13px;
text-decoration: none;
white-space: nowrap;
transition: background-color 0.15s ease, border-color 0.15s ease;
}
.comments-jump-form:hover {
border-color: #0b74d1;
background-color: #e8f1ff;
color: #0b74d1;
text-decoration: none;
}
.comments-closed,
.comments-empty {
margin: 20px 0 4px;
padding: 14px 16px;
border-radius: 3px;
background-color: #fafbfc;
color: #8a9099;
font-size: 13.5px;
text-align: center;
}
/*--------------------------------------------------------------
2. 评论列表
--------------------------------------------------------------*/
.yang-comment-list,
.yang-children {
margin: 0;
padding: 0;
list-style: none;
}
.yang-comment-list {
margin-top: 4px;
}
/* 顶层评论之间用细分隔线;嵌套评论之间不用,靠缩进区分 */
.yang-comment-list > .yang-comment {
border-bottom: 1px solid #f5f6f8;
}
.yang-comment-list > .yang-comment:last-child {
border-bottom: 0;
}
/*--------------------------------------------------------------
3. 单条评论
--------------------------------------------------------------*/
/* 单条评论的布局:头像列 + 主体列
*
* ⚠️ 这里**必须是 grid,不能是 flex**。
* 原因:wp_list_comments 输出的是
* <li class="yang-comment">
* <div class="yang-comment-avatar">…</div>
* <div class="yang-comment-main">…</div>
* <ol class="yang-children">…子评论…</ol> ← 也是 li 的直接子元素
* </li>
* 若 li 用 display:flex,那个 <ol> 就成了**第三个 flex 项**,
* 被排到主体右边的同一行里 —— 表现为"子评论跑到父评论右侧"、
* 父评论正文被挤成一条、页面出现横向溢出。这是真实渲染量出来的,
* 光看代码几乎想不到(flex 项的位置不体现在 li 自己的属性上)。
*
* grid 用两列:第 1 列 auto(由头像定宽),第 2 列 minmax(0,1fr)
* (minmax 的下界 0 是关键,否则长英文/长 URL 会把列撑宽)。
* 子评论用 grid-column: 1 / -1 独占下一整行。 */
.yang-comment {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
column-gap: 12px;
align-items: start;
padding: 16px 0;
list-style: none;
}
/* 子评论容器独占整行,排在头像与主体下方 */
.yang-comment > .yang-children {
grid-column: 1 / -1;
}
/* 头像列:固定宽度,不参与伸缩 —— 否则长评论把它挤扁 */
.yang-comment-avatar {
flex: 0 0 auto;
width: 42px;
line-height: 0;
}
.yang-comment-avatar img,
.yang-comment-avatar-img {
display: block;
width: 42px;
height: 42px;
border: 1px solid #eaeaea;
border-radius: 50%;
object-fit: cover;
background-color: #f5f5f5;
}
/* 没有头像时的首字占位 */
.yang-comment-avatar-ph {
display: flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
border-radius: 50%;
background-color: #eef2f7;
color: #7c8794;
font-size: 17px;
font-weight: 600;
line-height: 1;
text-transform: uppercase;
}
/* 主体列 */
.yang-comment-main {
flex: 1 1 auto;
min-width: 0; /* 关键:让内部的 text-overflow / 换行能生效 */
}
.yang-comment-head {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 4px;
}
.yang-comment-author {
color: #252525;
font-weight: 600;
font-style: normal;
font-size: 14px;
line-height: 1.5;
/* 超长昵称截断,别把时间挤到下一行去 */
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.yang-comment-author a {
color: #252525;
text-decoration: none;
}
.yang-comment-author a:hover {
color: #00a2ff;
}
.yang-comment-time {
flex: 0 0 auto;
color: #a4abb5;
font-size: 12.5px;
line-height: 1.5;
}
/* 徽标:作者 / 待审核 */
.yang-comment-badge {
flex: 0 0 auto;
padding: 1px 6px;
border-radius: 3px;
background-color: #eaf3ff;
color: #0b74d1;
font-size: 11.5px;
font-weight: 400;
line-height: 1.5;
}
.yang-comment-badge.is-pending {
background-color: #fff6e5;
color: #b27412;
}
/* 待审核的整条评论淡化,一眼区分 */
.yang-comment.is-pending .yang-comment-content {
opacity: 0.72;
}
/* 评论正文 */
.yang-comment-content {
color: #3c414a;
font-size: 14px;
line-height: 1.78;
word-wrap: break-word;
overflow-wrap: break-word;
}
.yang-comment-content > p:first-child {
margin-top: 0;
}
.yang-comment-content > p:last-child {
margin-bottom: 0;
}
.yang-comment-content p {
margin: 0 0 8px;
}
/* @某人 的提及高亮(由 comment_add_at 过滤器插入的 <span>) */
.yang-comment-content > span:first-child {
color: #0b74d1;
font-weight: 500;
}
/* 评论里的图片:限制最大宽度,别撑破容器 */
.yang-comment-content img {
max-width: 100%;
height: auto;
}
/* 引用块 */
.yang-comment-content blockquote {
margin: 8px 0;
padding: 6px 12px;
border-left: 3px solid #e2e6ee;
background-color: #fafbfc;
color: #6b7280;
font-size: 13.5px;
}
.yang-comment-content code {
padding: 1px 5px;
border-radius: 3px;
background-color: #f4f5f7;
color: #c7254e;
font-size: 13px;
}
.yang-comment-content pre {
overflow-x: auto;
padding: 10px 12px;
border-radius: 3px;
background-color: #f6f7f9;
font-size: 13px;
}
/* 表情图片与正文基线对齐 */
.yang-comment-content img.wp-smiley {
display: inline-block;
margin: 0 1px;
width: auto;
max-height: 1.4em;
vertical-align: text-bottom;
}
/*--------------------------------------------------------------
4. 操作按钮
--------------------------------------------------------------*/
.yang-comment-actions {
display: flex;
align-items: center;
gap: 14px;
margin-top: 6px;
}
.yang-comment-reply,
.yang-comment-edit {
padding: 0;
border: 0;
background: none;
color: #8a9099;
font-size: 13px;
line-height: 1.6;
text-decoration: none;
cursor: pointer;
transition: color 0.15s ease;
}
.yang-comment-reply:hover,
.yang-comment-edit:hover {
color: #00a2ff;
text-decoration: none;
}
.yang-comment-reply .kicon {
margin-right: 3px;
font-size: 12px;
}
/* 已进入"回复这条"状态时,按钮藏起来(由 JS 加 class) */
.yang-comment-reply.is-hidden {
visibility: hidden;
}
/*--------------------------------------------------------------
5. 嵌套回复
--------------------------------------------------------------*/
.yang-children {
margin-top: 2px;
padding-left: 20px;
/* 左侧引导线:让"谁回谁"在视觉上有明确归属 */
border-left: 2px solid #f0f2f5;
}
.yang-children > .yang-comment {
padding: 11px 0;
}
/* 深层的头像小一号,靠尺寸建立层级感 */
.yang-children .yang-children .yang-comment-avatar,
.yang-children .yang-children .yang-comment-avatar img,
.yang-children .yang-children .yang-comment-avatar-ph {
width: 34px;
}
.yang-children .yang-children .yang-comment-avatar {
width: 34px;
}
.yang-children .yang-children .yang-comment-avatar img {
height: 34px;
}
.yang-children .yang-children .yang-comment-avatar-ph {
height: 34px;
font-size: 14px;
}
/* 第三层以后不再继续缩进 —— 否则窄屏上正文只剩一条竖线 */
.yang-children .yang-children .yang-children {
padding-left: 8px;
}
/* 新提交的评论:淡入 + 短暂底色,让用户看到自己刚发的那条 */
@keyframes yang-comment-in {
from {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: none;
}
}
.yang-comment.is-new {
animation: yang-comment-in 0.28s ease;
background-color: #fffdf5;
}
/*--------------------------------------------------------------
6. 加载更多
--------------------------------------------------------------*/
.comments-more {
padding: 16px 0 4px;
text-align: center;
}
.comments-more[hidden] {
display: none;
}
.comments-more-btn {
padding: 8px 26px;
border: 1px solid #e2e6ee;
border-radius: 3px;
background-color: #fff;
color: #5a616b;
font-size: 13.5px;
cursor: pointer;
transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}
.comments-more-btn:hover:not(:disabled) {
border-color: #00a2ff;
background-color: #f5fbff;
color: #00a2ff;
}
.comments-more-btn:disabled {
opacity: 0.6;
cursor: default;
}
/*--------------------------------------------------------------
7. 评论表单
--------------------------------------------------------------*/
.comment-respond {
margin-top: 22px;
padding-top: 20px;
border-top: 1px solid #f0f0f0;
}
/* 表单被"搬"进某条评论时,去掉那条顶部分隔线,视觉上更连贯 */
.yang-children > #respond {
margin-top: 10px;
padding-top: 12px;
border-top: 1px dashed #e8ebf0;
}
.comment-reply-title {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
margin: 0 0 14px;
color: #1f2328;
font-weight: 600;
font-size: 16px;
line-height: 1.5;
}
.comment-cancel-reply {
padding: 3px 10px;
border: 1px solid #e4e8ee;
border-radius: 3px;
background-color: #f7f8fa;
color: #6b7280;
font-weight: 400;
font-size: 12.5px;
cursor: pointer;
transition: all 0.15s ease;
}
.comment-cancel-reply[hidden] {
display: none;
}
.comment-cancel-reply:hover {
border-color: #f0b4b4;
background-color: #fff5f5;
color: #d9534f;
}
/* 「正在回复 @某人」提示条 */
.comment-reply-hint {
margin: 0 0 12px;
padding: 7px 12px;
border-left: 3px solid #00a2ff;
border-radius: 2px;
background-color: #f2f8ff;
color: #4a6b8a;
font-size: 13px;
line-height: 1.6;
}
.comment-reply-hint[hidden] {
display: none;
}
/* 昵称/邮箱/网址:三列,窄屏自动堆叠 */
.comment-info {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 12px;
}
.comment-field {
position: relative;
flex: 1 1 180px;
min-width: 0;
}
.comment-field-icon {
position: absolute;
left: 9px;
top: 50%;
transform: translateY(-50%);
color: #b6bcc6;
font-size: 14px;
line-height: 1;
pointer-events: none;
}
.comment-input {
display: block;
width: 100%;
padding: 9px 12px;
border: 1px solid #e6e9ef;
border-radius: 3px;
background-color: #fcfcfd;
color: #303133;
font-family: inherit;
font-size: 14px;
line-height: 1.5;
transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
-webkit-appearance: none;
}
.comment-field .comment-input {
padding-left: 32px;
}
.comment-input::placeholder {
color: #b0b6bf;
}
.comment-input:focus {
outline: none;
border-color: #00a2ff;
background-color: #fff;
box-shadow: 0 0 0 3px rgba(0, 162, 255, 0.1);
}
.comment-input:disabled,
.comment-input[readonly] {
background-color: #f7f8fa;
color: #8a9099;
}
/* textarea 容器 */
.comment-textarea {
position: relative;
border: 1px solid #e6e9ef;
border-radius: 3px;
background-color: #fcfcfd;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.comment-textarea:focus-within {
border-color: #00a2ff;
box-shadow: 0 0 0 3px rgba(0, 162, 255, 0.1);
}
.comment-textarea .comment-input {
min-height: 132px;
padding: 11px 13px;
border: 0;
border-radius: 0;
background: none;
resize: vertical;
box-shadow: none;
}
.comment-textarea .comment-input:focus {
box-shadow: none;
}
/* 工具条 */
.comment-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 7px 10px;
border-top: 1px solid #f0f2f6;
background-color: #fafbfc;
}
.comment-bar-left,
.comment-bar-right {
display: flex;
align-items: center;
gap: 8px;
}
.comment-bar-left {
position: relative;
}
.comment-smile-btn {
padding: 4px 7px;
border: 0;
border-radius: 3px;
background: none;
color: #8a9099;
font-size: 17px;
line-height: 1;
cursor: pointer;
transition: color 0.15s ease, background-color 0.15s ease;
}
.comment-smile-btn:hover {
background-color: #eef2f7;
color: #00a2ff;
}
/* 表情面板:3 行 8 列 */
.comment-smile-panel {
position: absolute;
bottom: calc(100% + 8px);
left: 0;
z-index: 20;
display: flex;
flex-wrap: wrap;
width: 264px;
padding: 8px;
border: 1px solid #e6e9ef;
border-radius: 4px;
background-color: #fff;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1);
}
.comment-smile-panel[hidden] {
display: none;
}
.comment-smile-panel a {
float: none;
display: block;
margin: 2px;
padding: 3px;
width: 28px;
height: 28px;
border: 1px solid transparent;
border-radius: 3px;
background-color: #f7f8fa;
transition: background-color 0.12s ease, border-color 0.12s ease;
}
.comment-smile-panel a:hover {
border-color: #b9dcff;
background-color: #eaf4ff;
}
.comment-smile-panel img {
display: block;
width: 20px;
height: 20px;
}
/* 状态提示 */
.comment-status {
color: #8a9099;
font-size: 12.5px;
line-height: 1.6;
transition: color 0.15s ease;
}
.comment-status.is-ok {
color: #2f9e44;
}
.comment-status.is-err {
color: #d9534f;
}
/* 提交按钮 */
.comment-submit {
padding: 8px 22px;
border: 0;
border-radius: 3px;
background-color: #00a2ff;
color: #fff;
font-size: 14px;
line-height: 1.5;
cursor: pointer;
white-space: nowrap;
transition: background-color 0.15s ease, opacity 0.15s ease;
}
.comment-submit:hover:not(:disabled) {
background-color: #0090e6;
}
.comment-submit:disabled,
.comment-submit.is-loading {
opacity: 0.62;
cursor: default;
}
/* honeypot:移出视口而不是 display:none(display:none 的字段部分爬虫不填) */
.comment-hp {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
/* 只需登录才能评论的提示 */
.comments-login-required {
margin: 0;
padding: 18px;
border: 1px solid #e5e5e5;
border-radius: 4px;
background-color: #fafbfc;
color: #6b7280;
font-size: 13.5px;
text-align: center;
}
.comments-login-required a {
color: #00a2ff;
text-decoration: none;
}
/*--------------------------------------------------------------
8. 无障碍:屏幕阅读器专用文本
--------------------------------------------------------------*/
.k-main .details .comments .sr-only {
position: absolute;
overflow: hidden;
clip: rect(0, 0, 0, 0);
width: 1px;
height: 1px;
padding: 0;
border: 0;
margin: -1px;
white-space: nowrap;
}
/*--------------------------------------------------------------
9. 响应式
--------------------------------------------------------------*/
@media screen and (max-width: 768px) {
.k-main .details .comments {
padding: 16px 14px 18px;
}
.comments-head {
flex-wrap: wrap;
gap: 8px;
}
.comments-head .title {
font-size: 17px;
}
.yang-comment {
/* 只收窄列间距;写 gap 会同时给 grid 的 row-gap 赋值,
导致"主体"与"子评论"之间多出一道空隙 */
column-gap: 10px;
padding: 13px 0;
}
.yang-comment-avatar,
.yang-comment-avatar img,
.yang-comment-avatar-img,
.yang-comment-avatar-ph {
width: 36px;
}
.yang-comment-avatar img,
.yang-comment-avatar-img,
.yang-comment-avatar-ph {
height: 36px;
}
/* 窄屏缩进收窄:原来 20px 在三层后正文只剩很窄一条 */
.yang-children {
padding-left: 11px;
}
.yang-children .yang-children {
padding-left: 8px;
}
.yang-children .yang-children .yang-children {
padding-left: 0;
border-left: 0;
}
.comment-info {
flex-direction: column;
gap: 8px;
}
.comment-field {
flex: 1 1 auto;
}
.comment-bar {
flex-wrap: wrap;
gap: 8px;
}
.comment-bar-right {
flex: 1 1 100%;
justify-content: space-between;
}
.comment-status {
flex: 1 1 auto;
min-width: 0;
}
.comment-smile-panel {
width: 240px;
}
}
@media screen and (max-width: 400px) {
.yang-comment-avatar,
.yang-comment-avatar img,
.yang-comment-avatar-img,
.yang-comment-avatar-ph {
width: 32px;
}
.yang-comment-avatar img,
.yang-comment-avatar-img,
.yang-comment-avatar-ph {
height: 32px;
}
/* ⚠️ 必须一并覆盖深层头像。
上面的基础规则里有 `.yang-children .yang-children .yang-comment-avatar
{ width: 34px }`,它选择器特异性 (0,3,0) 高于这里的 `.yang-comment-avatar`
(0,1,0) —— 媒体查询**不增加**特异性。所以只写这一条的话,窄屏下
深层头像仍是 34px,反而比顶层的 32px 更大,层级关系是反的。 */
.yang-children .yang-children .yang-comment-avatar,
.yang-children .yang-children .yang-comment-avatar img,
.yang-children .yang-children .yang-comment-avatar-ph {
width: 26px;
}
.yang-children .yang-children .yang-comment-avatar img,
.yang-children .yang-children .yang-comment-avatar-ph {
height: 26px;
font-size: 12px;
}
.comment-smile-panel {
width: 216px;
}
}
/*--------------------------------------------------------------
10. 打印:评论不需要按钮
--------------------------------------------------------------*/
@media print {
.comments-jump-form,
.yang-comment-actions,
.comments-more,
.comment-respond {
display: none !important;
}
.yang-comment-content {
color: #000;
}
}