feat: Implement dark mode

#11 says we need this.
This commit is contained in:
Kumi 2025-05-02 08:35:06 +02:00
parent 1f169b0131
commit ba4866919e
Signed by: kumi
GPG key ID: ECBCC9082395383F
9 changed files with 479 additions and 2 deletions

View file

@ -0,0 +1,61 @@
name: Build and Deploy Dark-theme Static Site
on:
push:
branches:
- main
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
build:
runs-on: docker
container: git.private.coffee/privatecoffee/static-site-builder:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
python3 -m pip install -r requirements.txt --break-system-packages
- name: Generate static site
run: python3 main.py --theme dark --domains dark.private.coffee
- name: Deploy to pages-pride branch
run: |
# Configure Git
git config --global user.name "Forgejo"
git config --global user.email "noreply@private.coffee"
# Move generated static site files to a temporary location
mv build ../static_site_temp
cp .gitignore ../static_site_temp
# Create a new orphan branch named 'pages-dark'
git checkout --orphan pages-dark
# Remove all files from the working directory
git rm -rf .
# Move the static site files back to the working directory
mv ../static_site_temp/* ./
mv ../static_site_temp/.* ./ 2>/dev/null || true
# Add and commit the static site files
git add .
git commit -m "Deploy static site"
# Set the URL again
git remote set-url origin "https://${{ secrets.FORGEJO_USER }}:${{ secrets.FORGEJO_TOKEN }}@git.private.coffee/PrivateCoffee/privatecoffee-website.git"
# Force push to the 'pages-dark' branch
git push origin pages-dark --force
- name: Save as artifact
uses: https://code.forgejo.org/forgejo/upload-artifact@v4
with:
name: static-site.zip
path: .

View file

@ -1,4 +1,4 @@
name: Build and Deploy Static Site
name: Build and Deploy Development Static Site
on:
push:

View file

@ -1,4 +1,4 @@
name: Build and Deploy Static Site
name: Build and Deploy Pride-Theme Static Site
on:
push:

View file

@ -390,4 +390,36 @@ h5 {
html {
scroll-behavior: smooth;
}
}
/* Theme toggle styles */
.theme-toggle-container {
display: inline-flex;
align-items: center;
}
.theme-toggle-container .btn {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
padding: 0;
border-radius: 50%;
}
.theme-toggle-container .btn svg {
width: 20px;
height: 20px;
}
/* Dark theme specific toggle styles */
[data-bs-theme="dark"] .theme-toggle-container .btn-outline-primary {
color: #f785c4;
border-color: #f785c4;
}
[data-bs-theme="dark"] .theme-toggle-container .btn-outline-primary:hover {
background-color: #f785c4;
color: #121212;
}

357
assets/css/theme/dark.css Normal file
View file

