mirror of
https://github.com/root-fr/jmap-webmail.git
synced 2026-09-27 08:01:19 +00:00
The previous :has()-based print rules worked in Chromium's headless PDF export but produced a blank page in Firefox. Rather than fight browser-specific differences in @media print with :has(), flex, and visibility on deeply-nested ancestors, clone the email viewer DOM into a dedicated #print-overlay appended directly to document.body before calling window.print(), and add a html.is-printing class. Print CSS now targets a single well-known id (#print-overlay) at the body root: everything else is display:none'd (no :has() needed), the overlay flows naturally on the page, and the subtree is forced to black text on a transparent backdrop with a white root background so dark-mode rendering prints cleanly on paper with Firefox's default "Print background" setting off. afterprint removes the overlay and clears the class, so both successful prints and cancellations clean up.
534 lines
12 KiB
CSS
534 lines
12 KiB
CSS
@import "tailwindcss";
|
|
|
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
|
|
:root {
|
|
--color-border: #e2e8f0;
|
|
--color-input: #e2e8f0;
|
|
--color-ring: #94a3b8;
|
|
--color-background: #ffffff;
|
|
--color-foreground: #0f172a;
|
|
--color-primary: #3b82f6;
|
|
--color-primary-foreground: #ffffff;
|
|
--color-secondary: #f8fafc;
|
|
--color-secondary-foreground: #0f172a;
|
|
--color-muted: #f1f5f9;
|
|
--color-muted-foreground: #64748b;
|
|
--color-accent: #dbeafe;
|
|
--color-accent-foreground: #1e40af;
|
|
--color-destructive: #dc2626;
|
|
--color-destructive-foreground: #ffffff;
|
|
|
|
/* Settings variables */
|
|
--font-size-base: 16px;
|
|
--list-item-height: 48px;
|
|
--transition-duration: 0.2s;
|
|
}
|
|
|
|
.dark {
|
|
--color-border: #262626;
|
|
--color-input: #262626;
|
|
--color-ring: #d4d4d4;
|
|
--color-background: #0a0a0a;
|
|
--color-foreground: #fafafa;
|
|
--color-primary: #fafafa;
|
|
--color-primary-foreground: #171717;
|
|
--color-secondary: #262626;
|
|
--color-secondary-foreground: #fafafa;
|
|
--color-muted: #262626;
|
|
--color-muted-foreground: #a3a3a3;
|
|
--color-accent: #1e3a8a;
|
|
--color-accent-foreground: #dbeafe;
|
|
--color-destructive: #ef4444;
|
|
--color-destructive-foreground: #ffffff;
|
|
}
|
|
|
|
@theme inline {
|
|
--color-border: var(--color-border);
|
|
--color-input: var(--color-input);
|
|
--color-ring: var(--color-ring);
|
|
--color-background: var(--color-background);
|
|
--color-foreground: var(--color-foreground);
|
|
--color-primary: var(--color-primary);
|
|
--color-primary-foreground: var(--color-primary-foreground);
|
|
--color-secondary: var(--color-secondary);
|
|
--color-secondary-foreground: var(--color-secondary-foreground);
|
|
--color-muted: var(--color-muted);
|
|
--color-muted-foreground: var(--color-muted-foreground);
|
|
--color-accent: var(--color-accent);
|
|
--color-accent-foreground: var(--color-accent-foreground);
|
|
--color-destructive: var(--color-destructive);
|
|
--color-destructive-foreground: var(--color-destructive-foreground);
|
|
}
|
|
|
|
* {
|
|
border-color: var(--color-border);
|
|
}
|
|
|
|
body {
|
|
background-color: var(--color-background);
|
|
color: var(--color-foreground);
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
font-size: var(--font-size-base);
|
|
font-feature-settings: "rlig" 1, "calt" 1;
|
|
}
|
|
|
|
.btn-destructive {
|
|
background-color: var(--color-destructive);
|
|
color: var(--color-destructive-foreground);
|
|
}
|
|
|
|
.btn-destructive:hover {
|
|
background-color: color-mix(in srgb, var(--color-destructive) 90%, black);
|
|
}
|
|
|
|
/* Minimalist scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background-color: transparent;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background-color: var(--color-border);
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background-color: var(--color-muted-foreground);
|
|
}
|
|
|
|
/* Enhanced Email Content Styling */
|
|
|
|
.email-content-wrapper {
|
|
min-width: 100%;
|
|
}
|
|
|
|
.email-content {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
font-size: 0.9375rem;
|
|
line-height: 1.6;
|
|
color: var(--color-foreground);
|
|
max-width: none;
|
|
overflow-wrap: break-word;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.email-content p {
|
|
margin: 0.75rem 0;
|
|
}
|
|
|
|
.email-content a {
|
|
color: var(--color-primary);
|
|
text-decoration: none;
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.email-content a:hover {
|
|
text-decoration: underline;
|
|
color: var(--color-accent-foreground);
|
|
}
|
|
|
|
.email-content img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
border-radius: 0.375rem;
|
|
margin: 1rem 0;
|
|
display: block;
|
|
}
|
|
|
|
.email-content blockquote {
|
|
border-left: 3px solid #d1d5db;
|
|
padding-left: 1rem;
|
|
margin: 1rem 0;
|
|
color: #4b5563;
|
|
font-style: italic;
|
|
}
|
|
|
|
.dark .email-content blockquote {
|
|
border-left-color: #4b5563;
|
|
color: #9ca3af;
|
|
}
|
|
|
|
.email-content pre {
|
|
background-color: #f5f5f7;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
font-family: 'SF Mono', Monaco, Menlo, Consolas, monospace;
|
|
font-size: 0.875rem;
|
|
overflow-x: auto;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.dark .email-content pre {
|
|
background-color: #1f2937;
|
|
border-color: #374151;
|
|
}
|
|
|
|
.email-content code {
|
|
background-color: #f3f4f6;
|
|
padding: 0.125rem 0.375rem;
|
|
border-radius: 0.25rem;
|
|
font-family: 'SF Mono', Monaco, Menlo, Consolas, monospace;
|
|
font-size: 0.875rem;
|
|
color: #dc2626;
|
|
}
|
|
|
|
.dark .email-content code {
|
|
background-color: #374151;
|
|
color: #f87171;
|
|
}
|
|
|
|
.email-content h1,
|
|
.email-content h2,
|
|
.email-content h3,
|
|
.email-content h4 {
|
|
margin-top: 1.5rem;
|
|
margin-bottom: 0.75rem;
|
|
font-weight: 600;
|
|
line-height: 1.25;
|
|
color: var(--color-foreground);
|
|
}
|
|
|
|
.email-content h1 { font-size: 1.5rem; }
|
|
.email-content h2 { font-size: 1.25rem; }
|
|
.email-content h3 { font-size: 1.125rem; }
|
|
.email-content h4 { font-size: 1rem; }
|
|
|
|
.email-content ul,
|
|
.email-content ol {
|
|
margin: 1rem 0;
|
|
padding-left: 1.75rem;
|
|
}
|
|
|
|
.email-content li {
|
|
margin: 0.375rem 0;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.email-content ul li {
|
|
list-style-type: disc;
|
|
}
|
|
|
|
.email-content ol li {
|
|
list-style-type: decimal;
|
|
}
|
|
|
|
/* Only style tables that are actual data tables, not layout tables */
|
|
.email-content table.data-table,
|
|
.email-content table[border="1"] {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin: 1rem 0;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.email-content table.data-table th,
|
|
.email-content table.data-table td,
|
|
.email-content table[border="1"] th,
|
|
.email-content table[border="1"] td {
|
|
padding: 0.625rem;
|
|
border: 1px solid #e5e7eb;
|
|
text-align: left;
|
|
}
|
|
|
|
.email-content table.data-table th,
|
|
.email-content table[border="1"] th {
|
|
background-color: #f9fafb;
|
|
font-weight: 600;
|
|
color: #374151;
|
|
}
|
|
|
|
.dark .email-content table.data-table th,
|
|
.dark .email-content table[border="1"] th {
|
|
background-color: #1f2937;
|
|
color: #d1d5db;
|
|
}
|
|
|
|
.dark .email-content table.data-table td,
|
|
.dark .email-content table[border="1"] td {
|
|
border-color: #374151;
|
|
}
|
|
|
|
/* Reset styles for layout tables (commonly used in HTML emails) */
|
|
.email-content table {
|
|
border-collapse: collapse;
|
|
border-spacing: 0;
|
|
}
|
|
|
|
/* Let HTML email's own styles take precedence */
|
|
.email-content table:not(.data-table):not([border="1"]) {
|
|
border: initial;
|
|
}
|
|
|
|
.email-content table:not(.data-table):not([border="1"]) td,
|
|
.email-content table:not(.data-table):not([border="1"]) th {
|
|
border: initial;
|
|
padding: initial;
|
|
}
|
|
|
|
.email-content hr {
|
|
border: none;
|
|
border-top: 1px solid #e5e7eb;
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.dark .email-content hr {
|
|
border-top-color: #374151;
|
|
}
|
|
|
|
/* Email thread quoted text */
|
|
.email-content .quoted-text {
|
|
border-left: 3px solid #d1d5db;
|
|
padding-left: 1rem;
|
|
margin: 1rem 0;
|
|
color: #4b5563;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.dark .email-content .quoted-text {
|
|
border-left-color: #4b5563;
|
|
color: #9ca3af;
|
|
}
|
|
|
|
/* Toast animations */
|
|
@keyframes slide-in {
|
|
from {
|
|
transform: translateX(100%);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.animate-slide-in {
|
|
animation: slide-in 0.3s ease-out;
|
|
}
|
|
|
|
|
|
/* Mobile Responsive Utilities */
|
|
|
|
/* Safe area insets for notched devices (iPhone X+, etc.) */
|
|
.safe-area-inset-top {
|
|
padding-top: env(safe-area-inset-top);
|
|
}
|
|
|
|
.safe-area-inset-bottom {
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
|
|
.safe-area-inset-left {
|
|
padding-left: env(safe-area-inset-left);
|
|
}
|
|
|
|
.safe-area-inset-right {
|
|
padding-right: env(safe-area-inset-right);
|
|
}
|
|
|
|
/* Minimum touch targets (44x44px recommended by Apple HIG) */
|
|
.touch-target {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
}
|
|
|
|
/* Prevent text selection on mobile for UI elements */
|
|
.no-select {
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-touch-callout: none;
|
|
}
|
|
|
|
/* Smooth transitions for view switching */
|
|
.view-transition {
|
|
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
|
}
|
|
|
|
/* Hide scrollbar on mobile while keeping functionality */
|
|
@media (max-width: 767px) {
|
|
.mobile-scroll-hidden::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.mobile-scroll-hidden {
|
|
-ms-overflow-style: none;
|
|
scrollbar-width: none;
|
|
}
|
|
}
|
|
|
|
/* Mobile backdrop blur support */
|
|
@supports (backdrop-filter: blur(8px)) {
|
|
.mobile-backdrop {
|
|
backdrop-filter: blur(8px);
|
|
-webkit-backdrop-filter: blur(8px);
|
|
}
|
|
}
|
|
|
|
/* Prevent overscroll bounce on iOS */
|
|
.no-overscroll {
|
|
overscroll-behavior: none;
|
|
}
|
|
|
|
/* Slide in from right animation (for mobile views) */
|
|
@keyframes slide-in-from-right {
|
|
from {
|
|
transform: translateX(100%);
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
.animate-slide-in-from-right {
|
|
animation: slide-in-from-right 0.3s ease-out;
|
|
}
|
|
|
|
/* Slide in from left animation (for sidebar) */
|
|
@keyframes slide-in-from-left {
|
|
from {
|
|
transform: translateX(-100%);
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
.animate-slide-in-from-left {
|
|
animation: slide-in-from-left 0.3s ease-out;
|
|
}
|
|
|
|
/* Extra-compact density: enforce minimum touch target on touch devices */
|
|
@media (pointer: coarse) {
|
|
[data-density="extra-compact"] .email-list-item {
|
|
min-height: 44px;
|
|
}
|
|
}
|
|
|
|
/* Reduced motion: respect user OS preference */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|
|
|
|
/* Disable backdrop-filter on mobile for performance */
|
|
@media (max-width: 767px) {
|
|
.mobile-backdrop {
|
|
backdrop-filter: none !important;
|
|
-webkit-backdrop-filter: none !important;
|
|
}
|
|
}
|
|
|
|
/* Print support. The user clicks Print on an email, which clones the
|
|
viewer into #print-overlay at the body level and flips html.is-printing
|
|
on. The live app is hidden, the overlay is positioned at top-left and
|
|
styled for paper: black text, transparent-over-white background,
|
|
no shadows, no animations. Keeping the overlay at the body root
|
|
avoids cross-browser :has()/flex/visibility quirks entirely.
|
|
Cleanup runs on the afterprint event. */
|
|
html.is-printing body > *:not(#print-overlay) {
|
|
display: none !important;
|
|
}
|
|
|
|
#print-overlay {
|
|
position: absolute;
|
|
left: -10000px;
|
|
top: -10000px;
|
|
width: 1px;
|
|
height: 1px;
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@media print {
|
|
@page {
|
|
margin: 12mm;
|
|
}
|
|
|
|
html, body {
|
|
background: white !important;
|
|
color: #000 !important;
|
|
margin: 0 !important;
|
|
padding: 0 !important;
|
|
height: auto !important;
|
|
min-height: 0 !important;
|
|
overflow: visible !important;
|
|
}
|
|
|
|
html.is-printing #print-overlay {
|
|
position: static !important;
|
|
left: auto !important;
|
|
top: auto !important;
|
|
width: auto !important;
|
|
height: auto !important;
|
|
overflow: visible !important;
|
|
}
|
|
|
|
#print-overlay,
|
|
#print-overlay * {
|
|
color: #000 !important;
|
|
background: transparent !important;
|
|
background-color: transparent !important;
|
|
background-image: none !important;
|
|
box-shadow: none !important;
|
|
text-shadow: none !important;
|
|
border-color: #ccc !important;
|
|
animation: none !important;
|
|
transition: none !important;
|
|
transform: none !important;
|
|
filter: none !important;
|
|
opacity: 1 !important;
|
|
visibility: visible !important;
|
|
}
|
|
|
|
#print-overlay #email-viewer-container {
|
|
position: static !important;
|
|
display: block !important;
|
|
width: 100% !important;
|
|
height: auto !important;
|
|
min-height: 0 !important;
|
|
max-height: none !important;
|
|
margin: 0 !important;
|
|
padding: 0 !important;
|
|
overflow: visible !important;
|
|
background: #fff !important;
|
|
box-shadow: none !important;
|
|
border: none !important;
|
|
}
|
|
|
|
#print-overlay #email-content-area {
|
|
display: block !important;
|
|
width: 100% !important;
|
|
height: auto !important;
|
|
max-height: none !important;
|
|
overflow: visible !important;
|
|
}
|
|
|
|
#print-overlay iframe {
|
|
height: auto !important;
|
|
min-height: 0 !important;
|
|
max-height: none !important;
|
|
width: 100% !important;
|
|
background: #fff !important;
|
|
}
|
|
|
|
#print-overlay a {
|
|
color: #0645ad !important;
|
|
text-decoration: underline !important;
|
|
}
|
|
|
|
#print-overlay .print\:hidden {
|
|
display: none !important;
|
|
}
|
|
}
|