- ol-spin 改为逆时针,与 refresh(ccw)图标方向一致 - 按钮固定宽度 + 内容居中;图标固定 14x14 容器,check/refresh 切换与旋转时不再跳动
305 lines
9.6 KiB
CSS
305 lines
9.6 KiB
CSS
html, body, #root {
|
||
height: 100%;
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 0;
|
||
background: transparent;
|
||
overflow: hidden;
|
||
}
|
||
|
||
html[data-ol-platform="linux"],
|
||
html[data-ol-platform="linux"] body,
|
||
html[data-ol-platform="linux"] #root {
|
||
background: var(--ol-surface);
|
||
}
|
||
|
||
html[data-ol-platform="android"],
|
||
html[data-ol-platform="android"] body,
|
||
html[data-ol-platform="android"] #root {
|
||
background: var(--ol-surface);
|
||
}
|
||
|
||
html {
|
||
color-scheme: light dark;
|
||
}
|
||
|
||
body {
|
||
background: transparent;
|
||
user-select: none;
|
||
-webkit-user-select: none;
|
||
}
|
||
|
||
#root {
|
||
display: flex;
|
||
isolation: isolate;
|
||
}
|
||
|
||
button {
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
border: none;
|
||
background: transparent;
|
||
color: inherit;
|
||
padding: 0;
|
||
/* Windows Chromium / WebView2 默认 user-agent style 会给 <button> 加 OS-native
|
||
appearance(outset 边框 + 内阴影)、min-width / min-height(让 button 至少容下
|
||
系统外观)、:focus 时的实线 outline。这些在 Vocab 词条这种嵌套 button 上叠加
|
||
起来变成一圈灰色"大圈"。macOS WKWebView 处理较弱所以看不出来。一次性清掉。
|
||
键盘焦点指示统一走 .ol-focus-ring + :focus-visible 走 box-shadow(不受 outline:0 影响)。 */
|
||
appearance: none;
|
||
-webkit-appearance: none;
|
||
outline: none;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
@keyframes ol-select-pop {
|
||
from { opacity: 0; transform: translateY(-4px) scale(.98); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
|
||
/* SelectLite popover 关闭动画 —— 反向收缩 + 淡出 + 上移,跟入场对偶。 */
|
||
@keyframes ol-select-pop-out {
|
||
from { opacity: 1; transform: translateY(0) scale(1); }
|
||
to { opacity: 0; transform: translateY(-4px) scale(.98); }
|
||
}
|
||
|
||
/* 键盘焦点指示:鼠标点击 (:focus) 不显示,键盘 Tab (:focus-visible) 才显示。
|
||
用 box-shadow 而非 outline 是因为组件内联 style 可能用 outline:none
|
||
把默认 ring 干掉,box-shadow 不受影响仍能上色。配合 pr-agent #407 的 a11y 要求。 */
|
||
.ol-focus-ring:focus-visible {
|
||
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.45);
|
||
}
|
||
|
||
input, textarea {
|
||
font-family: inherit;
|
||
user-select: text;
|
||
-webkit-user-select: text;
|
||
}
|
||
|
||
a { color: inherit; text-decoration: none; }
|
||
|
||
@keyframes ol-window-enter {
|
||
from {
|
||
opacity: 0;
|
||
transform: translate3d(0, 8px, 0) scale(0.992);
|
||
filter: blur(8px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translate3d(0, 0, 0) scale(1);
|
||
filter: blur(0);
|
||
}
|
||
}
|
||
|
||
/* 弹窗动画统一入口 —— 纯 opacity / transform,合成器友好。
|
||
背景不再逐帧动画 backdrop-filter(会卡顿、闪烁);模糊由宿主元素静态设定,
|
||
动画只负责淡入淡出。设置弹窗 / 各市场弹窗 / GitHub 登录弹窗共用这套。 */
|
||
@keyframes ol-modal-backdrop-in {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes ol-modal-backdrop-out {
|
||
from { opacity: 1; }
|
||
to { opacity: 0; }
|
||
}
|
||
|
||
@keyframes ol-modal-card-in {
|
||
from { opacity: 0; transform: translateY(8px) scale(0.985); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
|
||
/* 设置弹窗切换 tab 时右侧内容的轻微淡入。 */
|
||
@keyframes ol-tab-fade {
|
||
from { opacity: 0; transform: translateY(4px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* 通用旋转 —— 「检查更新」按钮检查中的转圈动画等。
|
||
refresh 图标是逆时针(ccw)箭头,旋转方向与其一致,避免视觉上"反着转"。 */
|
||
@keyframes ol-spin {
|
||
to { transform: rotate(-360deg); }
|
||
}
|
||
|
||
@keyframes ol-modal-drawer-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translate3d(12px, 0, 0) scale(0.985);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translate3d(0, 0, 0) scale(1);
|
||
}
|
||
}
|
||
|
||
@keyframes ol-modal-drawer-out {
|
||
from {
|
||
opacity: 1;
|
||
transform: translate3d(0, 0, 0) scale(1);
|
||
}
|
||
to {
|
||
opacity: 0;
|
||
transform: translate3d(12px, 0, 0) scale(0.985);
|
||
}
|
||
}
|
||
|
||
/* 风格市场全高抽屉从右缘滑入(MarketplaceModal 引用此名)。 */
|
||
@keyframes ol-drawer-in-right {
|
||
from { opacity: 0; transform: translateX(48px); }
|
||
to { opacity: 1; transform: translateX(0); }
|
||
}
|
||
|
||
/* ── 假毛玻璃面(qa 追问框 / capsule 语音胶囊等透明浮窗)─────────────────────
|
||
webview 拿不到透明窗口背后的桌面像素 —— CSS backdrop-filter 模糊不了桌面,
|
||
是 Tauri 已知的上游限制(issue #2827 / #10064 / #6876)。所以这里**不做
|
||
真实高斯模糊**:宿主元素保留半透明白底,这层 ::before 叠一张细颗粒噪点纹理
|
||
(磨砂质感来源);内描边高光与柔和阴影由宿主自带的 box-shadow 提供。
|
||
纯静态 CSS,零运行时 / 动画开销。Linux 主动禁用透明窗口后走纯不透明面。
|
||
宿主元素自带 border-radius 即可,::before 噪点层 inherit 圆角;宿主不要再设
|
||
backdrop-filter(死代码)。 */
|
||
.ol-frost {
|
||
position: relative;
|
||
isolation: isolate;
|
||
background: var(--ol-frost-bg);
|
||
}
|
||
|
||
.ol-frost::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: -1;
|
||
border-radius: inherit;
|
||
/* 不透明灰度噪点,normal 混合 —— 这是"磨砂颗粒"的来源。opacity 直接等于颗粒
|
||
强度:0.1 几乎看不见,0.32 能明显看出磨砂质感又不脏。要更糙往 0.45 调。 */
|
||
background-image: var(--ol-frost-grain);
|
||
/* tile 与 SVG 视口同尺寸(100),stitchTiles 的无缝拼接才成立 */
|
||
background-size: 100px 100px;
|
||
opacity: 0.32;
|
||
pointer-events: none;
|
||
}
|
||
|
||
html[data-ol-platform="linux"] .ol-frost {
|
||
background: var(--ol-surface);
|
||
}
|
||
|
||
html[data-ol-platform="linux"] .ol-frost::before {
|
||
content: none;
|
||
}
|
||
|
||
html[data-ol-platform="android"] .ol-frost {
|
||
background: var(--ol-surface);
|
||
}
|
||
|
||
html[data-ol-platform="android"] .ol-frost::before {
|
||
content: none;
|
||
}
|
||
|
||
.ol-app-shell-bg {
|
||
background: var(--ol-app-shell-bg);
|
||
}
|
||
|
||
.ol-panel-surface {
|
||
background: var(--ol-panel-bg);
|
||
border: 0.5px solid var(--ol-panel-border);
|
||
box-shadow: var(--ol-panel-shadow);
|
||
}
|
||
|
||
.ol-card-surface {
|
||
background: var(--ol-card-bg);
|
||
border: 0.5px solid var(--ol-card-border);
|
||
box-shadow: var(--ol-card-shadow);
|
||
}
|
||
|
||
.ol-settings-surface {
|
||
background: var(--ol-settings-content-bg);
|
||
}
|
||
|
||
.ol-settings-surface[data-ol-mobile="true"] {
|
||
border-radius: var(--ol-panel-radius) var(--ol-panel-radius) 0 0;
|
||
}
|
||
|
||
.ol-capsule-pill {
|
||
background: var(--ol-capsule-pill-bg) !important;
|
||
border-color: var(--ol-capsule-pill-border) !important;
|
||
}
|
||
|
||
html[data-ol-platform="linux"] .ol-capsule-pill,
|
||
html[data-ol-platform="android"] .ol-capsule-pill {
|
||
background: var(--ol-capsule-pill-bg) !important;
|
||
}
|
||
|
||
/* ── 滚动边缘渐隐(.ol-scroll-fade)──────────────────────────────────────────
|
||
参照 shadcn scroll-fade 工具的行为,纯 CSS scroll-driven animations 实现:
|
||
静止在顶部时顶缘清晰、底缘 28px 渐隐提示「下面还有内容」;滚动中双边渐隐;
|
||
滚到底部时底缘恢复清晰。两个注册的 <length> 自定义属性充当 mask 渐变里的
|
||
动画插值目标(初始值 = 静止在顶部的状态)。 */
|
||
@property --ol-sf-t {
|
||
syntax: '<length>';
|
||
inherits: false;
|
||
initial-value: 0px;
|
||
}
|
||
|
||
@property --ol-sf-b {
|
||
syntax: '<length>';
|
||
inherits: false;
|
||
initial-value: 28px;
|
||
}
|
||
|
||
/* 三层 mask 取交集:顶缘渐隐层 ∩ 底缘渐隐层 ∩ 全黑兜底层。
|
||
-webkit-mask-composite: source-in 等价于标准 mask-composite: intersect;
|
||
两者都写、标准属性在后 —— 新引擎用标准值,老 WebKit 走前缀值。 */
|
||
.ol-scroll-fade {
|
||
-webkit-mask-image:
|
||
linear-gradient(to bottom, transparent, black var(--ol-sf-t)),
|
||
linear-gradient(to top, transparent, black var(--ol-sf-b)),
|
||
linear-gradient(black, black);
|
||
mask-image:
|
||
linear-gradient(to bottom, transparent, black var(--ol-sf-t)),
|
||
linear-gradient(to top, transparent, black var(--ol-sf-b)),
|
||
linear-gradient(black, black);
|
||
-webkit-mask-composite: source-in;
|
||
mask-composite: intersect;
|
||
}
|
||
|
||
@supports (animation-timeline: scroll(self y)) {
|
||
/* 滚动驱动:开头 96px 行程里顶缘渐隐 0 → 28px,末尾 96px 行程里底缘
|
||
渐隐 28px → 0,fill both 在行程外保持端点值。
|
||
注意:animation-timeline / animation-range 必须写在 animation 简写之后,
|
||
简写会把它们重置回初始值。
|
||
已知限制:内容不溢出时 scroll(self y) 时间线不激活,自定义属性回落到
|
||
初始值 —— 底缘仍有 28px 渐隐轻微作用在不可滚动容器的末端(通常落在
|
||
内边距上几乎不可见),与下方静态降级的限制一致,接受。 */
|
||
.ol-scroll-fade {
|
||
animation:
|
||
ol-sf-top linear both,
|
||
ol-sf-bottom linear both;
|
||
animation-timeline: scroll(self y);
|
||
animation-range: 0 96px, calc(100% - 96px) 100%;
|
||
}
|
||
|
||
@keyframes ol-sf-top {
|
||
from { --ol-sf-t: 0px; }
|
||
to { --ol-sf-t: 28px; }
|
||
}
|
||
|
||
@keyframes ol-sf-bottom {
|
||
from { --ol-sf-b: 28px; }
|
||
to { --ol-sf-b: 0px; }
|
||
}
|
||
}
|
||
|
||
/* 降级(macOS 现行 WKWebView 尚无 scroll-driven animations):静态双边渐隐,
|
||
不随滚动位置变化;单层 mask 无需分层合成,把 composite 重置回默认,
|
||
规避老 WebKit 单层 + source-in 的兼容怪癖。限制同上:不可滚动的容器
|
||
两缘也会有 28px 轻微渐隐。 */
|
||
@supports not (animation-timeline: scroll(self y)) {
|
||
.ol-scroll-fade {
|
||
-webkit-mask-image: linear-gradient(to bottom, transparent, black 28px, black calc(100% - 28px), transparent);
|
||
mask-image: linear-gradient(to bottom, transparent, black 28px, black calc(100% - 28px), transparent);
|
||
-webkit-mask-composite: source-over;
|
||
mask-composite: add;
|
||
}
|
||
}
|