fix(ui): 深色模式下补齐卡片与弹层的层次感
- 深色里沿用浅色那套低透明度黑色阴影没有对比度(黑投影落在 zinc-950 上), 悬停和弹层都看不出「浮起来」,本次为深色单独配一套阴影与状态色 - 卡片:静止态改「近距投影 + 顶部内高光」;悬停改「外扩暗晕 + 极淡白光晕 + 内高光 12%」,同时底色提亮一档(zinc-900 → zinc-800)、边框提到 zinc-600, 位移补偿到 2px —— 暗底上 1px 的移动肉眼看不出来 - 弹层:新增 shadow-pop-dark / shadow-panel-dark 标记类,只挂给深色底面的 下拉与抽屉;toast 在深色下仍是亮色块(dark:bg-zinc-100),保留原黑色投影, 避免亮块套白光晕发脏 - 抽屉补 dark:border-l:它在深色下既无边框又无可视阴影,与背景糊成一片 - 实现说明:Tailwind v4 会把 @theme 的阴影值内联进 --tw-shadow,覆盖 CSS 变量 对已生成的工具类无效,因此直接重写 box-shadow;规则不放进 @layer 以便稳定 压过原子类,且选择器需按 hover: 变体转义后的类名书写 验证:vue-tsc -b 与 eslint src 干净;TaskCard / SideDrawer / TagFilter / TagInput / BatchSelectionBar 共 114 例单测通过;另用 Playwright 采样像素亮度 与 getComputedStyle 确认深浅两套阴影各自命中。
This commit is contained in:
@@ -236,7 +236,7 @@ function onDueChange(value: string | null): void {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="dueOpen"
|
v-if="dueOpen"
|
||||||
class="absolute top-full left-0 z-30 mt-1.5 w-56 rounded-lg border border-slate-200 bg-white p-2 shadow-pop dark:border-zinc-700 dark:bg-zinc-900"
|
class="absolute top-full left-0 z-30 mt-1.5 w-56 rounded-lg border border-slate-200 bg-white p-2 shadow-pop shadow-pop-dark dark:border-zinc-700 dark:bg-zinc-900"
|
||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
:model-value="batchDue"
|
:model-value="batchDue"
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ onBeforeUnmount(stopListening)
|
|||||||
|
|
||||||
<aside
|
<aside
|
||||||
ref="panelRef"
|
ref="panelRef"
|
||||||
class="absolute inset-y-0 right-0 flex w-full max-w-md flex-col bg-white shadow-panel outline-none dark:bg-zinc-900"
|
class="absolute inset-y-0 right-0 flex w-full max-w-md flex-col bg-white shadow-panel shadow-panel-dark outline-none dark:border-l dark:border-zinc-800 dark:bg-zinc-900"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
:aria-label="title"
|
:aria-label="title"
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="open"
|
v-if="open"
|
||||||
class="absolute top-full left-0 z-20 mt-1.5 w-56 rounded-lg border border-slate-200 bg-white p-1.5 shadow-pop dark:border-zinc-700 dark:bg-zinc-900"
|
class="absolute top-full left-0 z-20 mt-1.5 w-56 rounded-lg border border-slate-200 bg-white p-1.5 shadow-pop shadow-pop-dark dark:border-zinc-700 dark:bg-zinc-900"
|
||||||
>
|
>
|
||||||
<p v-if="tags.length === 0" class="px-2 py-3 text-center text-xs text-slate-400 dark:text-zinc-500">
|
<p v-if="tags.length === 0" class="px-2 py-3 text-center text-xs text-slate-400 dark:text-zinc-500">
|
||||||
还没有任何标签
|
还没有任何标签
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ function onBlur(): void {
|
|||||||
id="tag-suggestions"
|
id="tag-suggestions"
|
||||||
role="listbox"
|
role="listbox"
|
||||||
aria-label="已有标签"
|
aria-label="已有标签"
|
||||||
class="absolute top-full left-0 z-20 mt-1 max-h-44 w-full overflow-y-auto rounded-lg border border-slate-200 bg-white p-1 shadow-pop dark:border-zinc-700 dark:bg-zinc-900"
|
class="absolute top-full left-0 z-20 mt-1 max-h-44 w-full overflow-y-auto rounded-lg border border-slate-200 bg-white p-1 shadow-pop shadow-pop-dark dark:border-zinc-700 dark:bg-zinc-900"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="(tag, index) in candidates"
|
v-for="(tag, index) in candidates"
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ const cardClass = computed(() => {
|
|||||||
}
|
}
|
||||||
return isDone.value
|
return isDone.value
|
||||||
? 'border-slate-200 bg-white opacity-60 dark:border-zinc-800 dark:bg-zinc-900'
|
? 'border-slate-200 bg-white opacity-60 dark:border-zinc-800 dark:bg-zinc-900'
|
||||||
: 'border-slate-200 bg-white hover:-translate-y-px hover:border-slate-300 hover:shadow-card-hover dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-zinc-700'
|
: 'border-slate-200 bg-white hover:-translate-y-px hover:border-slate-300 hover:shadow-card-hover dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-zinc-600 dark:hover:bg-zinc-800'
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -81,3 +81,68 @@
|
|||||||
* 试过把 `::-webkit-datetime-edit` 系列伪元素设成透明,但部分环境下不生效,
|
* 试过把 `::-webkit-datetime-edit` 系列伪元素设成透明,但部分环境下不生效,
|
||||||
* 已改为在 DatePicker.vue 里用不透明占位层直接盖住。
|
* 已改为在 DatePicker.vue 里用不透明占位层直接盖住。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 深色模式下的卡片阴影
|
||||||
|
*
|
||||||
|
* 上面 @theme 里的阴影是「低透明度黑色」,落在近黑的 zinc-950 背景上几乎没有
|
||||||
|
* 对比度 —— 浅色模式下鼠标悬停能明显看到卡片被抬起来,深色模式下就基本没反应。
|
||||||
|
*
|
||||||
|
* 深色不能照搬同一套参数,改用三件事来还原同样的「悬浮感」:
|
||||||
|
* 1. 外扩暗晕:把卡片四周压得更暗,卡片本身因此显得更亮更高;
|
||||||
|
* 2. 顶部内高光:模拟顶面受光,这是深色下唯一能画出「厚度」的手段;
|
||||||
|
* 3. 悬停时同步提亮卡片底色(见 TaskCard.vue 的 dark:hover:bg-*)。
|
||||||
|
*
|
||||||
|
* 注意两点:
|
||||||
|
* 1. Tailwind v4 会把 @theme 里的阴影值内联进 --tw-shadow,改 CSS 变量对
|
||||||
|
* 已经生成的工具类无效,所以这里直接重写 box-shadow;
|
||||||
|
* 2. 悬停阴影在组件里写的是 `hover:shadow-card-hover`,生成的类名带转义前缀
|
||||||
|
* (.hover\:shadow-card-hover),必须按这个选择器写,否则静默不生效。
|
||||||
|
*
|
||||||
|
* 这些规则不在任何 @layer 里,优先级高于 utilities 层,能稳定覆盖原子类。
|
||||||
|
*/
|
||||||
|
html.dark .shadow-card {
|
||||||
|
box-shadow:
|
||||||
|
var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow),
|
||||||
|
var(--tw-ring-shadow), 0 1px 2px rgb(0 0 0 / 0.55), inset 0 1px 0 rgb(255 255 255 / 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 悬停:暗晕压四周 + 一圈极淡的白光晕 + 顶部内高光。
|
||||||
|
* 白光晕是深色下唯一能真正"画出来"的扩散效果,黑色投影只负责把近处压暗。
|
||||||
|
*/
|
||||||
|
html.dark .hover\:shadow-card-hover:hover {
|
||||||
|
box-shadow:
|
||||||
|
var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow),
|
||||||
|
var(--tw-ring-shadow), 0 14px 30px -10px rgb(0 0 0 / 0.9), 0 0 22px -4px rgb(255 255 255 / 0.07),
|
||||||
|
0 0 0 1px rgb(255 255 255 / 0.08), inset 0 1px 0 rgb(255 255 255 / 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 深色下 1px 的位移在暗底上几乎看不出来,补偿到 2px */
|
||||||
|
html.dark .hover\:-translate-y-px:hover {
|
||||||
|
--tw-translate-y: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 深色模式下的弹层阴影(下拉 / 抽屉)
|
||||||
|
*
|
||||||
|
* 和卡片同样的道理:shadow-pop / shadow-panel 在深色下是「黑色投在黑底上」。
|
||||||
|
* 但这里不能用 `html.dark .shadow-pop` 统一覆盖 —— ToastHost / PwaUpdatePrompt
|
||||||
|
* 在深色下仍然是浅色底(dark:bg-zinc-100),它们需要保留原来的黑色投影,
|
||||||
|
* 否则亮块上套一圈白光晕会很脏。
|
||||||
|
*
|
||||||
|
* 所以改成显式标记:深色底面的弹层多挂一个 shadow-pop-dark / shadow-panel-dark,
|
||||||
|
* 只有挂了标记的元素才会被下面的规则接管。类名带 html.dark 前缀 + 未分层,
|
||||||
|
* 特异性 (0,2,1) 高于原子类的 (0,1,0),不受 CSS 生成顺序影响。
|
||||||
|
*/
|
||||||
|
html.dark .shadow-pop-dark {
|
||||||
|
box-shadow:
|
||||||
|
0 12px 28px -8px rgb(0 0 0 / 0.85), 0 2px 8px -2px rgb(0 0 0 / 0.6),
|
||||||
|
0 0 22px -6px rgb(255 255 255 / 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 抽屉从右侧滑入,主投影朝左;其余方向补一圈淡光晕把边缘从暗背景里勾出来 */
|
||||||
|
html.dark .shadow-panel-dark {
|
||||||
|
box-shadow:
|
||||||
|
-16px 0 48px -12px rgb(0 0 0 / 0.9), 0 0 30px -10px rgb(255 255 255 / 0.05);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user