mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
style(client): unify modal backdrops with dedicated scrim tokens
Replace tinted modal veils with dedicated scrim tokens so dialogs, sheets, and image lightboxes dim consistently in both light and dark themes. Enforce scrim token usage in style validation to prevent backdrops that wash out in dark mode.
This commit is contained in:
@@ -32,6 +32,19 @@ const TUNED_TEXT_TOKENS = [
|
||||
// (`hover:`, `placeholder:`, `group-data-[active=true]/item:` and so on).
|
||||
const FADED_TEXT_PATTERN = new RegExp(String.raw`(?:[\w[\]=.\-/]+:)*text-(?:${TUNED_TEXT_TOKENS.join('|')})\/\d+`, 'g')
|
||||
const LEGACY_SETTINGS_BUTTON_PATTERN = /\bsettings-btn-(?:primary|outline|secondary|danger)\b/g
|
||||
|
||||
/**
|
||||
* Modal backdrops take their veil from `bg-scrim`, or `bg-scrim-media` behind image viewers, so
|
||||
* every dialog and sheet dims the page the same way in both themes. A backdrop tinted with a theme
|
||||
* colour inverts in dark mode: `--foreground` is near-white there, so the veil washes the page grey
|
||||
* instead of dimming it. A full-screen layer counts as a backdrop when it is fixed or dismisses on
|
||||
* click; tinted overlays that only decorate a cover or card are left alone.
|
||||
*/
|
||||
const ELEMENT_OPENING_TAG_PATTERN = /<[A-Za-z][\w-]*\b[^<>]*>/g
|
||||
const DIALOG_OVERLAY_TAG_PATTERN = /^<(?:Dialog|AlertDialog)Overlay\b/
|
||||
const SCRIM_CLASS_PATTERN = /\bbg-scrim(?:-media)?\b/
|
||||
const TINTED_BACKDROP_PATTERN = /\bbg-(?:black|white|foreground|background)\/\d+/
|
||||
const BACKDROP_ROLE_PATTERN = /\bfixed\b|@click/
|
||||
const BUTTON_OPENING_TAG_PATTERN = /<Button\b[\s\S]*?>/g
|
||||
const DESTRUCTIVE_BUTTON_VARIANT_PATTERN = /\bvariant=["']destructive(?:-outline|-ghost)?["']/
|
||||
const DESTRUCTIVE_BUTTON_COLOR_OVERRIDE_PATTERN =
|
||||
@@ -74,6 +87,17 @@ for (const file of await sourceFiles(sourceDirectory)) {
|
||||
errors.push(`${relativePath}:${line}: destructive Button color override - keep destructive styling centralized in the shared Button variants.`)
|
||||
}
|
||||
|
||||
for (const match of source.matchAll(ELEMENT_OPENING_TAG_PATTERN)) {
|
||||
const tag = match[0]
|
||||
const isDialogOverlay = DIALOG_OVERLAY_TAG_PATTERN.test(tag)
|
||||
const isTintedBackdrop = /\binset-0\b/.test(tag) && BACKDROP_ROLE_PATTERN.test(tag) && TINTED_BACKDROP_PATTERN.test(tag)
|
||||
if ((!isDialogOverlay || SCRIM_CLASS_PATTERN.test(tag)) && !isTintedBackdrop) continue
|
||||
const line = source.slice(0, match.index).split('\n').length
|
||||
errors.push(
|
||||
`${relativePath}:${line}: modal backdrop - use bg-scrim, or bg-scrim-media behind an image viewer, so it dims the page in both themes.`,
|
||||
)
|
||||
}
|
||||
|
||||
for (const [index, line] of lines.entries()) {
|
||||
for (const match of line.matchAll(LEGACY_SETTINGS_BUTTON_PATTERN)) {
|
||||
errors.push(`${relativePath}:${index + 1}: ${match[0]} - use the shared Button component and its variants.`)
|
||||
@@ -99,4 +123,4 @@ if (errors.length > 0) {
|
||||
throw new Error(`Style validation failed:\n${errors.join('\n')}`)
|
||||
}
|
||||
|
||||
console.log('Validated style tokens and shared settings buttons')
|
||||
console.log('Validated style tokens, modal backdrops and shared settings buttons')
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
|
||||
--color-diff-del: var(--diff-del);
|
||||
--color-diff-ins: var(--diff-ins);
|
||||
--color-scrim: var(--scrim);
|
||||
--color-scrim-media: var(--scrim-media);
|
||||
--color-path-dim: var(--path-dim);
|
||||
--color-path-fold: var(--path-fold);
|
||||
|
||||
|
||||
@@ -102,6 +102,13 @@
|
||||
--surface-3: oklch(0.93 var(--tint-c-surface) var(--tint-h));
|
||||
--surface-4: oklch(0.91 var(--tint-c-surface) var(--tint-h));
|
||||
|
||||
/* Modal scrims: the veil behind every dialog and sheet. Dark in both themes. Tinting it with the
|
||||
foreground colour washed dark mode out to grey instead of dimming it, because there the
|
||||
foreground is near-white. --scrim-media sits behind image viewers, where the picture is the
|
||||
point, so it is near-opaque in both themes. */
|
||||
--scrim: oklch(0.2 0.02 var(--tint-h) / 0.35);
|
||||
--scrim-media: oklch(0 0 0 / 0.82);
|
||||
|
||||
/* Shadows */
|
||||
--elevation-xs: 0 1px 2px oklch(0 0 0 / 0.05);
|
||||
--elevation-sm: 0 1px 3px oklch(0 0 0 / 0.08), 0 1px 2px oklch(0 0 0 / 0.06);
|
||||
@@ -362,6 +369,9 @@
|
||||
--surface-3: oklch(calc(0.245 + var(--bg-lift)) var(--tint-c-surface) var(--tint-h));
|
||||
--surface-4: oklch(calc(0.295 + var(--bg-lift)) var(--tint-c-surface) var(--tint-h));
|
||||
|
||||
/* A dark surface needs a deeper veil than a light one to read as dimmed at all. */
|
||||
--scrim: oklch(0.06 0.01 var(--tint-h) / 0.62);
|
||||
|
||||
/* Shadows */
|
||||
--elevation-xs: 0 1px 2px oklch(0 0 0 / 0.4);
|
||||
--elevation-sm: 0 1px 3px oklch(0 0 0 / 0.5), 0 1px 2px oklch(0 0 0 / 0.4);
|
||||
|
||||
@@ -15,7 +15,7 @@ const ADDITIONAL_TERMS_URL = `${PROJECT_URL}/blob/main/ADDITIONAL_TERMS.md`
|
||||
<template>
|
||||
<DialogRoot :open="open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/50" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
aria-modal="true"
|
||||
class="fixed left-1/2 top-1/2 z-50 max-h-[calc(100svh-2rem)] w-[calc(100%-2rem)] max-w-xl -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-lg border border-border bg-card p-6 shadow-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
|
||||
@@ -50,7 +50,7 @@ function handleCancel() {
|
||||
<template>
|
||||
<DialogRoot :open="open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-[70] bg-foreground/50" />
|
||||
<DialogOverlay class="fixed inset-0 z-[70] bg-scrim" />
|
||||
<DialogContent
|
||||
aria-modal="true"
|
||||
class="fixed left-1/2 top-1/2 z-[70] w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-card p-6 shadow-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('ConfirmDialog', () => {
|
||||
// Simulate an underlying drawer/sheet overlay rendered at z-50
|
||||
const sheetOverlay = document.createElement('div')
|
||||
sheetOverlay.setAttribute('data-slot', 'sheet-overlay')
|
||||
sheetOverlay.className = 'fixed inset-0 z-50 bg-black/15'
|
||||
sheetOverlay.className = 'fixed inset-0 z-50 bg-scrim'
|
||||
document.body.appendChild(sheetOverlay)
|
||||
|
||||
const wrapper = mountDialog()
|
||||
|
||||
@@ -15,7 +15,7 @@ const delegatedProps = reactiveOmit(props, 'class')
|
||||
data-slot="sheet-overlay"
|
||||
:class="
|
||||
cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/15',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-scrim',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
||||
@@ -53,7 +53,7 @@ function handleCopy() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 px-4" @click.self="handleClose">
|
||||
<div class="fixed inset-0 z-[60] flex items-center justify-center bg-scrim px-4" @click.self="handleClose">
|
||||
<div class="w-full max-w-md rounded-lg border border-border bg-card p-6 shadow-xl">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ t('adminFeature.resetLink.title') }}</h2>
|
||||
|
||||
@@ -52,7 +52,7 @@ async function handleSubmit() {
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center bg-black/50 px-4">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center bg-scrim px-4">
|
||||
<div class="w-full max-w-sm rounded-lg border border-border bg-card shadow-xl p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ t('auth.changePassword.title') }}</h2>
|
||||
|
||||
@@ -32,7 +32,7 @@ const resolvedCancelLabel = computed(() => props.cancelLabel ?? t('common.cancel
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-[70] flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="emit('cancel')" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="emit('cancel')" />
|
||||
<div class="relative z-10 mx-4 w-full max-w-md rounded-lg border border-border bg-card p-6 shadow-2xl">
|
||||
<div class="mb-5 flex items-start gap-4">
|
||||
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-full" :class="destructive ? 'bg-destructive/10' : 'bg-primary/10'">
|
||||
|
||||
@@ -225,7 +225,7 @@ watch(resolvedBio, () => {
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="imageLightboxOpen && displayImageUrl"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-scrim-media"
|
||||
@click="imageLightboxOpen = false"
|
||||
>
|
||||
<button
|
||||
|
||||
@@ -114,7 +114,7 @@ function handleClose() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
<div class="relative z-10 w-full max-w-md mx-4 bg-card border border-border rounded-lg shadow-2xl overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h2 class="text-base font-semibold text-foreground">
|
||||
|
||||
@@ -136,7 +136,7 @@ function onCoverLoad(event: Event) {
|
||||
|
||||
<DialogRoot :open="!!lightbox" @update:open="onLightboxOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=open]:fade-in-0" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim-media data-[state=open]:animate-in data-[state=open]:fade-in-0" />
|
||||
<DialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 max-h-[90vh] max-w-[90vw] -translate-x-1/2 -translate-y-1/2 outline-none data-[state=open]:animate-in data-[state=open]:zoom-in-95"
|
||||
>
|
||||
|
||||
@@ -443,7 +443,7 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex">
|
||||
<div class="hidden sm:block flex-1 bg-black/50 backdrop-blur-sm" @click="$emit('close')" />
|
||||
<div class="hidden sm:block flex-1 bg-scrim" @click="$emit('close')" />
|
||||
|
||||
<div
|
||||
class="relative flex flex-col w-full h-full bg-background sm:border-l border-border shadow-2xl overflow-hidden transition-[width,max-width] duration-300"
|
||||
|
||||
@@ -338,7 +338,7 @@ async function handleDiscardResultDuplicates() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
<div class="relative z-10 w-full max-w-2xl mx-4 bg-card border border-border rounded-lg shadow-2xl overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h2 class="text-base font-semibold text-foreground">
|
||||
|
||||
@@ -99,7 +99,7 @@ function handleClose() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
<div class="relative z-10 w-full max-w-xl mx-4 bg-card border border-border rounded-lg shadow-2xl overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h2 class="text-base font-semibold text-foreground">
|
||||
|
||||
@@ -49,7 +49,7 @@ onMounted(() => loadPage(1))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="emit('close')">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="emit('close')">
|
||||
<div class="bg-background border border-border rounded-lg shadow-xl w-full max-w-2xl max-h-[80vh] flex flex-col">
|
||||
<div class="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
|
||||
<h2 class="text-sm font-semibold">{{ t('bookMetadataFetch.report.authorTitle') }}</h2>
|
||||
|
||||
@@ -49,7 +49,7 @@ onMounted(() => loadPage(1))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="emit('close')">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="emit('close')">
|
||||
<div class="bg-background border border-border rounded-lg shadow-xl w-full max-w-2xl max-h-[80vh] flex flex-col">
|
||||
<div class="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
|
||||
<h2 class="text-sm font-semibold">{{ t('bookMetadataFetch.report.bookTitle') }}</h2>
|
||||
|
||||
@@ -117,7 +117,7 @@ function handleSubmit() {
|
||||
<template>
|
||||
<DialogRoot :open="isOpen" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/50" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
aria-modal="true"
|
||||
class="fixed left-1/2 top-1/2 z-50 max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-lg border border-border bg-card p-6 shadow-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
|
||||
@@ -64,7 +64,7 @@ function handleOpenChange(open: boolean) {
|
||||
<DialogRoot :open="props.open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 motion-reduce:animate-none"
|
||||
class="fixed inset-0 z-50 bg-scrim-media data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 motion-reduce:animate-none"
|
||||
/>
|
||||
<DialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 flex max-h-[90vh] max-w-[90vw] -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-3 outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 motion-reduce:animate-none"
|
||||
|
||||
@@ -456,7 +456,7 @@ function handleDelete() {
|
||||
<DialogRoot :open="coverLightboxOpen" @update:open="coverLightboxOpen = $event">
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
class="fixed inset-0 z-50 bg-scrim-media data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
/>
|
||||
<DialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 max-w-[90vw] max-h-[90vh] outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
|
||||
|
||||
@@ -204,7 +204,7 @@ watch(
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
<div class="relative z-10 w-full max-w-lg mx-4 max-h-[90vh] bg-card border border-border rounded-lg shadow-2xl flex flex-col">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-6 py-4 border-b border-border shrink-0">
|
||||
|
||||
@@ -11,7 +11,7 @@ const { t } = useI18n()
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="emit('cancel')" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="emit('cancel')" />
|
||||
<div class="relative z-10 w-full max-w-sm mx-4 bg-card border border-border rounded-lg shadow-2xl p-6">
|
||||
<div class="flex items-start gap-4 mb-5">
|
||||
<div class="shrink-0 h-10 w-10 rounded-full bg-destructive/10 flex items-center justify-center">
|
||||
|
||||
@@ -47,12 +47,7 @@ const sections = computed(() => [
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="shortcut-overlay">
|
||||
<div
|
||||
v-if="open"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
||||
@click.self="handleClose"
|
||||
@keydown.escape="handleClose"
|
||||
>
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="handleClose" @keydown.escape="handleClose">
|
||||
<div class="relative w-full max-w-md rounded-xl border border-border bg-card shadow-xl" role="dialog" :aria-label="t('book.shortcuts.title')">
|
||||
<div class="flex items-center justify-between border-b border-border px-5 py-3">
|
||||
<h2 class="text-sm font-semibold text-foreground">{{ t('book.shortcuts.title') }}</h2>
|
||||
|
||||
@@ -213,7 +213,7 @@ watch(
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="closeDialog" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="closeDialog" />
|
||||
<div class="relative z-10 mx-4 w-full max-w-2xl rounded-lg border border-border bg-card shadow-2xl">
|
||||
<div class="flex items-center justify-between border-b border-border px-5 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
@@ -29,7 +29,7 @@ function handleConfirm() {
|
||||
<template>
|
||||
<DialogRoot :open="props.open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/20" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-card p-5 shadow-xl"
|
||||
>
|
||||
|
||||
@@ -125,7 +125,7 @@ function formatPillClass(filename: string): string {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center p-2 sm:p-4">
|
||||
<div class="absolute inset-0 bg-black/50 backdrop-blur-[2px]" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
|
||||
<div
|
||||
class="relative flex flex-col w-full max-w-2xl bg-background rounded-lg shadow-2xl border border-border overflow-hidden"
|
||||
|
||||
@@ -80,7 +80,7 @@ function handleSubmit() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
<div class="relative z-10 w-full max-w-md mx-4 bg-card border border-border rounded-lg shadow-2xl p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ t('book.detail.addSession.title') }}</h2>
|
||||
|
||||
@@ -329,7 +329,7 @@ async function confirmDelete() {
|
||||
|
||||
<!-- Rename -->
|
||||
<div v-if="renameTarget" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="closeRenameModal">
|
||||
<button class="absolute inset-0 bg-black/45" :aria-label="t('common.cancel')" @click="closeRenameModal" />
|
||||
<button class="absolute inset-0 bg-scrim" :aria-label="t('common.cancel')" @click="closeRenameModal" />
|
||||
<div
|
||||
class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5"
|
||||
role="dialog"
|
||||
@@ -365,7 +365,7 @@ async function confirmDelete() {
|
||||
|
||||
<!-- Delete -->
|
||||
<div v-if="deleteTarget" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="closeDeleteModal">
|
||||
<button class="absolute inset-0 bg-black/45" :aria-label="t('common.cancel')" @click="closeDeleteModal" />
|
||||
<button class="absolute inset-0 bg-scrim" :aria-label="t('common.cancel')" @click="closeDeleteModal" />
|
||||
<div
|
||||
class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5"
|
||||
role="dialog"
|
||||
|
||||
@@ -464,7 +464,7 @@ watch(
|
||||
<DialogRoot :open="lightboxSrc !== null" @update:open="handleCoverPreviewOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-[60] bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 motion-reduce:animate-none"
|
||||
class="fixed inset-0 z-[60] bg-scrim-media data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 motion-reduce:animate-none"
|
||||
/>
|
||||
<DialogContent
|
||||
:aria-describedby="undefined"
|
||||
|
||||
@@ -84,7 +84,7 @@ async function handleToggleCoverLock(field: 'cover' | 'audioCover') {
|
||||
<template>
|
||||
<DialogRoot :open="book !== null" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 w-full max-w-sm -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-card shadow-xl outline-none"
|
||||
@escape-key-down="handleOpenChange(false)"
|
||||
|
||||
@@ -56,7 +56,7 @@ async function submit() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="emit('close')" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="emit('close')" />
|
||||
<div class="relative z-10 w-full max-w-md mx-4 bg-card border border-border rounded-lg shadow-2xl p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ dialogTitle }}</h2>
|
||||
|
||||
@@ -104,7 +104,7 @@ async function confirmDelete() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="emit('close')" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="emit('close')" />
|
||||
<div class="relative z-10 w-full max-w-md mx-4 bg-card border border-border rounded-lg shadow-2xl p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ t('collection.editDialog.title') }}</h2>
|
||||
|
||||
@@ -182,7 +182,7 @@ function applyServerErrors(items: { filename: string; status: string; error?: st
|
||||
<template>
|
||||
<DialogRoot :open="open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
class="fixed left-1/2 top-1/2 z-50 flex max-h-[85vh] w-[calc(100vw-2rem)] max-w-2xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-border bg-card shadow-xl outline-none"
|
||||
@escape-key-down="closeDialog"
|
||||
|
||||
@@ -235,7 +235,7 @@ function cancelDelete() {
|
||||
</div>
|
||||
|
||||
<div v-if="deleteConfirm" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="deleteConfirm = null">
|
||||
<button class="absolute inset-0 bg-black/45" @click="cancelDelete" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="cancelDelete" />
|
||||
<div class="relative w-full rounded-t-xl border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">{{ t('email.groups.deleteTitle') }}</p>
|
||||
<p class="mt-1 text-sm text-muted-foreground">{{ t('email.deleteConfirm', { name: deleteConfirm.name }) }}</p>
|
||||
|
||||
@@ -136,7 +136,7 @@ function cancelDelete() {
|
||||
</Button>
|
||||
|
||||
<div v-if="deleteConfirm" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="deleteConfirm = null">
|
||||
<button class="absolute inset-0 bg-black/45" @click="cancelDelete" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="cancelDelete" />
|
||||
<div class="relative w-full rounded-t-xl border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">{{ t('email.history.deleteTitle') }}</p>
|
||||
<p class="mt-1 text-sm text-muted-foreground line-clamp-2">{{ deleteConfirm.subject ?? t('email.history.noSubject') }}</p>
|
||||
|
||||
@@ -456,7 +456,7 @@ function cancelDelete() {
|
||||
</div>
|
||||
|
||||
<div v-if="deleteConfirm" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="deleteConfirm = null">
|
||||
<button class="absolute inset-0 bg-black/45" @click="cancelDelete" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="cancelDelete" />
|
||||
<div class="relative w-full rounded-t-xl border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">{{ t('email.providers.deleteTitle') }}</p>
|
||||
<p class="mt-1 text-sm text-muted-foreground">{{ t('email.deleteConfirm', { name: deleteConfirm.name }) }}</p>
|
||||
|
||||
@@ -282,7 +282,7 @@ function cancelDelete() {
|
||||
</div>
|
||||
|
||||
<div v-if="deleteConfirm" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="deleteConfirm = null">
|
||||
<button class="absolute inset-0 bg-black/45" @click="cancelDelete" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="cancelDelete" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">{{ t('email.recipients.deleteTitle') }}</p>
|
||||
<p class="mt-1 text-sm text-muted-foreground">{{ t('email.deleteConfirm', { name: deleteConfirm.name }) }}</p>
|
||||
|
||||
@@ -121,7 +121,7 @@ function close() {
|
||||
<Teleport to="body">
|
||||
<Transition name="dialog-fade">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<div class="absolute inset-0 bg-black/50 backdrop-blur-[1px]" @click="close" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="close" />
|
||||
|
||||
<div class="relative w-full max-w-md bg-background border border-border rounded-lg shadow-2xl overflow-hidden">
|
||||
<!-- Header -->
|
||||
|
||||
@@ -315,7 +315,7 @@ function cancelDelete() {
|
||||
</div>
|
||||
|
||||
<div v-if="deleteConfirm" class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4" @click.self="deleteConfirm = null">
|
||||
<button class="absolute inset-0 bg-black/45" @click="cancelDelete" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="cancelDelete" />
|
||||
<div class="relative w-full rounded-t-xl border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">{{ t('email.templates.deleteTitle') }}</p>
|
||||
<p class="mt-1 text-sm text-muted-foreground">{{ t('email.deleteConfirm', { name: deleteConfirm.name }) }}</p>
|
||||
|
||||
@@ -231,7 +231,7 @@ describe('SendBookDialog', () => {
|
||||
|
||||
it('emits close when the backdrop is clicked', async () => {
|
||||
const wrapper = mountDialog()
|
||||
await wrapper.find('.absolute.inset-0.bg-black\\/50').trigger('click')
|
||||
await wrapper.find('.absolute.inset-0.bg-scrim').trigger('click')
|
||||
expect(wrapper.emitted('update:open')).toEqual([[false]])
|
||||
})
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ function normalizeSearch(value: string): string {
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-[70] flex items-end justify-center bg-foreground/35 px-2 py-3 md:items-center md:px-6" @click.self="handleClose">
|
||||
<div class="fixed inset-0 z-[70] flex items-end justify-center bg-scrim px-2 py-3 md:items-center md:px-6" @click.self="handleClose">
|
||||
<section
|
||||
ref="dialogRef"
|
||||
class="flex max-h-[92vh] w-full max-w-6xl flex-col rounded-lg border border-border bg-background shadow-xl outline-none"
|
||||
|
||||
@@ -185,7 +185,7 @@ function formatPillClass(filename: string): string {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center p-2 sm:p-4">
|
||||
<div class="absolute inset-0 bg-black/50 backdrop-blur-[2px]" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
|
||||
<div
|
||||
class="relative flex flex-col w-full max-w-2xl bg-background rounded-lg shadow-2xl border border-border overflow-hidden"
|
||||
|
||||
@@ -80,11 +80,7 @@ useModal({
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
class="fixed inset-0 z-[80] flex items-end justify-center bg-foreground/30 sm:items-center sm:p-4"
|
||||
role="presentation"
|
||||
@click.self="requestClose"
|
||||
>
|
||||
<div class="fixed inset-0 z-[80] flex items-end justify-center bg-scrim sm:items-center sm:p-4" role="presentation" @click.self="requestClose">
|
||||
<div
|
||||
ref="panel"
|
||||
class="flex h-dvh w-full flex-col overflow-hidden bg-background shadow-2xl outline-none sm:h-[min(82dvh,700px)] sm:max-w-xl sm:rounded-lg sm:border sm:border-border"
|
||||
|
||||
@@ -566,7 +566,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-70 bg-foreground/25 motion-safe:animate-in motion-safe:fade-in-0" role="presentation" @click.self="requestClose">
|
||||
<div class="fixed inset-0 z-70 bg-scrim motion-safe:animate-in motion-safe:fade-in-0" role="presentation" @click.self="requestClose">
|
||||
<div
|
||||
ref="panel"
|
||||
class="fixed inset-y-0 end-0 flex w-full flex-col bg-background shadow-2xl outline-none sm:max-w-[56rem] sm:border-s sm:border-border motion-safe:animate-in motion-safe:duration-300 ltr:motion-safe:slide-in-from-right rtl:motion-safe:slide-in-from-left"
|
||||
|
||||
@@ -1307,7 +1307,7 @@ const sourceTypeCompatibility = computed<SourceTypeCompatibility | null>(() => {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center p-4">
|
||||
<div class="absolute inset-0 bg-black/50 backdrop-blur-[2px]" @click="handleClose" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="handleClose" />
|
||||
|
||||
<div
|
||||
class="relative flex flex-col w-full max-w-5xl bg-background rounded-lg shadow-2xl overflow-hidden border border-border"
|
||||
|
||||
@@ -1190,7 +1190,7 @@ onMounted(async () => {
|
||||
|
||||
<!-- Chapter sheet backdrop -->
|
||||
<Transition name="fade">
|
||||
<div v-if="showChapters" class="absolute inset-0 z-20 bg-black/40" @click="showChapters = false" />
|
||||
<div v-if="showChapters" class="absolute inset-0 z-20 bg-scrim" @click="showChapters = false" />
|
||||
</Transition>
|
||||
|
||||
<!-- Chapter / Bookmarks sheet (slide up) -->
|
||||
@@ -1272,7 +1272,7 @@ onMounted(async () => {
|
||||
|
||||
<!-- Settings sheet backdrop -->
|
||||
<Transition name="fade">
|
||||
<div v-if="showSettings" class="absolute inset-0 z-20 bg-black/40" @click="showSettings = false" />
|
||||
<div v-if="showSettings" class="absolute inset-0 z-20 bg-scrim" @click="showSettings = false" />
|
||||
</Transition>
|
||||
|
||||
<!-- Settings sheet (slide up) -->
|
||||
|
||||
@@ -63,7 +63,7 @@ onUnmounted(() => document.removeEventListener('keydown', onKeyDown, true))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-backdrop class="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 px-4 backdrop-blur-sm" @click="handleBackdropClick">
|
||||
<div data-backdrop class="fixed inset-0 z-[60] flex items-center justify-center bg-scrim px-4" @click="handleBackdropClick">
|
||||
<div class="w-full max-w-md rounded-xl border border-border/80 bg-card shadow-2xl">
|
||||
<div class="flex items-center justify-between border-b border-border/80 bg-gradient-to-br from-muted/75 via-card to-muted/40 px-4 py-3">
|
||||
<div class="inline-flex items-center gap-2">
|
||||
|
||||
@@ -548,7 +548,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="pendingExternalUrl" class="fixed inset-0 z-[70] flex items-center justify-center bg-background/70 p-4 backdrop-blur-sm">
|
||||
<div v-if="pendingExternalUrl" class="fixed inset-0 z-[70] flex items-center justify-center bg-scrim p-4">
|
||||
<div class="w-full max-w-md rounded-xl border border-border bg-card p-5 text-card-foreground shadow-2xl">
|
||||
<div class="mb-4 flex items-start gap-3">
|
||||
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary"><ExternalLink :size="18" /></div>
|
||||
|
||||
@@ -33,7 +33,7 @@ function handleSave() {
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center bg-foreground/35 p-4" @click.self="handleBackdropClick">
|
||||
<div class="fixed inset-0 z-[70] flex items-center justify-center bg-scrim p-4" @click.self="handleBackdropClick">
|
||||
<div class="bg-card text-card-foreground rounded-lg shadow-2xl p-4 w-full max-w-sm flex flex-col gap-3">
|
||||
<p class="text-sm font-medium">{{ t('reader.note.title') }}</p>
|
||||
<p class="text-xs text-muted-foreground line-clamp-2 italic">"{{ selectedText }}"</p>
|
||||
|
||||
@@ -66,7 +66,7 @@ function handleSubmit(): void {
|
||||
<template>
|
||||
<DialogRoot :open="props.open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/50" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
aria-modal="true"
|
||||
class="fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-card p-6 shadow-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
|
||||
@@ -1438,7 +1438,7 @@ async function handleDownloadPlugin() {
|
||||
class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4"
|
||||
@click.self="handleCloseDeleteConfirm"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="handleCloseDeleteConfirm" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="handleCloseDeleteConfirm" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">
|
||||
{{ t('settings.reader.koreader.deleteConfirmTitle') }}
|
||||
@@ -1462,7 +1462,7 @@ async function handleDownloadPlugin() {
|
||||
class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4"
|
||||
@click.self="handleCloseUnlink"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="handleCloseUnlink" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="handleCloseUnlink" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">
|
||||
{{ t('settings.reader.koreader.hashLinks.unlinkConfirmTitle') }}
|
||||
@@ -1502,7 +1502,7 @@ async function handleDownloadPlugin() {
|
||||
class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4"
|
||||
@click.self="handleCloseRemoveDevice"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="handleCloseRemoveDevice" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="handleCloseRemoveDevice" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">
|
||||
{{
|
||||
@@ -1534,7 +1534,7 @@ async function handleDownloadPlugin() {
|
||||
class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4"
|
||||
@click.self="handleCloseDismiss"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="handleCloseDismiss" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="handleCloseDismiss" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">
|
||||
{{
|
||||
@@ -1571,7 +1571,7 @@ async function handleDownloadPlugin() {
|
||||
class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4"
|
||||
@click.self="handleCloseDismissAll"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="handleCloseDismissAll" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="handleCloseDismissAll" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">
|
||||
{{
|
||||
@@ -1603,7 +1603,7 @@ async function handleDownloadPlugin() {
|
||||
@click.self="handleCloseLink"
|
||||
@keydown.esc="handleCloseLink"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="handleCloseLink" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="handleCloseLink" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-2xl md:rounded-lg md:p-5">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
|
||||
@@ -196,7 +196,7 @@ function isExpired(expiresAt: string | null | undefined): boolean {
|
||||
|
||||
<DialogRoot :open="showCreateForm" @update:open="handleCreateOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/50" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
aria-modal="true"
|
||||
class="fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-card p-6 shadow-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
|
||||
@@ -401,7 +401,7 @@ function cancelDelete() {
|
||||
class="fixed inset-0 z-[70] flex items-end justify-center md:items-center md:px-4"
|
||||
@click.self="deleteConfirmUser = null"
|
||||
>
|
||||
<button class="absolute inset-0 bg-black/45" @click="cancelDelete" />
|
||||
<button class="absolute inset-0 bg-scrim" @click="cancelDelete" />
|
||||
<div class="relative w-full rounded-t-lg border border-border bg-card p-4 shadow-xl md:max-w-md md:rounded-lg md:p-5">
|
||||
<p class="text-base font-semibold text-foreground">
|
||||
{{ t('settings.reader.opds.deleteConfirmTitle') }}
|
||||
|
||||
@@ -434,7 +434,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="deleteConfirmField" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" @click.self="cancelDeleteConfirm">
|
||||
<div v-if="deleteConfirmField" class="fixed inset-0 z-50 flex items-center justify-center bg-scrim p-4" @click.self="cancelDeleteConfirm">
|
||||
<div class="w-full max-w-md rounded-xl border border-border bg-card shadow-xl">
|
||||
<div class="p-5 border-b border-border flex items-start gap-3">
|
||||
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-destructive/10 text-destructive">
|
||||
|
||||
@@ -75,7 +75,7 @@ async function submit() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="emit('close')" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="emit('close')" />
|
||||
<div class="relative z-10 w-full max-w-md mx-4 bg-card border border-border rounded-lg shadow-2xl p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ dialogTitle }}</h2>
|
||||
|
||||
@@ -59,7 +59,7 @@ async function submit() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="emit('close')" />
|
||||
<div class="absolute inset-0 bg-scrim" @click="emit('close')" />
|
||||
<div class="relative z-10 w-full max-w-md mx-4 bg-card border border-border rounded-lg shadow-2xl p-6">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<h2 class="text-base font-semibold text-foreground">{{ t('smartScope.saveAsDialog.title') }}</h2>
|
||||
|
||||
@@ -161,7 +161,7 @@ async function save() {
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="smartScope-fade">
|
||||
<div v-if="open" class="fixed inset-0 z-40 bg-black/40 backdrop-blur-sm" @click="emit('close')" />
|
||||
<div v-if="open" class="fixed inset-0 z-40 bg-scrim" @click="emit('close')" />
|
||||
</Transition>
|
||||
|
||||
<Transition name="smartScope-slide">
|
||||
|
||||
@@ -39,7 +39,7 @@ function handleCancel(): void {
|
||||
<template>
|
||||
<DialogRoot :open="props.open" @update:open="handleOpenChange">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/50 backdrop-blur-[2px]" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
class="fixed top-1/2 left-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-popover p-5 shadow-2xl focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
|
||||
>
|
||||
|
||||
@@ -41,7 +41,7 @@ function preventDismiss(event: Event): void {
|
||||
<template>
|
||||
<DialogRoot :open="props.open">
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-foreground/50 backdrop-blur-[2px]" />
|
||||
<DialogOverlay class="fixed inset-0 z-50 bg-scrim" />
|
||||
<DialogContent
|
||||
class="fixed top-1/2 left-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-popover p-5 shadow-2xl focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
|
||||
@escape-key-down="preventDismiss"
|
||||
|
||||
@@ -36,7 +36,7 @@ function handleCancel(): void {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="handleCancel">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="handleCancel">
|
||||
<div class="bg-card border border-border rounded-lg shadow-lg w-full max-w-lg mx-4 overflow-hidden flex flex-col h-150 max-h-[85vh]">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border shrink-0">
|
||||
<h3 class="text-base font-semibold flex items-center gap-2">
|
||||
|
||||
@@ -32,7 +32,7 @@ function handleCancel(): void {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="handleCancel">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="handleCancel">
|
||||
<div class="bg-card border border-border rounded-lg shadow-lg w-full max-w-md mx-4 overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h3 class="text-base font-semibold text-destructive flex items-center gap-2">
|
||||
|
||||
@@ -31,7 +31,7 @@ function handleCancel(): void {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="handleCancel">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="handleCancel">
|
||||
<div class="bg-card border border-border rounded-lg shadow-lg w-full max-w-md mx-4 overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h3 class="text-base font-semibold text-destructive flex items-center gap-2">
|
||||
|
||||
@@ -31,7 +31,7 @@ function handleCancel(): void {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="handleCancel">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="handleCancel">
|
||||
<div class="bg-card border border-border rounded-lg shadow-lg w-full max-w-md mx-4 overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h3 class="text-base font-semibold">{{ t('tools.entityManager.renameModal.title') }}</h3>
|
||||
|
||||
@@ -49,7 +49,7 @@ function isValid(): boolean {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="handleCancel">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-scrim" @click.self="handleCancel">
|
||||
<div class="bg-card border border-border rounded-lg shadow-lg w-full max-w-md mx-4 overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<h3 class="text-base font-semibold">{{ t('tools.entityManager.splitModal.title') }}</h3>
|
||||
|
||||
@@ -200,7 +200,7 @@ function handleClose() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||||
<div class="fixed inset-0 bg-scrim z-50 flex items-center justify-center p-4">
|
||||
<div class="bg-card border border-border rounded-xl w-full max-w-2xl max-h-[90vh] flex flex-col shadow-xl">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between p-5 border-b border-border flex-shrink-0">
|
||||
|
||||
@@ -45,7 +45,7 @@ useModal({
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
class="fixed inset-0 z-[75] flex items-end justify-center bg-black/50 motion-safe:animate-in motion-safe:fade-in sm:items-center sm:px-4"
|
||||
class="fixed inset-0 z-[75] flex items-end justify-center bg-scrim motion-safe:animate-in motion-safe:fade-in sm:items-center sm:px-4"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="whats-new-title"
|
||||
|
||||
@@ -50,7 +50,7 @@ onUnmounted(() => {
|
||||
<Teleport to="body">
|
||||
<div
|
||||
ref="panel"
|
||||
class="fixed inset-0 z-[90] flex items-center justify-center bg-black/80 p-4 motion-safe:animate-in motion-safe:fade-in"
|
||||
class="fixed inset-0 z-[90] flex items-center justify-center bg-scrim-media p-4 motion-safe:animate-in motion-safe:fade-in"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
:aria-label="t('whatsNew.imageViewer')"
|
||||
|
||||
Reference in New Issue
Block a user