diff --git a/README.md b/README.md
index 83a53f5..562f804 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,12 @@ Install it with the same procedure above, copying `eternity-wasteland` instead o
$config['skin'] = 'eternity-wasteland';
```
+### Login Theme Toggle
+
+Wasteland opens in dark mode. Its login screen includes a **Dark mode** toggle that switches to the Eternity light palette without reloading the page. The choice is stored in the browser's `colorMode` cookie for one year and is applied on later visits to the login screen.
+
+Clearing site cookies resets the Wasteland login screen to dark mode. This setting only changes the Wasteland login page; choose the desired Roundcube interface skin in **Settings > Preferences > User Interface > Interface skin** after signing in.
+
## Docker Install
### Docker Compose (persistent)
@@ -54,7 +60,7 @@ Place this repository alongside the Compose file, then bind-mount the skin into
services:
roundcube:
volumes:
- - ./ep-roundcube-skin/eternity:/var/www/html/skins/eternity:ro
+ - ./ep-roundcube-skin/eternity-wasteland:/var/www/html/skins/eternity-wasteland:ro
- ./roundcube-config/config.inc.php:/var/roundcube/config/config.inc.php:ro
```
@@ -63,7 +69,7 @@ Create `./roundcube-config/config.inc.php` if it does not already exist and incl
```php
/dev/null'` to identify the active configuration file. Restart the service after changing either mount:
@@ -79,16 +85,16 @@ Use this method to test the skin on a container that is already running. Replace
1. Copy the skin from this repository into the container:
```sh
- docker cp ./eternity roundcube:/var/www/html/skins/eternity
+ docker cp ./eternity-wasteland roundcube:/var/www/html/skins/eternity-wasteland
```
2. Confirm Roundcube can see the skin files:
```sh
- docker exec roundcube sh -c 'test -f /var/www/html/skins/eternity/meta.json && test -f /var/www/html/skins/eternity/styles/styles.css'
+ docker exec roundcube sh -c 'test -f /var/www/html/skins/eternity-wasteland/meta.json && test -f /var/www/html/skins/eternity-wasteland/styles/styles.css'
```
-3. Add `$config['skin'] = 'eternity';` to the active `config.inc.php` in the container, then restart it:
+3. Add `$config['skin'] = 'eternity-wasteland';` to the active `config.inc.php` in the container, then restart it:
```sh
docker restart roundcube
@@ -96,7 +102,7 @@ Use this method to test the skin on a container that is already running. Replace
This live-container approach is not persistent: `docker compose up --force-recreate`, `docker rm`, or an image update removes the copied skin and in-container configuration change. Once the skin is confirmed, add the two bind mounts from the Compose example so both survive container recreation.
-After either deployment method, hard-refresh the browser or clear its cache. If the old appearance remains, inspect the browser network panel to confirm that `/skins/eternity/styles/styles.css` returns HTTP 200.
+After either deployment method, hard-refresh the browser or clear its cache. If the old appearance remains, inspect the browser network panel to confirm that `/skins/eternity-wasteland/styles/styles.css` returns HTTP 200.
If instead the page looks entirely unstyled — login credential fields stacked in the wrong place, an oversized logo, or mailbox toolbars/tabs scattered and hard to read — `styles/styles.css` is loading but its `@import` of `../../elastic/styles/styles.css` is not, so none of Elastic's layout rules apply. Confirm that file exists and is readable:
@@ -111,15 +117,15 @@ If it is `MISSING`, the Elastic skin was installed from source without its LESS
Run these commands on the Docker host, replacing `roundcube` with the container name or ID from `docker ps`. They distinguish an installation issue from an activation issue:
```sh
-docker exec roundcube sh -c 'for file in /var/www/html/skins/eternity/meta.json /var/www/html/skins/eternity/styles/styles.css /var/www/html/skins/elastic/styles/styles.css; do test -f "$file" && echo "FOUND $file" || echo "MISSING $file"; done'
+docker exec roundcube sh -c 'for file in /var/www/html/skins/eternity-wasteland/meta.json /var/www/html/skins/eternity-wasteland/styles/styles.css /var/www/html/skins/elastic/styles/styles.css; do test -f "$file" && echo "FOUND $file" || echo "MISSING $file"; done'
docker exec roundcube sh -c 'grep -RIn "\$config\[.skin.\]" /var/roundcube/config /var/www/html/config 2>/dev/null || true'
```
-The first command must report all three paths as `FOUND`. The second must show `eternity` as the configured skin. On the official image, the equivalent persistent environment setting is:
+The first command must report all three paths as `FOUND`. The second must show `eternity-wasteland` as the configured skin. On the official image, the equivalent persistent environment setting is:
```yaml
environment:
- ROUNDCUBEMAIL_SKIN: eternity
+ ROUNDCUBEMAIL_SKIN: eternity-wasteland
```
Roundcube can store a skin preference for each user. A user who previously selected another appearance may continue to receive that preference instead of the newly configured default. Sign out and test in a private browser window or select **Eternity** from Roundcube's **Settings > Preferences > User Interface > Interface skin**, when the preference is available.
@@ -127,10 +133,10 @@ Roundcube can store a skin preference for each user. A user who previously selec
For a browser-level check, open this URL directly, replacing the hostname with the Roundcube site:
```text
-https://webmail.example.com/skins/eternity/styles/styles.css
+https://webmail.example.com/skins/eternity-wasteland/styles/styles.css
```
-It must return CSS beginning with `@import url("../../elastic/styles/styles.css");`. A `404` means the skin is not mounted at the path served by the container. If it returns `200` but Roundcube still looks unchanged, inspect the mailbox page source: it must include a stylesheet URL containing `/skins/eternity/styles/styles.css`. If it instead contains `/skins/elastic/`, the active configuration or the user's stored preference still selects Elastic.
+It must return CSS beginning with `@import url("../../elastic/styles/styles.css");`. A `404` means the skin is not mounted at the path served by the container. If it returns `200` but Roundcube still looks unchanged, inspect the mailbox page source: it must include a stylesheet URL containing `/skins/eternity-wasteland/styles/styles.css`. If it instead contains `/skins/elastic/`, the active configuration or the user's stored preference still selects Elastic.
## Structure
@@ -138,6 +144,12 @@ It must return CSS beginning with `@import url("../../elastic/styles/styles.css"
eternity/
meta.json Skin registration and Elastic inheritance
styles/styles.css Visual overrides for Elastic
+eternity-wasteland/
+ images/logo.svg High-contrast Wasteland login mark
+ meta.json Skin registration and Elastic inheritance
+ styles/styles.css Dark and light Wasteland visual overrides
+ templates/login.html Login form with the theme toggle
+ watermark.html Wasteland login background
```
`styles/styles.css` imports Elastic before applying Eternity's overrides. Do not remove the import: the base skin provides the complete component layout and image assets.
diff --git a/eternity-wasteland/images/logo.svg b/eternity-wasteland/images/logo.svg
new file mode 100644
index 0000000..aee99d9
--- /dev/null
+++ b/eternity-wasteland/images/logo.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/eternity-wasteland/styles/styles.css b/eternity-wasteland/styles/styles.css
index 30b09e1..a75b213 100644
--- a/eternity-wasteland/styles/styles.css
+++ b/eternity-wasteland/styles/styles.css
@@ -32,6 +32,17 @@
--border-radius: 0;
}
+html[data-theme="light"] {
+ --ink: #11212b;
+ --paper: #f1f1e9;
+ --acid: #c7ef4b;
+ --orange: #ff6b35;
+ --line: #b9c5bc;
+ --muted: #59706b;
+ --field: #fbfbf6;
+ --flash-error: #ffe3d9;
+}
+
html,
body,
#layout,
@@ -49,6 +60,10 @@ body {
background-size: 70px 70px, 7px 7px;
}
+html[data-theme="light"] body {
+ background-image: linear-gradient(90deg, transparent 49.5%, rgb(17 33 43 / 5%) 50%, transparent 50.5%), linear-gradient(rgb(17 33 43 / 4%) 1px, transparent 1px);
+}
+
body > #layout,
#layout {
position: relative;
@@ -64,6 +79,10 @@ body > #layout,
background: rgb(22 18 16 / 82%);
}
+html[data-theme="light"] #layout::before {
+ background: rgb(241 241 233 / 82%);
+}
+
body a,
body .link,
#taskmenu a,
@@ -281,6 +300,77 @@ select:focus,
box-sizing: border-box;
}
+/* Elastic's login controls use more specific component rules than the shared
+ form selectors above, so set the Wasteland surfaces at the login boundary. */
+body.task-login,
+body.task-login #layout-content,
+body.task-login #login-form,
+body.task-login #login-footer {
+ color: var(--ink);
+}
+
+body.task-login #login-form .form-control,
+body.task-login #login-form .input-group-text,
+body.task-login #login-form .input-group .icon {
+ background-color: var(--field) !important;
+ color: var(--ink) !important;
+}
+
+body.task-login #login-form .form-control::placeholder {
+ color: var(--muted);
+ opacity: 1;
+}
+
+.theme-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: 1rem;
+ color: var(--muted);
+ cursor: pointer;
+ font-family: var(--font-family-monospace);
+ font-size: 11px;
+ letter-spacing: 0.5px;
+ text-transform: uppercase;
+}
+
+.theme-toggle input {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+}
+
+.theme-toggle-track {
+ position: relative;
+ width: 2.5rem;
+ height: 1.25rem;
+ border: 1px solid var(--ink);
+ background: var(--field);
+}
+
+.theme-toggle-track::after {
+ content: "";
+ position: absolute;
+ top: 0.1875rem;
+ left: 0.1875rem;
+ width: 0.75rem;
+ height: 0.75rem;
+ background: var(--orange);
+ transition: transform 150ms ease, background-color 150ms ease;
+}
+
+.theme-toggle input:checked + .theme-toggle-track::after {
+ transform: translateX(1.25rem);
+ background: var(--acid);
+}
+
+.theme-toggle input:focus-visible + .theme-toggle-track {
+ outline: 3px solid var(--acid);
+ outline-offset: 2px;
+}
+
body.task-login #messagestack {
top: auto;
bottom: 1rem;
diff --git a/eternity-wasteland/templates/login.html b/eternity-wasteland/templates/login.html
new file mode 100644
index 0000000..80d0ddc
--- /dev/null
+++ b/eternity-wasteland/templates/login.html
@@ -0,0 +1,51 @@
+