diff --git a/config/apache/standardebooks.org.conf b/config/apache/standardebooks.org.conf
index d5e2cb1e..e0ee2d1a 100644
--- a/config/apache/standardebooks.org.conf
+++ b/config/apache/standardebooks.org.conf
@@ -138,6 +138,14 @@ Define domain standardebooks.org
ProxySet connectiontimeout=5 timeout=240
+ # In RewriteCond, RewriteRule gets evaluated BEFORE RewriteCond, so $1 refers to the first
+ # match in RewriteRule
+ # Rewrite POST /some/url -> POST /some/url/post.php
+ RewriteCond %{REQUEST_METHOD} ^POST$
+ RewriteCond %{DOCUMENT_ROOT}/$1/ -d
+ RewriteCond %{DOCUMENT_ROOT}/$1/post.php -f
+ RewriteRule ^([^\.]+)$ $1/post.php [L]
+
# In case of 404, serve the 404 page specified by ErrorDocument, not the default FPM error page.
# Note that we can't use `ProxyErrorOverride on` because that catches ALL 4xx and 5xx HTTP headers
# and serves the default Apache page for them.
diff --git a/config/apache/standardebooks.test.conf b/config/apache/standardebooks.test.conf
index ff07bfd7..03d04e0f 100644
--- a/config/apache/standardebooks.test.conf
+++ b/config/apache/standardebooks.test.conf
@@ -137,6 +137,14 @@ Define domain standardebooks.test
ProxySet connectiontimeout=5 timeout=240
+ # In RewriteCond, RewriteRule gets evaluated BEFORE RewriteCond, so $1 refers to the first
+ # match in RewriteRule
+ # Rewrite POST /some/url -> POST /some/url/post.php
+ RewriteCond %{REQUEST_METHOD} ^POST$
+ RewriteCond %{DOCUMENT_ROOT}/$1/ -d
+ RewriteCond %{DOCUMENT_ROOT}/$1/post.php -f
+ RewriteRule ^([^\.]+)$ $1/post.php [L]
+
# In case of 404, serve the 404 page specified by ErrorDocument, not the default FPM error page.
# Note that we can't use `ProxyErrorOverride on` because that catches ALL 4xx and 5xx HTTP headers
# and serves the default Apache page for them.
diff --git a/templates/Footer.php b/templates/Footer.php
index 4f91e9b8..8848d44e 100644
--- a/templates/Footer.php
+++ b/templates/Footer.php
@@ -12,6 +12,9 @@
Donate
+
+ Settings
+
GitHub
diff --git a/templates/Header.php b/templates/Header.php
index 765b3fe1..9b6d52e8 100644
--- a/templates/Header.php
+++ b/templates/Header.php
@@ -16,6 +16,8 @@ if(!isset($manual)){
$manual = false;
}
+$colorScheme = $_COOKIE['color-scheme'] ?? 'auto';
+
# We hash with crc32 because it's faster than md5 and "good enough" for this simple cache-busting use case
header('content-type: application/xhtml+xml');
@@ -28,8 +30,10 @@ print("\n");
if($title != ''){ ?>= Formatter::ToPlainText($title) ?> - } ?>Standard Ebooks: Free and liberated ebooks, carefully produced for the true book lover.
if($description != ''){ ?> } ?>
-
+ if($colorScheme == 'auto' || $colorScheme == 'dark'){ ?>
+
+ } ?>
if($manual){ ?>
} ?>
diff --git a/www/css/core.css b/www/css/core.css
index d2f4b68e..a80df2de 100644
--- a/www/css/core.css
+++ b/www/css/core.css
@@ -95,22 +95,85 @@
--link-highlight: var(--button);
}
-@media(prefers-color-scheme: dark){
- :root{
- --body-text: var(--dark-body-text);
- --header: var(--dark-header);
- --button: var(--dark-button);
- --button-highlight: var(--dark-button-highlight);
- --border: var(--dark-border);
- --sub-text: var(--dark-sub-text);
- --body-bg: var(--dark-body-bg);
- --input-hover: var(--dark-input-hover);
- --input-border: var(--dark-input-border);
- --input-outline: var(--dark-input-outline);
- --link-highlight: var(--button-highlight); /* lighter looks better in dark mode */
- }
+/* Start CSS reset */
+/* Dont @import this, to prevent FOUC */
+html{
+ margin: 0;
+ padding: 0;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
}
+body{
+ margin: 0;
+ padding: 0;
+}
+
+a{
+ outline: 0;
+}
+
+blockquote{
+ margin: 0;
+ quotes: none;
+}
+
+p{
+ margin: 0;
+}
+
+ol{
+ margin: 0;
+ padding: 0;
+}
+
+ul{
+ margin: 0;
+ padding: 0;
+}
+
+h1{
+ margin: 0;
+}
+
+h2{
+ margin: 0;
+}
+
+h3{
+ margin: 0;
+}
+
+h4{
+ margin: 0;
+}
+
+h5{
+ margin: 0;
+}
+
+h6{
+ margin: 0;
+}
+
+figure{
+ margin: 0;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+q{
+ quotes: none;
+}
+
+button::-moz-focus-inner{
+ border: 0;
+}
+/* End reset */
+
html{
color: var(--body-text);
font-family: "Crimson Pro", Georgia, serif;
@@ -539,6 +602,11 @@ form button{
hyphens: none;
}
+form button{
+ line-height: 1;
+ margin-left: 1rem;
+}
+
.ebooks nav li.highlighted a:focus,
a.button:focus,
input[type="search"]:focus,
@@ -1480,6 +1548,10 @@ select[multiple] option:last-child{
margin-bottom: 1rem; /* needed for firefox */
}
+form[action="/settings"]{
+ margin: 0 1rem;
+}
+
form[action="/ebooks"] label{
max-width: 100%;
}
@@ -1932,7 +2004,7 @@ article.ebook section aside.donation p::after{
display: inline;
}
- footer ul li:nth-child(3)::after{
+ footer ul li:nth-child(4)::after{
content: "\A"; /* This is a line break */
white-space: pre;
margin: 0;
@@ -2051,6 +2123,24 @@ article.ebook section aside.donation p::after{
main.ebooks > ol{
grid-template-columns: repeat(2, 1fr);
}
+
+ footer ul li:nth-child(2)::after{
+ content: "\A"; /* This is a line break */
+ white-space: pre;
+ margin: 0;
+ }
+
+ footer ul li:nth-child(4)::after{
+ content: "\b7";
+ white-space: normal;
+ margin: 0 10px;
+ }
+
+ footer ul li:nth-child(5)::after{
+ content: "\A"; /* This is a line break */
+ white-space: pre;
+ margin: 0;
+ }
}
@media(max-width: 680px){
@@ -2242,10 +2332,16 @@ article.ebook section aside.donation p::after{
text-align: left;
}
- footer ul li:nth-child(5)::after{
- content: "\A"; /* This is a line break */
- white-space: pre;
- margin: 0;
+ form[action="/settings"] button{
+ display: block;
+ margin-top: 1rem;
+ margin-left: 0;
+ }
+
+ form[action="/settings"] button,
+ form[action="/settings"] span,
+ form[action="/settings"] select{
+ width: 100%;
}
}
@@ -2259,16 +2355,6 @@ article.ebook section aside.donation p::after{
.masthead ol li img.contact{
height: .75rem;
}
-}
-
-@media(max-width: 380px){
- main.front-page h1{
- font-size: 2rem;
- }
-
- h1,h2,h3,h4,h5,h6,code,.ebook h1 + p{
- hyphens: auto;
- }
body > footer{
padding-top: 0;
@@ -2282,6 +2368,16 @@ article.ebook section aside.donation p::after{
footer ul li::after{
display: none;
}
+}
+
+@media(max-width: 380px){
+ main.front-page h1{
+ font-size: 2rem;
+ }
+
+ h1,h2,h3,h4,h5,h6,code,.ebook h1 + p{
+ hyphens: auto;
+ }
main.ebooks > ol{
grid-template-columns: 1fr;
@@ -2308,7 +2404,6 @@ article.ebook section aside.donation p::after{
grid-row: 6;
}
-
body > header ul li:nth-child(2n)::after{
display: none;
}
@@ -2321,7 +2416,6 @@ article.ebook section aside.donation p::after{
display: none;
}
-
body > header ul li:nth-child(2) ~ li,
body > header ul li + li{
margin-top: 1rem;
@@ -2341,73 +2435,3 @@ article.ebook section aside.donation p::after{
text-align: left;
}
}
-
-@media(prefers-color-scheme: dark){
-
-
- main.front-page > section > section figure img{
- box-shadow: 3px 3px 1px rgba(0, 0, 0, .5);
- }
-
- body > header > a:focus{
- /* Since we use invert() to change the color of the SE logo,
- we must specify the light outline (which is dark)
- so that it inverts to white */
- outline: 1px dashed var(--light-input-outline);
- }
-
- article.ebook section#read-online a::before,
- article.ebook section#download ul li a[class]::before,
- article.ebook section#details ul li a[class]::before,
- footer p:last-child a::before,
- main.front-page > section > section figure.oss img + img,
- main.front-page > section > h2::before,
- main.front-page > section > h2::after{
- filter: invert() grayscale(100%) brightness(120%) drop-shadow(3px 3px 1px rgba(0, 0, 0, .25)); /* grayscale and brightness makes it hit about #eeeeee */
- }
-
- .masthead img[src*="portrait.svg"],
- .masthead img.contact{
- filter: invert();
- }
-
- .masthead img[src*="portrait.svg"]{
- border-color: var(--border-light);
- }
-
- aside.donation,
- article.ebook section aside.donation{
- background: rgba(0, 0, 0, .25);
- }
-
- aside.donation,
- article.ebook section aside.donation{
- box-shadow: none;
- }
-
- select,
- input[type="search"]{
- box-shadow: 1px 1px 0 rgba(0, 0, 0, .5) inset;
- }
-
- input::placeholder{
- color: rgba(255, 255, 255, .75);
- }
-
- main.ebooks > ol img:hover{
- box-shadow: 3px 3px 1px rgba(0, 0, 0, .5);
- }
-
- article.ebook #more-ebooks img:hover{
- box-shadow: 3px 3px 1px rgba(0, 0, 0, .5);
- }
-
- label.select > span + span::after,
- label.search::before{
- text-shadow: 1px 1px 0 #000;
- }
-
- h1,h2,h3,h4,h5,h6{
- text-shadow: 2px 2px 0 rgba(0, 0, 0, .75);
- }
-}
diff --git a/www/css/dark.css b/www/css/dark.css
new file mode 100644
index 00000000..8cfd425f
--- /dev/null
+++ b/www/css/dark.css
@@ -0,0 +1,79 @@
+:root{
+ --body-text: var(--dark-body-text);
+ --header: var(--dark-header);
+ --button: var(--dark-button);
+ --button-highlight: var(--dark-button-highlight);
+ --border: var(--dark-border);
+ --sub-text: var(--dark-sub-text);
+ --body-bg: var(--dark-body-bg);
+ --input-hover: var(--dark-input-hover);
+ --input-border: var(--dark-input-border);
+ --input-outline: var(--dark-input-outline);
+ --link-highlight: var(--button-highlight); /* lighter looks better in dark mode */
+}
+
+main.front-page > section > section figure img{
+ box-shadow: 3px 3px 1px rgba(0, 0, 0, .5);
+}
+
+body > header > a:focus{
+ /* Since we use invert() to change the color of the SE logo,
+ we must specify the light outline (which is dark)
+ so that it inverts to white */
+ outline: 1px dashed var(--light-input-outline);
+}
+
+article.ebook section#read-online a::before,
+article.ebook section#download ul li a[class]::before,
+article.ebook section#details ul li a[class]::before,
+footer p:last-child a::before,
+main.front-page > section > section figure.oss img + img,
+main.front-page > section > h2::before,
+main.front-page > section > h2::after{
+ filter: invert() grayscale(100%) brightness(120%) drop-shadow(3px 3px 1px rgba(0, 0, 0, .25)); /* grayscale and brightness makes it hit about #eeeeee */
+}
+
+.masthead img[src*="portrait.svg"],
+.masthead img.contact{
+ filter: invert();
+}
+
+.masthead img[src*="portrait.svg"]{
+ border-color: var(--border-light);
+}
+
+aside.donation,
+article.ebook section aside.donation{
+ background: rgba(0, 0, 0, .25);
+}
+
+aside.donation,
+article.ebook section aside.donation{
+ box-shadow: none;
+}
+
+select,
+input[type="search"]{
+ box-shadow: 1px 1px 0 rgba(0, 0, 0, .5) inset;
+}
+
+input::placeholder{
+ color: rgba(255, 255, 255, .75);
+}
+
+main.ebooks > ol img:hover{
+ box-shadow: 3px 3px 1px rgba(0, 0, 0, .5);
+}
+
+article.ebook #more-ebooks img:hover{
+ box-shadow: 3px 3px 1px rgba(0, 0, 0, .5);
+}
+
+label.select > span + span::after,
+label.search::before{
+ text-shadow: 1px 1px 0 #000;
+}
+
+h1,h2,h3,h4,h5,h6{
+ text-shadow: 2px 2px 0 rgba(0, 0, 0, .75);
+}
diff --git a/www/css/reset.css b/www/css/reset.css
deleted file mode 100644
index b692a4e1..00000000
--- a/www/css/reset.css
+++ /dev/null
@@ -1,94 +0,0 @@
-/* Dont @import this, to prevent FOUC */
-html{
- margin: 0;
- padding: 0;
- -ms-text-size-adjust: 100%;
- -webkit-text-size-adjust: 100%;
-}
-
-body{
- margin: 0;
- padding: 0;
-}
-
-a{
- outline: 0;
-}
-
-blockquote{
- margin: 0;
- quotes: none;
-}
-
-p{
- margin: 0;
-}
-
-ol{
- margin: 0;
- padding: 0;
-}
-
-ul{
- margin: 0;
- padding: 0;
-}
-
-h1{
- margin: 0;
-}
-
-h2{
- margin: 0;
-}
-
-h3{
- margin: 0;
-}
-
-h4{
- margin: 0;
-}
-
-h5{
- margin: 0;
-}
-
-h6{
- margin: 0;
-}
-
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-q{
- quotes: none;
-}
-
-figure{
- margin: 0;
-}
-
-button::-moz-focus-inner{
- border: 0;
-}
-
-/* ie 11 compatibility */
-header{
- display: block;
-}
-
-nav{
- display: block;
-}
-
-main{
- display: block;
-}
-
-footer{
- display: block;
-}
-/* end ie 11 compatibility */
diff --git a/www/settings/index.php b/www/settings/index.php
new file mode 100644
index 00000000..55aefa76
--- /dev/null
+++ b/www/settings/index.php
@@ -0,0 +1,23 @@
+
+require_once('Core.php');
+
+$colorScheme = $_COOKIE['color-scheme'] ?? 'auto';
+
+?>= Template::Header(['title' => 'Website Settings', 'description' => 'Adjust your settings for viewing the Standard Ebooks website.']) ?>
+
+ Website Settings
+
+
+= Template::Footer() ?>
diff --git a/www/settings/post.php b/www/settings/post.php
new file mode 100644
index 00000000..9419ac01
--- /dev/null
+++ b/www/settings/post.php
@@ -0,0 +1,21 @@
+
+require_once('Core.php');
+
+$colorScheme = $_POST['color-scheme'] ?? 'auto';
+
+if($colorScheme !== 'dark' && $colorScheme !== 'light' && $colorScheme !== 'auto'){
+ $colorScheme = 'auto';
+}
+
+if($colorScheme == 'auto'){
+ // Delete the cookie; auto is the default
+ setcookie('color-scheme', null, ['expires' => 0, 'path' => '/', 'secure' => true, 'httponly' => true]);
+}
+else{
+ setcookie('color-scheme', $colorScheme, ['expires' => strtotime('+10 years'), 'path' => '/', 'secure' => true, 'httponly' => true]);
+}
+
+// HTTP 303, See other
+http_response_code(303);
+header('Location: /settings');
+?>