From feb9d1b821537e5318cfdd2e8bf2329e5ed4afd8 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Sat, 17 Jan 2026 23:58:53 +0100 Subject: [PATCH 1/2] dark mode implementation --- cmd/web/client/scss/_dark-mode.scss | 291 ++++++++++++++++++++++++++ cmd/web/client/scss/_syntax-dark.scss | 144 +++++++++++++ cmd/web/client/scss/index.scss | 4 + 3 files changed, 439 insertions(+) create mode 100644 cmd/web/client/scss/_dark-mode.scss create mode 100644 cmd/web/client/scss/_syntax-dark.scss diff --git a/cmd/web/client/scss/_dark-mode.scss b/cmd/web/client/scss/_dark-mode.scss new file mode 100644 index 0000000..e11a558 --- /dev/null +++ b/cmd/web/client/scss/_dark-mode.scss @@ -0,0 +1,291 @@ +// Dark mode styles using Bootstrap's data-bs-theme approach +// This file contains dark mode overrides for custom components +// Supports both manual (data-bs-theme="dark") and automatic (prefers-color-scheme) dark mode + +// Mixin to avoid code duplication +@mixin dark-mode-styles { + // Custom CSS variables for dark mode + --primary-color: #5dade2; + --secondary-color: #ecf0f1; + --background-color: #1a1a1a; + --text-color: #e0e0e0; + --muted-text: #a0a0a0; + + // Body background + body { + background-color: var(--background-color); + color: var(--text-color); + } + + // Blockquote styling + blockquote { + border-left-color: #4a4a4a; + } + + // Carousel controls + .carousel-caption, + .carousel-control-prev, + .carousel-control-next { + background-color: rgba(255, 255, 255, 0.1); + } + + // Edit preview containers + .block-container-edit-preview-cut, + .block-container-edit-preview-spoiler { + border-color: #6c757d; + } + + .block-container-edit-preview-spoiler { + background-color: #2d2d2d; + } + + .block-container-edit-preview-cut { + background-color: #252525; + } + + // Markdown editor loading overlay + .mdeditor__loading { + background: rgba(50, 50, 50, 0.5); + } + + // YouTube fallback + .lite-youtube-fallback { + background-color: #1a1a1a; + color: #e0e0e0; + } + + // User header links + .user-header a { + color: var(--primary-color); + } + + // Post prompt + .post-prompt { + background-color: #2d2d2d; + border-color: #404040; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + } + + .post-prompt .form-control { + background-color: #1a1a1a; + border-color: #404040; + color: var(--text-color); + } + + .post-prompt .form-control:focus { + background-color: #252525; + border-color: var(--primary-color); + color: var(--text-color); + } + + .post-prompt .btn-primary { + background-color: var(--primary-color); + border-color: var(--primary-color); + color: #1a1a1a; + } + + .post-prompt .btn-primary:hover { + background-color: #4a9fd8; + border-color: #4a9fd8; + } + + // Feed cards + .feed-comment .card-header, + .feed-post .card-header { + background-color: #2d2d2d; + border-bottom-color: #404040; + } + + .feed-comment .card, + .feed-post .card { + background-color: #252525; + border-color: #404040; + } + + .feed-comment .card-header a, + .feed-post .card-header a { + color: var(--secondary-color); + } + + .feed-comment .card-body, + .feed-post .card-body { + background-color: #252525; + color: var(--text-color); + } + + // Post in feed + .post-in-feed { + color: var(--text-color); + } + + // Stats and links + .feed-post-stats, + .comment-stats { + color: var(--muted-text); + } + + .feed-post-stats a, + .comment-stats a { + color: var(--primary-color); + } + + .feed-post-stats a:hover, + .comment-stats a:hover { + color: #4a9fd8; + } + + .post-date { + color: var(--muted-text); + } + + // Spoiler container + .block-container-spoiler-summary { + color: var(--primary-color); + } + + // Form controls + .form-control, + .form-select { + background-color: #2d2d2d; + border-color: #404040; + color: var(--text-color); + } + + .form-control:focus, + .form-select:focus { + background-color: #353535; + border-color: var(--primary-color); + color: var(--text-color); + box-shadow: 0 0 0 0.25rem rgba(93, 173, 226, 0.25); + } + + .form-control::placeholder { + color: #6c757d; + } + + // Textarea + textarea.form-control, + .comment-textarea { + background-color: #2d2d2d; + border-color: #404040; + color: var(--text-color); + } + + textarea.form-control:focus, + .comment-textarea:focus { + background-color: #353535; + border-color: var(--primary-color); + color: var(--text-color); + } + + // Links + a { + color: var(--primary-color); + } + + a:hover { + color: #4a9fd8; + } + + // Navbar overrides (if needed) + .navbar { + background-color: #2d2d2d !important; + border-bottom: 1px solid #404040; + } + + .navbar-light .navbar-nav .nav-link { + color: var(--text-color); + } + + .navbar-light .navbar-nav .nav-link:hover { + color: var(--primary-color); + } + + // Dropdown menus + .dropdown-menu { + background-color: #2d2d2d; + border-color: #404040; + } + + .dropdown-item { + color: var(--text-color); + } + + .dropdown-item:hover, + .dropdown-item:focus { + background-color: #353535; + color: var(--primary-color); + } + + // Modals + .modal-content { + background-color: #252525; + border-color: #404040; + } + + .modal-header { + border-bottom-color: #404040; + } + + .modal-footer { + border-top-color: #404040; + } + + // Tables + .table { + color: var(--text-color); + border-color: #404040; + } + + .table-striped > tbody > tr:nth-of-type(odd) > * { + color: var(--text-color); + } + + // Alerts + .alert { + border-color: #404040; + } + + // Badges + .badge { + background-color: #404040; + color: var(--text-color); + } + + // Pagination + .pagination .page-link { + background-color: #2d2d2d; + border-color: #404040; + color: var(--primary-color); + } + + .pagination .page-link:hover { + background-color: #353535; + border-color: #404040; + color: #4a9fd8; + } + + .pagination .page-item.active .page-link { + background-color: var(--primary-color); + border-color: var(--primary-color); + color: #1a1a1a; + } + + .pagination .page-item.disabled .page-link { + background-color: #2d2d2d; + border-color: #404040; + color: #6c757d; + } +} + +// Apply dark mode styles manually with data-bs-theme="dark" +[data-bs-theme="dark"] { + @include dark-mode-styles; +} + +// Apply dark mode styles automatically based on system preference +@media (prefers-color-scheme: dark) { + :root:not([data-bs-theme="light"]) { + @include dark-mode-styles; + } +} diff --git a/cmd/web/client/scss/_syntax-dark.scss b/cmd/web/client/scss/_syntax-dark.scss new file mode 100644 index 0000000..01f8f95 --- /dev/null +++ b/cmd/web/client/scss/_syntax-dark.scss @@ -0,0 +1,144 @@ +// Dark mode syntax highlighting styles +// Supports both manual (data-bs-theme="dark") and automatic (prefers-color-scheme) dark mode + +@mixin syntax-dark-mode-styles { + .chroma { + // Dark background for code blocks + background-color: #1e1e1e; + border: 1px solid #404040; + + // Line numbers + .ln { + color: #858585; + } + + // Keywords + .k, .kd, .kn { + color: #569cd6; + font-weight: bold; + } + + // Strings + .s, .s1, .s2 { + color: #ce9178; + } + + // Comments + .c, .c1, .cm { + color: #6a9955; + font-style: italic; + } + + // Functions and types + .nf, .nx { + color: #dcdcaa; + } + + // Numbers + .mi, .mf { + color: #b5cea8; + } + + // Operators + .o { + color: #d4d4d4; + font-weight: bold; + } + + // Built-ins + .nb { + color: #4ec9b0; + } + + // Variables + .nv { + color: #9cdcfe; + } + + // Types + .kt, .nc { + color: #4ec9b0; + } + + // Punctuation + .p { + color: #d4d4d4; + } + + // Errors + .err { + color: #f48771; + } + + // Generic + .g { + color: #d4d4d4; + } + + // Names + .n, .na, .nd { + color: #d4d4d4; + } + + // Literals + .l { + color: #b5cea8; + } + + // Escapes + .se { + color: #d7ba7d; + } + + // Symbols + .ss { + color: #ce9178; + } + + // Booleans + .kc { + color: #569cd6; + } + + // Imports + .kp { + color: #c586c0; + } + + // Preprocessor + .cp { + color: #9b9b9b; + } + } + + // Inline code + code { + background-color: #2d2d2d; + color: #e0e0e0; + border: 1px solid #404040; + } + + // Pre blocks + pre { + background-color: #1e1e1e; + color: #d4d4d4; + border: 1px solid #404040; + } + + pre code { + background-color: transparent; + border: none; + } +} + +// Apply syntax dark mode manually with data-bs-theme="dark" +[data-bs-theme="dark"] { + @include syntax-dark-mode-styles; +} + +// Apply syntax dark mode automatically based on system preference +@media (prefers-color-scheme: dark) { + :root:not([data-bs-theme="light"]) { + @include syntax-dark-mode-styles; + } +} diff --git a/cmd/web/client/scss/index.scss b/cmd/web/client/scss/index.scss index 67fa1c8..a405cc7 100644 --- a/cmd/web/client/scss/index.scss +++ b/cmd/web/client/scss/index.scss @@ -8,6 +8,10 @@ $bootstrap-icons-font-dir: "~bootstrap-icons/font/fonts"; // Syntax highlighting styles @import "syntax"; +// Dark mode styles +@import "dark-mode"; +@import "syntax-dark"; + // @TODO: we should use class there blockquote { border-left: 4px solid #dedede; From d9ebfa2b6b757b747098dfc21ccb792331565978 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Sun, 18 Jan 2026 00:10:05 +0100 Subject: [PATCH 2/2] vibe code dark mode --- cmd/web/client/html/single_post.html | 4 +-- cmd/web/client/scss/_dark-mode.scss | 48 ++++++++++++++++++++++++++++ cmd/web/client/scss/index.scss | 5 +++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/cmd/web/client/html/single_post.html b/cmd/web/client/html/single_post.html index 70b0988..800d34c 100644 --- a/cmd/web/client/html/single_post.html +++ b/cmd/web/client/html/single_post.html @@ -72,7 +72,7 @@

