This commit is contained in:
John McLear 2026-07-14 16:06:03 +00:00 committed by GitHub
commit df4a338675
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 2717 additions and 0 deletions

View file

@ -0,0 +1,61 @@
# margin — Etherpad skin
A standalone drop-in skin with six themes and an orthogonal Light/Dark toggle:
| Theme | Light | Dark | Natural mode |
| --- | --- | --- | --- |
| `colibris` | ✓ | — | light (no dark palette) |
| `editorial` | ✓ | ✓ | light |
| `brutalist` | ✓ | ✓ | light |
| `paper` | ✓ | ✓ | light |
| `crt` | ✓ | ✓ | dark |
| `industrial` | ✓ | ✓ | dark |
The current `data-theme` and `data-mode` attributes live on `<html>`. Mode is paired with theme in CSS via `[data-theme="X"][data-mode="light|dark"]`.
No external dependency on colibris — all component partials are vendored under `src/`.
## Install
1. Copy this `margin/` folder into `src/static/skins/`.
2. In `settings.json`, set:
```json
"skinName": "margin"
```
No template edits are required. The skin applies the user's saved theme + mode on load (defaulting to `colibris` + the theme's natural mode), the Google Fonts stylesheet is `@import`-ed from `pad.css` / `index.css`, and a **Theme** dropdown plus a **Dark mode** checkbox are injected into both the User Settings and Pad-wide Settings columns of the Settings popup.
## Switch themes at runtime
The Settings popup (gear icon in the toolbar) has:
- a **Theme** dropdown with the six themes,
- a **Dark mode** checkbox (orthogonal — flips light↔dark for any theme that has a dark palette).
Choices persist in `localStorage` under `marginTheme` + `marginMode` and propagate across the pad and the lobby.
Programmatically, from DevTools:
```js
document.documentElement.dataset.theme = 'crt';
document.documentElement.dataset.mode = 'dark';
```
## Folder layout
```
margin/
├─ index.css lobby / pad-list themes
├─ index.js lobby JS (early theme bootstrap)
├─ pad.css pad themes + component imports
├─ pad.js pad JS hooks (theme bootstrap, Settings dropdown,
│ iframe theme propagation)
├─ timeslider.css version timeline
├─ timeslider.js timeslider JS
├─ src/
│ ├─ general.css, layout.css, pad-editor.css, pad-variants.css
│ ├─ components/ toolbar, chat, popups, users, gritter, scrollbars, …
│ └─ plugins/ comments, color picker, tables, …
└─ README.md
```
The `src/` partials are vendored from upstream colibris so this skin is fully self-contained — themes layer on top via `data-theme="…"` overrides in `pad.css` and `index.css`, and inherit the same CSS-variable contract (`--primary-color`, `--bg-color`, `--main-font-family`, `--editor-horizontal-padding`, …) that colibris exposes.

View file

