mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 16:13:15 +00:00
Settings get a page of their own for accessibility, with presets on top (screen reader, low vision, dyslexia, focus, keyboard and motor, motion and seizure safe, deaf and hard of hearing) that change several options at once and can be undone, then the options by topic, and a reset. Text size used to be a fixed 14/16/18px on the root, which threw away the size people set in their browser. It is now a percentage of the browser's size, from 87.5% to 200%. Motion, contrast and transparency follow the system by default and can be overridden either way; the old Animations switch becomes Reduce motion. The visual options are applied before the first paint, so the page no longer flashes at the wrong size. - `lib/accessibility-settings.ts`: the new settings, their allowed values, and the code that writes them onto `<html>` - `app/accessibility.css`: bundled fonts (Atkinson Hyperlegible Next, Lexend, OpenDyslexic), text spacing, a focus outline that survives Windows contrast themes, more-contrast colours on any theme, reduced transparency, a large pointer - admins can preset every new option but lock only shortcuts, send confirmation and the notification duration; a lock on text size or another reading aid works as a default - always-light mail and toolbar labels now sync, and imports drop values the page doesn't know - font size and animations move off Appearance, the plain text font off Reading; everyday settings such as conversation view and undo send show on both their own page and Accessibility
1281 lines
34 KiB
CSS
1281 lines
34 KiB
CSS
@import "tailwindcss";
|
|
@import "tw-animate-css";
|
|
|
|
@import "./theme-inline.css";
|
|
@import "./accessibility.css";
|
|
|
|
/* The app's font stack replaces Tailwind's default --font-sans, so the
|
|
font-sans utility and body use the same stack, and a theme's
|
|
typography.fontSans (compiled to --font-sans on :root) restyles both. */
|
|
@theme {
|
|
--font-sans:
|
|
-apple-system,
|
|
BlinkMacSystemFont,
|
|
"Segoe UI",
|
|
Roboto,
|
|
"Helvetica Neue",
|
|
Arial,
|
|
"Noto Sans Thai",
|
|
"Leelawadee UI",
|
|
Tahoma,
|
|
sans-serif;
|
|
}
|
|
|
|
:root {
|
|
font-size: var(--font-size-base);
|
|
|
|
--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: #ef4444;
|
|
--color-destructive-foreground: #ffffff;
|
|
--color-popover: #ffffff;
|
|
--color-popover-foreground: #0f172a;
|
|
|
|
/* Sidebar */
|
|
--color-sidebar: #f8fafc;
|
|
--color-sidebar-foreground: #0f172a;
|
|
--color-sidebar-border: #e2e8f0;
|
|
--color-sidebar-accent: #f1f5f9;
|
|
--color-sidebar-accent-foreground: #0f172a;
|
|
|
|
/* Card */
|
|
--color-card: #ffffff;
|
|
--color-card-foreground: #0f172a;
|
|
|
|
/* Semantic status colors */
|
|
--color-success: #22c55e;
|
|
--color-success-foreground: #ffffff;
|
|
--color-warning: #eab308;
|
|
--color-warning-foreground: #ffffff;
|
|
--color-info: #3b82f6;
|
|
--color-info-foreground: #ffffff;
|
|
|
|
/* Selection states */
|
|
--color-selection: #dbeafe;
|
|
--color-selection-foreground: #1e40af;
|
|
|
|
/* Unread indicator */
|
|
--color-unread: #3b82f6;
|
|
|
|
/* Chart colors */
|
|
--color-chart-1: #3b82f6;
|
|
--color-chart-2: #22c55e;
|
|
--color-chart-3: #f59e0b;
|
|
--color-chart-4: #ef4444;
|
|
--color-chart-5: #8b5cf6;
|
|
|
|
/* Settings variables. The text size is a percentage of the browser's own
|
|
default, so the browser's font size setting still counts (EN 301 549
|
|
9.7); Settings > Accessibility multiplies it. */
|
|
--font-size-base: 100%;
|
|
--list-item-height: 48px;
|
|
--transition-duration: 0.2s;
|
|
|
|
/* Density spacing variables (defaults = regular) */
|
|
--density-item-py: 12px;
|
|
--density-item-gap: 12px;
|
|
--density-header-py: 12px;
|
|
--density-card-p: 16px;
|
|
--density-sidebar-py: 4px;
|
|
|
|
/* Minimum height of a tappable list row below the lg breakpoint, where
|
|
* folder rows use max-lg:min-h-[44px]. Plugin frames get it as a variable
|
|
* because their own viewport is the frame, not the page. */
|
|
--touch-row-min-h: 0px;
|
|
}
|
|
|
|
@media (width < 64rem) {
|
|
:root {
|
|
--touch-row-min-h: 44px;
|
|
}
|
|
}
|
|
|
|
.dark {
|
|
--color-border: rgba(128, 128, 128, 0.3);
|
|
--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: #fafafa;
|
|
--color-popover: #1c1c1c;
|
|
--color-popover-foreground: #fafafa;
|
|
|
|
/* Sidebar */
|
|
--color-sidebar: #0a0a0a;
|
|
--color-sidebar-foreground: #fafafa;
|
|
--color-sidebar-border: #262626;
|
|
--color-sidebar-accent: #1a1a1a;
|
|
--color-sidebar-accent-foreground: #fafafa;
|
|
|
|
/* Card */
|
|
--color-card: #141414;
|
|
--color-card-foreground: #fafafa;
|
|
|
|
/* Semantic status colors */
|
|
--color-success: #16a34a;
|
|
--color-success-foreground: #ffffff;
|
|
--color-warning: #ca8a04;
|
|
--color-warning-foreground: #ffffff;
|
|
--color-info: #60a5fa;
|
|
--color-info-foreground: #ffffff;
|
|
|
|
/* Selection states */
|
|
--color-selection: rgba(59, 130, 246, 0.25);
|
|
--color-selection-foreground: #93c5fd;
|
|
|
|
/* Unread indicator */
|
|
--color-unread: #60a5fa;
|
|
|
|
/* Chart colors */
|
|
--color-chart-1: #60a5fa;
|
|
--color-chart-2: #4ade80;
|
|
--color-chart-3: #fbbf24;
|
|
--color-chart-4: #f87171;
|
|
--color-chart-5: #a78bfa;
|
|
}
|
|
|
|
/* A colour bar along an element's start edge: --start-bar is its colour,
|
|
--start-bar-width its width (3px). A thick border on a rounded element
|
|
curves on its inner side too; this bar is a background stripe, so only the
|
|
element's own outer corners round it and its inner edge stays straight.
|
|
It lies over the element's padding (and a transparent start border). */
|
|
@utility start-bar {
|
|
background-image: linear-gradient(var(--start-bar), var(--start-bar));
|
|
background-origin: border-box;
|
|
background-position: left top;
|
|
background-repeat: no-repeat;
|
|
background-size: var(--start-bar-width, 3px) 100%;
|
|
|
|
&:dir(rtl) {
|
|
background-position: right top;
|
|
}
|
|
}
|
|
|
|
|
|
/* The default border colour. In the base layer, so a border colour utility
|
|
(border-primary, border-transparent, border-destructive, border-input)
|
|
wins over it; unlayered, it outranked every utility and drew them grey. */
|
|
@layer base {
|
|
* {
|
|
border-color: var(--color-border);
|
|
}
|
|
}
|
|
|
|
body {
|
|
background-color: var(--color-background);
|
|
color: var(--color-foreground);
|
|
font-family: var(--font-sans);
|
|
font-feature-settings:
|
|
"rlig" 1,
|
|
"calt" 1;
|
|
}
|
|
|
|
/* Native <select> option lists are painted by the browser, not by the
|
|
Tailwind classes on the control, so they only honour color-scheme and an
|
|
explicit option colour. Themes whose --color-muted is translucent (Aurora
|
|
Glass dark: rgba(255,255,255,0.05)) composited to a light popup with
|
|
near-white text (#999). Pin options to the popover tokens, which every
|
|
theme defines as an opaque surface, and let dark themes ask the browser
|
|
for a dark popup where option colours are ignored (Safari). */
|
|
option {
|
|
background-color: var(--color-popover);
|
|
color: var(--color-popover-foreground);
|
|
}
|
|
|
|
/* The group labels (the From dropdown's accounts) are painted the same way.
|
|
Without colours of their own they kept the control's text colour on the
|
|
popup's base, which a transparent control leaves light: in dark themes,
|
|
light text on a white bar. */
|
|
optgroup {
|
|
background-color: var(--color-popover);
|
|
color: var(--color-popover-foreground);
|
|
}
|
|
|
|
.dark select {
|
|
color-scheme: dark;
|
|
}
|
|
|
|
/* Tailwind v4's preflight no longer gives buttons a pointer cursor, so most
|
|
controls showed the arrow. Give everything clickable the pointer again, and
|
|
disabled controls not-allowed, as the components that style it themselves
|
|
already do. This sits in the base layer so a cursor utility on a component
|
|
(cursor-grab, cursor-text, disabled:cursor-default) still wins. */
|
|
@layer base {
|
|
button:not(:disabled),
|
|
select:not(:disabled),
|
|
input:is([type="checkbox"], [type="radio"], [type="color"]):not(:disabled),
|
|
summary,
|
|
[role="button"]:not([aria-disabled="true"]),
|
|
[role="tab"]:not([aria-disabled="true"]),
|
|
[role="switch"]:not([aria-disabled="true"]),
|
|
[role="checkbox"]:not([aria-disabled="true"]),
|
|
[role="radio"]:not([aria-disabled="true"]),
|
|
[role="option"]:not([aria-disabled="true"]),
|
|
[role="menuitem"]:not([aria-disabled="true"]),
|
|
[role="menuitemcheckbox"]:not([aria-disabled="true"]),
|
|
[role="menuitemradio"]:not([aria-disabled="true"]) {
|
|
cursor: pointer;
|
|
}
|
|
|
|
button:disabled,
|
|
select:disabled,
|
|
input:is([type="checkbox"], [type="radio"], [type="color"]):disabled,
|
|
[role="button"][aria-disabled="true"],
|
|
[role="tab"][aria-disabled="true"],
|
|
[role="switch"][aria-disabled="true"],
|
|
[role="checkbox"][aria-disabled="true"],
|
|
[role="radio"][aria-disabled="true"],
|
|
[role="option"][aria-disabled="true"],
|
|
[role="menuitem"][aria-disabled="true"],
|
|
[role="menuitemcheckbox"][aria-disabled="true"],
|
|
[role="menuitemradio"][aria-disabled="true"] {
|
|
cursor: not-allowed;
|
|
}
|
|
}
|
|
|
|
/* Minimalist scrollbar, unless the user asked for the browser's own wider
|
|
one in Settings > Accessibility: Chromium draws its native scrollbar only
|
|
while no ::-webkit-scrollbar rule matches. */
|
|
:root:not([data-native-scrollbars="on"]) ::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
:root:not([data-native-scrollbars="on"]) ::-webkit-scrollbar-track {
|
|
background-color: transparent;
|
|
}
|
|
|
|
:root:not([data-native-scrollbars="on"]) ::-webkit-scrollbar-thumb {
|
|
background-color: var(--color-border);
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
:root:not([data-native-scrollbars="on"]) ::-webkit-scrollbar-thumb:hover {
|
|
background-color: var(--color-muted-foreground);
|
|
}
|
|
|
|
/* Enhanced Email Content Styling */
|
|
|
|
.email-content-wrapper {
|
|
min-width: 100%;
|
|
}
|
|
|
|
/* Forces light-theme CSS variables inside the email content area, so when
|
|
"Always Show Emails in Light Mode" is enabled in dark theme the surrounding
|
|
sender info / attachments / plain-text body don't end up with light text
|
|
on a white background. */
|
|
.email-content-light {
|
|
--color-background: #ffffff;
|
|
--color-foreground: #0f172a;
|
|
--color-muted: #f1f5f9;
|
|
--color-muted-foreground: #64748b;
|
|
--color-border: #e2e8f0;
|
|
--color-card: #ffffff;
|
|
--color-card-foreground: #0f172a;
|
|
--color-popover: #ffffff;
|
|
--color-popover-foreground: #0f172a;
|
|
--color-secondary: #f8fafc;
|
|
--color-secondary-foreground: #0f172a;
|
|
--color-accent: #dbeafe;
|
|
--color-accent-foreground: #1e40af;
|
|
--color-input: #e2e8f0;
|
|
}
|
|
|
|
.email-content-light .email-content-text a {
|
|
color: #2563eb;
|
|
}
|
|
|
|
.email-content-text {
|
|
padding: 1rem 1.25rem;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.email-content-text {
|
|
padding-left: 0.75rem;
|
|
padding-right: 0.75rem;
|
|
}
|
|
}
|
|
|
|
.email-content-text a {
|
|
color: #2563eb;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.dark .email-content-text a {
|
|
color: #60a5fa;
|
|
}
|
|
|
|
.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;
|
|
padding: 1rem 1.25rem;
|
|
}
|
|
|
|
/* Remove padding for rich HTML emails with table-based layouts */
|
|
.email-content:has(> table) {
|
|
padding: 0;
|
|
}
|
|
|
|
.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 var(--color-border);
|
|
padding-left: 1rem;
|
|
margin: 1rem 0;
|
|
color: var(--color-muted-foreground);
|
|
font-style: italic;
|
|
}
|
|
|
|
.email-content pre {
|
|
background-color: var(--color-muted);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
font-family: "SF Mono", Monaco, Menlo, Consolas, monospace;
|
|
font-size: 0.875rem;
|
|
overflow-x: auto;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.email-content code {
|
|
background-color: var(--color-muted);
|
|
padding: 0.125rem 0.375rem;
|
|
border-radius: 0.25rem;
|
|
font-family: "SF Mono", Monaco, Menlo, Consolas, monospace;
|
|
font-size: 0.875rem;
|
|
color: var(--color-destructive);
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
/* Reply quoted HTML - preserves original inline styles/colors */
|
|
.email-reply-quote {
|
|
overflow-wrap: break-word;
|
|
word-wrap: break-word;
|
|
max-width: none;
|
|
}
|
|
|
|
.email-reply-quote p {
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.email-reply-quote img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
.email-reply-quote a {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* 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 var(--color-border);
|
|
text-align: left;
|
|
}
|
|
|
|
.email-content table.data-table th,
|
|
.email-content table[border="1"] th {
|
|
background-color: var(--color-muted);
|
|
font-weight: 600;
|
|
color: var(--color-foreground);
|
|
}
|
|
|
|
/* 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 var(--color-border);
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
/* Email thread quoted text */
|
|
.email-content .quoted-text {
|
|
border-left: 3px solid var(--color-border);
|
|
padding-left: 1rem;
|
|
margin: 1rem 0;
|
|
color: var(--color-muted-foreground);
|
|
opacity: 0.8;
|
|
}
|
|
|
|
/* Toast animations. They slide in from the end edge: --toast-slide is -1 in RTL. */
|
|
@keyframes toast-enter {
|
|
from {
|
|
transform: translateX(calc((100% + 1.25rem) * var(--toast-slide, 1)));
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes toast-exit {
|
|
from {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
to {
|
|
transform: translateX(calc((100% + 1.25rem) * var(--toast-slide, 1)));
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes toast-progress {
|
|
from {
|
|
width: 100%;
|
|
}
|
|
to {
|
|
width: 0%;
|
|
}
|
|
}
|
|
|
|
.toast-enter {
|
|
animation: toast-enter 0.32s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
|
|
}
|
|
|
|
.toast-exit {
|
|
animation: toast-exit 0.28s cubic-bezier(0.06, 0.71, 0.55, 1) forwards;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
.scroll-hidden::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
/* iOS Safari zooms the viewport whenever a focused field computes below 16px,
|
|
* and leaves the page zoomed and panned once the keyboard closes (#838). Every
|
|
* field here is text-sm (0.875rem), so no --font-size-base setting escapes it:
|
|
* small 12.25px, medium 14px, large 15.75px. The viewport meta deliberately
|
|
* omits maximum-scale so pinch zoom keeps working, which leaves CSS as the
|
|
* only place to put the floor.
|
|
*
|
|
* max() raises the small fields to 16px while letting the "Large" setting go
|
|
* past it (18px) rather than pinning every field to the threshold, and
|
|
* pointer: coarse keeps desktop typography untouched. Checkbox and radio are
|
|
* excluded because their box is font-relative in Safari; the :not() chain also
|
|
* lifts specificity above .text-sm, so no !important is needed.
|
|
*
|
|
* Quoted replies are exempt by construction: they render inside a shadow root
|
|
* (components/email/quoted-html.ts) so they match the sent mail 1:1, and page
|
|
* CSS cannot reach in. */
|
|
@media (pointer: coarse) {
|
|
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
|
|
textarea,
|
|
select,
|
|
[contenteditable="true"] {
|
|
font-size: max(16px, 1rem);
|
|
}
|
|
}
|
|
|
|
/* Shake animation (for rejected input) */
|
|
@keyframes shake {
|
|
0%,
|
|
100% {
|
|
transform: translateX(0);
|
|
}
|
|
20%,
|
|
60% {
|
|
transform: translateX(-4px);
|
|
}
|
|
40%,
|
|
80% {
|
|
transform: translateX(4px);
|
|
}
|
|
}
|
|
|
|
.animate-shake {
|
|
animation: shake 0.4s ease-in-out;
|
|
}
|
|
|
|
/* Fade in animation (for popovers) */
|
|
@keyframes fade-in {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.animate-fade-in {
|
|
animation: fade-in 0.2s ease-out;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Calendar swipe animations (subtler slide for view transitions) */
|
|
@keyframes cal-slide-in-right {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(30%);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
@keyframes cal-slide-in-left {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(-30%);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
.animate-slide-in-right {
|
|
animation: cal-slide-in-right 0.25s ease-out;
|
|
}
|
|
|
|
.animate-slide-in-left {
|
|
animation: cal-slide-in-left 0.25s ease-out;
|
|
}
|
|
|
|
/* Reduced motion: respect the OS preference unless the user turned Reduce
|
|
motion off in Settings > Accessibility. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
:root:not([data-motion="full"]) *,
|
|
:root:not([data-motion="full"]) *::before,
|
|
:root:not([data-motion="full"]) *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
/* Every element transitions `all` by default once it has a duration.
|
|
Visibility is left out: a transition from hidden would keep a newly
|
|
shown menu or dialog hidden for a frame, and focus() would miss it. */
|
|
transition-property: all, visibility !important;
|
|
transition-duration: 0.01ms, 0s !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|
|
|
|
/* The same when the user turns Reduce motion on in the settings (#1215). */
|
|
[data-motion="reduce"] *,
|
|
[data-motion="reduce"] *::before,
|
|
[data-motion="reduce"] *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-property: all, visibility !important;
|
|
transition-duration: 0.01ms, 0s !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;
|
|
}
|
|
}
|
|
|
|
/* TipTap Rich Text Editor */
|
|
.tiptap {
|
|
outline: none;
|
|
}
|
|
|
|
/* View-only quote decorations must survive a node view refreshing its inline styles. */
|
|
.tiptap [data-reply-quote-collapsed] {
|
|
display: none !important;
|
|
}
|
|
|
|
.tiptap p {
|
|
margin: 0.25rem 0;
|
|
}
|
|
|
|
.tiptap h1 {
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.tiptap h2 {
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.tiptap ul {
|
|
list-style-type: disc;
|
|
padding-inline-start: 1.5rem;
|
|
margin: 0.25rem 0;
|
|
}
|
|
|
|
.tiptap ol {
|
|
list-style-type: decimal;
|
|
padding-inline-start: 1.5rem;
|
|
margin: 0.25rem 0;
|
|
}
|
|
|
|
.tiptap li {
|
|
margin: 0.125rem 0;
|
|
}
|
|
|
|
.tiptap blockquote {
|
|
border-inline-start: 3px solid var(--color-border);
|
|
padding-inline-start: 1rem;
|
|
margin: 0.5rem 0;
|
|
color: var(--color-muted-foreground);
|
|
}
|
|
|
|
.tiptap pre {
|
|
background-color: var(--color-muted);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 0.375rem;
|
|
padding: 0.75rem;
|
|
font-family: monospace;
|
|
font-size: 0.875rem;
|
|
overflow-x: auto;
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.tiptap code {
|
|
background-color: var(--color-muted);
|
|
padding: 0.125rem 0.25rem;
|
|
border-radius: 0.25rem;
|
|
font-family: monospace;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.tiptap a {
|
|
color: var(--color-primary);
|
|
text-decoration: underline;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* @mention chip (mention-chip.tsx). In the sent mail it is a plain mailto link. */
|
|
.tiptap a.mention {
|
|
display: inline-block;
|
|
padding: 0 0.3em;
|
|
border-radius: 0.375rem;
|
|
background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
color: var(--color-primary);
|
|
font-weight: 500;
|
|
line-height: 1.35;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
transition: background-color 120ms, box-shadow 120ms;
|
|
}
|
|
|
|
/* Hover says it opens something on click; the card itself waits for the click. */
|
|
.tiptap a.mention:hover {
|
|
background-color: color-mix(in srgb, var(--color-primary) 24%, transparent);
|
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 45%, transparent);
|
|
}
|
|
|
|
.tiptap a.mention.mention-outside:hover {
|
|
background-color: color-mix(in srgb, currentColor 10%, transparent);
|
|
box-shadow: inset 0 0 0 1px currentColor;
|
|
}
|
|
|
|
.tiptap a.mention.ProseMirror-selectednode {
|
|
outline: 2px solid var(--color-primary);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
/* Named in the text but not in To or Cc. */
|
|
.tiptap a.mention.mention-outside {
|
|
background-color: transparent;
|
|
color: var(--color-muted-foreground);
|
|
box-shadow: inset 0 0 0 1px currentColor;
|
|
border: 0;
|
|
}
|
|
|
|
.tiptap img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
.tiptap hr {
|
|
border: none;
|
|
border-top: 1px solid var(--color-border);
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.tiptap p.is-editor-empty:first-child::before {
|
|
content: attr(data-placeholder);
|
|
float: inline-start;
|
|
color: var(--color-muted-foreground);
|
|
pointer-events: none;
|
|
height: 0;
|
|
}
|
|
|
|
.tiptap .ProseMirror-selectednode img {
|
|
outline: none;
|
|
}
|
|
|
|
.tiptap table {
|
|
border-collapse: collapse;
|
|
margin: 0.5rem 0;
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tiptap table td,
|
|
.tiptap table th {
|
|
border: 1px solid var(--color-border);
|
|
padding: 0.375rem 0.5rem;
|
|
vertical-align: top;
|
|
position: relative;
|
|
min-width: 1em;
|
|
}
|
|
|
|
.tiptap table th {
|
|
background-color: var(--color-muted) !important;
|
|
color: var(--color-foreground) !important;
|
|
font-weight: 600;
|
|
text-align: left;
|
|
}
|
|
|
|
.tiptap table p {
|
|
margin: 0;
|
|
}
|
|
|
|
.tiptap table .selectedCell::after {
|
|
background: rgba(99, 102, 241, 0.15);
|
|
content: "";
|
|
inset: 0;
|
|
pointer-events: none;
|
|
position: absolute;
|
|
z-index: 2;
|
|
}
|
|
|
|
.tiptap table .column-resize-handle {
|
|
background-color: var(--color-primary);
|
|
bottom: -2px;
|
|
pointer-events: none;
|
|
position: absolute;
|
|
right: -2px;
|
|
top: 0;
|
|
width: 4px;
|
|
}
|
|
|
|
.tiptap.resize-cursor {
|
|
cursor: col-resize;
|
|
}
|
|
|
|
/* Smooth halo applied to a setting row when the user clicks a sub-result in
|
|
the settings search. It is drawn by a pseudo-element behind the row's
|
|
content, so the row keeps its own shape and divider. It stays inside the
|
|
row's padding box vertically (never over the dividers above and below) and
|
|
reaches 10px past it sideways so the text isn't flush with its edge. Uses
|
|
the primary theme color so it adapts to light/dark. */
|
|
@keyframes settings-search-pulse {
|
|
0% {
|
|
background-color: color-mix(in srgb, var(--color-primary) 0%, transparent);
|
|
border-color: color-mix(in srgb, var(--color-primary) 0%, transparent);
|
|
}
|
|
30% {
|
|
background-color: color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
border-color: color-mix(in srgb, var(--color-primary) 40%, transparent);
|
|
}
|
|
100% {
|
|
background-color: color-mix(in srgb, var(--color-primary) 0%, transparent);
|
|
border-color: color-mix(in srgb, var(--color-primary) 0%, transparent);
|
|
}
|
|
}
|
|
|
|
.settings-search-highlight {
|
|
position: relative;
|
|
isolation: isolate;
|
|
}
|
|
|
|
.settings-search-highlight::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0 -10px;
|
|
z-index: -1;
|
|
border: 1px solid transparent;
|
|
border-radius: 8px;
|
|
pointer-events: none;
|
|
animation: settings-search-pulse 1.6s ease-in-out forwards;
|
|
}
|
|
|
|
/* RTL: mirror directional icons (chevrons/arrows) so prev/next, back/forward,
|
|
reply/forward and panel-collapse affordances point the correct way in
|
|
right-to-left layouts.
|
|
Tabler emits a `tabler-icon-<name>` class per icon, so we target the
|
|
directional ones only — vertical chevrons (up/down) are intentionally left. */
|
|
[dir="rtl"] .tabler-icon-chevron-left,
|
|
[dir="rtl"] .tabler-icon-chevron-right,
|
|
[dir="rtl"] .tabler-icon-chevrons-left,
|
|
[dir="rtl"] .tabler-icon-chevrons-right,
|
|
[dir="rtl"] .tabler-icon-arrow-left,
|
|
[dir="rtl"] .tabler-icon-arrow-right,
|
|
[dir="rtl"] .tabler-icon-arrow-big-left,
|
|
[dir="rtl"] .tabler-icon-arrow-big-right,
|
|
[dir="rtl"] .tabler-icon-arrow-back-up,
|
|
[dir="rtl"] .tabler-icon-arrow-back-up-double,
|
|
[dir="rtl"] .tabler-icon-arrow-forward-up,
|
|
[dir="rtl"] .tabler-icon-arrow-forward-up-double,
|
|
[dir="rtl"] .tabler-icon-layout-sidebar-left,
|
|
[dir="rtl"] .tabler-icon-layout-sidebar-right {
|
|
transform: scaleX(-1);
|
|
}
|
|
|
|
/* ==========================================================================
|
|
Interface layouts
|
|
--------------------------------------------------------------------------
|
|
`data-interface-layout` is set on <html> by the settings store
|
|
(`applyInterfaceLayout`). The default layout declares no overrides, so
|
|
everything below is additive and switched off by a single attribute.
|
|
Mountain View sets its own geometry - bar heights, rail widths, pill shapes,
|
|
row rhythm - while colour, iconography and type keep coming from the active
|
|
theme.
|
|
========================================================================== */
|
|
|
|
[data-interface-layout="mountain-view"] {
|
|
--mv-topbar-h: 64px;
|
|
--mv-rail-w: 72px;
|
|
--mv-rail-peek-w: 256px;
|
|
--mv-nav-row-h: 32px;
|
|
--mv-nav-radius: 9999px;
|
|
--mv-compose-h: 56px;
|
|
--mv-search-h: 48px;
|
|
--mv-search-radius: 8px;
|
|
--mv-popover-w: 352px;
|
|
--mv-popover-radius: 28px;
|
|
--mv-card-radius: 16px;
|
|
/* Surfaces, tinted from the theme's primary so any theme keeps the same
|
|
relationships: a faintly tinted canvas behind the chrome, a plain card
|
|
for the content, a slightly deeper search field, a selected-row pill and
|
|
a soft Compose button. */
|
|
--mv-canvas: color-mix(in oklab, var(--color-primary) 4%, var(--color-background));
|
|
--mv-search-bg: color-mix(in oklab, var(--color-primary) 9%, var(--color-background));
|
|
--mv-nav-hover: color-mix(in oklab, var(--color-foreground) 7%, var(--mv-canvas));
|
|
--mv-nav-selected: color-mix(in oklab, var(--color-primary) 18%, var(--color-background));
|
|
--mv-compose-bg: color-mix(in oklab, var(--color-primary) 24%, var(--color-background));
|
|
--mv-compose-fg: color-mix(in oklab, var(--color-primary) 30%, var(--color-foreground));
|
|
--mv-row-read: color-mix(in oklab, var(--color-primary) 3%, var(--color-background));
|
|
}
|
|
|
|
/* The content card: everything that is not chrome sits on one rounded sheet
|
|
inset from the canvas. */
|
|
[data-interface-layout="mountain-view"] [data-mv-card] {
|
|
border-radius: var(--mv-card-radius);
|
|
background-color: var(--color-background);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Search: a filled 48px field that lifts onto a card while focused, rather
|
|
than the outlined input the default layout uses. */
|
|
[data-interface-layout="mountain-view"] .mv-topbar input[data-search-input] {
|
|
height: var(--mv-search-h);
|
|
border-radius: var(--mv-search-radius);
|
|
border-color: transparent;
|
|
background-color: var(--mv-search-bg);
|
|
padding-inline-start: 3rem;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
[data-interface-layout="mountain-view"] .mv-topbar input[data-search-input]:hover {
|
|
border-color: transparent;
|
|
background-color: var(--mv-search-bg);
|
|
box-shadow: 0 1px 1px 0 rgb(0 0 0 / 0.08), 0 1px 3px 1px rgb(0 0 0 / 0.04);
|
|
}
|
|
|
|
[data-interface-layout="mountain-view"] .mv-topbar input[data-search-input]:focus-visible {
|
|
background-color: var(--color-background);
|
|
border-color: transparent;
|
|
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.16), 0 4px 8px 3px rgb(0 0 0 / 0.08);
|
|
}
|
|
|
|
/* The filter button sits inside the field's trailing end: keep typed text and
|
|
the clear button clear of it. */
|
|
[data-interface-layout="mountain-view"] .mv-topbar input[data-search-input] {
|
|
padding-inline-end: 5.5rem;
|
|
}
|
|
|
|
[data-interface-layout="mountain-view"] .mv-topbar-search > div > form > div > div {
|
|
inset-inline-end: 3.25rem;
|
|
}
|
|
|
|
/* The magnifier sits on the field's baseline grid, one 48px hit area in. */
|
|
[data-interface-layout="mountain-view"] .mv-topbar-search > div > form > div > svg {
|
|
inset-inline-start: 1rem;
|
|
width: 1.25rem;
|
|
height: 1.25rem;
|
|
}
|
|
|
|
/* Folder rows: 32px pills flush with the rail's leading edge and rounded only
|
|
on the trailing side, so the selected label reads as one continuous shape
|
|
with the rail. The default layout's leading accent stripe goes away with it. */
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row] {
|
|
min-height: var(--mv-nav-row-h);
|
|
margin-inline-end: 12px;
|
|
padding-inline-end: 16px;
|
|
border-inline-start-width: 0;
|
|
border-start-start-radius: 0;
|
|
border-end-start-radius: 0;
|
|
border-start-end-radius: var(--mv-nav-radius);
|
|
border-end-end-radius: var(--mv-nav-radius);
|
|
}
|
|
|
|
/* Selected and hovered rows fill the pill; a folder with unread mail reads
|
|
bold, the way the message rows do. Icons are 20px with an 18px gap. */
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row] {
|
|
font-size: 0.875rem;
|
|
color: var(--color-foreground);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row]:hover {
|
|
background-color: var(--mv-nav-hover);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row][data-selected="true"] {
|
|
background-color: var(--mv-nav-selected);
|
|
color: var(--color-foreground);
|
|
font-weight: 700;
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row][data-has-unread] {
|
|
font-weight: 700;
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row] > button {
|
|
gap: 18px;
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row] > button > span:first-child,
|
|
[data-interface-layout="mountain-view"] [data-mv-nav-row] > button > span:first-child > svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
|
|
/* Compose: a 56px button with a 16px corner, which shrinks to a square with
|
|
the rail. */
|
|
[data-interface-layout="mountain-view"] [data-mv-compose] {
|
|
height: var(--mv-compose-h);
|
|
border-radius: 16px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Message rows: read mail sits on a faint tint and unread on the plain card,
|
|
a ticked row takes the selection colour, and the row under the pointer
|
|
lifts on a shadow instead of changing colour. */
|
|
[data-interface-layout="mountain-view"] [data-mv-row] {
|
|
background-color: var(--mv-row-read);
|
|
box-shadow: none;
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-row][data-unread="true"] {
|
|
background-color: var(--color-background);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-row][data-checked] {
|
|
background-color: var(--mv-nav-selected);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-row][aria-current="true"] {
|
|
background-color: var(--color-accent);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-row]:hover {
|
|
z-index: 1;
|
|
box-shadow:
|
|
inset 1px 0 0 var(--color-border),
|
|
inset -1px 0 0 var(--color-border),
|
|
0 1px 2px 0 rgb(60 64 67 / 0.3),
|
|
0 1px 3px 1px rgb(60 64 67 / 0.15);
|
|
}
|
|
|
|
/* The open message's toolbar: round 40px icon buttons in the muted colour,
|
|
20px icons. Menus that drop from it keep their own rows. */
|
|
[data-interface-layout="mountain-view"] [data-mv-viewer-toolbar] button:not([role="menu"] *):not([data-mv-pager] *) {
|
|
height: 40px;
|
|
min-width: 40px;
|
|
padding-inline: 10px;
|
|
border-radius: 9999px;
|
|
color: var(--color-muted-foreground);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-viewer-toolbar] button:not([role="menu"] *):not([data-mv-pager] *):hover {
|
|
color: var(--color-foreground);
|
|
}
|
|
[data-interface-layout="mountain-view"] [data-mv-viewer-toolbar] button:not([role="menu"] *) > svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
|
|
/* The compose window's bottom bar: its two halves fold into one row, Send
|
|
leads as a round split button without its icon, the tools follow as bare
|
|
icons, and the bin for Discard sits at the far end. */
|
|
[data-mv-compose-footer] > * {
|
|
display: contents;
|
|
}
|
|
[data-mv-compose-footer] > * > * {
|
|
order: 1;
|
|
}
|
|
[data-mv-compose-footer] [data-mv-send] {
|
|
order: 0;
|
|
margin-inline-end: 0.5rem;
|
|
}
|
|
[data-mv-compose-footer] [data-mv-discard] {
|
|
order: 2;
|
|
margin-inline-start: auto;
|
|
}
|
|
[data-mv-compose-footer] [data-mv-send] .tabler-icon-send {
|
|
display: none;
|
|
}
|
|
[data-mv-compose-footer] button[data-mv-send],
|
|
[data-mv-compose-footer] [data-mv-send] > button:first-child {
|
|
height: 36px;
|
|
padding-inline: 1.25rem 1rem;
|
|
border-radius: 18px;
|
|
}
|
|
[data-mv-compose-footer] [data-mv-send] > button:first-child:not(:only-child) {
|
|
border-start-end-radius: 0;
|
|
border-end-end-radius: 0;
|
|
}
|
|
[data-mv-compose-footer] [data-mv-send] > button:nth-child(2) {
|
|
height: 36px;
|
|
border-radius: 0 18px 18px 0;
|
|
}
|
|
[dir="rtl"] [data-mv-compose-footer] [data-mv-send] > button:nth-child(2) {
|
|
border-radius: 18px 0 0 18px;
|
|
}
|
|
[data-mv-compose-footer] button:not([data-mv-send] *):not([data-mv-send]) > span.text-sm,
|
|
[data-mv-compose-footer] button[data-testid$="-menu"] > .tabler-icon-chevron-down {
|
|
display: none;
|
|
}
|
|
[data-mv-compose-footer] button[data-testid$="-menu"] {
|
|
position: relative;
|
|
width: 36px;
|
|
padding-inline: 0;
|
|
justify-content: center;
|
|
border-radius: 9999px;
|
|
}
|
|
[data-mv-compose-footer] button[data-testid$="-menu"] > span:not(.text-sm) {
|
|
position: absolute;
|
|
top: 0;
|
|
inset-inline-end: 0;
|
|
}
|
|
|
|
/* The account popover: a wide card with a very round corner, rather
|
|
than the narrow menu the rail and the sidebar switchers drop. */
|
|
[data-interface-layout="mountain-view"] [data-mv-account-popover] {
|
|
width: var(--mv-popover-w);
|
|
border-radius: var(--mv-popover-radius);
|
|
}
|
|
|
|
/* The advanced filters drop from the search field with a small gap and take
|
|
its corner, so the two read as one control. Position and width come from
|
|
the field's own box, measured by FilterPanelHost. */
|
|
[data-interface-layout="mountain-view"] [data-mv-filter-panel] {
|
|
border-radius: var(--mv-search-radius);
|
|
}
|
|
|
|
/* The collapsed rail's hover peek. Only on desktop: below that breakpoint the
|
|
sidebar is already a drawer, opened by a tap rather than by a pointer that
|
|
does not exist. The width beats Tailwind's own `lg:w-full` on specificity,
|
|
which is what keeps the floating panel from filling the 72px column. */
|
|
@media (min-width: 1024px) {
|
|
[data-interface-layout="mountain-view"] [data-mv-rail-peek] {
|
|
width: var(--mv-rail-peek-w);
|
|
}
|
|
}
|