{{ .Auth {{ if .Capabilities.CanLeaveComments }}
-
+
{{ template "form--comment.html" toMap "PostID" .ID }}
@@ -112,7 +112,7 @@
{{ if $canLeaveComments }}
-
+
{{ template "form--comment.html" toMap "PostID" .PostID "ReplyTo" .ID "ReplyToAuthor" .Author.Username }}
diff --git a/cmd/web/client/scss/_dark-mode.scss b/cmd/web/client/scss/_dark-mode.scss index e11a558..d1a1aa9 100644 --- a/cmd/web/client/scss/_dark-mode.scss +++ b/cmd/web/client/scss/_dark-mode.scss @@ -113,6 +113,36 @@ color: var(--text-color); } + // Card backgrounds + .card { + background-color: #252525; + border-color: #404040; + } + + .card-header { + background-color: #2d2d2d; + border-bottom-color: #404040; + color: var(--text-color); + } + + .card-header a { + color: var(--primary-color); + } + + .card-header a:hover { + color: #4a9fd8; + } + + .card-body { + background-color: #252525; + color: var(--text-color); + } + + // Theme-aware background for forms and content areas + .bg-theme-surface { + background-color: #252525; + } + // Post in feed .post-in-feed { color: var(--text-color); @@ -201,6 +231,24 @@ color: var(--primary-color); } + // Navbar text (e.g., "Hi username!") + .navbar-text { + color: var(--text-color) !important; + } + + // Navbar toggler (hamburger menu) for mobile + .navbar-toggler { + border-color: rgba(255, 255, 255, 0.3); + } + + .navbar-toggler:focus { + box-shadow: 0 0 0 0.25rem rgba(93, 173, 226, 0.25); + } + + .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.85%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + } + // Dropdown menus .dropdown-menu { background-color: #2d2d2d; diff --git a/cmd/web/client/scss/index.scss b/cmd/web/client/scss/index.scss index a405cc7..93f8dc1 100644 --- a/cmd/web/client/scss/index.scss +++ b/cmd/web/client/scss/index.scss @@ -8,6 +8,11 @@ $bootstrap-icons-font-dir: "~bootstrap-icons/font/fonts"; // Syntax highlighting styles @import "syntax"; +// Theme-aware background class (light mode default) +.bg-theme-surface { + background-color: #ffffff; +} + // Dark mode styles @import "dark-mode"; @import "syntax-dark";