@ -0,0 +1,43 @@
/* margin / index.css lobby + pad-list page
* Styles the home / pad-list using the same data-theme system as pad.css.
* Drop alongside pad.css in src/static/skins/margin/. */
@import url("//fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300..700;1,6..72,300..700&family=Instrument+Serif:ital@0;1&family=Lora:ital,wght@0,400..700;1,400..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&family=VT323&family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;0,700&display=swap");
/* Theme definitions are mirrored from pad.css so the lobby themes too.
* Both light and dark variants of each named theme are defined so the
* Settings popup's Theme dropdown can apply consistently across pages. */
[data-theme="colibris"] { --m-bg:#f2f3f4; --m-fg:#485365; --m-soft:#576273; --m-accent:#64d29b; --m-panel:#fff; --m-rule:#dadada; --m-radius:3px; --m-shadow:0 2px 8px rgba(68,68,68,.08); --m-font:Quicksand, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; --m-ui-style:normal; --m-ui-case:none; }
[data-theme="editorial"][data-mode="light"] { --m-bg:#f5f0e8; --m-fg:#1c1916; --m-soft:#5a534a; --m-accent:#a8442b; --m-panel:#fbf8f2; --m-rule:rgba(28,25,22,.12); --m-radius:2px; --m-shadow:0 12px 36px -18px rgba(28,25,22,.20); --m-font:"Newsreader",Georgia,serif; --m-ui-style:italic; --m-ui-case:none; }
[data-theme="editorial"][data-mode="dark"] { color-scheme:dark; --m-bg:#16130f; --m-fg:#f0eadd; --m-soft:#a8a098; --m-accent:#d27047; --m-panel:#1c1916; --m-rule:rgba(240,234,221,.12); --m-radius:2px; --m-shadow:0 12px 36px -18px rgba(0,0,0,.6); --m-font:"Newsreader",Georgia,serif; --m-ui-style:italic; --m-ui-case:none; }
[data-theme="brutalist"][data-mode="light"] { --m-bg:#f3f3f0; --m-fg:#000; --m-soft:#222; --m-accent:#ff3b00; --m-panel:#fff; --m-rule:#000; --m-radius:0; --m-shadow:4px 4px 0 #000; --m-font:"Space Mono",ui-monospace,monospace; --m-ui-style:normal; --m-ui-case:uppercase; }
[data-theme="brutalist"][data-mode="dark"] { color-scheme:dark; --m-bg:#0c0c09; --m-fg:#fff; --m-soft:#ddd; --m-accent:#ff3b00; --m-panel:#000; --m-rule:#fff; --m-radius:0; --m-shadow:4px 4px 0 #fff; --m-font:"Space Mono",ui-monospace,monospace; --m-ui-style:normal; --m-ui-case:uppercase; }
[data-theme="paper"][data-mode="light"] { --m-bg:#f6f1e8; --m-fg:#2a2520; --m-soft:#6b6259; --m-accent:#b87333; --m-panel:#fbf6ec; --m-rule:rgba(42,37,32,.08); --m-radius:12px; --m-shadow:0 14px 40px -16px rgba(42,37,32,.18); --m-font:"Lora",Georgia,serif; --m-ui-style:normal; --m-ui-case:none; }
[data-theme="paper"][data-mode="dark"] { color-scheme:dark; --m-bg:#231e19; --m-fg:#efe7d4; --m-soft:#a89e8d; --m-accent:#d99560; --m-panel:#2a2520; --m-rule:rgba(239,231,212,.10); --m-radius:12px; --m-shadow:0 14px 40px -16px rgba(0,0,0,.6); --m-font:"Lora",Georgia,serif; --m-ui-style:normal; --m-ui-case:none; }
[data-theme="crt"][data-mode="light"] { --m-bg:#edf6ee; --m-fg:#04200d; --m-soft:#1c4a2b; --m-accent:#006b3f; --m-panel:#e9f5ed; --m-rule:rgba(0,107,63,.20); --m-radius:0; --m-shadow:0 0 0 1px rgba(0,107,63,.25); --m-font:"IBM Plex Mono",ui-monospace,monospace; --m-ui-style:normal; --m-ui-case:uppercase; }
[data-theme="crt"][data-mode="dark"] { color-scheme:dark; --m-bg:#04140a; --m-fg:#7fffae; --m-soft:#4ed188; --m-accent:#ffb84d; --m-panel:#08200f; --m-rule:rgba(127,255,174,.20); --m-radius:0; --m-shadow:0 0 24px rgba(127,255,174,.10); --m-font:"IBM Plex Mono",ui-monospace,monospace; --m-ui-style:normal; --m-ui-case:uppercase; }
[data-theme="industrial"][data-mode="light"] { --m-bg:#f0f1f4; --m-fg:#14171c; --m-soft:#525965; --m-accent:#cc9900; --m-panel:#f5f6f8; --m-rule:rgba(20,23,28,.08); --m-radius:3px; --m-shadow:0 12px 32px rgba(20,23,28,.10); --m-font:"IBM Plex Sans",ui-sans-serif,system-ui,sans-serif; --m-ui-style:normal; --m-ui-case:uppercase; }
[data-theme="industrial"][data-mode="dark"] { color-scheme:dark; --m-bg:#0d0f12; --m-fg:#e6e8eb; --m-soft:#9aa0a8; --m-accent:#ffcc00; --m-panel:#14171c; --m-rule:rgba(255,255,255,.08); --m-radius:3px; --m-shadow:0 12px 32px rgba(0,0,0,.4); --m-font:"IBM Plex Sans",ui-sans-serif,system-ui,sans-serif; --m-ui-style:normal; --m-ui-case:uppercase; }
[data-theme] body, [data-theme] .body { background: var(--m-bg) !important; color: var(--m-fg); font-family: var(--m-font); }
[data-theme] body nav { border-bottom: 1px solid var(--m-rule); }
[data-theme] .logo-box { background: var(--m-accent); border-radius: var(--m-radius); }
[data-theme] #wrapper, [data-theme] .pad-datalist {
background: var(--m-panel); border: 1px solid var(--m-rule);
border-radius: var(--m-radius); box-shadow: var(--m-shadow);
}
[data-theme] .mission-statement h2 { color: var(--m-fg); text-transform: var(--m-ui-case); font-style: var(--m-ui-style); }
[data-theme] .mission-statement p { color: var(--m-soft); }
[data-theme] #padname { background: var(--m-panel); color: var(--m-fg); border-color: var(--m-rule); border-radius: var(--m-radius); }
[data-theme] #go2Name [type="submit"], [data-theme] #transferSessionButton {
background: var(--m-accent); border-radius: var(--m-radius);
text-transform: var(--m-ui-case);
}
[data-theme] #button { background: var(--m-panel); color: var(--m-fg); border: 1px solid var(--m-rule); border-radius: var(--m-radius); }
[data-theme] .pad-datalist h2 { border-bottom-color: var(--m-rule); color: var(--m-fg); }
[data-theme] .recent-pad:hover a { color: var(--m-accent); }
[data-theme="brutalist"] #wrapper, [data-theme="brutalist"] .pad-datalist,
[data-theme="brutalist"] #button, [data-theme="brutalist"] #go2Name [type="submit"] {
border: 2px solid #000 !important; box-shadow: 4px 4px 0 #000;
}

View file

@ -0,0 +1,173 @@
'use strict';
// Apply the user's saved theme + light/dark mode as early as possible so the
// lobby paints in the same theme as the last pad they visited. The controls
// that write these localStorage keys live in the pad's Settings popup
// (see pad.js).
const MARGIN_THEME_KEY = 'marginTheme';
const MARGIN_MODE_KEY = 'marginMode';
const MARGIN_THEME_DEFAULT = 'colibris';
const MARGIN_MODE_DEFAULTS = {
colibris: 'light', editorial: 'light', brutalist: 'light',
paper: 'light', crt: 'dark', industrial: 'dark',
};
try {
const theme = localStorage.getItem(MARGIN_THEME_KEY) || MARGIN_THEME_DEFAULT;
const mode = localStorage.getItem(MARGIN_MODE_KEY) || MARGIN_MODE_DEFAULTS[theme] || 'light';
document.documentElement.setAttribute('data-theme', theme);
document.documentElement.setAttribute('data-mode', mode);
} catch (_) {
document.documentElement.setAttribute('data-theme', MARGIN_THEME_DEFAULT);
document.documentElement.setAttribute('data-mode', MARGIN_MODE_DEFAULTS[MARGIN_THEME_DEFAULT]);
}
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
window.customStart();
} else {
window.addEventListener('DOMContentLoaded', window.customStart, {once: true});
}
}
});
window.customStart = () => {
const recentPadList = document.getElementById('recent-pads');
if (recentPadList) {
recentPadList.replaceChildren();
}
// define your javascript here
// jquery is available - except index.js
// you can load extra scripts with $.getScript http://api.jquery.com/jQuery.getScript/
const divHoldingPlaceHolderLabel = document
.querySelector('[data-l10n-id="index.placeholderPadEnter"]');
const observer = new MutationObserver(() => {
document.querySelector('#go2Name input')
.setAttribute('placeholder', divHoldingPlaceHolderLabel.textContent);
});
observer
.observe(divHoldingPlaceHolderLabel, {childList: true, subtree: true, characterData: true});
const recentPadListHeading = document.querySelector('[data-l10n-id="index.recentPads"]');
// localStorage may be unavailable (private mode, disabled cookies) and the
// stored value may be malformed if another tab corrupted it. Either case
// would throw out of customStart() and break the rest of the lobby init,
// so swallow both and fall back to an empty list.
let recentPadListData = [];
try {
const recentPadsFromLocalStorage = localStorage.getItem('recentPads');
if (recentPadsFromLocalStorage != null) {
const parsed = JSON.parse(recentPadsFromLocalStorage);
if (Array.isArray(parsed)) {
recentPadListData = parsed.filter(
(p) => p && typeof p === 'object' && typeof p.name === 'string');
}
}
} catch (_) { /* private mode / corrupted entry */ }
// Remove duplicates based on pad name and sort by timestamp
recentPadListData = recentPadListData.filter(
(pad, index, self) => index === self.findIndex((p) => p.name === pad.name)
).sort((a, b) => new Date(a.timestamp) > new Date(b.timestamp) ? -1 : 1);
if (recentPadList && recentPadListData.length === 0) {
const parentStyle = recentPadList.parentElement.style;
recentPadListHeading.setAttribute('data-l10n-id', 'index.recentPadsEmpty');
parentStyle.display = 'flex';
parentStyle.justifyContent = 'center';
parentStyle.alignItems = 'center';
parentStyle.maxHeight = '100%';
recentPadList.remove();
} else if (recentPadList) {
/**
* @typedef {Object} Pad
* @property {string} name
*/
/**
* @param {Pad} pad
*/
const arrowIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-right w-4 h-4 text-gray-400"><path d="M5 12h14"></path><path d="m12 5 7 7-7 7"></path></svg>';
const clockIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock w-3 h-3"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>';
const personalIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users w-3 h-3"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>';
recentPadListData.forEach((pad) => {
const li = document.createElement('li');
li.style.cursor = 'pointer';
li.className = 'recent-pad';
// Use new URL() so a trailing slash, query string, or hash on
// window.location.href doesn't produce a broken link, and so pad
// names with characters that need encoding still resolve.
const padPath = new URL(`p/${encodeURIComponent(pad.name)}`,
window.location.href).href;
const link = document.createElement('a');
link.style.textDecoration = 'none';
link.href = padPath;
link.innerText = pad.name;
li.appendChild(link);
const arrowIconElement = document.createElement('span');
arrowIconElement.className = 'recent-pad-arrow';
arrowIconElement.innerHTML = arrowIcon;
li.appendChild(arrowIconElement);
const nextRow = document.createElement('div');
nextRow.style.display = 'flex';
nextRow.style.gap = '10px';
nextRow.style.marginTop = '10px';
const clockIconElement = document.createElement('span');
clockIconElement.className = 'recent-pad-clock';
clockIconElement.innerHTML = clockIcon;
nextRow.appendChild(clockIconElement);
const time = new Date(pad.timestamp);
const userLocale = navigator.language || 'en-US';
const formattedTime = time.toLocaleDateString(userLocale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
const timeElement = document.createElement('span');
timeElement.className = 'recent-pad-time';
timeElement.innerText = formattedTime;
nextRow.appendChild(timeElement);
const personalIconElement = document.createElement('span');
personalIconElement.className = 'recent-pad-personal';
personalIconElement.innerHTML = personalIcon;
personalIconElement.style.marginLeft = '5px';
const members = document.createElement('span');
members.className = 'recent-pad-members';
members.innerText = pad.members;
nextRow.appendChild(personalIconElement);
nextRow.appendChild(members);
li.appendChild(nextRow);
li.addEventListener('click', () => {
window.location.href = padPath;
});
// https://v0.dev/chat/etherpad-design-clone-qZnwOrVRXxH
recentPadList.appendChild(li);
});
}
};

View file

@ -0,0 +1,416 @@
/*
* margin / pad.css a standalone Etherpad skin with 5 themes.
*
* Drop the entire `margin/` folder into src/static/skins/, then in
* settings.json set:
*
* "skinName": "margin"
*
* Choose a theme by setting data-theme on <html> in src/templates/pad.html:
*
* <html data-theme="editorial">
*
* Themes: editorial · brutalist · paper · crt · industrial
*
* Switch live in devtools: document.documentElement.dataset.theme='crt'
*
* This file inherits the same variable contract colibris uses
* (--primary-color, --text-color, --bg-color, --main-font-family, ) so
* Etherpad's built-in components pick up the theme automatically.
* */
/* Google Fonts powering the five themed type stacks (Newsreader/Editorial,
* Space Mono/Brutalist, Lora/Paper, IBM Plex Mono+VT323/CRT, IBM Plex
* Sans/Industrial, plus Instrument Serif for display H1/H2). */
@import url("//fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300..700;1,6..72,300..700&family=Instrument+Serif:ital@0;1&family=Lora:ital,wght@0,400..700;1,400..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&family=VT323&family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;0,700&display=swap");
/* Component partials — vendored from colibris so this skin is standalone. */
@import url("src/general.css");
@import url("src/layout.css");
@import url("src/pad-editor.css");
@import url("src/components/scrollbars.css");
@import url("src/components/buttons.css");
@import url("src/components/popup.css");
@import url("src/components/chat.css");
@import url("src/components/sidediv.css");
@import url("src/components/gritter.css");
@import url("src/components/table-of-content.css");
@import url("src/components/toolbar.css");
@import url("src/components/users.css");
@import url("src/components/form.css");
@import url("src/components/import-export.css");
@import url("src/plugins/brightcolorpicker.css");
@import url("src/plugins/font_color.css");
@import url("src/plugins/tables2.css");
@import url("src/plugins/set_title_on_pad.css");
@import url("src/plugins/author_hover.css");
@import url("src/plugins/comments.css");
/* Default theme if data-theme isn't set */
:root { color-scheme: light; }
html:not([data-theme]) { /* fallback */ }
/* Mirror colibris's narrow-viewport behavior: the per-theme blocks below
* each set --editor-horizontal-padding (40-64px) so the editor frame has
* room to breathe on desktop. Below 1000px (matching the layout.css media
* query that drops `max-width: 900px` on the iframe), zero them so the
* editor surface fills the available container width. */
@media (max-width: 1000px) {
[data-theme] {
--editor-horizontal-padding: 0px !important;
--editor-vertical-padding: 0px !important;
}
}
/* ─── 1. EDITORIAL ───────────────────────────────────────────────────── */
[data-theme="editorial"][data-mode="light"] {
--super-dark-color:#1c1916; --dark-color:#5a534a;
--primary-color:#a8442b; --middle-color:rgba(28,25,22,.18);
--light-color:#f0eadd; --super-light-color:#fbf8f2;
--text-color:#1c1916; --text-soft-color:#5a534a;
--border-color:rgba(28,25,22,.18);
--bg-soft-color:#f0eadd; --bg-color:#fbf8f2;
--toolbar-border:1px solid rgba(28,25,22,.12);
--main-font-family:"Newsreader",Georgia,serif;
--editor-horizontal-padding:64px; --editor-vertical-padding:40px;
--m-bg:#f5f0e8; --m-rule:rgba(28,25,22,.12);
--m-radius:2px; --m-shadow:0 12px 36px -18px rgba(28,25,22,.20);
--m-ui-style:italic; --m-ui-case:none; --m-ui-track:.04em;
}
/* ─── 2. BRUTALIST ──────────────────────────────────────────────────── */
[data-theme="brutalist"][data-mode="light"] {
--super-dark-color:#000; --dark-color:#222;
--primary-color:#ff3b00; --middle-color:#000;
--light-color:#f3f3f0; --super-light-color:#fff;
--text-color:#000; --text-soft-color:#222;
--border-color:#000;
--bg-soft-color:#f3f3f0; --bg-color:#fff;
--toolbar-border:2px solid #000;
--main-font-family:"Space Mono",ui-monospace,monospace;
--editor-horizontal-padding:48px; --editor-vertical-padding:32px;
--m-bg:#f3f3f0; --m-rule:#000;
--m-radius:0; --m-shadow:4px 4px 0 #000;
--m-ui-style:normal; --m-ui-case:uppercase; --m-ui-track:.06em;
}
/* ─── 3. PAPER ──────────────────────────────────────────────────────── */
[data-theme="paper"][data-mode="light"] {
--super-dark-color:#2a2520; --dark-color:#6b6259;
--primary-color:#b87333; --middle-color:rgba(42,37,32,.18);
--light-color:#efe7d4; --super-light-color:#fbf6ec;
--text-color:#2a2520; --text-soft-color:#6b6259;
--border-color:rgba(42,37,32,.18);
--bg-soft-color:#efe7d4; --bg-color:#fbf6ec;
--toolbar-border:1px solid rgba(42,37,32,.08);
--main-font-family:"Lora",Georgia,serif;
--editor-horizontal-padding:56px; --editor-vertical-padding:40px;
--m-bg:#f6f1e8; --m-rule:rgba(42,37,32,.08);
--m-radius:10px; --m-shadow:0 14px 40px -16px rgba(42,37,32,.18);
--m-ui-style:normal; --m-ui-case:none; --m-ui-track:0;
--m-display-font:"Instrument Serif",Georgia,serif;
}
/* ─── 4. CRT TERMINAL ───────────────────────────────────────────────── */
[data-theme="crt"][data-mode="dark"] {
color-scheme: dark;
--super-dark-color:#7fffae; --dark-color:#4ed188;
--primary-color:#ffb84d; --middle-color:rgba(127,255,174,.45);
--light-color:#0c2a14; --super-light-color:#08200f;
--text-color:#7fffae; --text-soft-color:#4ed188;
--border-color:rgba(127,255,174,.45);
--bg-soft-color:#0c2a14; --bg-color:#08200f;
--toolbar-border:1px solid rgba(127,255,174,.45);
--main-font-family:"IBM Plex Mono",ui-monospace,monospace;
--editor-horizontal-padding:48px; --editor-vertical-padding:32px;
--m-bg:#04140a; --m-rule:rgba(127,255,174,.20);
--m-radius:0; --m-shadow:0 0 0 1px rgba(127,255,174,.25),0 0 24px rgba(127,255,174,.10);
--m-ui-style:normal; --m-ui-case:uppercase; --m-ui-track:.10em;
--m-display-font:"VT323",ui-monospace,monospace;
}
/* ─── 5. INDUSTRIAL ─────────────────────────────────────────────────── */
[data-theme="industrial"][data-mode="dark"] {
color-scheme: dark;
--super-dark-color:#e6e8eb; --dark-color:#9aa0a8;
--primary-color:#ffcc00; --middle-color:rgba(255,255,255,.18);
--light-color:#1c2027; --super-light-color:#14171c;
--text-color:#e6e8eb; --text-soft-color:#9aa0a8;
--border-color:rgba(255,255,255,.18);
--bg-soft-color:#1c2027; --bg-color:#14171c;
--toolbar-border:1px solid rgba(255,255,255,.08);
--main-font-family:"IBM Plex Sans",ui-sans-serif,system-ui,sans-serif;
--editor-horizontal-padding:56px; --editor-vertical-padding:36px;
--m-bg:#0d0f12; --m-rule:rgba(255,255,255,.08);
--m-radius:3px; --m-shadow:0 12px 32px rgba(0,0,0,.4);
--m-ui-style:normal; --m-ui-case:uppercase; --m-ui-track:.08em;
}
/* ─── 0. COLIBRIS (default, mirrors colibris/pad.css) ──────────────── */
[data-theme="colibris"] {
--super-dark-color:#485365; --dark-color:#576273;
--primary-color:#64d29b; --middle-color:#dadada;
--light-color:#f2f3f4; --super-light-color:#ffffff;
--text-color:#485365; --text-soft-color:#576273;
--border-color:#dadada;
--bg-soft-color:#f2f3f4; --bg-color:#ffffff;
--toolbar-border:none;
--main-font-family:Quicksand, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
--editor-horizontal-padding:40px; --editor-vertical-padding:25px;
--m-bg:#f2f3f4; --m-rule:#dadada;
--m-radius:3px; --m-shadow:0 2px 8px rgba(68,68,68,.08);
--m-ui-style:normal; --m-ui-case:none; --m-ui-track:0;
}
/* ─── 1b. EDITORIAL — DARK ─────────────────────────────────────────── */
[data-theme="editorial"][data-mode="dark"] {
color-scheme:dark;
--super-dark-color:#f0eadd; --dark-color:#a8a098;
--primary-color:#d27047; --middle-color:rgba(240,234,221,.18);
--light-color:#221e1a; --super-light-color:#1c1916;
--text-color:#f0eadd; --text-soft-color:#a8a098;
--border-color:rgba(240,234,221,.18);
--bg-soft-color:#221e1a; --bg-color:#1c1916;
--toolbar-border:1px solid rgba(240,234,221,.12);
--main-font-family:"Newsreader",Georgia,serif;
--editor-horizontal-padding:64px; --editor-vertical-padding:40px;
--m-bg:#16130f; --m-rule:rgba(240,234,221,.12);
--m-radius:2px; --m-shadow:0 12px 36px -18px rgba(0,0,0,.6);
--m-ui-style:italic; --m-ui-case:none; --m-ui-track:.04em;
}
/* ─── 2b. BRUTALIST — DARK ─────────────────────────────────────────── */
[data-theme="brutalist"][data-mode="dark"] {
color-scheme:dark;
--super-dark-color:#fff; --dark-color:#ddd;
--primary-color:#ff3b00; --middle-color:#fff;
--light-color:#111; --super-light-color:#000;
--text-color:#fff; --text-soft-color:#ddd;
--border-color:#fff;
--bg-soft-color:#111; --bg-color:#000;
--toolbar-border:2px solid #fff;
--main-font-family:"Space Mono",ui-monospace,monospace;
--editor-horizontal-padding:48px; --editor-vertical-padding:32px;
--m-bg:#0c0c09; --m-rule:#fff;
--m-radius:0; --m-shadow:4px 4px 0 #fff;
--m-ui-style:normal; --m-ui-case:uppercase; --m-ui-track:.06em;
}
/* ─── 3b. PAPER — DARK ─────────────────────────────────────────────── */
[data-theme="paper"][data-mode="dark"] {
color-scheme:dark;
--super-dark-color:#efe7d4; --dark-color:#a89e8d;
--primary-color:#d99560; --middle-color:rgba(239,231,212,.18);
--light-color:#332c25; --super-light-color:#2a2520;
--text-color:#efe7d4; --text-soft-color:#a89e8d;
--border-color:rgba(239,231,212,.18);
--bg-soft-color:#332c25; --bg-color:#2a2520;
--toolbar-border:1px solid rgba(239,231,212,.10);
--main-font-family:"Lora",Georgia,serif;
--editor-horizontal-padding:56px; --editor-vertical-padding:40px;
--m-bg:#231e19; --m-rule:rgba(239,231,212,.10);
--m-radius:10px; --m-shadow:0 14px 40px -16px rgba(0,0,0,.6);
--m-ui-style:normal; --m-ui-case:none; --m-ui-track:0;
--m-display-font:"Instrument Serif",Georgia,serif;
}
/* ─── 4b. CRT — LIGHT (paper-terminal, less iconic but readable) ──── */
[data-theme="crt"][data-mode="light"] {
--super-dark-color:#04200d; --dark-color:#1c4a2b;
--primary-color:#006b3f; --middle-color:rgba(0,107,63,.30);
--light-color:#dfeee2; --super-light-color:#e9f5ed;
--text-color:#04200d; --text-soft-color:#1c4a2b;
--border-color:rgba(0,107,63,.30);
--bg-soft-color:#dfeee2; --bg-color:#e9f5ed;
--toolbar-border:1px solid rgba(0,107,63,.30);
--main-font-family:"IBM Plex Mono",ui-monospace,monospace;
--editor-horizontal-padding:48px; --editor-vertical-padding:32px;
--m-bg:#edf6ee; --m-rule:rgba(0,107,63,.20);
--m-radius:0; --m-shadow:0 0 0 1px rgba(0,107,63,.25);
--m-ui-style:normal; --m-ui-case:uppercase; --m-ui-track:.10em;
--m-display-font:"VT323",ui-monospace,monospace;
}
/* ─── 5b. INDUSTRIAL — LIGHT ──────────────────────────────────────── */
[data-theme="industrial"][data-mode="light"] {
--super-dark-color:#14171c; --dark-color:#525965;
--primary-color:#cc9900; --middle-color:rgba(20,23,28,.18);
--light-color:#e6e8eb; --super-light-color:#f5f6f8;
--text-color:#14171c; --text-soft-color:#525965;
--border-color:rgba(20,23,28,.18);
--bg-soft-color:#e6e8eb; --bg-color:#f5f6f8;
--toolbar-border:1px solid rgba(20,23,28,.08);
--main-font-family:"IBM Plex Sans",ui-sans-serif,system-ui,sans-serif;
--editor-horizontal-padding:56px; --editor-vertical-padding:36px;
--m-bg:#f0f1f4; --m-rule:rgba(20,23,28,.08);
--m-radius:3px; --m-shadow:0 12px 32px rgba(20,23,28,.10);
--m-ui-style:normal; --m-ui-case:uppercase; --m-ui-track:.08em;
}
/*
* Cross-theme overrides (apply to anything under [data-theme])
* */
[data-theme] body { background: var(--m-bg); color: var(--text-color); }
[data-theme] #editorcontainerbox,
[data-theme] #editorcontainer,
[data-theme] #padeditor,
[data-theme] #outerdocbody,
[data-theme] iframe[name="ace_outer"],
[data-theme] iframe[name="ace_inner"] { background: var(--m-bg) !important; }
/* Editor frame outline gives the pad surface a visible edge against
* the page background when both share dark/light tones. */
[data-theme] iframe[name="ace_outer"] {
border: 1px solid var(--border-color) !important;
border-radius: 4px;
box-shadow: 0 1px 0 rgba(0,0,0,.04);
}
[data-theme="brutalist"] iframe[name="ace_outer"] { border-radius: 0; border-width: 2px !important; box-shadow: var(--m-shadow); border-color: var(--m-rule) !important; }
[data-theme="crt"][data-mode="dark"] iframe[name="ace_outer"] { box-shadow: 0 0 0 1px var(--border-color), 0 0 18px rgba(127,255,174,.18) inset; }
[data-theme="crt"][data-mode="light"] iframe[name="ace_outer"] { box-shadow: 0 0 0 1px var(--border-color); }
/* Show-users / chat / share buttons in the menu_right have hard-coded
* background colors in upstream force them to follow the theme.
* No left-borders: keep the right-side icons visually flush like the left. */
[data-theme] .toolbar .menu_right li a,
[data-theme] #chaticon,
[data-theme] #chaticon a {
background: transparent !important;
color: var(--text-color) !important;
border-left: 0 !important;
}
[data-theme] #chaticon .chatlabel { color: var(--text-color) !important; }
[data-theme] .toolbar ul li[data-key=showusers] > a {
background: var(--primary-color) !important;
color: var(--bg-color) !important;
}
/* Normalize <select> chrome inside popups so Font type / Language / Theme
* align with the rest of the form fields. line-height matches height so the
* selected option text sits on the vertical centerline. */
[data-theme] .popup-content select,
[data-theme] #settings select,
[data-theme] #users select {
height: 32px;
line-height: 30px;
padding: 0 26px 0 10px;
background-color: var(--bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
border-radius: 4px;
font: inherit;
appearance: none;
-webkit-appearance: none;
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
linear-gradient(135deg, currentColor 50%, transparent 50%);
background-position: calc(100% - 14px) 14px, calc(100% - 9px) 14px;
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
}
/* In the toolbar the .nice-select widget is a flex child of its <li>, which
* stretches it past the natural inline-block centering the popups get. Pin
* .current to the vertical centerline with flex alignment so the selected
* text reads right regardless of the row line-height. */
[data-theme] .toolbar .nice-select {
display: inline-flex;
align-items: center;
}
[data-theme] .toolbar .nice-select .current {
line-height: 1;
}
/* Cap every open dropdown list (toolbar AND popups) long font-family
* / heading / theme labels would otherwise stretch the <ul> to fit the
* widest <li> and could run a long way across the viewport. Truncate
* overflowing option labels with ellipsis. */
[data-theme] .nice-select .list {
max-width: 220px;
}
[data-theme] .nice-select .option {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* ep_comments_page hard-codes #ffffff / #f9f9f9 / #eeeeed on the sidebar
* + popup wrappers, which clashes with dark themes (crt, industrial) and
* out-of-palette light themes (paper, editorial). Pull them onto the
* margin variable palette so the comment UI reads against any theme. */
[data-theme] .full-display-content,
[data-theme] .comment-modal-comment .full-display-content {
background: var(--bg-color);
color: var(--text-color);
box-shadow: var(--m-shadow);
}
[data-theme] .compact-display-content {
background: var(--bg-soft-color);
color: var(--text-color);
}
[data-theme] .comment-reply {
background: var(--bg-soft-color);
border-top: 1px solid var(--border-color);
color: var(--text-color);
}
[data-theme] .new-comment textarea,
[data-theme] .new-comment input[type="text"],
[data-theme] .comment-edit-form textarea {
background: var(--bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}
[data-theme] .toolbar { background: var(--bg-color); border-bottom: var(--toolbar-border); font-family: var(--main-font-family); }
[data-theme] .toolbar ul li a {
border-radius: var(--m-radius) !important;
letter-spacing: var(--m-ui-track);
text-transform: var(--m-ui-case);
font-style: var(--m-ui-style);
}
[data-theme] .toolbar ul li a:hover,
[data-theme] .toolbar ul li a.selected { background: var(--bg-soft-color); color: var(--text-color); }
[data-theme] .toolbar ul li a.pressed { color: var(--primary-color); }
[data-theme] .toolbar ul li.separator { background: var(--border-color); }
[data-theme] .popup, [data-theme] #chatbox {
background: var(--bg-color); color: var(--text-color);
border: 1px solid var(--border-color);
border-radius: var(--m-radius); box-shadow: var(--m-shadow);
font-family: var(--main-font-family);
}
[data-theme] #chatbox #titlebar { background: var(--bg-soft-color); border-bottom: 1px solid var(--border-color); }
[data-theme] #chattext, [data-theme] #chatinput { background: var(--bg-color); color: var(--text-color); border-color: var(--border-color); }
[data-theme] button, [data-theme] input[type="text"], [data-theme] input[type="submit"], [data-theme] select {
font-family: var(--main-font-family); border-radius: var(--m-radius);
}
/* ─── per-theme touches ──────────────────────────────────────────── */
[data-theme="editorial"] .toolbar { font-style: italic; }
[data-theme="editorial"] iframe[name="ace_outer"] {
background:
repeating-linear-gradient(transparent, transparent calc(1.65em - 1px),
rgba(28,25,22,.025) calc(1.65em - 1px), rgba(28,25,22,.025) 1.65em),
var(--m-bg) !important;
}
[data-theme="brutalist"] .toolbar, [data-theme="brutalist"] #editbar { border-bottom: 2px solid var(--m-rule) !important; }
[data-theme="brutalist"] .popup { box-shadow: var(--m-shadow); border: 2px solid var(--m-rule) !important; }
[data-theme="brutalist"] button { box-shadow: none; border: 0 !important; }
[data-theme="paper"] .popup, [data-theme="paper"] button { border-radius: 12px; }
[data-theme="crt"][data-mode="dark"] body::before {
content:""; position:fixed; inset:0; pointer-events:none; z-index:9998;
background: repeating-linear-gradient(to bottom, transparent 0, transparent 2px, rgba(0,0,0,.18) 2px, rgba(0,0,0,.18) 3px);
mix-blend-mode: multiply;
}
[data-theme="crt"][data-mode="dark"] body::after {
content:""; position:fixed; inset:0; pointer-events:none; z-index:9997;
background: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,.55) 100%);
}
[data-theme="crt"][data-mode="dark"] h1, [data-theme="crt"][data-mode="dark"] h2 { font-family: var(--m-display-font); text-shadow: 0 0 6px rgba(127,255,174,.55); }
[data-theme="crt"][data-mode="light"] h1, [data-theme="crt"][data-mode="light"] h2 { font-family: var(--m-display-font); }
[data-theme="industrial"] .toolbar ul li a.pressed,
[data-theme="industrial"] .toolbar ul li a.selected { box-shadow: inset 0 -2px 0 var(--primary-color); }

View file

@ -0,0 +1,253 @@
'use strict';
const MAX_PADS_IN_HISTORY = 3;
// Selectable themes — orthogonal to light/dark mode. `value` is the
// data-theme attribute slug used in pad.css [data-theme="…"] selectors;
// `label` is the dropdown option text. Colibris is the default.
const MARGIN_THEMES = [
{value: 'colibris', label: 'Colibris'},
{value: 'editorial', label: 'Editorial'},
{value: 'brutalist', label: 'Brutalist'},
{value: 'paper', label: 'Paper'},
{value: 'crt', label: 'CRT'},
{value: 'industrial', label: 'Industrial'},
];
const MARGIN_THEME_VALUES = MARGIN_THEMES.map((t) => t.value);
const MARGIN_THEME_DEFAULT = 'colibris';
const MARGIN_THEME_KEY = 'marginTheme';
// Light/dark mode is orthogonal — paired with the theme in CSS via
// [data-theme="X"][data-mode="light|dark"]. Colibris has only a light palette;
// the others define both. Each theme has a "natural" default mode used when
// the user hasn't expressed a preference (CRT/Industrial start dark, the
// rest start light).
const MARGIN_MODE_KEY = 'marginMode';
const MARGIN_MODE_DEFAULTS = {
colibris: 'light',
editorial: 'light',
brutalist: 'light',
paper: 'light',
crt: 'dark',
industrial: 'dark',
};
const applyMarginTheme = (theme) => {
if (!MARGIN_THEME_VALUES.includes(theme)) theme = MARGIN_THEME_DEFAULT;
document.documentElement.setAttribute('data-theme', theme);
try { localStorage.setItem(MARGIN_THEME_KEY, theme); } catch (_) { /* private mode */ }
};
const applyMarginMode = (mode) => {
if (mode !== 'light' && mode !== 'dark') mode = 'light';
document.documentElement.setAttribute('data-mode', mode);
try { localStorage.setItem(MARGIN_MODE_KEY, mode); } catch (_) { /* private mode */ }
};
// Apply saved or default theme + mode as early as possible so first paint
// matches the user's last choice. If no mode is saved, fall back to the
// natural default for the selected theme.
let initialTheme = MARGIN_THEME_DEFAULT;
let initialMode = MARGIN_MODE_DEFAULTS[MARGIN_THEME_DEFAULT];
try {
initialTheme = localStorage.getItem(MARGIN_THEME_KEY) || initialTheme;
initialMode = localStorage.getItem(MARGIN_MODE_KEY) || MARGIN_MODE_DEFAULTS[initialTheme] || 'light';
} catch (_) { /* private mode */ }
applyMarginTheme(initialTheme);
applyMarginMode(initialMode);
// Build a single dropdown-line row mirroring the markup used by the built-in
// Font type / Language rows so margin's Theme picker inherits the same
// styling. `selectId` keeps the user-settings vs pad-settings copies distinct.
const buildThemeRow = (selectId) => {
const row = document.createElement('p');
row.className = 'dropdown-line margin-theme-row';
const label = document.createElement('label');
label.htmlFor = selectId;
// Match the colon convention used by the built-in "Font type:" / "Language:"
// rows so the field reads consistently in the Settings popup.
label.textContent = 'Theme:';
// popup.css sets `.popup .dropdowns-container label { width: 120px;
// display: inline-block }`, but apply it inline too so the select's
// left edge sits at exactly the same x as the Font type / Language
// selects regardless of stylesheet load order.
label.style.cssText = 'width: 120px; display: inline-block;';
const select = document.createElement('select');
select.id = selectId;
MARGIN_THEMES.forEach(({value, label: optLabel}) => {
const opt = document.createElement('option');
opt.value = value;
opt.textContent = optLabel;
select.appendChild(opt);
});
select.value = document.documentElement.getAttribute('data-theme') || MARGIN_THEME_DEFAULT;
// nice-select wraps <select> and dispatches change via $().trigger('change'),
// which only fires jQuery-bound handlers — not native addEventListener. Bind
// through jQuery so the widget actually drives applyMarginTheme.
$(select).on('change', () => {
applyMarginTheme(select.value);
// Mirror to the sibling select (user-settings ↔ pad-settings).
$('.margin-theme-row select').each(function () {
if (this !== select) this.value = select.value;
});
if ($.fn.niceSelect) $('.margin-theme-row select').niceSelect('update');
});
row.appendChild(label);
row.appendChild(select);
return row;
};
// Build the Dark mode toggle row. Same checkbox-on-label pattern as the
// existing "Disable Chat" / "Show line numbers" rows in pad.html so margin's
// form.css picks up identical chrome.
const buildDarkModeRow = (checkboxId) => {
const row = document.createElement('p');
row.className = 'margin-mode-row';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = checkboxId;
checkbox.checked = document.documentElement.getAttribute('data-mode') === 'dark';
const label = document.createElement('label');
label.htmlFor = checkboxId;
label.textContent = 'Dark mode';
$(checkbox).on('change', () => {
applyMarginMode(checkbox.checked ? 'dark' : 'light');
$('.margin-mode-row input[type="checkbox"]').each(function () {
if (this !== checkbox) this.checked = checkbox.checked;
});
});
row.appendChild(checkbox);
row.appendChild(label);
return row;
};
const injectThemeSelector = () => {
// Two .dropdowns-container blocks exist: user settings + pad-wide settings.
// Mirror the Theme dropdown into both and the Dark mode toggle into the
// section the column lives in (so the row sits next to other checkboxes).
const sections = [
{
sectionId: 'user-settings-section',
dropdownId: 'margin-theme-user',
checkboxId: 'margin-mode-user',
},
{
sectionId: 'pad-settings-section',
dropdownId: 'margin-theme-pad',
checkboxId: 'margin-mode-pad',
},
];
let injectedAny = false;
let allDone = true;
let injectedThisCall = false;
sections.forEach(({sectionId, dropdownId, checkboxId}) => {
const section = document.getElementById(sectionId);
if (!section) { allDone = false; return; }
const dropdownsContainer = section.querySelector('.dropdowns-container');
if (!dropdownsContainer) { allDone = false; return; }
if (!document.getElementById(dropdownId)) {
dropdownsContainer.appendChild(buildThemeRow(dropdownId));
injectedThisCall = true;
}
if (!document.getElementById(checkboxId)) {
// Place the Dark mode checkbox among the section's other checkboxes,
// before the dropdowns container so it reads in toggle-then-pickers
// order — matching the natural top-to-bottom flow of the section.
section.insertBefore(buildDarkModeRow(checkboxId), dropdownsContainer);
}
injectedAny = true;
});
// Etherpad runs $('select').niceSelect() once at pad-init (pad_editbar.ts),
// so a freshly appended <select> is still native chrome. Wrap ours now so
// it matches the Font type / Language widgets visually.
if (injectedThisCall && window.$ && $.fn.niceSelect) {
$('.margin-theme-row select').niceSelect();
}
return injectedAny && allDone;
};
// Propagate data-theme + data-mode from the host page into the editor iframes
// so [data-theme="X"][data-mode="Y"] rules in pad-editor.css apply inside.
const propagateTheme = () => {
try {
const theme = document.documentElement.getAttribute('data-theme');
const mode = document.documentElement.getAttribute('data-mode');
if (!theme) return;
const setOn = (doc) => {
if (!doc) return;
const apply = (el) => {
if (!el) return;
el.setAttribute('data-theme', theme);
if (mode) el.setAttribute('data-mode', mode);
};
apply(doc.documentElement);
apply(doc.getElementById && doc.getElementById('outerdocbody'));
apply(doc.getElementById && doc.getElementById('innerdocbody'));
};
const outer = document.querySelector('iframe[name="ace_outer"]');
if (!outer) return;
setOn(outer.contentDocument);
const inner = outer.contentDocument && outer.contentDocument.querySelector('iframe[name="ace_inner"]');
if (inner) setOn(inner.contentDocument);
} catch (_) { /* iframe not ready yet */ }
};
window.customStart = () => {
$('#pad_title').show();
$('.buttonicon').on('mousedown', function () { $(this).parent().addClass('pressed'); });
$('.buttonicon').on('mouseup', function () { $(this).parent().removeClass('pressed'); });
// Inject the Theme selector into Settings → user-settings section.
// Retry until the popup DOM is mounted (it's rendered eagerly in pad.html
// but we keep a short poll for safety).
let injectAttempts = 0;
const injectTick = setInterval(() => {
if (injectThemeSelector() || ++injectAttempts > 40) clearInterval(injectTick);
}, 250);
// Try repeatedly until iframes have mounted.
let attempts = 0;
const tick = setInterval(() => {
propagateTheme();
if (++attempts > 40) clearInterval(tick); // ~10s
}, 250);
// Also re-apply if theme/mode is changed at runtime
new MutationObserver(propagateTheme).observe(document.documentElement, {
attributes: true, attributeFilter: ['data-theme', 'data-mode'],
});
const pathSegments = window.location.pathname.split('/');
const padName = pathSegments[pathSegments.length - 1];
// localStorage access and JSON.parse can both throw (private mode,
// restricted storage, corrupted value). Treat any failure as "no recent
// pads yet" rather than aborting customStart().
let recentPadsList = [];
try {
const raw = localStorage.getItem('recentPads');
if (raw != null) {
const parsed = JSON.parse(raw);
if (Array.isArray(parsed)) {
recentPadsList = parsed.filter(
(p) => p && typeof p === 'object' && typeof p.name === 'string');
}
}
} catch (_) { /* private mode / corrupted entry */ }
if (!recentPadsList.some((pad) => pad.name === padName)) {
if (recentPadsList.length >= MAX_PADS_IN_HISTORY) {
recentPadsList.shift(); // Remove the oldest pad if we have more than 10
}
recentPadsList.push({
name: padName,
timestamp: new Date().toISOString(), // Store the timestamp for sorting
members: 1,
});
} else {
// Update the timestamp if the pad already exists
const existingPad = recentPadsList.find((pad) => pad.name === padName);
if (existingPad) {
existingPad.timestamp = new Date().toISOString();
}
}
try {
localStorage.setItem('recentPads', JSON.stringify(recentPadsList));
} catch (_) { /* quota / private mode — skip silently */ }
};

View file

@ -0,0 +1,74 @@
button, .btn
{
padding: 5px 20px;
border-radius: 4px;
line-height: 1.5;
width: auto;
border: none;
font-weight: bold;
position: relative;
background: none;
cursor: pointer;
}
.btn-primary
{
background-color: #64d29b;
background-color: var(--primary-color);
color: #ffffff;
color: var(--bg-color);
}
.btn-default {
color: #485365;
color: var(--text-color);
}
/* Sekundär (outlined) */
.btn-secondary {
background: transparent;
color: #1f8a3e;
border: 2px solid #1f8a3e;
box-shadow: none;
}
.active-btn {
text-underline-offset: 10px;
text-decoration: underline;
text-decoration-style: solid;
text-decoration-thickness: 2px;
text-decoration-color: #1f8a3e;
cursor: pointer;
}
.btn-secondary:hover {
background: #1f8a3e;
color: #fff;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.08), 0 1px 2px rgba(0,0,0,0.12);
transform: translateY(-1px);
}
.btn-secondary:disabled {
background: transparent;
color: #aaa;
border-color: #aaa;
box-shadow: none;
cursor: not-allowed;
}
.btn-danger {
background: #d1242f;
color: #fff;
}
.btn-danger:hover {
background: #b71c26;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.08), 0 1px 2px rgba(0,0,0,0.12);
transform: translateY(-1px);
}
.btn-danger:disabled {
background: #aaa;
color: #fff;
cursor: not-allowed;
}

View file

@ -0,0 +1,91 @@
#chatbox {
background-color: transparent !important;
color: var(--text-color);
}
.chat-content {
background: none;
padding: 0;
width: 400px;
height: 300px;
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
.chat-content, #chaticon {
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
border: none;
}
#chaticon {
padding: 10px 20px;
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
color: #485365;
color: var(--text-color);
right: 30px;
}
#chatbox.stickyChat .chat-content {
border: none;
box-shadow: none;
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
#titlebar {
bottom: 0;
line-height: 44px;
height: 44px;
padding: 0 7px;
z-index: 20000;
}
#titlelabel, #chatlabel {
text-transform: uppercase;
font-weight: bold;
}
#titlebar #titlelabel { font-size: 16px; }
#chatlabel { margin-right: 15px; }
#chattext {
padding: 0;
border-top: 1px solid #ffffff;
border-top: 1px solid var(--bg-color);
border-bottom: 1px solid #ffffff;
border-bottom: 1px solid var(--bg-color);
background-color: inherit;
color: inherit;
}
#chattext p {
padding: 4px 10px;
}
#chattext:not(.authorColors) p:first-child {
padding-top: 10px;
}
#chattext:not(.authorColors) p:last-child {
padding-bottom: 10px;
}
#chatinputbox {
padding: 8px;
}
#chatinputbox #chatinput {
background-color: #ffffff;
background-color: var(--bg-color);
}
@media (prefers-reduced-motion) {
.chat-content {
transform: scale(1);
transition: none;
}
}
@media (max-width: 800px) {
#chaticon {
right: 0;
}
.stick-to-screen-btn { display: none; }
}

