/* dsh-channel-wechat 站点共用样式。 站点由 GitHub Pages 从 /docs 发布在 /dsh-channel-wechat/ 子路径下, 所以引用本文件一律用相对路径:。 写成 /assets/site.css 会指到 wangx-wx.github.io 根上并 404。 */ :root { --green: #07C160; --green-dark: #06A350; --ink: #0F172A; --muted: #5A6B7B; --line: #E3E9EE; --bg: #FFFFFF; --soft: #F5F8FA; --code-bg: #0F172A; --radius: 14px; --max: 960px; } * { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } body { margin: 0; background: var(--bg); color: var(--ink); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; font-size: 16px; line-height: 1.75; } a { color: var(--green-dark); text-decoration: none; } a:hover { text-decoration: underline; } .wrap { max-width: var(--max); margin: 0 auto; padding: 0 24px; } /* ---------- hero ---------- */ .hero { text-align: center; padding: 72px 24px 56px; background: linear-gradient(180deg, #F3FBF7 0%, #FFFFFF 100%); border-bottom: 1px solid var(--line); } .logo { width: 96px; height: 96px; display: block; margin: 0 auto 24px; border-radius: 22px; } .hero h1 { margin: 0 0 16px; font-size: clamp(28px, 5vw, 42px); letter-spacing: -0.02em; font-weight: 700; } .tagline { margin: 0 auto 20px; max-width: 620px; font-size: clamp(16px, 2.2vw, 19px); color: var(--ink); } .meta { margin: 0 0 32px; font-size: 14px; color: var(--muted); } .cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; } .btn { display: inline-block; padding: 11px 26px; border-radius: 999px; border: 1px solid var(--line); background: #fff; color: var(--ink); font-size: 15px; font-weight: 500; transition: border-color .15s, background .15s; } .btn:hover { text-decoration: none; border-color: #C8D4DE; } .btn.primary { background: var(--green); border-color: var(--green); color: #fff; } .btn.primary:hover { background: var(--green-dark); border-color: var(--green-dark); } /* ---------- sections ---------- */ section { padding: 64px 0; border-bottom: 1px solid var(--line); } section:last-of-type { border-bottom: 0; } h2 { margin: 0 0 12px; font-size: clamp(22px, 3.4vw, 28px); letter-spacing: -0.01em; font-weight: 700; } .lede { margin: 0 0 36px; color: var(--muted); max-width: 680px; } /* ---------- release history ---------- */ /* 发布历史:一行一个版本,整行是链接。之前是一张「当前版本」大卡,只显示最新一版, 读者想找旧版本得自己跳到 GitHub;现在把 8 个版本平铺出来,扫一眼就知道节奏。 行内四列:版本徽章 / 名称 / (弹性空白)/ 日期。窄屏时日期换行到第二行。 */ .releases { margin: 0 0 24px; padding: 0; list-style: none; border: 1px solid #D5DEE5; border-radius: var(--radius); overflow: hidden; background: #fff; } .releases li + li { border-top: 1px solid var(--line); } .releases a { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 12px; padding: 13px 22px; color: inherit; transition: background .15s; } .releases a:hover { background: var(--soft); text-decoration: none; } .release-tag { flex: none; padding: 2px 10px; border-radius: 999px; background: rgba(7, 193, 96, .1); color: var(--green-dark); font-size: 13px; font-weight: 600; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; } .release-latest { flex: none; padding: 2px 9px; border-radius: 999px; background: var(--green); color: #fff; font-size: 12px; font-weight: 600; letter-spacing: .02em; } .release-name { flex: 1 1 auto; min-width: 0; font-size: 15px; line-height: 1.6; } .releases a:hover .release-name { color: var(--green-dark); } .release-date { flex: none; color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; } /* ---------- screenshots ---------- */ /* 截图卡:三点窗口栏 + 等宽标题 + 1px 外框 + 内高光与投影。展示语言取自 dsh-plugins 落地页的 .lp-shot,底色仍用本站浅色,不引入它的深色主题。 图片是原图、不压缩。手机长图(1200×6772)在卡片里固定高度、从顶部开始裁, 完整内容点开看原图。别改成 height: auto——那样整页会被一张图拉长约八千像素。 */ .shots { display: grid; gap: 24px; } .shots .pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 24px; } .shot { margin: 0; display: flex; flex-direction: column; overflow: hidden; border: 1px solid #D5DEE5; border-radius: 12px; background: #fff; box-shadow: 0 1px 0 0 rgba(255, 255, 255, .7) inset, 0 16px 40px -24px rgba(15, 23, 42, .45); } .shot-bar { display: flex; align-items: center; gap: 6px; flex: none; height: 36px; padding: 0 14px; background: var(--code-bg); } .shot-bar i { width: 9px; height: 9px; border-radius: 50%; background: #475569; } .shot-bar i:nth-child(1) { background: #F87171B3; } .shot-bar i:nth-child(2) { background: #FBBF24B3; } .shot-bar i:nth-child(3) { background: #34D399B3; } .shot-bar span { margin-left: 8px; overflow: hidden; color: #94A3B8; font-size: 12px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; white-space: nowrap; text-overflow: ellipsis; } /* .crop 负责「只露开头」:a 是相对定位的容器,::after 在底部压一层渐隐, 让人一眼看出下面还有内容、需要点开。没有渐隐时,长图会在某行文字中间硬切, 看起来像图片被截坏了。 */ .shot a { display: block; position: relative; line-height: 0; } .shot a:hover img { opacity: .94; } .shot img { display: block; width: 100%; height: auto; object-fit: cover; object-position: top; transition: opacity .15s; } /* 手机长图:卡内只露出开头。 */ .shot img.tall { height: 520px; } .shot .crop::after { content: ""; position: absolute; inset: auto 0 0; height: 96px; background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff); pointer-events: none; } .shot figcaption { margin-top: auto; padding: 14px 18px; border-top: 1px solid var(--line); font-size: 13px; line-height: 1.7; color: var(--muted); } /* ---------- feature list ---------- */ .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; } .card { padding: 24px; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; } .card h3 { margin: 0 0 8px; font-size: 17px; font-weight: 600; } /* 卡片标题可以是链接(如「先看的东西」那组),保持与正文同级颜色, 否则同一排卡片里会有一半是绿色标题。悬停时再给下划线。 */ .card h3 a { color: inherit; } .card h3 a:hover { color: var(--green-dark); } .card p { margin: 0; font-size: 15px; color: var(--muted); } /* 「文档与入口」那组卡片。.links 这个类名从落地页第一版就在 HTML 里,但**从来没有对应样式**, 所以整组看起来和上面的功能介绍一模一样——读者不知道这几张能点。 这里把整张卡片变成链接: 铺满,箭头在悬停时右移一格作为可点的提示。 */ .cards.links .card { display: flex; flex-direction: column; gap: 4px; color: inherit; transition: border-color .15s, box-shadow .15s; } .cards.links .card:hover { border-color: var(--green); text-decoration: none; box-shadow: 0 12px 32px -22px rgba(7, 193, 96, .5); } .cards.links .card h3 { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0; } .cards.links .card h3::after { content: "→"; flex: none; color: var(--green); opacity: .55; transition: transform .15s, opacity .15s; } .cards.links .card:hover h3::after { transform: translateX(3px); opacity: 1; } .cards.links .card p { margin: 0; } /* ---------- code ---------- */ pre { margin: 0 0 20px; padding: 18px 20px; overflow-x: auto; border-radius: var(--radius); background: var(--code-bg); color: #E6EDF3; font-size: 14px; line-height: 1.7; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; } code { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; font-size: 0.92em; } p code, li code, td code { padding: 2px 6px; border-radius: 5px; background: var(--soft); color: var(--green-dark); } /* ---------- steps ---------- */ ol.steps { margin: 0; padding: 0; list-style: none; counter-reset: step; } ol.steps > li { position: relative; counter-increment: step; padding: 0 0 32px 48px; border-left: 2px solid var(--line); margin-left: 16px; } ol.steps > li:last-child { border-left-color: transparent; padding-bottom: 0; } ol.steps > li::before { content: counter(step); position: absolute; left: -17px; top: 0; width: 32px; height: 32px; display: grid; place-items: center; border-radius: 50%; background: var(--green); color: #fff; font-size: 14px; font-weight: 600; } ol.steps h3 { margin: 0 0 10px; font-size: 17px; font-weight: 600; } ol.steps p { margin: 0 0 12px; font-size: 15px; color: var(--muted); } ol.steps pre { margin-bottom: 12px; } .note { margin: 0; padding: 14px 18px; border-left: 3px solid var(--green); border-radius: 0 8px 8px 0; background: var(--soft); font-size: 14px; color: var(--ink); } .note strong { font-weight: 600; } /* ---------- tables ---------- */ .table-wrap { overflow-x: auto; margin: 0 0 20px; } table { width: 100%; border-collapse: collapse; font-size: 15px; } th, td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; } th { font-weight: 600; color: var(--muted); font-size: 14px; white-space: nowrap; } /* ---------- diagram ---------- */ .diagram { margin: 0 0 24px; padding: 8px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--soft); overflow-x: auto; } .diagram svg { display: block; margin: 0 auto; max-width: 100%; height: auto; } /* ---------- limits ---------- */ .limits li { margin-bottom: 10px; color: var(--muted); } .limits strong { color: var(--ink); font-weight: 600; } /* ---------- footer ---------- */ footer { padding: 48px 0 64px; background: var(--soft); border-top: 1px solid var(--line); font-size: 14px; color: var(--muted); } footer .wrap { display: flex; flex-wrap: wrap; gap: 24px 40px; justify-content: space-between; } footer ul { margin: 0; padding: 0; list-style: none; } footer li { margin-bottom: 6px; } @media (max-width: 600px) { .hero { padding: 52px 20px 44px; } section { padding: 48px 0; } .wrap { padding: 0 20px; } th, td { padding: 10px 8px; } /* 窄屏时手机长图再收一档,免得截图区比正文还长。 */ .shot img.tall { height: 380px; } /* 发布历史在窄屏挤在一行时,行与行之间的换行位置不一致:名称长的独占一行, 名称短的却和徽章同行,日期又各自落在不同位置。改成两行网格: 第一行徽章 + 最新标,第二行名称,日期靠右与名称同行。 */ .releases a { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 6px 12px; padding: 14px 18px; } .releases .release-tag { grid-column: 1; grid-row: 1; justify-self: start; } .releases .release-latest { grid-column: 2; grid-row: 1; justify-self: start; } .releases .release-name { grid-column: 1 / -1; grid-row: 2; } .releases .release-date { grid-column: 1 / -1; grid-row: 3; } } /* ---------- article(文章页 article/*.html 用) ---------- */ .article { max-width: 760px; margin: 0 auto; padding: 56px 24px 80px; } .article-header { margin-bottom: 40px; padding-bottom: 28px; border-bottom: 1px solid var(--line); } .article-header h1 { margin: 0 0 12px; font-size: clamp(26px, 4.2vw, 36px); line-height: 1.35; letter-spacing: -0.02em; } .article-header .byline { margin: 0; font-size: 14px; color: var(--muted); } .article h2 { margin: 52px 0 14px; padding-top: 8px; font-size: 22px; } .article h3 { margin: 32px 0 10px; font-size: 17px; font-weight: 600; } .article p { margin: 0 0 18px; } .article ul, .article ol { margin: 0 0 18px; padding-left: 22px; } .article li { margin-bottom: 10px; } /* 文章里的引用块用来说明「为什么这样做」,与落地页的 .note 区分开 */ .article blockquote { margin: 0 0 20px; padding: 16px 20px; border-left: 3px solid var(--line); border-radius: 0 8px 8px 0; background: var(--soft); font-size: 15px; color: var(--muted); } .article blockquote p:last-child { margin-bottom: 0; } .article pre { font-size: 13px; } /* 目录 */ .toc { margin: 0 0 44px; padding: 20px 24px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--soft); } .toc p { margin: 0 0 10px; font-size: 13px; font-weight: 600; color: var(--muted); } .toc ol { margin: 0; padding-left: 20px; font-size: 15px; } .toc li { margin-bottom: 6px; } .article-footer { margin-top: 56px; padding-top: 28px; border-top: 1px solid var(--line); font-size: 14px; color: var(--muted); } /* 文章页的返回链接 */ .back { display: inline-block; margin-bottom: 28px; font-size: 14px; } @media (max-width: 600px) { .article { padding: 40px 20px 60px; } .article h2 { margin-top: 40px; } } /* 文章里的表格:第一列是「标签」,被挤成四行很难读。给一个下限, 超宽时由外层 .table-wrap 横向滚动接住(不换行压缩列宽)。 */ .article table td:first-child, .article table th:first-child { min-width: 6.5em; } /* ---------- 命令块与复制按钮 ---------- */ /* .cmd 包住一个
 和一个复制按钮。按钮绝对定位在右上角,
  所以 
 需要预留右上内边距,否则长命令会钻到按钮下面。
*/
.cmd { position: relative; margin: 0 0 20px; }

.cmd pre { margin-bottom: 0; padding-right: 84px; }

/*
  命令块换行而不横向滚动。横向滚动时,超出的文字会滚到右侧 padding 区域
  被复制按钮压住(Chrome 不把容器的右 padding 算进滚动区末端)。
  这些命令都很短,换行比滚动更合适,也保证按钮永远不遮字。
*/
.cmd pre code { white-space: pre-wrap; overflow-wrap: anywhere; }

.cmd .copy {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 7px;
  background: rgba(255, 255, 255, .08);
  color: #C9D6E2;
  font-size: 12px;
  line-height: 1.5;
  font-family: inherit;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}

.cmd .copy:hover { background: rgba(255, 255, 255, .16); color: #fff; }
.cmd .copy:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

/* 复制成功后短暂变绿,作为「点到了」的反馈 */
.cmd .copy.copied {
  border-color: var(--green);
  background: rgba(7, 193, 96, .18);
  color: #7BE3AD;
}

/* ---------- 更新日志 ---------- */

.releases { margin: 0; padding: 0; list-style: none; }

.releases > li {
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}

.releases > li:last-child { border-bottom: 0; }

.releases .ver {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}

.releases .ver a {
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
}

.releases .ver a:hover { color: var(--green-dark); }

/* 最新版本给一个标记,避免读者以为列表顺序不可靠 */
.releases .tag {
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(7, 193, 96, .12);
  color: var(--green-dark);
  font-size: 12px;
  font-weight: 500;
}

.releases .date { font-size: 13px; color: var(--muted); }

.releases p { margin: 0; font-size: 15px; color: var(--muted); }

/* ---------- 安装方式二选一 ---------- */

.ways {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
  margin: 0 0 32px;
}

.way {
  padding: 22px 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
}

.way h3 { margin: 0 0 8px; font-size: 16px; font-weight: 600; }
.way > p { margin: 0 0 14px; font-size: 14px; color: var(--muted); }

/* 安装方式里的图,尺寸与截图位一致,但不需要虚线框 */
.way figure { margin: 0; }
.way img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 320px;
  object-fit: contain;
  margin-bottom: 10px;
  border-radius: 8px;
  background: var(--soft);
}
.way figcaption { font-size: 13px; color: var(--muted); }

/*
  缺图时的占位。判据是「figure 里还有没有 img」,与 .shot 一致:
  图片加载失败时 onerror 把它移出 DOM,:not(:has(img)) 随之成立,
  占位提示就出现。图片加载成功则 onload 清掉 data-placeholder,恢复普通排版。

  注意不能靠 data-placeholder 自己去 display:none 掉 img——那样图片永远不会加载,
  onload 也就永远不触发,占位和真图会同时消失。
*/
.way figure[data-placeholder]:not(:has(img)) {
  display: grid;
  place-items: center;
  min-height: 200px;
  padding: 16px;
  border: 1px dashed #B9D8C6;
  border-radius: 8px;
  background: var(--soft);
  text-align: center;
}

.way figure[data-placeholder]:not(:has(img))::before {
  content: attr(data-placeholder);
  white-space: pre-line;
  font-size: 13px;
  line-height: 1.9;
  color: #6B8A79;
}

.way figure[data-placeholder]:not(:has(img)) figcaption { display: none; }