@ -0,0 +1,357 @@
/* Dark theme */
:root,
[data-bs-theme="dark"] {
--bs-primary: #d25ea0;
--bs-primary-rgb: 210, 94, 160;
--bs-primary-text-emphasis: #e07eb4;
--bs-primary-bg-subtle: #3d2936;
--bs-primary-border-subtle: #5e3e52;
--bs-body-color: #e0e0e0;
--bs-body-color-rgb: 224, 224, 224;
--bs-body-bg: #2a2a2a;
--bs-body-bg-rgb: 42, 42, 42;
--bs-secondary-color: rgba(224, 224, 224, 0.75);
--bs-secondary-color-rgb: 224, 224, 224;
--bs-tertiary-color: rgba(224, 224, 224, 0.5);
--bs-tertiary-color-rgb: 224, 224, 224;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-border-color: #444444;
--bs-border-color-translucent: rgba(255, 255, 255, 0.1);
--bs-light-rgb: 50, 50, 50;
--bs-dark-rgb: 34, 34, 34;
--bs-white-rgb: 42, 42, 42;
}
/* Background gradient */
.bg-primary-gradient {
background: linear-gradient(135deg, #3d2936, #4d3946);
}
/* Logo images */
#logoContainer {
background-image: url(../../img/logo-white.svg);
}
#smallLogoContainer {
background-image: url(../../img/logo-white.svg);
}
/* Card and accordion styles */
.card, .accordion {
background-color: #333333;
border-color: #444444;
}
.card-body, .accordion-body {
color: var(--bs-body-color);
}
.accordion-header {
background-color: #3a3a3a;
color: var(--bs-body-color);
}
.accordion-header:hover {
background-color: #444444;
}
/* Dropdown styles */
.dropdown-content {
background-color: #333333;
border: 1px solid #444444;
}
.dropdown-content a {
color: var(--bs-body-color);
}
.dropdown-content a:hover {
background-color: #444444;
}
/* Table styles */
.transparency-start-balance-row>td {
background-color: rgba(61, 41, 54, 0.3) !important;
}
.table {
color: var(--bs-body-color);
}
.table-light {
--bs-table-bg: #3a3a3a;
--bs-table-color: var(--bs-body-color);
}
.table-secondary {
--bs-table-bg: #444444;
--bs-table-color: var(--bs-body-color);
}
.table-bordered {
border-color: #444444;
}
/* Button styles */
.btn-primary {
--bs-btn-color: #222222;
--bs-btn-bg: #d25ea0;
--bs-btn-border-color: #d25ea0;
--bs-btn-hover-color: #222222;
--bs-btn-hover-bg: #db74ab;
--bs-btn-hover-border-color: #d868a6;
--bs-btn-focus-shadow-rgb: 210, 94, 160;
--bs-btn-active-color: #222222;
--bs-btn-active-bg: #e07eb4;
--bs-btn-active-border-color: #d868a6;
}
.btn-outline-primary {
--bs-btn-color: #d25ea0;
--bs-btn-border-color: #d25ea0;
--bs-btn-hover-color: #222222;
--bs-btn-hover-bg: #d25ea0;
--bs-btn-hover-border-color: #d25ea0;
--bs-btn-active-color: #222222;
--bs-btn-active-bg: #d25ea0;
--bs-btn-active-border-color: #d25ea0;
}
/* Link styles */
a {
color: #d25ea0;
}
a:hover {
color: #e07eb4;
}
/* Text styles */
.text-muted {
color: #aaaaaa !important; /* My thoughts. */
}
.fancy-text-primary {
background: -webkit-linear-gradient(45deg, #d25ea0, #e07eb4);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Navbar styles */
.navbar-light {
background-color: #333333 !important;
}
.navbar-light .navbar-nav .nav-link {
color: #e0e0e0;
}
.navbar-light .navbar-nav .nav-link:hover,
.navbar-light .navbar-nav .nav-link.active {
color: #d25ea0;
}
/* Fix for navbar brand text color */
.navbar-brand p {
color: #e0e0e0 !important;
}
.navbar-brand span[style*="color: rgb(35, 35, 35)"] {
color: #e0e0e0 !important;
}
/* Footer styles */
footer.bg-primary-gradient {
color: #e0e0e0;
}
footer a {
color: #d25ea0;
}
footer a:hover {
color: #e07eb4;
}
footer .text-muted {
color: #aaaaaa !important;
}
/* Alert styles */
.alert-warning {
background-color: #3d3223;
border-color: #594832;
color: #e0c088;
}
.alert-warning .alert-link {
color: #e0c088;
}
/* Icon styles */
.bs-icon.bs-icon-primary svg {
fill: #d25ea0 !important;
}
.bs-icon-circle.bs-icon-primary {
background-color: #3d2936;
}
.bs-icon-circle.bs-icon-primary svg {
fill: #d25ea0 !important;
}
/* Fix for service icons */
.bs-icon-sm.bs-icon-circle.homemade svg,
.bs-icon-sm.bs-icon-circle.upstream svg,
.bs-icon-sm.bs-icon-circle.fork svg,
.bs-icon-sm.bs-icon-circle.members-only svg {
fill: #d25ea0 !important;
}
.bs-icon-sm.bs-icon-circle.homemade,
.bs-icon-sm.bs-icon-circle.upstream,
.bs-icon-sm.bs-icon-circle.fork,
.bs-icon-sm.bs-icon-circle.members-only {
background-color: #3d2936 !important;
border: 1px solid #d25ea0;
}
/* Fix for icon in accordion headers */
.accordion-header .bs-icon svg {
fill: #d25ea0 !important;
}
/* Background color overrides */
.bg-light {
background-color: #333333 !important;
}
.bg-white {
background-color: #2a2a2a !important;
}
.bg-secondary-subtle {
background-color: #3a3a3a !important;
}
.bg-primary-subtle {
background-color: #3d2936 !important;
}
/* Theme toggle styles */
.theme-toggle-container {
display: inline-flex;
align-items: center;
}
.theme-toggle-container .btn {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
padding: 0;
border-radius: 50%;
background-color: transparent;
border: 2px solid #d25ea0; /* Toned down from #f570b9 */
color: #d25ea0; /* Toned down from #f570b9 */
}
.theme-toggle-container .btn:hover {
background-color: #d25ea0; /* Toned down from #f570b9 */
color: #333333;
}
.theme-toggle-container .btn svg {
width: 20px;
height: 20px;
fill: currentColor;
}
/* Fix for any remaining white sections */
section.bg-white {
background-color: #2a2a2a !important;
}
section.bg-light {
background-color: #333333 !important;
}
.container.bg-white {
background-color: #2a2a2a !important;
}
[class*="bg-white"] {
background-color: #2a2a2a !important;
}
/* Code blocks */
code {
color: #d25ea0; /* Toned down from #f570b9 */
background-color: #333333;
padding: 0.2em 0.4em;
border-radius: 3px;
}
pre {
background-color: #333333;
color: #e0e0e0;
border: 1px solid #444444;
border-radius: 4px;
padding: 1em;
}
/* Input fields */
input, textarea, select {
background-color: #3a3a3a !important;
border-color: #444444 !important;
color: #e0e0e0 !important;
}
input::placeholder, textarea::placeholder {
color: #888888 !important;
}
/* Blockquotes */
blockquote {
border-left: 4px solid #3d2936;
padding-left: 1em;
color: #cccccc;
}
/* Fix for inline styles that might set text to black */
[style*="color: rgb(35, 35, 35)"] {
color: #e0e0e0 !important;
}
[style*="color: #232323"] {
color: #e0e0e0 !important;
}
/* Make sure the body background is consistently applied */
body {
background-color: #2a2a2a;
}
/* Ensure header bar is lighter */
#mainNav {
background-color: #333333 !important;
}
/* Legend section icons */
.col .d-flex .bs-icon-md svg {
fill: #d25ea0 !important; /* Toned down from #f570b9 */
}
/* Fix for any SVG icons that might be hard to see */
svg {
fill: currentColor;
}

1
assets/dist/icons/moon.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z"></path></svg>

After

Width:  |  Height:  |  Size: 491 B

1
assets/dist/icons/sun.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z"></path></svg>

After

Width:  |  Height:  |  Size: 709 B

14
main.py
View file

@ -67,10 +67,24 @@ env.filters["month_name"] = month_name
def render_template_to_file(template_name, output_name, **kwargs):
"""Render a template to a file.
Args:
template_name (str): The name of the template file.
output_name (str): The name of the output file.
**kwargs: Additional keyword arguments to pass to the template.
"""
try:
template = env.get_template(template_name)
output_path = output_dir / output_name
kwargs.setdefault("theme", "plain")
path = "/" + output_name
if path.endswith("/index.html"):
path = path[:-10]
elif path == "/index.html":
path = "/"
kwargs.setdefault("request", {"path": path})
output_path.parent.mkdir(parents=True, exist_ok=True)

View file

@ -79,6 +79,17 @@
<a class="nav-link" href="https://status.private.coffee/">Status</a>
</li>
</ul>
<div class="theme-toggle-container me-2">
{% if theme == 'dark' %}
<a href="https://private.coffee{{ request.path }}"
class="btn btn-outline-primary"
aria-label="Switch to light theme">{{ "sun" | icon | safe }}</a>
{% else %}
<a href="https://dark.private.coffee{{ request.path }}"
class="btn btn-outline-primary"
aria-label="Switch to dark theme">{{ "moon" | icon | safe }}</a>
{% endif %}
</div>
<a class="btn btn-primary shadow navbar-btn"
role="button"
href="/membership.html">JOIN &amp; SUPPORT</a>