View file

@ -0,0 +1,122 @@
input[type="text"], select, textarea, .nice-select {
border-radius: 3px;
box-shadow: none;
border: none;
outline: 0;
}
input[type="text"], textarea {
padding: 8px 10px;
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
border: none;
color: #485365;
color: var(--text-color);
}
input[type="text"]::placeholder, textarea::placeholder {
color: #576273;
color: var(--text-soft-color);
}
select, .nice-select {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
border: 1px solid var(--bg-soft-color);
/* Zero vertical padding so the line-height fully owns the widget's
* inner height; the inherited box is 28px (border-box) and we want the
* line-box centered on its content. */
padding: 0 25px 0 10px;
font-weight: bold;
line-height: 20px;
}
select:hover, .nice-select:hover {
border-color: var(--bg-soft-color)
}
.nice-select .list {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
.nice-select .option:hover,.nice-select .option.focus,.nice-select .option.selected.focus {
background-color: #ffffff;
background-color: var(--bg-color);
}
.nice-select .option {
padding: 0 15px;
}
.popup .nice-select .list {
right: 0;
left: auto;
}
/* Checkboxes
========================================================================== */
/* Remove default checkbox */
[type="checkbox"]:not(:checked),
[type="checkbox"]:checked {
position: absolute;
opacity: 0;
pointer-events: none;
}
[type="checkbox"] + label {
position: relative;
padding-left: 2.5rem;
cursor: pointer;
display: inline-block;
height: 1.4rem;
line-height: 1.4rem;
font-size: 1rem;
}
[type="checkbox"] + label:before,
[type="checkbox"] + label:after {
content: '';
position: absolute;
margin-top: 4px;
transition: all .2s ease-in-out;
}
/* BEFORE, the container*/
[type="checkbox"] + label:before {
width: 24px;
height: 14px;
top: 0;
left: 0;
border-radius: 6px;
border: 2px solid #576273;
border: 2px solid var(--text-soft-color);
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
opacity: .7;
}
[type="checkbox"]:checked + label:before {
background-color: transparent;
border-color: #64d29b;
border-color: var(--primary-color);
}
/* AFTER, the circle moving */
[type="checkbox"] + label:after {
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #576273;
background-color: var(--text-soft-color);
top: -1px;
left: -3px;
}
[type="checkbox"]:checked + label:after {
background-color: #64d29b;
background-color: var(--primary-color);
transform: translateX(14px);
}
/* Apply to any disabled checkbox (checked or unchecked), not just checked
ones, so dependent toggles like "Chat always on screen" visibly grey out
when "Disable chat" is ticked. Fixes #7592. */
[type="checkbox"]:disabled + label,
[type="checkbox"]:disabled + label:before,
[type="checkbox"]:disabled + label:after {
cursor: not-allowed;
opacity: .4;
}

View file

@ -0,0 +1,82 @@
.gritter-item:not(.error) .popup-content{
background-color: #64d29b;
background-color: var(--primary-color);
color: #ffffff;
color: var(--super-light-color);
}
.gritter-item .popup-content {
padding: 15px;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
}
#gritter-container.bottom .gritter-item .popup-content {
margin-top: 10px;
}
#gritter-container.top .gritter-item .popup-content {
margin-bottom: 10px;
}
.gritter-item p {
margin: 0 !important;
}
.gritter-item .gritter-title {
margin-bottom: 10px;
}
.gritter-item .gritter-close {
margin-left: 15px;
margin-right: 0px;
}
.gritter-item:not(.error) .gritter-close .buttonicon {
color: #ffffff;
color: var(--super-light-color);
}
/* CHAT GRIITER ITEM */
.gritter-item.chat-gritter-msg:not(.error) .popup-content {
background-color: white;
background-color: var(--bg-color);
color: #485365;
color: var(--text-color);
}
.gritter-item.chat-gritter-msg .gritter-content {
text-align: left;
}
.gritter-item.chat-gritter-msg .author-name {
font-weight: bold;
margin-right: 5px;
}
.gritter-item.chat-gritter-msg:not(.error) .gritter-close .buttonicon {
color: #485365;
color: var(--text-color);
}
.gritter-item.saved-revision {
max-width: 600px;
}
#gritter-container.top .gritter-item.popup > .popup-content {
transform: scale(0.8) translateY(-100px);
}
#gritter-container.bottom .gritter-item.popup > .popup-content {
transform: scale(0.8) translateY(0px);
}
.gritter-item.popup.popup-show > .popup-content {
transform: scale(1) translateY(0) !important;
transition: all 0.4s cubic-bezier(0.74, -0.05, 0.27, 1.75) !important;
}
@media (prefers-reduced-motion) {
#gritter-container.top .gritter-item.popup > .popup-content {
transform: scale(1) translateY(0px) !important;
}
#gritter-container.bottom .gritter-item.popup > .popup-content {
transform: scale(1) translateY(0px) !important;
}
.gritter-item.popup.popup-show > .popup-content {
transform: scale(1) translateY(0px) !important;
transition: none;
}
}
/* for ep_deleted_after_delay */
.gritter-item #close_expiration_notif {
display: none;
}

