feat(renderer): add fluid chapter reorder motion
This commit is contained in:
parent
a2d2d4858b
commit
40a2fb305e
11 changed files with 695 additions and 38 deletions
|
|
@ -134,6 +134,14 @@ export function createChapterListFeature(ctx: BookCtx): ChapterListFeature {
|
|||
}
|
||||
|
||||
function renderChapterList() {
|
||||
const previousPositions = animateNextRender
|
||||
? new Map(
|
||||
Array.from(chapterList.querySelectorAll<HTMLElement>(".chapter-row"), (row) => [
|
||||
row.dataset.id ?? "",
|
||||
row.getBoundingClientRect().top,
|
||||
])
|
||||
)
|
||||
: null;
|
||||
chapterList.innerHTML = "";
|
||||
for (const ch of state.ordered) {
|
||||
const li = document.createElement("li");
|
||||
|
|
@ -190,6 +198,26 @@ export function createChapterListFeature(ctx: BookCtx): ChapterListFeature {
|
|||
});
|
||||
chapterList.appendChild(li);
|
||||
}
|
||||
if (!previousPositions) return;
|
||||
animateNextRender = false;
|
||||
const rows = Array.from(chapterList.querySelectorAll<HTMLElement>(".chapter-row"));
|
||||
for (const row of rows) {
|
||||
const oldTop = previousPositions.get(row.dataset.id ?? "");
|
||||
if (oldTop === undefined) continue;
|
||||
const delta = oldTop - row.getBoundingClientRect().top;
|
||||
if (!delta) continue;
|
||||
row.classList.add("reordering");
|
||||
row.style.transform = `translateY(${delta}px)`;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
for (const row of rows) row.style.transform = "";
|
||||
});
|
||||
setTimeout(() => {
|
||||
for (const row of rows) {
|
||||
row.classList.remove("reordering");
|
||||
row.style.transform = "";
|
||||
}
|
||||
}, 420);
|
||||
}
|
||||
|
||||
async function create() {
|
||||
|
|
@ -305,6 +333,10 @@ export function createChapterListFeature(ctx: BookCtx): ChapterListFeature {
|
|||
let dragStartY = 0;
|
||||
let dragMoved = false;
|
||||
let dragJustHappened = false;
|
||||
let animateNextRender = false;
|
||||
let dragGhost: HTMLElement | null = null;
|
||||
let dragPlaceholder: HTMLElement | null = null;
|
||||
let dragOffsetY = 0;
|
||||
|
||||
chapterList.addEventListener("pointerdown", (e) => {
|
||||
if (e.button !== 0) return; // left button only
|
||||
|
|
@ -327,15 +359,38 @@ export function createChapterListFeature(ctx: BookCtx): ChapterListFeature {
|
|||
const src = chapterList.querySelector(
|
||||
`li.chapter-row[data-id="${draggingId}"]`
|
||||
) as HTMLElement | null;
|
||||
src?.classList.add("dragging");
|
||||
if (src) {
|
||||
const rect = src.getBoundingClientRect();
|
||||
dragOffsetY = e.clientY - rect.top;
|
||||
dragGhost = src.cloneNode(true) as HTMLElement;
|
||||
dragGhost.classList.add("drag-ghost");
|
||||
dragGhost.style.width = `${rect.width}px`;
|
||||
dragGhost.style.left = `${rect.left}px`;
|
||||
dragGhost.style.top = `${rect.top}px`;
|
||||
document.body.appendChild(dragGhost);
|
||||
|
||||
dragPlaceholder = document.createElement("li");
|
||||
dragPlaceholder.className = "chapter-drop-placeholder";
|
||||
dragPlaceholder.style.height = `${rect.height}px`;
|
||||
src.replaceWith(dragPlaceholder);
|
||||
}
|
||||
document.body.classList.add("dragging-active");
|
||||
}
|
||||
if (dragGhost) dragGhost.style.top = `${e.clientY - dragOffsetY}px`;
|
||||
const overEl = document.elementFromPoint(e.clientX, e.clientY);
|
||||
const overLi = overEl?.closest("li.chapter-row") as HTMLElement | null;
|
||||
chapterList
|
||||
.querySelectorAll(".chapter-row")
|
||||
.forEach((n) => n.classList.remove("drop-target"));
|
||||
if (overLi && overLi.dataset.id !== draggingId) overLi.classList.add("drop-target");
|
||||
chapterList.querySelectorAll(".chapter-row").forEach((n) => {
|
||||
n.classList.remove("drop-target", "drop-before", "drop-after");
|
||||
});
|
||||
if (overLi && overLi.dataset.id !== draggingId) {
|
||||
overLi.classList.add("drop-target");
|
||||
const before = e.clientY < overLi.getBoundingClientRect().top + overLi.offsetHeight / 2;
|
||||
overLi.classList.add(before ? "drop-before" : "drop-after");
|
||||
if (dragPlaceholder) {
|
||||
if (before) chapterList.insertBefore(dragPlaceholder, overLi);
|
||||
else chapterList.insertBefore(dragPlaceholder, overLi.nextSibling);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("pointerup", (e) => {
|
||||
|
|
@ -347,23 +402,26 @@ export function createChapterListFeature(ctx: BookCtx): ChapterListFeature {
|
|||
return;
|
||||
}
|
||||
dragJustHappened = true;
|
||||
const overEl = document.elementFromPoint(e.clientX, e.clientY);
|
||||
const overLi = overEl?.closest("li.chapter-row") as HTMLElement | null;
|
||||
const targetId = overLi?.dataset.id ?? null;
|
||||
const src = chapterList.querySelector(
|
||||
`li.chapter-row[data-id="${draggingId}"]`
|
||||
) as HTMLElement | null;
|
||||
src?.classList.remove("dragging");
|
||||
chapterList
|
||||
.querySelectorAll(".chapter-row")
|
||||
.forEach((n) => n.classList.remove("drop-target"));
|
||||
if (targetId && targetId !== draggingId) {
|
||||
const targetIndex = dragPlaceholder
|
||||
? Array.from(chapterList.children).indexOf(dragPlaceholder)
|
||||
: -1;
|
||||
dragGhost?.remove();
|
||||
dragGhost = null;
|
||||
dragPlaceholder?.remove();
|
||||
dragPlaceholder = null;
|
||||
chapterList.querySelectorAll(".chapter-row").forEach((n) => {
|
||||
n.classList.remove("drop-target", "drop-before", "drop-after");
|
||||
});
|
||||
let reordered = false;
|
||||
if (targetIndex >= 0) {
|
||||
const from = state.ordered.findIndex((c) => c.id === draggingId);
|
||||
const to = state.ordered.findIndex((c) => c.id === targetId);
|
||||
if (from >= 0 && to >= 0) {
|
||||
if (from >= 0 && targetIndex !== from) {
|
||||
const to = targetIndex;
|
||||
state.ordered = moveInList(state.ordered, from, to);
|
||||
state.meta!.chapterOrder = state.ordered.map((c) => c.id);
|
||||
animateNextRender = true;
|
||||
renderChapterList();
|
||||
reordered = true;
|
||||
// Selecting the dragged row must go through selectChapter: assigning
|
||||
// selectedId directly would pair the id with whatever document is
|
||||
// still in the editor, and the next autosave would write that
|
||||
|
|
@ -372,6 +430,10 @@ export function createChapterListFeature(ctx: BookCtx): ChapterListFeature {
|
|||
void persistOrder();
|
||||
}
|
||||
}
|
||||
// The source row was replaced by the gap while dragging. Restore the list
|
||||
// even when the pointer is released over its original slot or outside a
|
||||
// valid destination.
|
||||
if (!reordered) renderChapterList();
|
||||
draggingId = null;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
markdownToPlain,
|
||||
openEl,
|
||||
setActive,
|
||||
showView,
|
||||
showToast,
|
||||
} from "./ui.js";
|
||||
import {
|
||||
|
|
@ -127,7 +128,7 @@ export function createEditorFeature(ctx: BookCtx): EditorFeature {
|
|||
clearTimers();
|
||||
$("fullBookBtn").classList.remove("active");
|
||||
$("fullBookView").classList.add("hidden");
|
||||
$("chapterView").classList.remove("hidden");
|
||||
showView($("chapterView"));
|
||||
$("editorPlaceholder").classList.add("hidden");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,8 +131,10 @@ export function createExportsFeature(ctx: BookCtx): ExportsFeature {
|
|||
e.stopPropagation();
|
||||
const wasOpen = !exportMenu.classList.contains("hidden");
|
||||
closeExportMenu();
|
||||
if (!wasOpen) openEl(exportMenu);
|
||||
exportMenuToggle.classList.toggle("open", wasOpen);
|
||||
if (!wasOpen) {
|
||||
openEl(exportMenu);
|
||||
exportMenuToggle.classList.add("open");
|
||||
}
|
||||
});
|
||||
|
||||
exportMenu.addEventListener("click", (e) => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import type { BookMeta } from "../../main/project.js";
|
|||
import type { WikilinkTarget } from "../wikilinks.js";
|
||||
import type { BookCtx } from "./types.js";
|
||||
import { alertMessage } from "../dialog.js";
|
||||
import { $, countWords } from "./ui.js";
|
||||
import { $, countWords, showView } from "./ui.js";
|
||||
import {
|
||||
createTiptapEditor,
|
||||
setEditorDoc,
|
||||
|
|
@ -195,7 +195,7 @@ export function createFullBookFeature(ctx: BookCtx): FullBookFeature {
|
|||
$("fullBookBtn").classList.add("active");
|
||||
$("chapterView").classList.add("hidden");
|
||||
$("editorPlaceholder").classList.add("hidden");
|
||||
fullBookView.classList.remove("hidden");
|
||||
showView(fullBookView);
|
||||
await renderFullBook();
|
||||
fullBookEditor.commands.focus();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,16 @@ export function openEl(el: HTMLElement) {
|
|||
el.classList.remove("hidden", "closing");
|
||||
}
|
||||
|
||||
// Reveal a primary surface with a short continuity cue. The surface remains
|
||||
// usable before the animation starts, and the class is removed so later
|
||||
// updates do not replay it accidentally.
|
||||
export function showView(el: HTMLElement) {
|
||||
el.classList.remove("hidden", "view-enter");
|
||||
void el.offsetWidth;
|
||||
el.classList.add("view-enter");
|
||||
el.addEventListener("animationend", () => el.classList.remove("view-enter"), { once: true });
|
||||
}
|
||||
|
||||
export function closeEl(el: HTMLElement | null) {
|
||||
if (!el || el.classList.contains("hidden")) return;
|
||||
el.classList.add("closing");
|
||||
|
|
@ -35,7 +45,11 @@ export function closeEl(el: HTMLElement | null) {
|
|||
el.removeEventListener("animationend", onEnd);
|
||||
delete (el as unknown as { __close?: () => void }).__close;
|
||||
};
|
||||
const onEnd = () => finish();
|
||||
const onEnd = (event: AnimationEvent) => {
|
||||
// The modal animation bubbles through its backdrop; only the backdrop's
|
||||
// own exit should decide when the overlay is safe to hide.
|
||||
if (event.target === el) finish();
|
||||
};
|
||||
el.addEventListener("animationend", onEnd);
|
||||
const timer = setTimeout(finish, 420);
|
||||
(el as unknown as { __close?: () => void }).__close = () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import type { BookCtx } from "./types.js";
|
|||
import type { ChapterEntry, Recent } from "./types.js";
|
||||
import type { BookMeta } from "../../main/project.js";
|
||||
import { alertMessage, promptInput } from "../dialog.js";
|
||||
import { $, showUndoToast } from "./ui.js";
|
||||
import { $, showUndoToast, showView } from "./ui.js";
|
||||
|
||||
export interface WelcomeFeature {
|
||||
showWelcome(): Promise<void>;
|
||||
|
|
@ -37,7 +37,7 @@ export function createWelcomeFeature(ctx: BookCtx): WelcomeFeature {
|
|||
const homeBtn = $("homeBtn") as HTMLButtonElement | null;
|
||||
|
||||
async function showWelcome() {
|
||||
welcomeEl.classList.remove("hidden");
|
||||
showView(welcomeEl);
|
||||
chapterView.classList.add("hidden");
|
||||
fullBookView.classList.add("hidden");
|
||||
editorPlaceholder.classList.add("hidden");
|
||||
|
|
@ -69,7 +69,7 @@ export function createWelcomeFeature(ctx: BookCtx): WelcomeFeature {
|
|||
chapterView.classList.add("hidden");
|
||||
fullBookView.classList.add("hidden");
|
||||
fullBookBtn.classList.remove("active");
|
||||
editorPlaceholder.classList.remove("hidden");
|
||||
showView(editorPlaceholder);
|
||||
}
|
||||
|
||||
function setCurrentBook(data: { path: string; title: string }) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// In-app modal dialog used in place of window.prompt/confirm/alert,
|
||||
// which Electron does not support (prompt() throws).
|
||||
import { closeEl, openEl } from "./book/ui.js";
|
||||
|
||||
type Mode = "prompt" | "confirm" | "alert";
|
||||
|
||||
function el(id: string): HTMLElement {
|
||||
|
|
@ -29,7 +31,7 @@ function open(mode: Mode, title: string, body: string, value: string): Promise<s
|
|||
msgEl.classList.toggle("hidden", mode === "prompt");
|
||||
cancelBtn.classList.toggle("hidden", mode === "alert");
|
||||
okBtn.textContent = mode === "confirm" ? "Delete" : "OK";
|
||||
backdrop.classList.remove("hidden");
|
||||
openEl(backdrop);
|
||||
if (mode === "prompt") {
|
||||
inputEl.focus();
|
||||
inputEl.select();
|
||||
|
|
@ -42,7 +44,7 @@ function open(mode: Mode, title: string, body: string, value: string): Promise<s
|
|||
}
|
||||
|
||||
function close(value: string | null): void {
|
||||
backdrop.classList.add("hidden");
|
||||
closeEl(backdrop);
|
||||
if (resolver) {
|
||||
const r = resolver;
|
||||
resolver = null;
|
||||
|
|
|
|||
|
|
@ -395,7 +395,9 @@ html, body {
|
|||
:root[data-theme="dark-mode"] #addChapterBtn,
|
||||
:root[data-theme="neon"] #addChapterBtn,
|
||||
:root[data-theme="meridian"] #addChapterBtn,
|
||||
:root[data-theme="impeccable-dark"] #addChapterBtn {
|
||||
:root[data-theme="impeccable-dark"] #addChapterBtn,
|
||||
:root[data-theme="cosmic"] #addChapterBtn,
|
||||
:root[data-theme="cosmic-stardust"] #addChapterBtn {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +405,9 @@ html, body {
|
|||
:root[data-theme="dark-mode"] #homeBtn .home-label,
|
||||
:root[data-theme="neon"] #homeBtn .home-label,
|
||||
:root[data-theme="meridian"] #homeBtn .home-label,
|
||||
:root[data-theme="impeccable-dark"] #homeBtn .home-label {
|
||||
:root[data-theme="impeccable-dark"] #homeBtn .home-label,
|
||||
:root[data-theme="cosmic"] #homeBtn .home-label,
|
||||
:root[data-theme="cosmic-stardust"] #homeBtn .home-label {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +426,11 @@ html, body {
|
|||
:root[data-theme="meridian"] .brand-icon-light,
|
||||
:root[data-theme="meridian"] .welcome-brand-light,
|
||||
:root[data-theme="impeccable-dark"] .brand-icon-light,
|
||||
:root[data-theme="impeccable-dark"] .welcome-brand-light {
|
||||
:root[data-theme="impeccable-dark"] .welcome-brand-light,
|
||||
:root[data-theme="cosmic"] .brand-icon-light,
|
||||
:root[data-theme="cosmic"] .welcome-brand-light,
|
||||
:root[data-theme="cosmic-stardust"] .brand-icon-light,
|
||||
:root[data-theme="cosmic-stardust"] .welcome-brand-light {
|
||||
display: none;
|
||||
}
|
||||
:root[data-theme="dark-mode"] .brand-icon-dark,
|
||||
|
|
@ -432,7 +440,11 @@ html, body {
|
|||
:root[data-theme="meridian"] .brand-icon-dark,
|
||||
:root[data-theme="meridian"] .welcome-brand-dark,
|
||||
:root[data-theme="impeccable-dark"] .brand-icon-dark,
|
||||
:root[data-theme="impeccable-dark"] .welcome-brand-dark {
|
||||
:root[data-theme="impeccable-dark"] .welcome-brand-dark,
|
||||
:root[data-theme="cosmic"] .brand-icon-dark,
|
||||
:root[data-theme="cosmic"] .welcome-brand-dark,
|
||||
:root[data-theme="cosmic-stardust"] .brand-icon-dark,
|
||||
:root[data-theme="cosmic-stardust"] .welcome-brand-dark {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
|
@ -582,10 +594,50 @@ body.dragging-active * {
|
|||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.drag-ghost {
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
pointer-events: none;
|
||||
opacity: 1;
|
||||
background: var(--color-panel);
|
||||
border: 1px solid var(--color-accent);
|
||||
box-shadow: 0 10px 24px var(--color-shadow-strong);
|
||||
transform: rotate(1deg) scale(1.02);
|
||||
transition: transform 140ms var(--ease-out), box-shadow 140ms var(--ease-out);
|
||||
}
|
||||
|
||||
.chapter-drop-placeholder {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
border: 1px dashed var(--color-accent);
|
||||
border-radius: var(--radius);
|
||||
background: color-mix(in srgb, var(--color-accent) 10%, transparent);
|
||||
}
|
||||
|
||||
.chapter-row.drop-target {
|
||||
border-color: var(--color-accent);
|
||||
background: var(--color-accent);
|
||||
color: var(--color-on-accent);
|
||||
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
|
||||
}
|
||||
|
||||
/* Keep the destination edge visible while dragging. The FLIP pass carries the
|
||||
rows into their new positions after release. */
|
||||
.chapter-row.drop-before {
|
||||
background-image: linear-gradient(var(--color-accent), var(--color-accent));
|
||||
background-size: 100% 3px;
|
||||
background-position: top;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.chapter-row.drop-after {
|
||||
background-image: linear-gradient(var(--color-accent), var(--color-accent));
|
||||
background-size: 100% 3px;
|
||||
background-position: bottom;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.chapter-row.reordering {
|
||||
transition: transform 360ms var(--ease-out), background var(--motion-fast) var(--ease-out),
|
||||
color var(--motion-fast) var(--ease-out), border-color var(--motion-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.chapter-row:focus-visible {
|
||||
|
|
@ -1771,6 +1823,10 @@ body.focus-mode .editor {
|
|||
from { opacity: 0; transform: scale(0.985); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
@keyframes viewIn {
|
||||
from { opacity: 0.72; transform: translateY(5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Dialogs / modals */
|
||||
.modal-backdrop:not(.hidden) .modal {
|
||||
|
|
@ -1803,6 +1859,12 @@ body.focus-mode .editor {
|
|||
animation: rootIn var(--motion-window) var(--ease-out) both;
|
||||
}
|
||||
|
||||
/* Primary navigation changes keep the manuscript's spatial continuity without
|
||||
delaying input or hiding the destination before it is ready. */
|
||||
.view-enter {
|
||||
animation: viewIn var(--motion-base) var(--ease-out) both;
|
||||
}
|
||||
|
||||
/* List item entrances */
|
||||
.recent-list .recent-item {
|
||||
animation: listIn var(--motion-base) var(--ease-out) both;
|
||||
|
|
@ -1865,7 +1927,7 @@ body:not(.dragging-active) .chapter-row {
|
|||
scroll-behavior: auto !important;
|
||||
}
|
||||
.modal, .modal-backdrop, .dropdown-menu, .color-popover,
|
||||
.recent-item, .chapter-row, #root {
|
||||
.recent-item, .chapter-row, .view-enter, #root {
|
||||
transform: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@
|
|||
<option value="meridian">Meridian</option>
|
||||
<option value="impeccable">Impeccable Light</option>
|
||||
<option value="impeccable-dark">Impeccable Dark</option>
|
||||
<option value="cosmic">Cosmic</option>
|
||||
<option value="cosmic-light">Cosmic Light</option>
|
||||
<option value="cosmic-stardust">Cosmic Stardust</option>
|
||||
<option value="cosmic-stardust-light">Cosmic Stardust Light</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const LIGHT = "light";
|
|||
// Themes with a real entry in themes.css + the selector. Anything else that
|
||||
// might linger in saved settings (a removed theme, a typo) falls back to
|
||||
// Light so the <select> never desyncs from its <option> list.
|
||||
const KNOWN = new Set(["light", "dark-mode", "neon", "meridian", "impeccable", "impeccable-dark"]);
|
||||
const KNOWN = new Set(["light", "dark-mode", "neon", "meridian", "impeccable", "impeccable-dark", "cosmic", "cosmic-light", "cosmic-stardust", "cosmic-stardust-light"]);
|
||||
|
||||
function normalizeTheme(id: unknown): string {
|
||||
if (typeof id !== "string" || !id || id === "default") return LIGHT;
|
||||
|
|
|
|||
|
|
@ -380,3 +380,513 @@ html[data-theme="impeccable-dark"] .chapter-list li.is-active {
|
|||
color: var(--color-accent);
|
||||
border-left-color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* ---- Cosmic (Original/Dark) ----
|
||||
Warm, atmospheric dark theme inspired by painted night skies.
|
||||
Deep purple-brown surfaces with rose/gold accents and colorful headings. */
|
||||
:root[data-theme="cosmic"] {
|
||||
--color-bg: #0a0d17;
|
||||
--color-panel: #060910;
|
||||
--color-border: #2a2040;
|
||||
--color-text: #f4ebec;
|
||||
--color-muted: #b8a5cc;
|
||||
--color-accent: #b4797f;
|
||||
--color-accent-hover: #c57e89;
|
||||
|
||||
--color-on-accent: #020412;
|
||||
--color-on-accent-soft: rgba(2, 4, 18, 0.18);
|
||||
--color-on-accent-border: rgba(2, 4, 18, 0.38);
|
||||
|
||||
--color-selection: rgba(180, 121, 127, 0.28);
|
||||
--color-selection-strong: rgba(180, 121, 127, 0.55);
|
||||
|
||||
--color-shadow: rgba(0, 0, 0, 0.55);
|
||||
--color-shadow-strong: rgba(0, 0, 0, 0.7);
|
||||
--color-backdrop: rgba(2, 4, 18, 0.6);
|
||||
|
||||
--color-scrollbar: #2a1838;
|
||||
--color-scrollbar-hover: #45285e;
|
||||
|
||||
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-serif: "Tirra", "Source Serif 4", Georgia, serif;
|
||||
--font-display: "Yeseva One", "Cormorant Garamond", Georgia, serif;
|
||||
|
||||
--color-h1: #dda2a1;
|
||||
--color-h2: #97c7dc;
|
||||
--color-h3: #d8c2e3;
|
||||
--color-h4: #d8c2e3;
|
||||
--color-h5: #97c7dc;
|
||||
--color-h6: #f2def2;
|
||||
}
|
||||
|
||||
html[data-theme="cosmic"] h1,
|
||||
html[data-theme="cosmic"] h2,
|
||||
html[data-theme="cosmic"] h3 {
|
||||
font-family: var(--font-display);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
}
|
||||
html[data-theme="cosmic"] h1 { color: var(--color-h1); }
|
||||
html[data-theme="cosmic"] h2 { color: var(--color-h2); }
|
||||
html[data-theme="cosmic"] h3 { color: var(--color-h3); }
|
||||
html[data-theme="cosmic"] h4 { color: var(--color-h4); }
|
||||
html[data-theme="cosmic"] h5 { color: var(--color-h5); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.92em; }
|
||||
html[data-theme="cosmic"] h6 { color: var(--color-h6); text-transform: uppercase; letter-spacing: 0.12em; }
|
||||
|
||||
html[data-theme="cosmic"] .sidebar {
|
||||
border-right-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic"] .chapter-list li.is-active {
|
||||
background: linear-gradient(90deg, color-mix(in srgb, var(--color-accent) 16%, transparent), transparent);
|
||||
color: var(--color-text);
|
||||
box-shadow: inset 3px 0 0 var(--color-accent);
|
||||
}
|
||||
html[data-theme="cosmic"] .button.button-primary {
|
||||
background: linear-gradient(135deg, var(--color-accent), color-mix(in srgb, var(--color-accent) 74%, var(--color-h2)));
|
||||
color: var(--color-on-accent);
|
||||
border-color: transparent;
|
||||
box-shadow: 0 8px 22px color-mix(in srgb, var(--color-accent) 22%, transparent);
|
||||
}
|
||||
html[data-theme="cosmic"] .button.button-primary:hover:not(:disabled) {
|
||||
filter: brightness(1.08) saturate(1.08);
|
||||
}
|
||||
html[data-theme="cosmic"] .button.button-ghost {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic"] input[type="text"],
|
||||
html[data-theme="cosmic"] input[type="search"],
|
||||
html[data-theme="cosmic"] select,
|
||||
html[data-theme="cosmic"] textarea,
|
||||
html[data-theme="cosmic"] .modal-input {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic"] code,
|
||||
html[data-theme="cosmic"] .folio-prose code {
|
||||
background: color-mix(in srgb, var(--color-accent) 32%, transparent);
|
||||
border-color: color-mix(in srgb, var(--color-accent) 52%, var(--color-h4));
|
||||
color: var(--color-h1) !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ---- Cosmic Light ----
|
||||
Light variant of the Cosmic theme. Warm cream surfaces with deep rose accents. */
|
||||
:root[data-theme="cosmic-light"] {
|
||||
--color-bg: #f0e8f4;
|
||||
--color-panel: #f4ebec;
|
||||
--color-border: #d8cce8;
|
||||
--color-text: #201b43;
|
||||
--color-muted: #3a2e5f;
|
||||
--color-accent: #8b526c;
|
||||
--color-accent-hover: #7e4265;
|
||||
|
||||
--color-on-accent: #f4ebec;
|
||||
--color-on-accent-soft: rgba(244, 235, 236, 0.18);
|
||||
--color-on-accent-border: rgba(244, 235, 236, 0.38);
|
||||
|
||||
--color-selection: rgba(139, 82, 108, 0.25);
|
||||
--color-selection-strong: rgba(139, 82, 108, 0.45);
|
||||
|
||||
--color-shadow: rgba(32, 27, 67, 0.15);
|
||||
--color-shadow-strong: rgba(32, 27, 67, 0.25);
|
||||
--color-backdrop: rgba(32, 27, 67, 0.4);
|
||||
|
||||
--color-scrollbar: #d8cce8;
|
||||
--color-scrollbar-hover: #c8b8d8;
|
||||
|
||||
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-serif: "Tirra", "Source Serif 4", Georgia, serif;
|
||||
--font-display: "Yeseva One", "Cormorant Garamond", Georgia, serif;
|
||||
|
||||
--color-h1: #8b526c;
|
||||
--color-h2: #30506f;
|
||||
--color-h3: #593670;
|
||||
--color-h4: #593670;
|
||||
--color-h5: #30506f;
|
||||
--color-h6: #412e5f;
|
||||
}
|
||||
|
||||
html[data-theme="cosmic-light"] h1,
|
||||
html[data-theme="cosmic-light"] h2,
|
||||
html[data-theme="cosmic-light"] h3 {
|
||||
font-family: var(--font-display);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
}
|
||||
html[data-theme="cosmic-light"] h1 { color: var(--color-h1); }
|
||||
html[data-theme="cosmic-light"] h2 { color: var(--color-h2); }
|
||||
html[data-theme="cosmic-light"] h3 { color: var(--color-h3); }
|
||||
html[data-theme="cosmic-light"] h4 { color: var(--color-h4); }
|
||||
html[data-theme="cosmic-light"] h5 { color: var(--color-h5); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.92em; }
|
||||
html[data-theme="cosmic-light"] h6 { color: var(--color-h6); text-transform: uppercase; letter-spacing: 0.12em; }
|
||||
|
||||
html[data-theme="cosmic-light"] .sidebar {
|
||||
border-right-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-light"] .chapter-list li.is-active {
|
||||
background: linear-gradient(90deg, color-mix(in srgb, var(--color-accent) 16%, transparent), transparent);
|
||||
color: var(--color-text);
|
||||
box-shadow: inset 3px 0 0 var(--color-accent);
|
||||
}
|
||||
html[data-theme="cosmic-light"] .button.button-primary {
|
||||
background: linear-gradient(135deg, var(--color-accent), color-mix(in srgb, var(--color-accent) 74%, var(--color-h2)));
|
||||
color: var(--color-on-accent);
|
||||
border-color: transparent;
|
||||
box-shadow: 0 8px 22px color-mix(in srgb, var(--color-accent) 22%, transparent);
|
||||
}
|
||||
html[data-theme="cosmic-light"] .button.button-primary:hover:not(:disabled) {
|
||||
filter: brightness(1.08) saturate(1.08);
|
||||
}
|
||||
html[data-theme="cosmic-light"] .button.button-ghost {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-light"] input[type="text"],
|
||||
html[data-theme="cosmic-light"] input[type="search"],
|
||||
html[data-theme="cosmic-light"] select,
|
||||
html[data-theme="cosmic-light"] textarea,
|
||||
html[data-theme="cosmic-light"] .modal-input {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-light"] code,
|
||||
html[data-theme="cosmic-light"] .folio-prose code {
|
||||
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-h1) !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ---- Cosmic Stardust (Dark) ----
|
||||
Warm amber/gold dark theme. Deep brown-black surfaces with gold accents,
|
||||
teal headings, and coral highlights. Inspired by stardust and cosmic dust clouds. */
|
||||
:root[data-theme="cosmic-stardust"] {
|
||||
--color-bg: #171109;
|
||||
--color-panel: #0d0a06;
|
||||
--color-border: #2a2210;
|
||||
--color-text: #fff5dc;
|
||||
--color-muted: #d5bd8d;
|
||||
--color-accent: #efbd62;
|
||||
--color-accent-hover: #ffdb91;
|
||||
|
||||
--color-on-accent: #0d0a06;
|
||||
--color-on-accent-soft: rgba(13, 10, 6, 0.18);
|
||||
--color-on-accent-border: rgba(13, 10, 6, 0.38);
|
||||
|
||||
--color-selection: rgba(239, 189, 98, 0.3);
|
||||
--color-selection-strong: rgba(239, 189, 98, 0.55);
|
||||
|
||||
--color-shadow: rgba(0, 0, 0, 0.6);
|
||||
--color-shadow-strong: rgba(0, 0, 0, 0.75);
|
||||
--color-backdrop: rgba(13, 10, 6, 0.65);
|
||||
|
||||
--color-scrollbar: #2a2210;
|
||||
--color-scrollbar-hover: #403518;
|
||||
|
||||
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-serif: "Tirra", "Source Serif 4", Georgia, serif;
|
||||
--font-display: "Yeseva One", "Cormorant Garamond", Georgia, serif;
|
||||
|
||||
--color-h1: #efbd62;
|
||||
--color-h2: #74d7cb;
|
||||
--color-h3: #f08e7d;
|
||||
--color-h4: #d89ce7;
|
||||
--color-h5: #79c7ef;
|
||||
--color-h6: #f5ddae;
|
||||
}
|
||||
|
||||
html[data-theme="cosmic-stardust"] h1,
|
||||
html[data-theme="cosmic-stardust"] h2,
|
||||
html[data-theme="cosmic-stardust"] h3 {
|
||||
font-family: var(--font-display);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] h1 { color: var(--color-h1); }
|
||||
html[data-theme="cosmic-stardust"] h2 { color: var(--color-h2); }
|
||||
html[data-theme="cosmic-stardust"] h3 { color: var(--color-h3); }
|
||||
html[data-theme="cosmic-stardust"] h4 { color: var(--color-h4); }
|
||||
html[data-theme="cosmic-stardust"] h5 { color: var(--color-h5); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.92em; }
|
||||
html[data-theme="cosmic-stardust"] h6 { color: var(--color-h6); text-transform: uppercase; letter-spacing: 0.12em; }
|
||||
|
||||
html[data-theme="cosmic-stardust"] .sidebar {
|
||||
border-right-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] .chapter-list li.is-active {
|
||||
background: linear-gradient(90deg, color-mix(in srgb, var(--color-accent) 16%, transparent), transparent);
|
||||
color: var(--color-text);
|
||||
box-shadow: inset 3px 0 0 var(--color-accent);
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] .button.button-primary {
|
||||
background: linear-gradient(135deg, var(--color-accent), color-mix(in srgb, var(--color-accent) 74%, var(--color-h2)));
|
||||
color: var(--color-on-accent);
|
||||
border-color: transparent;
|
||||
box-shadow: 0 8px 22px color-mix(in srgb, var(--color-accent) 22%, transparent);
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] .button.button-primary:hover:not(:disabled) {
|
||||
filter: brightness(1.08) saturate(1.08);
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] .button.button-ghost {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] input[type="text"],
|
||||
html[data-theme="cosmic-stardust"] input[type="search"],
|
||||
html[data-theme="cosmic-stardust"] select,
|
||||
html[data-theme="cosmic-stardust"] textarea,
|
||||
html[data-theme="cosmic-stardust"] .modal-input {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-stardust"] code,
|
||||
html[data-theme="cosmic-stardust"] .folio-prose code {
|
||||
background: color-mix(in srgb, var(--color-accent) 32%, transparent);
|
||||
border-color: color-mix(in srgb, var(--color-accent) 52%, var(--color-h4));
|
||||
color: var(--color-h1) !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ---- Cosmic Stardust Light ----
|
||||
Light variant of Stardust. Warm cream/parchment surfaces with deep gold accents,
|
||||
teal headings, and terracotta highlights. */
|
||||
:root[data-theme="cosmic-stardust-light"] {
|
||||
--color-bg: #fff8e8;
|
||||
--color-panel: #f5ead2;
|
||||
--color-border: #e8d8b8;
|
||||
--color-text: #44341e;
|
||||
--color-muted: #786344;
|
||||
--color-accent: #8a5b15;
|
||||
--color-accent-hover: #633d0b;
|
||||
|
||||
--color-on-accent: #fff8e8;
|
||||
--color-on-accent-soft: rgba(255, 248, 232, 0.18);
|
||||
--color-on-accent-border: rgba(255, 248, 232, 0.38);
|
||||
|
||||
--color-selection: rgba(138, 91, 21, 0.25);
|
||||
--color-selection-strong: rgba(138, 91, 21, 0.45);
|
||||
|
||||
--color-shadow: rgba(68, 52, 30, 0.15);
|
||||
--color-shadow-strong: rgba(68, 52, 30, 0.25);
|
||||
--color-backdrop: rgba(68, 52, 30, 0.4);
|
||||
|
||||
--color-scrollbar: #d8c8a8;
|
||||
--color-scrollbar-hover: #c8b898;
|
||||
|
||||
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-serif: "Tirra", "Source Serif 4", Georgia, serif;
|
||||
--font-display: "Yeseva One", "Cormorant Garamond", Georgia, serif;
|
||||
|
||||
--color-h1: #8a5b15;
|
||||
--color-h2: #24756f;
|
||||
--color-h3: #a54c3f;
|
||||
--color-h4: #754c7e;
|
||||
--color-h5: #2e668b;
|
||||
--color-h6: #765525;
|
||||
}
|
||||
|
||||
html[data-theme="cosmic-stardust-light"] h1,
|
||||
html[data-theme="cosmic-stardust-light"] h2,
|
||||
html[data-theme="cosmic-stardust-light"] h3 {
|
||||
font-family: var(--font-display);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] h1 { color: var(--color-h1); }
|
||||
html[data-theme="cosmic-stardust-light"] h2 { color: var(--color-h2); }
|
||||
html[data-theme="cosmic-stardust-light"] h3 { color: var(--color-h3); }
|
||||
html[data-theme="cosmic-stardust-light"] h4 { color: var(--color-h4); }
|
||||
html[data-theme="cosmic-stardust-light"] h5 { color: var(--color-h5); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.92em; }
|
||||
html[data-theme="cosmic-stardust-light"] h6 { color: var(--color-h6); text-transform: uppercase; letter-spacing: 0.12em; }
|
||||
|
||||
html[data-theme="cosmic-stardust-light"] .sidebar {
|
||||
border-right-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] .chapter-list li.is-active {
|
||||
background: linear-gradient(90deg, color-mix(in srgb, var(--color-accent) 16%, transparent), transparent);
|
||||
color: var(--color-text);
|
||||
box-shadow: inset 3px 0 0 var(--color-accent);
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] .button.button-primary {
|
||||
background: linear-gradient(135deg, var(--color-accent), color-mix(in srgb, var(--color-accent) 74%, var(--color-h2)));
|
||||
color: var(--color-on-accent);
|
||||
border-color: transparent;
|
||||
box-shadow: 0 8px 22px color-mix(in srgb, var(--color-accent) 22%, transparent);
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] .button.button-primary:hover:not(:disabled) {
|
||||
filter: brightness(1.08) saturate(1.08);
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] .button.button-ghost {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] input[type="text"],
|
||||
html[data-theme="cosmic-stardust-light"] input[type="search"],
|
||||
html[data-theme="cosmic-stardust-light"] select,
|
||||
html[data-theme="cosmic-stardust-light"] textarea,
|
||||
html[data-theme="cosmic-stardust-light"] .modal-input {
|
||||
background: var(--color-panel);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
html[data-theme="cosmic-stardust-light"] code,
|
||||
html[data-theme="cosmic-stardust-light"] .folio-prose code {
|
||||
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-h1) !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ---- Cosmic Glass Effects ----
|
||||
Meridian-style frosted glass for all Cosmic themes. Each theme gets
|
||||
its own glass textures (grain + sheen) tuned to its palette. */
|
||||
|
||||
:root[data-theme="cosmic"],
|
||||
:root[data-theme="cosmic-light"],
|
||||
:root[data-theme="cosmic-stardust"],
|
||||
:root[data-theme="cosmic-stardust-light"] {
|
||||
--glass-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.07 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
:root[data-theme="cosmic"] {
|
||||
--glass-sheen:
|
||||
radial-gradient(900px 620px at 82% -18%, rgba(212, 158, 154, 0.18), transparent 62%),
|
||||
radial-gradient(820px 640px at -28% 118%, rgba(151, 199, 220, 0.16), transparent 60%),
|
||||
radial-gradient(720px 520px at 50% 45%, rgba(216, 194, 227, 0.12), transparent 65%);
|
||||
}
|
||||
:root[data-theme="cosmic-light"] {
|
||||
--glass-sheen:
|
||||
radial-gradient(900px 620px at 82% -18%, rgba(139, 82, 108, 0.14), transparent 62%),
|
||||
radial-gradient(820px 640px at -28% 118%, rgba(48, 80, 111, 0.12), transparent 60%),
|
||||
radial-gradient(720px 520px at 50% 45%, rgba(89, 54, 112, 0.1), transparent 65%);
|
||||
}
|
||||
:root[data-theme="cosmic-stardust"] {
|
||||
--glass-sheen:
|
||||
radial-gradient(900px 620px at 82% -18%, rgba(239, 189, 98, 0.18), transparent 62%),
|
||||
radial-gradient(820px 640px at -28% 118%, rgba(116, 215, 203, 0.16), transparent 60%),
|
||||
radial-gradient(720px 520px at 50% 45%, rgba(240, 142, 125, 0.12), transparent 65%);
|
||||
}
|
||||
:root[data-theme="cosmic-stardust-light"] {
|
||||
--glass-sheen:
|
||||
radial-gradient(900px 620px at 82% -18%, rgba(138, 91, 21, 0.14), transparent 62%),
|
||||
radial-gradient(820px 640px at -28% 118%, rgba(36, 117, 111, 0.12), transparent 60%),
|
||||
radial-gradient(720px 520px at 50% 45%, rgba(165, 76, 63, 0.1), transparent 65%);
|
||||
}
|
||||
|
||||
/* Ambient background layer for dark cosmic themes */
|
||||
:root[data-theme="cosmic"] body {
|
||||
background:
|
||||
var(--glass-grain),
|
||||
radial-gradient(1200px 800px at 85% -10%, rgba(212, 158, 154, 0.22), transparent 60%),
|
||||
radial-gradient(1000px 750px at -10% 110%, rgba(151, 199, 220, 0.18), transparent 58%),
|
||||
radial-gradient(720px 520px at 50% 45%, rgba(216, 194, 227, 0.1), transparent 65%),
|
||||
linear-gradient(170deg, #060910, #0a0d17 55%, #080b12) fixed;
|
||||
}
|
||||
:root[data-theme="cosmic-stardust"] body {
|
||||
background:
|
||||
var(--glass-grain),
|
||||
radial-gradient(1200px 800px at 85% -10%, rgba(239, 189, 98, 0.22), transparent 60%),
|
||||
radial-gradient(1000px 750px at -10% 110%, rgba(116, 215, 203, 0.18), transparent 58%),
|
||||
radial-gradient(720px 520px at 50% 45%, rgba(240, 142, 125, 0.1), transparent 65%),
|
||||
linear-gradient(170deg, #0d0a06, #171109 55%, #120d08) fixed;
|
||||
}
|
||||
|
||||
/* Chrome frosting — sidebar, modals, popovers */
|
||||
:root[data-theme="cosmic"] .sidebar,
|
||||
:root[data-theme="cosmic-stardust"] .sidebar {
|
||||
background-image: var(--glass-grain), var(--glass-sheen);
|
||||
-webkit-backdrop-filter: blur(12px) saturate(115%);
|
||||
backdrop-filter: blur(12px) saturate(115%);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
:root[data-theme="cosmic"] .modal,
|
||||
:root[data-theme="cosmic"] .color-popover,
|
||||
:root[data-theme="cosmic-stardust"] .modal,
|
||||
:root[data-theme="cosmic-stardust"] .color-popover {
|
||||
background-image: var(--glass-grain), var(--glass-sheen);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(115%);
|
||||
backdrop-filter: blur(16px) saturate(115%);
|
||||
}
|
||||
:root[data-theme="cosmic"] .modal,
|
||||
:root[data-theme="cosmic"] .color-popover,
|
||||
:root[data-theme="cosmic-stardust"] .modal,
|
||||
:root[data-theme="cosmic-stardust"] .color-popover {
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.09),
|
||||
0 8px 16px var(--color-shadow-strong);
|
||||
}
|
||||
|
||||
/* Light cosmic themes — lighter frosting */
|
||||
:root[data-theme="cosmic-light"] .sidebar,
|
||||
:root[data-theme="cosmic-stardust-light"] .sidebar {
|
||||
background-image: var(--glass-grain), var(--glass-sheen);
|
||||
-webkit-backdrop-filter: blur(10px) saturate(110%);
|
||||
backdrop-filter: blur(10px) saturate(110%);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
:root[data-theme="cosmic-light"] .modal,
|
||||
:root[data-theme="cosmic-light"] .color-popover,
|
||||
:root[data-theme="cosmic-stardust-light"] .modal,
|
||||
:root[data-theme="cosmic-stardust-light"] .color-popover {
|
||||
background-image: var(--glass-grain), var(--glass-sheen);
|
||||
-webkit-backdrop-filter: blur(14px) saturate(110%);
|
||||
backdrop-filter: blur(14px) saturate(110%);
|
||||
}
|
||||
:root[data-theme="cosmic-light"] .modal,
|
||||
:root[data-theme="cosmic-light"] .color-popover,
|
||||
:root[data-theme="cosmic-stardust-light"] .modal,
|
||||
:root[data-theme="cosmic-stardust-light"] .color-popover {
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.18),
|
||||
0 8px 16px var(--color-shadow-strong);
|
||||
}
|
||||
|
||||
/* Editor/manuscript pane joins the glass */
|
||||
:root[data-theme="cosmic"] .editor,
|
||||
:root[data-theme="cosmic-stardust"] .editor {
|
||||
background-image: var(--glass-grain), var(--glass-sheen);
|
||||
}
|
||||
:root[data-theme="cosmic"] .folio-prose,
|
||||
:root[data-theme="cosmic-stardust"] .folio-prose {
|
||||
background-color: transparent;
|
||||
}
|
||||
:root[data-theme="cosmic"] .editor-panes .folio-prose.ProseMirror,
|
||||
:root[data-theme="cosmic-stardust"] .editor-panes .folio-prose.ProseMirror {
|
||||
background: transparent !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Light themes — editor with subtle glass */
|
||||
:root[data-theme="cosmic-light"] .editor,
|
||||
:root[data-theme="cosmic-stardust-light"] .editor {
|
||||
background-image: var(--glass-grain), var(--glass-sheen);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
:root[data-theme="cosmic-light"] .folio-prose,
|
||||
:root[data-theme="cosmic-stardust-light"] .folio-prose {
|
||||
background-color: transparent;
|
||||
}
|
||||
:root[data-theme="cosmic-light"] .editor-panes .folio-prose.ProseMirror,
|
||||
:root[data-theme="cosmic-stardust-light"] .editor-panes .folio-prose.ProseMirror {
|
||||
background: transparent !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Fallback when backdrop-filter unsupported */
|
||||
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
|
||||
:root[data-theme="cosmic"] {
|
||||
--color-bg: rgba(6, 9, 16, 0.97);
|
||||
--color-panel: rgba(10, 13, 23, 0.94);
|
||||
}
|
||||
:root[data-theme="cosmic-stardust"] {
|
||||
--color-bg: rgba(13, 10, 6, 0.97);
|
||||
--color-panel: rgba(20, 16, 10, 0.94);
|
||||
}
|
||||
:root[data-theme="cosmic-light"] {
|
||||
--color-bg: rgba(240, 232, 244, 0.97);
|
||||
--color-panel: rgba(244, 235, 236, 0.94);
|
||||
}
|
||||
:root[data-theme="cosmic-stardust-light"] {
|
||||
--color-bg: rgba(255, 248, 232, 0.97);
|
||||
--color-panel: rgba(245, 234, 210, 0.94);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue