/* ============================================================ 家书 FamilyBook · 界面样式 配色取自图标:宣纸米 / 砖红封面 / 深褐书脊 / 纸白 原则:扁平、留白充足、字重克制(正文 400,标题 500-600) ============================================================ */ :root { --paper: #F7F3EA; --surface: #FFFFFF; --ink: #2C231D; --muted: #7E6B5A; --faint: #A8988A; --brand: #A84A28; --brand-2: #C96F45; --brand-ink:#6E3820; --green: #6F9E5C; --red: #C0533F; /* 🔴 v40(用户 2026-10-10):「影院管理部分…编辑使用蓝色的背景,删除使用红色的背景。」 ⇒ 新增一个中性的"操作蓝"(不是链接蓝、也不是模块品牌色)—— 它是**语义色**: 蓝=编辑、红=删除,全站统一,跟着模块换肤也不该变。 ⚠️ 暖纸底上要够暗才压得住白字:这里取 #2F6FB0(对比度 ≈ 4.9:1)。 */ --blue: #2F6FB0; /* 🔴 v41(用户 2026-10-10):旅程详情页四种动作按钮各一种底色 (计划=蓝 / 实际=绿 / 编辑=黄 / 删除=红)。蓝、绿(--green)、红已有; 这里补"编辑黄"。 ⚠️ 纯黄(#FFD400 之类)配白字几乎看不清(对比度 ≈ 1.3:1)。 取偏琥珀的深黄 #B8860B(DarkGoldenrod 系),暖纸底上白字对比度 ≈ 3.6:1, 且不会和品牌暖棕 (`--brand` #A84A28) 混淆 —— 后者明显更红。 ⚠️ "实际=绿"用既有的 `--green` (#6F9E5C),它在白字下对比度 ≈ 3.0:1, 深一步更保险 —— 见 `.btn-actual` 里单独取的 #4E7C3F(≈ 4.6:1)。 */ --yellow: #B8860B; --line: rgba(44, 35, 29, .10); --line-2: rgba(44, 35, 29, .06); --radius: 16px; --radius-s: 10px; --shadow: 0 1px 2px rgba(44, 35, 29, .05); --shadow-l: 0 4px 16px rgba(44, 35, 29, .08); /* ---- 动作按钮的统一度量 ---- 用户提过三次「设置按钮和记一场电影按钮高度要一致」。为杜绝再犯, 这里把按钮高度拆成 4 个变量,并算出唯一的高度: --act-h = 上下内边距 + 行高 + 上下边框 = 9*2 + 14*1.7 + 1*2 = 43.8px 文字按钮靠 padding + line-height 自然撑到这个高度; **只有图标的方形按钮**(.btn-icon)显式写高度,但取的是同一个 --act-h, 这样两边永远相等,改一处就同步。 */ --act-pad-y: 9px; --act-pad-x: 16px; --act-font: 14px; --act-line: 1.7; --act-h: calc(var(--act-pad-y) * 2 + var(--act-font) * var(--act-line) + 1px * 2); } * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; background: var(--paper); color: var(--ink); font: 15px/1.7 -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", sans-serif; -webkit-font-smoothing: antialiased; } a { color: var(--brand); text-decoration: none; } a:hover { text-decoration: underline; } /* ---------------- 顶栏 ---------------- */ .topbar { position: sticky; top: 0; z-index: 20; background: var(--surface); border-bottom: 1px solid var(--line); } .topbar-inner { max-width: 1080px; margin: 0 auto; padding: 0 24px; height: 60px; display: flex; align-items: center; gap: 24px; } .brand { display: flex; align-items: center; gap: 10px; color: var(--ink); } .brand:hover { text-decoration: none; } .brand img { width: 30px; height: 30px; border-radius: 8px; display: block; } .brand span { font-size: 16px; font-weight: 600; letter-spacing: .5px; } .nav { display: flex; gap: 4px; margin-left: 8px; } .nav a { padding: 6px 12px; border-radius: 999px; color: var(--muted); font-size: 14px; } .nav a:hover { background: var(--line-2); text-decoration: none; } .nav a.on { background: var(--brand); color: #fff; } .topbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; } .who { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 14px; } /* 姓名 + 头像是个下拉菜单的「把手」——hover 时给点反馈,让人知道它可点 */ .who-menu { position: relative; } .who-menu > summary { list-style: none; cursor: pointer; padding: 4px 6px; border-radius: 999px; } .who-menu > summary::-webkit-details-marker { display: none; } .who-menu > summary:hover { } /* 统一段处理 */ .who-menu > summary:hover .who-name { color: var(--brand); } .who-menu[open] > summary { background: var(--line-2); } .who-menu[open] > summary .caret { transform: rotate(180deg); } .who img { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; } .who img { transition: box-shadow .15s ease; } .who-menu > summary:hover img { box-shadow: 0 0 0 2px var(--brand-2); } /* 那个小三角:没有它,用户不知道"名字能点开" */ .caret { width: 0; height: 0; margin-left: -2px; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--faint); transition: transform .15s ease; } .menu-list { position: absolute; right: 0; top: calc(100% + 8px); z-index: 30; min-width: 148px; padding: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-s); box-shadow: var(--shadow-l); display: flex; flex-direction: column; } .menu-list a { padding: 8px 12px; border-radius: 8px; color: var(--ink); font-size: 14px; white-space: nowrap; } .menu-list a:hover { background: var(--line-2); text-decoration: none; color: var(--brand); } /* 退出放在最下面并隔一条线:它是"离开",和上面几个"改点什么"不是一类动作 */ .menu-list .menu-exit { margin-top: 4px; padding-top: 9px; border-top: 1px solid var(--line-2); border-radius: 0 0 8px 8px; color: var(--muted); } .menu-list .menu-exit:hover { color: var(--brand); } /* ---- 观影页的「设置」下拉:图标按钮 + 永远贴右 ---- 用户明确要求(2026-10-01): · 去掉「设置」二字,只留**三条横线**图标(☰); · 在**所有页面都排在最右侧**(位置统一); · 点开后的每条条目,**文字前都要有图标**; · 除设置外,其它按钮都要**有颜色**,跟设置区分开。 贴右的做法:`margin-left:auto` 把它推到动作区最右端 —— 这样无论各页把片段 require 在 actions 的哪个位置(有的写在最前、有的写在最后),它都贴右, 不必逐页调顺序,也就不会有"某个页面漏了"的情况。 */ .movie-set { position: relative; margin-left: auto; } .movie-set > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 6px; } .movie-set > summary::-webkit-details-marker { display: none; } .movie-set[open] > summary { border-color: var(--brand); color: var(--brand); } /* 方形图标按钮:只有图标、无文字。高度**必须**取 --act-h, 与「记一场电影」等实心按钮同高(那个高度由 --act-pad-y/--act-font/--act-line 推出)。 ⚠️ 别在这里另写一个数字(曾写死 40px,比文字按钮的 43.8px 矮了 3.8px,被用户连续投诉)。 */ .btn-icon { background: #fff; color: var(--ink); border: 1px solid var(--line); padding: 0 !important; width: var(--act-h); height: var(--act-h); min-width: var(--act-h); display: inline-flex; align-items: center; justify-content: center; line-height: 1; } .btn-icon:hover { color: var(--brand); border-color: var(--brand); } /* 底色交给统一段 */ .btn-icon svg { display: block; } /* 观影设置菜单:从右上角改成**按钮下方右对齐**(按钮贴右,菜单也贴右才不会跑出屏外) */ .movie-set .menu-list { left: auto; right: 0; top: calc(100% + 8px); } .movie-set .menu-list a { display: flex; align-items: center; gap: 10px; } .movie-set .menu-list a .mi { flex: 0 0 16px; color: var(--muted); } .movie-set .menu-list a:hover .mi, .movie-set .menu-list a.on .mi { color: currentColor; } .movie-set .menu-list a.on { color: var(--brand); background: var(--line-2); } /* ---- 日程页的设置下拉(v24 用户 2026-10-03 第 4、5 条)---- 它比观影/任务那个 ☰ 内容多:里面有个**表单**(默认视图 + 显示哪些本子)。 本子清单会随共享/新建变长,所以给列表一个最大高度 + 滚动, 否则菜单会顶出屏幕、底下的「保存」按钮够不着。 */ .cal-set .menu-list { min-width: 260px; max-width: min(320px, 92vw); } .cal-set-list { padding: 10px; gap: 0; } .cal-set-h { font-size: 12px; color: var(--muted); margin-bottom: 6px; } .cal-set-views { display: flex; gap: 12px; flex-wrap: wrap; } .cal-set-view { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; cursor: pointer; } .cal-set-view input { accent-color: var(--brand); } /* 🔴 v72(用户 2026-10-21)—— 这里原有一段 `.cal-set-books / .cal-set-book* / .cal-set-page / .cal-set-form|block|foot|links` 的样式,随 `calendar/display` 整页删除(显隐与管理合并进 `calendar/calendars`)**全部失去了使用者**, 已在本轮清掉。留着的代价不只是"没用": · 其中 `.cal-set-page .cal-set-books { max-height:none; overflow-y:visible; }` 曾被 v51 的反向验证当成"守内滚动"的锚点 —— 规则在、页面没了 ⇒ 注入照旧命中文本、页面照旧全绿 ⇒ **那条反向验证变成空转**, 在输出上却看起来"这条断言很结实"(实测踩中,见 `_b51_falsify.py` 注释)。 · ⇐ 教训:**断言只能锚"当前真有使用者"的规则**;删模板时必须同步删死 CSS, 否则验证脚本会静默退化成摆设。 现在"日历本清单不套内滚动"这件事由 `.cal-books` 守(它才是页面上真实的清单容器)。 */ /* ============================================================================ v72(用户 2026-10-21):日历本「显隐 + 管理」合并成一张清单 ---------------------------------------------------------------------------- 用户原话:「日历设置里面日历本显示和日历管理可以放在一起啊。就是把日历本全都 列出来,然后显示不显示,加个复选框就行了。另外把下面别人共享给我的 也提到上面来,把所有的日历都放在一块,只不过在后面标注出来, 这个是别人谁谁谁共享给我的?是只读的还是可写的,什么的就可以了呀。」 ⇒ 每行从左到右读下来就是一整句答案: [✓] ■ 名字 [谁·只读/可写] 共享 导出 编辑 删除 ⚠️ 用 grid 而不是 flex:归属那一列要**对齐**(否则每行标签长短不一时, 后面的操作按钮会跟着左右乱跳,看着很脏)。 ⚠️ 窄屏(<640px)改成两行:第一行勾选+色块+名字+归属,第二行操作。 44 行日历本在手机上横着排会挤成碎片(用户可能在手机浏览器里管日历)。 ========================================================================== */ .cal-books { display: flex; flex-direction: column; gap: 2px; } /* 分组分隔线:从"我的"切到"共享给我的" */ .cal-books-sep { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 14px 0 6px; padding-top: 12px; border-top: 1px solid var(--line); font-size: 13px; font-weight: 600; color: var(--muted); } .cal-books-sep-note { font-size: 12px; font-weight: 400; color: var(--faint); } .cal-book-row { display: grid; grid-template-columns: 22px 30px minmax(0, 1fr) auto auto; align-items: center; gap: 10px; padding: 8px 6px; border-radius: 8px; } .cal-book-row:hover { background: var(--line-2); } /* 关掉的本子整行变淡 —— 一眼看出"这本现在不显示", 但**不 disable**:点一下就能重新勾上(disable 会让人以为"这行坏了") */ .cal-book-row.is-off .cal-book-name, .cal-book-row.is-off .cal-book-dot, .cal-book-row.is-off .cp-trigger-dot { opacity: .45; } .cal-book-show { display: inline-flex; align-items: center; cursor: pointer; } .cal-book-show input { accent-color: var(--brand); width: 16px; height: 16px; cursor: pointer; } .cal-book-dot { display: inline-block; width: 13px; height: 13px; border-radius: 3px; border: 1px solid var(--line); justify-self: center; flex: 0 0 auto; } .cal-book-name { font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; } .cal-book-tags { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; } .cal-owner { font-size: 12.5px; color: var(--muted); white-space: nowrap; } .node-badge-quiet { opacity: .7; } /* 可写 / 只读两个徽标要**一眼能分**:可写用品牌色,只读用中性灰。 ⚠️ 别用"同色深浅"区分 —— 用户要的正是在列表里快速扫出"哪些我能动"。 */ .node-badge.is-write { background: var(--brand-soft, rgba(74, 124, 130, .12)); color: var(--brand-ink, var(--brand)); } .node-badge.is-read { background: var(--line-2); color: var(--muted); } .cal-book-acts { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-end; } .cal-book-acts .link-quiet { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; font-size: 13px; } .cal-book-acts .link-quiet:hover { color: var(--brand); text-decoration: underline; } .cal-book-acts .link-danger:hover { color: var(--danger, #C0533F); } .cal-book-acts .inline-form { display: inline; margin: 0; } /* 已共享给谁:第二行,靠名字下方 */ .cal-book-shares { grid-column: 3 / -1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 4px; font-size: 12.5px; color: var(--muted); } .cal-book-shares-label { color: var(--faint); } .cal-book-shares .inline-form { display: inline; margin: 0; } .cal-book-shares .link-quiet { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; font-size: 12.5px; } .cal-book-shares .link-danger:hover { color: var(--danger, #C0533F); } @media (max-width: 640px) { /* 窄屏:勾选+色块+名字+归属 一行,操作另起一行靠右 */ .cal-book-row { grid-template-columns: 22px 26px minmax(0, 1fr); row-gap: 6px; } .cal-book-tags { grid-column: 3 / -1; } .cal-book-acts { grid-column: 1 / -1; justify-content: flex-start; padding-left: 2px; } .cal-book-shares { grid-column: 1 / -1; } } /* ============================================================================ v72:内置调色器(替代 ``,不再弹 Windows 系统取色框) ---------------------------------------------------------------------------- 用户原话:「选颜色那个地方点了之后是弹出 Windows 的一个颜色选择框,这个不好, 我们把它改成内置的,就是就是在网页上弹出一个模态框或者一个什么样的, 然后选颜色,不要弹 Windows 的,那个一个体验也不好,另外一个也不好看。」 ========================================================================== */ .cp-field { display: inline-flex; align-items: center; } /* 触发按钮:小色块 + hex 值,长得像"一个可点的胶囊"而不是原生控件 */ .cp-trigger { display: inline-flex; align-items: center; gap: 7px; padding: 3px 9px 3px 4px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); cursor: pointer; font: inherit; color: var(--ink); transition: border-color .15s, box-shadow .15s; } .cp-trigger:hover { border-color: var(--brand); } .cp-trigger:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; } .cp-trigger-dot { width: 18px; height: 18px; border-radius: 50%; flex: 0 0 auto; border: 1px solid rgba(0, 0, 0, .12); } .cp-trigger-hex { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; color: var(--muted); letter-spacing: .3px; } /* 行内(日历本清单里)那个调色器更紧凑一点,别把行撑高 */ .cal-book-row .cp-trigger { padding: 2px 4px; border-color: transparent; background: transparent; } .cal-book-row .cp-trigger:hover { border-color: var(--line); background: var(--surface); } .cal-book-row .cp-trigger-hex { display: none; } .cal-book-row .cp-trigger-dot { width: 15px; height: 15px; } /* 模态框:与 .cal-modal 同一套"遮罩 + 居中面板",但独立命名 —— 它可能在**没有日历**的页面上也用(影院管理等),别跟日历耦合。 */ .cp-modal { position: fixed; inset: 0; z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 16px; background: var(--mask, rgba(20, 24, 26, .42)); } .cp-modal[hidden] { display: none; } .cp-modal-panel { width: min(420px, 100%); max-height: calc(100vh - 48px); display: flex; flex-direction: column; background: var(--surface); border-radius: 14px; box-shadow: 0 18px 48px rgba(0, 0, 0, .22); } .cp-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 10px; font-size: 15px; } .cp-modal-body { padding: 0 16px 4px; overflow-y: auto; } .cp-modal-foot { display: flex; gap: 10px; justify-content: flex-end; padding: 12px 16px 16px; } /* 大预览块:让用户看到"日程条上白字压在这个颜色上"的真实效果 */ .cp-preview { display: flex; align-items: center; justify-content: space-between; height: 62px; padding: 0 14px; border-radius: 10px; background: #4A7C82; color: #fff; transition: background .12s; } .cp-preview-chip { font-size: 13.5px; font-weight: 600; } .cp-preview-hex { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; opacity: .8; } .cp-swatches { display: grid; grid-template-columns: repeat(8, 1fr); gap: 8px; margin: 14px 0 12px; } .cp-swatch { aspect-ratio: 1 / 1; border-radius: 8px; border: 2px solid transparent; cursor: pointer; padding: 0; transition: transform .12s, border-color .12s; } .cp-swatch:hover { transform: scale(1.08); } /* 选中态:白环 + 深色外环,任何底色上都看得见(只用深色环时, 深色块上的环就"消失"了 —— 那正是"点了没反应"的观感来源之一) */ .cp-swatch.active { border-color: var(--surface); box-shadow: 0 0 0 2px var(--ink); } .cp-swatch:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; } .cp-custom { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-bottom: 8px; } .cp-custom label { font-size: 13px; color: var(--muted); } .cp-hash { font-family: ui-monospace, monospace; color: var(--faint); } .cp-hex-input { width: 96px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; text-transform: uppercase; } .cp-hex-input.is-bad { border-color: var(--danger, #C0533F); } .cp-bad { font-size: 12px; color: var(--danger, #C0533F); } /* 改色成功/失败的小浮条 */ .cp-toast { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); z-index: 2100; padding: 9px 16px; border-radius: 999px; background: var(--ink); color: var(--surface); font-size: 13px; box-shadow: 0 8px 24px rgba(0, 0, 0, .2); transition: opacity .3s, transform .3s; } .cp-toast.is-err { background: var(--danger, #C0533F); } .cp-toast.is-out { opacity: 0; transform: translateX(-50%) translateY(6px); } /* 模态框开着时锁住背景滚动(否则鼠标滚轮会滚到背后的长清单) */ body.cp-lock { overflow: hidden; } /* 点了日历空白处就建日程(能力保留,见 templates/calendar/index.php 的 data-new-at)。 🔴 v48(用户 2026-10-14 第 3 条):「现在鼠标放在视图上时,变成了一个十字。 我感觉这个没必要,不用有这样的变化。」 ⇒ **去掉十字光标**。原来给 `.rt-writable`(能写的人)挂 `cursor: crosshair` 是想"让人看出这里能点",但用户明确不要这个视觉变化。 ⚠️ 只删光标、**不删能力**:点击建日程的 JS 与 `data-new-at` 一律不动 (那是第 2 条要修好、而不是要撤掉的东西)。 ⚠️ 连 `.rt-writable` 类本身也留着 —— 它还被别处引用,且是"这个人能不能写" 这一状态在 DOM 上的标记位,删类名会牵连样式与断言。 */ .link-quiet { color: var(--muted); font-size: 14px; } .link-quiet:hover { color: var(--brand); } /* 长得像链接的**按钮**(v48)。`.link-quiet` 本来是给 / 写的, 直接套在