View file

@ -0,0 +1,5 @@
#importmessagefail {
margin-top: 10px;
}
#importsubmitinput[disabled] { opacity: .6; }

View file

@ -0,0 +1,177 @@
.popup-content {
border-radius: 5px;
padding: 25px;
background: none;
background-color: #ffffff;
background-color: var(--bg-color);
color: #576273;
color: var(--text-soft-color);
border: none;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
}
#mycolorpicker, #users {
min-width: 0;
}
.popup h1 {
margin-bottom: 20px;
font-size: 1.6rem;
}
.popup h2 {
margin-bottom: 15px;
margin-top: 20px;
color: #485365;
color: var(--text-color);
}
.settings-sections {
display: grid;
gap: 24px;
}
.settings-section {
margin-bottom: 20px;
min-width: 0;
}
#settings .settings-section > h2 {
margin-top: 0;
}
.popup:not(.comment-modal) p {
margin: 10px 0;
}
.popup .dropdowns-container .dropdown-line {
margin-top: 15px;
}
.popup .dropdowns-container label {
width: 120px;
display: inline-block;
}
.popup .dropdowns-container .nice-select {
min-width: 180px;
}
.settings-notice {
color: var(--text-color);
font-size: 0.95rem;
line-height: 1.4;
border-left: 3px solid var(--primary-color);
padding-left: 10px;
}
@media (min-width: 1100px) {
.settings-sections.has-pad-settings {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: start;
}
}
@media (prefers-reduced-motion) {
.popup>.popup-content {
transform: scale(1);
transition: none;
}
.nice-select .list {
transform: scale(1) translateY(0px);
-webkit-transform: scale(1) translateY(0px);
-ms-transform: scale(1) translateY(0px);
transition: none;
}
}
@media (max-width: 800px) {
.popup-content {
padding: 1rem;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), -1px 1px 16px 3px rgba(27, 39, 51, 0.12);
}
.popup .dropdowns-container select {
min-width: 0;
}
}
/* SKIN Variants Popup */
#skin-variants {
bottom: 0;
left: 0;
right: auto;
top: auto;
}
#skin-variants .popup-content > p {
margin-top: 25px;
}
#skin-variants-result{
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
.skin-variant-container {
text-transform: capitalize;
}
#delete-pad {
margin-top: 20px;
}
/* Pad deletion-token modal + delete-with-token disclosure (GDPR PR1) */
#deletiontoken-modal .popup-content {
max-width: 32rem;
}
#deletiontoken-modal .deletiontoken-row {
display: flex;
gap: 0.5rem;
margin: 1rem 0;
align-items: center;
}
#deletiontoken-modal #deletiontoken-copy {
padding-top: 10px;
}
#deletiontoken-modal #deletiontoken-value {
flex: 1;
font-family: monospace;
padding: 0.4rem;
user-select: all;
}
#delete-pad-with-token {
margin-top: 0.5rem;
}
#delete-pad-with-token summary {
display: inline-block;
list-style: none;
cursor: pointer;
padding: 5px 20px;
border-radius: 4px;
font-weight: bold;
background: #d1242f;
color: #fff;
}
#delete-pad-with-token summary::-webkit-details-marker {
display: none;
}
#delete-pad-with-token summary:hover {
background: #b71c26;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.08), 0 1px 2px rgba(0,0,0,0.12);
transform: translateY(-1px);
}
#delete-pad-with-token label {
display: block;
padding-top: 12px;
}
#delete-pad-with-token input {
margin: 0.5rem 0;
width: 100%;
font-family: monospace;
padding: 0.4rem;
}

View file

@ -0,0 +1,41 @@
@media (min-width: 721px) {
::-webkit-scrollbar-track {
background-color: white;
background-color: var(--scrollbar-track);
border-radius: 10px;
border: 7px solid #f2f3f4;
border: 7px solid var(--scrollbar-bg);
}
::-webkit-scrollbar {
width: 22px;
}
::-webkit-scrollbar-thumb {
min-height: 40px;
border-radius: 10px;
background-color: #576273;
background-color: var(--scrollbar-thumb);
border: 7px solid #f2f3f4;
border: 7px solid var(--scrollbar-bg);
}
}
.thin-scrollbar::-webkit-scrollbar-track {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
border-radius: 0px;
border: none;
}
.thin-scrollbar::-webkit-scrollbar {
width: 6px;
}
.thin-scrollbar::-webkit-scrollbar-thumb {
border-radius: 0px;
min-height: 40px;
background-color: #d2d2d2;
background-color: var(--middle-color);
border: none;
}

View file

@ -0,0 +1,31 @@
#sidediv {
background-color: transparent;
border: none;
opacity: .8;
}
#sidedivinner>div:before {
font-family: var(--main-font-family); /* the parent div have font-family monospace (line number) */
color: #485365;
color: var(--text-color);
font-weight: bold;
}
#sidedivinner>div .line-number {
line-height: inherit;
font-family: RobotoMono;
display: inline-block;
color: #576273;
color: var(--text-soft-color);
height:100%;
}
#sidedivinner>div .line-number:hover {
background-color: var(--bg-soft-color);
border-radius: 5px 0 0 5px;
font-weight: bold;
color: var(--text-color);
}
.plugin-ep_author_neat #sidedivinner>div .line-number:hover {
background-color: transparent;
}

View file

@ -0,0 +1,21 @@
#toc {
padding: 20px 20px 10px 10px !important;
min-width: 146px !important;
background-color: transparent !important;
border: none !important;
order: -2;
}
#tocItems {
line-height: 40px !important;
}
.plugin-ep_resizable_bars #toc {
min-width: 186px !important;
}
@media (max-width: 1200px) {
#toc {
padding-top: 10px !important
}
}

View file

@ -0,0 +1,154 @@
.toolbar {
border-bottom: none;
padding: 0;
background-color: #ffffff;
background-color: var(--bg-color);
color: #576273;
color: var(--text-soft-color);
border-bottom: none;
}
#editbar.editor-scrolled {
border-bottom: 1px solid #d2d2d2;
border-bottom: var(--toolbar-border);
}
.toolbar ul {
align-items: center;
}
.toolbar ul.menu_left {
padding-left: 5px;
}
.toolbar ul li {
margin: 7px 1px;
}
.toolbar ul li a, .toolbar .buttonicon {
color: inherit;
}
.toolbar .buttonicon {
background-color: transparent;
font-size: 15px;
}
.buttonicon-insertorderedlist:before,
.buttonicon-insertunorderedlist:before,
.buttonicon-indent:before,
.buttonicon-outdent:before {
font-size: 16px !important;
}
.toolbar ul li.separator {
visibility: hidden;
width: 1px;
margin: 0 10px;
position: relative;
}
.toolbar.condensed ul li {
margin-left: 0;
}
.toolbar.condensed ul li.separator {
margin: 0 5px;
}
.toolbar ul li a {
background-color: transparent;
background: none;
border: none;
border-radius: 3px !important;
transition: background-color .1s;
}
.toolbar ul li a:hover, .toolbar ul li select:hover {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
color: #485365;
color: var(--text-color);
}
.toolbar ul li a.selected {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
.toolbar ul li a.pressed,
.toolbar ul li select:active {
color: #64d29b;
color: var(--primary-color);
}
.toolbar ul li select:active option {
background-color: #ffffff;
background-color: var(--bg-color);
color: #576273;
color: var(--text-soft-color);
padding: 5px;
}
.toolbar .menu_right li a.selected {
background-color: #576273;
background-color: var(--text-soft-color);
color: #ffffff;
color: var(--bg-color);
}
.toolbar ul li[data-key=showusers] {
margin: 0;
margin-left: 15px;
width: 45px;
height: 100%;
}
.toolbar ul li[data-key=showusers] > a {
width: 100%;
height: 100%;
border-radius: 0 !important;
}
.toolbar .menu_right .separator {
display: none;
}
.toolbar .menu_right li {
margin-left: 10px;
}
.toolbar.cropped .menu_left {
height: 39px;
padding-top: 1px;
}
.toolbar .show-more-icon-btn {
font-size: 1.8rem;
color: #64d29b;
color: var(--primary-color);
}
@media (max-width: 1000px) {
.toolbar ul li.separator {
margin: 0 5px;
background: none;
display: block;
}
}
.mobile-layout .toolbar ul li {
margin: 5px 2px;
}
.mobile-layout .toolbar ul li.separator {
margin: 0 5px;
}
@media (max-width: 800px) {
.mobile-layout .toolbar ul li.separator {
display: none;
}
}
.mobile-layout .toolbar .menu_right {
border-top: 1px solid #d2d2d2;
border-top: var(--toolbar-border);
background-color: #ffffff;
background-color: var(--bg-color);
padding: 0;
}
.mobile-layout .toolbar ul li a:hover {
/* background-color: transparent; */
}

View file

@ -0,0 +1,65 @@
table#otheruserstable {
margin-top: 20px;
}
/*
* Clicking a row in the user list opens chat and prefills "@<name> ".
* The pointer cursor on the name cell makes the affordance visible
* the swatch keeps its own (color-picker) click semantics, so leave
* its cursor alone.
*/
#otheruserstable tr[data-authorId] .usertdname {
cursor: pointer;
}
#otheruserstable tr[data-authorId]:hover .usertdname {
text-decoration: underline;
}
.popup#users.chatAndUsers > .popup-content {
padding: 20px 10px;
height: 250px;
border-left: none;
transition: none;
border-bottom-color: #d2d2d2;
border-bottom-color: var(--border-color);
}
.popup#users.chatAndUsers #mycolorpicker.popup {
right: calc(100% + 30px);
top: 15px;
}
#otheruserstable .swatch {
border: none !important;
border-radius: 50%;
width: 18px;
height: 18px;
margin: 0;
margin-left: 1px;
margin-right: 15px;
}
#myusernameform {
margin-left: 10px;
}
input#myusernameedit {
min-width: 110px;
border: none !important;
background-color: transparent !important;
border-bottom: 1px solid #d2d2d2 !important;
border-bottom: 1px solid var(--border-color) !important;
border-radius: 0;
padding-bottom: 5px;
}
#myswatch {
border-radius: 50%;
}
#colorpicker {
margin-bottom: 25px;
}
#mycolorpickerpreview {
border-radius: 50%;
}

View file

@ -0,0 +1,11 @@
body {
color: #485365;
color: var(--text-color);
font-family: Quicksand, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
font-family: var(--main-font-family);
}
h1 {
color: #64d29b;
color: var(--primary-color);
}

View file

@ -0,0 +1,48 @@
#outerdocbody {
margin: 0 auto;
padding-top: 20px;
width: 100%;
}
#editorcontainerbox {
background-color: #f2f3f4;
background-color: var(--bg-color);
color: var(--text-color);
}
#editorcontainerbox .sticky-container {
width: 250px;
}
#outerdocbody iframe, #outerdocbody > #innerdocbody {
max-width: 900px;
padding: 40px 55px;
padding-left: var(--editor-horizontal-padding);
padding-right: var(--editor-horizontal-padding);
padding-top: var(--editor-vertical-padding);
padding-bottom: var(--editor-vertical-padding);
box-shadow: none;
border: 0;
border-radius: 8px 8px 0 0;
background-color: #ffffff;
background-color: var(--bg-color);
color: #485365;
color: var(--text-color);
}
#sidediv {
/* Padding must be the same than editor, otherwise it creates problem */
padding-top: 40px; /* = #innerdocbody iframe vertical padding */
padding-bottom: 40px;
padding-top: calc(var(--editor-vertical-padding) + 15px);
padding-bottom: calc(var(--editor-vertical-padding) + 15px);
}
@media (max-width:1000px) {
#outerdocbody {
padding-top: 0;
}
#outerdocbody iframe, #outerdocbody > #innerdocbody {
max-width: none;
border-radius: 0;
}
}

View file

@ -0,0 +1,48 @@
/* pad-editor.css loaded INSIDE the inner editor iframe by Etherpad.
* Defines theme tokens here too, since CSS from the parent document
* doesn't cascade into iframes. The data-theme attribute is propagated
* onto html/#innerdocbody by pad.js. */
#innerdocbody {
background: transparent;
color: var(--text-color, #485365);
}
[data-theme="editorial"] { --text-color:#1c1916; --bg-color:#fbf8f2; --m-bg:#f5f0e8; }
[data-theme="brutalist"] { --text-color:#000; --bg-color:#fff; --m-bg:#f3f3f0; }
[data-theme="paper"] { --text-color:#2a2520; --bg-color:#fbf6ec; --m-bg:#f6f1e8; }
[data-theme="crt"] { --text-color:#7fffae; --bg-color:#08200f; --m-bg:#04140a; }
[data-theme="industrial"] { --text-color:#e6e8eb; --bg-color:#14171c; --m-bg:#0d0f12; }
[data-theme] #innerdocbody,
[data-theme] body { background: var(--m-bg) !important; color: var(--text-color) !important; }
[data-theme] #innerdocbody a { color: var(--text-color); }
[data-theme="crt"] #innerdocbody { text-shadow: 0 0 6px rgba(127,255,174,.45); font-family: "IBM Plex Mono", ui-monospace, monospace; }
/* Comment highlights in the editor upstream forces #fffacc + dark text,
* which looks like a white slab on dark themes. Replace with a tinted
* underline-ish highlight that adapts to the current theme. */
[data-theme] #innerdocbody .ace-line .comment {
background-color: color-mix(in srgb, var(--accent, #f5d76e) 22%, transparent) !important;
color: inherit !important;
box-shadow: inset 0 -2px 0 0 var(--accent, #f5d76e);
border-radius: 1px;
}
[data-theme="editorial"] { --accent:#b8451f; }
[data-theme="brutalist"] { --accent:#ff3b00; }
[data-theme="paper"] { --accent:#7a5a2f; }
[data-theme="crt"] { --accent:#f5d76e; }
[data-theme="industrial"] { --accent:#ff8a3d; }
/* CRT drop-cap on the very first line of the pad. Etherpad wraps each line
* in a <div>, so target the first child block of #innerdocbody. */
[data-theme="crt"] #innerdocbody > div:first-child::first-letter {
font-family: "VT323", "IBM Plex Mono", monospace;
font-size: 3.2em;
line-height: 0.85;
float: left;
padding: 0.05em 0.12em 0 0;
color: #f5d76e;
text-shadow: 0 0 10px rgba(245,215,110,.7), 0 0 22px rgba(245,215,110,.35);
}

View file

@ -0,0 +1,228 @@
/* =========================== */
/* === Super Light Toolbar === */
/* =========================== */
.super-light-toolbar .toolbar, .super-light-toolbar .popup-content, .super-light-toolbar #pad_title {
--text-color: var(--super-dark-color);
--text-soft-color: var(--dark-color);
--border-color: #e4e6e9;
--bg-soft-color: var(--light-color);
--bg-color: var(--super-light-color);
}
/* ===================== */
/* === Light Toolbar === */
/* ===================== */
.light-toolbar .toolbar, .light-toolbar .popup-content, .light-toolbar #pad_title {
--text-color: var(--super-dark-color);
--text-soft-color: var(--dark-color);
--border-color: var(--middle-color);
--bg-soft-color: var(--super-light-color);
--bg-color: var(--light-color);
}
/* ========================== */
/* === Super Dark Toolbar === */
/* ========================== */
.super-dark-toolbar .toolbar, .super-dark-toolbar .popup-content, .super-dark-toolbar #pad_title {
--text-color: var(--super-light-color);
--text-soft-color: var(--light-color);
--border-color: var(--dark-color);
--bg-soft-color: var(--dark-color);
--bg-color: var(--super-dark-color);
}
.super-dark-toolbar.super-dark-editor .popup-content {
border: 1px solid var(--dark-color);
box-shadow: none;
}
/* ==================== */
/* === Dark Toolbar === */
/* ==================== */
.dark-toolbar .toolbar, .dark-toolbar .popup-content, .dark-toolbar #pad_title {
--text-color: var(--super-light-color);
--text-soft-color: var(--light-color);
--border-color: var(--super-dark-color);
--bg-soft-color: var(--super-dark-color);
--bg-color: var(--dark-color);
}
/* ============================ */
/* == Super Light Background == */
/* ============================ */
.super-light-background #editorcontainerbox, .super-light-background #sidediv,
.super-light-background #chatbox, .super-light-background #outerdocbody, .super-light-background {
--text-color: var(--super-dark-color);
--text-soft-color: var(--dark-color);
--border-color: #e4e6e9;
--bg-soft-color: var(--light-color);
--bg-color: var(--super-light-color);
}
.super-light-background body, .full-width-editor.super-light-editor body:not(.comments-active) {
--scrollbar-bg: var(--super-light-color);
--scrollbar-track: var(--light-color);
--scrollbar-thumb: var(--dark-color);
}
.super-light-background .compact-display-content {
background-color: var(--super-light-color);
}
/* ====================== */
/* == Light Background == */
/* ====================== */
.light-background #editorcontainerbox, .light-background #sidediv,
.light-background #chatbox, .light-background #outerdocbody, .light-background {
--text-color: var(--super-dark-color);
--text-soft-color: var(--dark-color);
--border-color: var(--middle-color);
--bg-soft-color: var(--super-light-color);
--bg-color: var(--light-color);
}
.light-background body, .full-width-editor.light-editor body:not(.comments-active) {
--scrollbar-bg: var(--light-color);
--scrollbar-track: var(--super-light-color);
--scrollbar-thumb: var(--dark-color);
}
.light-background .compact-display-content {
background-color: var(--light-color);
}
/* =========================== */
/* == Super Dark Background == */
/* =========================== */
.super-dark-background #editorcontainerbox, .super-dark-background #sidediv,
.super-dark-background #chatbox, .super-dark-background #outerdocbody, .super-dark-background {
--text-color: var(--super-light-color);
--text-soft-color: var(--light-color);
--border-color: var(--dark-color);
--bg-soft-color: var(--dark-color);
--bg-color: var(--super-dark-color);
}
.super-dark-background body, .full-width-editor.super-dark-editor body:not(.comments-active) {
--scrollbar-bg: var(--super-dark-color);
--scrollbar-track: var(--dark-color);
--scrollbar-thumb: var(--light-color);
}
.super-dark-background .compact-display-content {
background-color: var(--super-dark-color);
}
/* Special combinaison with toolbar */
.super-dark-background.super-dark-toolbar .popup-content {
border: 1px solid var(--dark-color);
box-shadow: none;
}
/* ===================== */
/* == Dark Background == */
/* ===================== */
.dark-background #editorcontainerbox, .dark-background #sidediv,
.dark-background #chatbox, .dark-background #outerdocbody, .dark-background {
--text-color: var(--super-light-color);
--text-soft-color: var(--light-color);
--border-color: var(--super-dark-color);
--bg-soft-color: var(--super-dark-color);
--bg-color: var(--dark-color);
}
.dark-background body, .full-width-editor.dark-editor body:not(.comments-active) {
--scrollbar-bg: var(--dark-color);
--scrollbar-track: var(--super-dark-color);
--scrollbar-thumb: var(--light-color);
}
.dark-background .compact-display-content {
background-color: var(--dark-color);
}
/* Special combinaison with toolbar */
.dark-background.dark-toolbar .popup-content, .dark-editor.dark-toolbar .popup-content {
box-shadow: 0 0 14px 0px var(--super-dark-color);
}
/* ======================== */
/* == Super Light Editor == */
/* ======================== */
.super-light-editor #outerdocbody iframe, .super-light-editor #outerdocbody > #innerdocbody {
--bg-color: var(--super-light-color);
}
.super-light-editor #innerdocbody {
--text-color: var(--super-dark-color);
}
/* ================== */
/* == Light Editor == */
/* ================== */
.light-editor #outerdocbody iframe, .light-editor #outerdocbody > #innerdocbody {
--bg-color: var(--light-color);
}
.light-editor #innerdocbody {
--text-color: var(--super-dark-color);
}
/* ======================= */
/* == Super Dark Editor == */
/* ======================= */
.super-dark-editor #outerdocbody iframe, .super-dark-editor #outerdocbody > #innerdocbody {
--bg-color: var(--super-dark-color);
}
.super-dark-editor #innerdocbody {
--text-color: var(--super-light-color);
}
/* ================= */
/* == Dark Editor == */
/* ================= */
.dark-editor #outerdocbody iframe, .dark-editor #outerdocbody > #innerdocbody {
--bg-color: var(--dark-color);
}
.dark-editor #innerdocbody {
--text-color: var(--super-light-color);
}
/* ======================================== */
/* == Combinaison with background/editor == */
/* ======================================== */
.super-light-editor.super-light-background #outerdocbody,
.light-editor.light-background #outerdocbody,
.super-dark-editor.super-dark-background #outerdocbody,
.dark-editor.dark-background #outerdocbody {
padding-top: 0;
}
@media (min-width: 1001px) {
.super-light-editor.super-light-background,
.light-editor.light-background,
.super-dark-editor.super-dark-background,
.dark-editor.dark-background {
--editor-horizontal-padding: 20px;
--editor-vertical-padding: 5px;
}
}
/* ===================================== */
/* == Combinaison with toolbar/editor == */
/* ===================================== */
.super-light-editor.super-light-toolbar .toolbar,
.light-editor.light-toolbar .toolbar,
.super-dark-editor.super-dark-toolbar .toolbar,
.dark-editor.dark-toolbar .toolbar {
--toolbar-border: 1px solid var(--border-color);
}
/* ======================= */
/* == Full Width Editor == */
/* ======================= */
.full-width-editor #outerdocbody iframe, .full-width-editor #outerdocbody > #innerdocbody {
max-width: none !important;
border-radius: 0;
}
.full-width-editor #outerdocbody {
padding: 0;
margin: 0;
}
@media (min-width: 1001px) {
.full-width-editor {
--editor-horizontal-padding: 20px !important;
--editor-vertical-padding: 5px !important;
}
}
.full-width-editor ::-webkit-scrollbar-track,
.full-width-editor ::-webkit-scrollbar-thumb {
border-radius: 0px;
}

View file

@ -0,0 +1,10 @@
.authortooltip {
opacity: 1!important;
border-radius: 2px;
padding: 4px 10px 3px!important;
text-transform: uppercase;
font-size: 13px!important;
font-weight: 700;
color: #000;
background-color: rgba(255, 255, 255, 0.85) !important;
}

View file

@ -0,0 +1,14 @@
#colorpicker a.brightColorPicker-cancelButton {
background: none;
padding: 0;
padding-top: 10px;
font-weight: bold;
border: none;
}
.brightColorPicker-colorPanel {
background-color: white !important;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08) !important;
border-radius: 3px !important;
padding: 15px !important;
}

View file

@ -0,0 +1,112 @@
.sidebar-comment .btn {
margin-top: 10px;
padding: 3px 8px;
font-size: .9rem;
margin: 10px 0 5px 0;
}
.sidebar-comment .btn.btn-primary:not(#comment-create-btn) {
background-color: #576273;
background-color: var(--text-soft-color);
}
.sidebar-comment .suggestion-create {
margin-top: 10px;
}
.suggestion-display .from-value, .suggestion-display .to-value {
color: #64d29b;
color: var(--primary-color);
font-weight: bold;
opacity: 1;
}
.suggestion-display .from-value {
margin-right: 5px;
}
.comment-actions-wrapper .buttonicon {
opacity: .8;
}
.comment-actions-wrapper .buttonicon:hover {
opacity: 1;
}
.comment-actions-wrapper .comment-edit {
margin-right: 5px;
}
[type="checkbox"] + label.label-suggestion-checkbox {
margin-left: 5px;
padding-left: 2.4rem;
}
.sidebar-comment .full-display-content {
margin-left: -10px;
box-shadow: none;
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
border: 1px solid #ffffff;
border: 1px solid var(--bg-color);
}
.comment-reply {
border-top: 1px solid #ffffff;
border-top: 1px solid var(--bg-color);
background-color: inherit;
}
.comment-reply textarea, .comment-reply input[type="text"] {
background-color: #ffffff;
background-color: var(--bg-color);
}
.btn.revert-suggestion-btn {
padding-left: 0;
}
.comment-edit-form {
margin-top: 15px;
}
/* MODAL */
.comment-modal .full-display-content {
box-shadow: none;
margin: 0 !important;
border: none;
background-color: #ffffff;
background-color: var(--bg-color);
}
.comment-modal .comment-modal-comment {
padding: 0;
}
.comment-modal .comment-reply textarea, .comment-modal .comment-reply input[type="text"] {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
.comment-modal .comment-reply {
border-top: 1px solid #f2f3f4;
border-top: 1px solid var(--bg-soft-color);
}
.comment-modal .full-display-content .comment-title-wrapper,
.comment-modal .full-display-content .comment-reply {
padding: 15px;
}
/* NEW COMMENT POPUP */
.new-comment-popup textarea {
background-color: #f2f3f4;
background-color: var(--bg-soft-color);
}
.new-comment-popup .suggestion {
margin-bottom: 10px;
}
/* EDITOR COMMENTEED LINE */
#innerdocbody .ace-line .comment {
background-color: #fffacc;
color: var(--super-dark-color);
}
@media (min-width: 1200px) {
#comments {
width: 300px;
}
.sidebar-comment .full-display-content {
margin-left: 10px;
}
.compact-display-content {
padding-left: 20px;
}
}

View file

@ -0,0 +1,41 @@
.font-color-icon {
display: none !important;
}
#font-color {
display: list-item !important;
}
.readonly #font-color {
display: none !important;
}
.color\:black,
[data-color=black] {
color: #485365;
color: var(--text-color);
}
.color\:red,
[data-color=red] {
color: #F44336;
}
.color\:green,
[data-color=green] {
color: #66d29c;
}
.color\:blue,
[data-color=blue] {
color: #2196f3;
}
.color\:yellow,
[data-color=yellow] {
color: #ffeb3b;
}
.color\:orange,
[data-color=orange] {
color: #FF9800;
}

View file

@ -0,0 +1,7 @@
#pad_title {
border-bottom: 1px solid var(--border-color) !important;
background-color: var(--bg-color) !important;
}
#edit_title {
color: var(--text-soft-color);
}

View file

@ -0,0 +1,239 @@
/* MENU ICON*/
#editbar #tbl_menu_list {
width: auto !important;
}
#tbl-menu {
background: none !important;
width: 18px !important;
padding-left: 2px !important;
}
#tbl-menu:before {
content: "\F0CE";
}
#tbl_menu_list > a {
font-size: 16px;
margin-top: 8px;
padding-left: 0;
padding-right: 2px;
padding-bottom: 4px;
}
/* DROP DOWN MENU */
#tbl_context_menu {
margin-left: -24px;
border: none;
margin-top: 9px;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
border-radius: 3px;
background-color: white;
font-size: 100%;
line-height: 1.7;
}
#tbl_context_menu > .bd {
border: none;
background-color: transparent;
}
#tbl_context_menu > .bd > ul {
padding: 6px 0;
}
/* TABLE SIZE PICKER */
#tbl_insert {
background-color: white;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
border-radius: 3px;
}
#tbl_insert .bd {
border: none;
text-align: center;
background-color: transparent;
padding-top: 4px;
}
#tbl_insert .yuimenuitemlabel { text-align: center; }
#tbl_insert .ft {
margin: 0;
border: none;
background-color: transparent;
padding: 6px;
padding-top: 0;
}
#matrix_table tr td {
border: 1px solid #d7d7d7;
height: 1px;
padding: 7px;
width: 11px;
background-color: #fbfbfb;
border-radius: 1px;
}
#matrix_table tr td.selected {
border: 1px solid #789dce;
background-color: #b3d4ff;
}
/* TABLE SETTINGS POPUP */
.yui-skin-sam .yui-panel-container {
padding: 0;
margin: 0;
background-color: #fff;
box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08);
border-radius: 5px;
padding-bottom: 15px;
}
.yui-skin-sam .yui-panel-container .yui-panel {
border: none !important;
background: none;
box-shadow: none !important;
}
.yui-skin-sam .yui-panel-container .yui-panel .hd {
cursor: move;
padding: 0;
border: 0;
background: 0;
margin: 0;
font-size: 14px;
line-height: 40px;
text-transform: uppercase;
padding: 0 15px;
padding-top: 5px;
font-weight: bold;
border-bottom: 1px solid #d2d2d2;
}
.yui-skin-sam .yui-panel-container .yui-panel .container-close {
top: 15px;
border: none;
background: none;
color: white;
text-indent: 0;
}
.yui-skin-sam .yui-panel-container .yui-panel .container-close::before {
content: "x";
color: #6f757a;
font-size: 16px;
font-weight: bold;
}
.yui-skin-sam .yui-panel-container .yui-panel .bd {
background: none;
border: none;
box-shadow: none;
padding: 15px;
background-color: transparent !important;
}
.yui-panel .underlay, .yui-skin-sam .yui-panel-container.shadow .underlay {
display: none !important;
}
#div_tbl_btn_close {
float: right;
position: relative;
width: 100%;
margin-top: 10px;
right: 0;
bottom: 0;
}
#tbl_btn_close {
border: none;
color: #ffffff;
height: 30px;
width: 100%;
border-radius: 3px;
text-transform: uppercase;
}
#tbl_btn_close:hover { cursor: pointer; }
.yui-skin-sam .yui-button {
background: none;
background-color: white;
border: none;
height: 24px;
margin-bottom: -4px;
margin-top: 5px;
}
.yui-skin-sam .yui-button .first-child { margin: 0; border: none; }
.yui-skin-sam .yui-split-button button {
padding: 0;
background: none !important;
}
.yui-skin-sam .yui-split-button button em:not(.color-picker-button) {
font-style: normal !important;
border-bottom: 1px solid #b5b7b7;
padding: 0 5px;
margin: 0 5px;
padding-bottom: 3px;
}
button#yui-gen13-button {
margin-left: -5px;
}
button .color-picker-button {
border: 1px solid #c1c2c2;
border-radius: 50%;
width: 16px;
height: 16px;
margin-top: 2px;
}
#even-row-bg-color, #single-row-bg-color {
margin-right: 5px;
}
#single-col-bg-color, #odd-row-bg-color {
margin-left: 7px;
}
#yui-tbl-prop-panel .text-input[type=text] {
border: 1px solid #d2d2d2;
float: right;
height: 10px;
border-radius: 3px;
padding: 8px 10px;
}
#text_input_message {
background-color: #64d29b;
padding: 0 5px;
color: white;
font-size: 12px;
border-radius: 5px;
font-weight: bold;
display: none;
}
/* TABLES INSIDE THE PAD */
td[name=tData] {
/*border: 1px solid grey !important;*/
}
#yui-picker-panel_c
{
padding-bottom: 40px;
}
div#yui-picker-panel_h {
line-height: 1.8em;
font-size: 13px;
padding: 9px 15px 5px;
}
#yui-picker-panel .ft {
position: relative;
border: none;
width: 100%;
padding: 0;
margin-top: 20px;
}

View file

@ -0,0 +1,108 @@
#timeslider-slider #ui-slider-handle {
border-radius: 3px;
width: 12px;
height: 28px;
background-color: #64d29b;
background-color: var(--primary-color);
}
#timeslider-slider #ui-slider-bar {
border-radius: 3px;
background-color: #d2d2d2;
background-color: var(--border-color);
}
#slider-btn-container {
margin: -18px 15px 0 20px;
}
#slider-btn-container #playpause_button_icon {
color: #ffffff;
color: var(--bg-color);
background-color: #64d29b;
background-color: var(--primary-color);
border: none;
margin-right: 5px;
padding-top: 3px;
width: 45px;
height: 45px;
}
#slider-btn-container #playpause_button_icon:not(.pause) {
padding-left: 4px;
}
#slider-btn-container .stepper {
border: 2px solid !important;
height: 30px;
width: 30px;
line-height: 28px;
margin-left: 5px;
font-size: 13px;
color: #64d29b;
color: var(--primary-color);
border-color: #64d29b;
border-color: var(--primary-color);
}
#slider-btn-container .stepper.disabled {
opacity: .5;
}
.timeslider #editbar .buttontext {
background-color: #576273;
background-color: var(--text-soft-color);
color: #ffffff;
color: var(--bg-color);
margin: 0;
}
#editbar {
display: block;
padding-bottom: 5px;
}
#editbar li > a {
border-radius: 3px;
}
#timeslider-slider #timer {
opacity: .7;
top: -12px;
font-size: .8em;
}
.timeslider #authorsList .author {
padding: 2px 5px;
border-radius: 3px;
margin-right: 4px;
margin-bottom: 4px;
}
.timeslider-title {
font-size: 1.8rem !important;
}
.timeslider-subtitle {
margin-top: 6px;
font-size: .9em;
}
.timeslider #outerdocbody > #sidediv {
padding-top: 30px;
padding-bottom: 30px;
}
.timeslider #outerdocbody > #innerdocbody {
padding-top: 30px;
padding-bottom: 30px;
}
@media (max-width: 800px) {
#slider-btn-container {
margin-top: 0;
margin-right: 5px;
}
#slider-btn-container #playpause_button_icon {
width: 30px;
height: 30px;
}
#slider-btn-container #playpause_button_icon:before {
font-size: 18px;
}
}

View file

@ -0,0 +1,42 @@
'use strict';
// The timeslider page loads margin's pad.css, whose theme tokens live under
// [data-theme="..."][data-mode="..."]. Without this bootstrap the history
// view (and the in-place #history-frame iframe spun up by pad_mode.ts) would
// render with no theme applied — visually broken vs the pad it came from.
// Keep this list in sync with MARGIN_THEMES in pad.js.
const MARGIN_THEME_VALUES = [
'colibris', 'editorial', 'brutalist', 'paper', 'crt', 'industrial',
];
const MARGIN_THEME_DEFAULT = 'colibris';
const MARGIN_MODE_DEFAULTS = {
colibris: 'light', editorial: 'light', brutalist: 'light',
paper: 'light', crt: 'dark', industrial: 'dark',
};
const readStorage = (key) => {
try { return localStorage.getItem(key); } catch (_) { return null; }
};
// When opened as the in-place history iframe (#history-frame) prefer the
// parent doc's live attribute over localStorage — that way history mirrors
// whatever the pad currently shows even if the user toggled theme after
// localStorage was last written.
const readFromParent = (attr) => {
try {
if (window.parent && window.parent !== window) {
return window.parent.document.documentElement.getAttribute(attr);
}
} catch (_) { /* cross-origin — ignore */ }
return null;
};
let theme = readFromParent('data-theme') || readStorage('marginTheme');
if (!MARGIN_THEME_VALUES.includes(theme)) theme = MARGIN_THEME_DEFAULT;
let mode = readFromParent('data-mode') || readStorage('marginMode');
if (mode !== 'light' && mode !== 'dark') mode = MARGIN_MODE_DEFAULTS[theme] || 'light';
document.documentElement.setAttribute('data-theme', theme);
document.documentElement.setAttribute('data-mode', mode);
window.customStart = () => {
};