From b2fc199856b8c01f98494961ced7e1f9c5718725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jok=C5=ABbas=20Banaitis?= Date: Wed, 13 May 2026 13:57:24 +0300 Subject: [PATCH 1/2] Reorganize styles structure --- src/app.css | 305 ++------------------------------- src/lib/styles/layout.css | 218 +++++++++++++++++++++++ src/lib/styles/theme.css | 150 ++++++++++++++++ src/lib/styles/themes/dark.css | 15 ++ src/lib/styles/tokens.css | 45 +++++ src/routes/+layout.svelte | 121 ++++--------- 6 files changed, 478 insertions(+), 376 deletions(-) create mode 100644 src/lib/styles/layout.css create mode 100644 src/lib/styles/theme.css create mode 100644 src/lib/styles/themes/dark.css create mode 100644 src/lib/styles/tokens.css diff --git a/src/app.css b/src/app.css index ea1966e..2bdf7d2 100644 --- a/src/app.css +++ b/src/app.css @@ -1,295 +1,18 @@ -@import "@fontsource/courier-prime"; -@import "@fontsource/fira-mono"; +/* ─── app.css ──────────────────────────────────────────────────────────────── + Entry point. Import order matters: + 1. tokens — variables must exist before anything references them + 2. themes — overrides must come after token defaults + 3. layout — structure (references tokens for sizing vars) + 4. theme — visuals (references tokens for all color/font values) -:root { - --font-body: "Fira Mono", "Courier New", "Courier Prime", monospace; - --font-mono: "Fira Mono", "Courier Prime", monospace; - --color-bg: #040204; - --column-width: 42rem; - --column-margin-top: 4rem; - font-family: var(--font-body); - color: var(--color-text); -} + To add a new theme, create src/lib/styles/themes/yourtheme.css + and add an @import here. Apply it with . +─────────────────────────────────────────────────────────────────────────────── */ -body { - color: var(--color-text); - min-height: 100vh; - margin: 0; - background: var(--color-bg); -} +@import "$lib/styles/tokens.css"; -h1, -h2, -p { - font-weight: 400; - line-height: 1.5; - word-break: break-word; -} +/* Themes */ +@import "$lib/styles/themes/dark.css"; -h2.title { - margin-bottom: 0.25em; -} - -a { - color: var(--link-color); - text-decoration: none; -} - -a:has(> strong) { - color: var(--color-text-2); -} - -a:hover:not(:has(img)) { - background: var(--color-text-2); - color: var(--link-hover-color); - text-decoration: none; -} - -p a { - text-decoration: underline; - text-underline-offset: 3px; -} - -h1 { - font-size: 2rem; - text-align: center; - margin: 0 0 0.5em 0; -} - -h2 { - font-size: 1.25rem; - margin-top: 0; -} - -h3 { - font-size: 1rem; - overflow-wrap: anywhere; -} - -hr { - margin-left: 0px; - width: 15rem; -} - -pre { - font-size: 16px; - font-family: var(--font-mono); - background-color: rgba(20, 20, 20, 0.45); - border-radius: 3px; - box-shadow: 2px 2px 6px rgba(20, 20, 20, 0.25); - padding: 0.5em; - overflow-x: auto; - color: var(--color-text-2); -} - -.inline { - display: inline; -} - -input { - font-size: inherit; - font-family: inherit; - color: var(--color-text); - accent-color: var(--color-text-2); -} - -button { - font-size: inherit; - font-family: inherit; - color: var(--color-text-2); - text-decoration: none; - border: none; - background-color: rgba(0, 0, 0, 0); - cursor: pointer; -} - -button:hover { - background: var(--color-text-2); - color: var(--color-bg); - text-decoration: none; -} - -button:focus:not(:focus-visible) { - outline: none; -} - -@media (min-width: 780px) { - h1 { - font-size: 2.4rem; - } -} - -strong { - color: var(--color-text-2); - font-weight: 700; -} - -div.actions { - display: flex; - margin-bottom: 0.5em; - gap: 0.5em; -} - -div.actions form { - margin-bottom: 0; -} - -.post.action:hover { - color: var(--color-text-2); -} - -a:hover strong { - background: var(--color-text-2); - color: var(--link-hover-color); - text-decoration: none; -} - -.dim { - color: var(--color-text-3); -} - -hr { - border-color: var(--color-text-2); - color: var(--color-text-2); -} - -.post.action { - color: var(--color-text); - border: none; - background-color: rgba(0, 0, 0, 0); - cursor: pointer; -} - -label { - display: block; - margin-top: 1em; -} - -form { - margin-bottom: 2em; -} - -form input { - margin-top: 0.83em; - margin-bottom: 0.5em; - background-color: #1c1c1c; - border: 1px solid rgba(255, 255, 255, 0.075); - border-radius: 3px; - color: var(--form-text-color); - font-weight: 400; - font-size: 1rem; - padding: 12px 10px; -} - -::selection { - color: var(--link-hover-color); - background: var(--color-text-2); -} - -form input:focus { - border: 1px solid var(--form-border-color); - outline: none; -} - -form textarea { - margin-top: 0.83em; - margin-bottom: 0.5em; - background-color: #1c1c1c; - border: 1px solid rgba(255, 255, 255, 0.075); - border-radius: 3px; - color: var(--form-text-color); - font-weight: 400; - font-size: 1rem; - padding: 12px 10px; - max-width: 25em; - min-width: 12.5em; - width: 40vw; - height: 10rem; -} - -form textarea:focus { - border: 1px solid var(--form-border-color); - outline: none; -} - -ul { - list-style: square; - padding-left: 1.5em; -} - -post div.content img { - max-width: 100%; - border-radius: 10px; - margin-bottom: 1em; - max-height: 65vh; -} - -event div.event-description img { - max-width: 100%; - border-radius: 10px; - margin-bottom: 1em; - max-height: 65vh; -} - -event div.event-description p { - margin-bottom: 0; - margin-top: 0; -} - -.font-size-small { - font-size: 0.8em; - font-weight: 200; -} - -iframe { - max-width: 100%; -} - -.opaque { - opacity: 0; - cursor: default; -} - -div.meta-info { - display: flex; - flex-direction: row; - gap: 1.5em; -} - -hr.long { - width: 100%; - color: rgba(255, 255, 255, 0.1); - border-color: rgba(255, 255, 255, 0.1); - margin-top: 2em; - margin-bottom: 2em; -} - -ul.item-list { - padding-left: 0; -} - -ul.item-list li { - list-style: none; - margin-bottom: 2em; -} - -div.center-block { - margin: 2em; - display: flex; - justify-content: center; -} - -div.text-block { - margin-bottom: 1em; -} - -p.align-right { - text-align: right; -} - -.lucide { - vertical-align: text-bottom; - max-width: 1.25em; - max-height: 1.25em; - align-self: center; -} +@import "$lib/styles/layout.css"; +@import "$lib/styles/theme.css"; diff --git a/src/lib/styles/layout.css b/src/lib/styles/layout.css new file mode 100644 index 0000000..e041070 --- /dev/null +++ b/src/lib/styles/layout.css @@ -0,0 +1,218 @@ +/* + Structural rules only: display, spacing, sizing, positioning. + No color values. No font-family. No box-shadow. + All visual properties live in theme.css. +*/ + +/* Base */ + +body { + min-height: 100vh; + margin: 0; +} + +/* Typography */ + +h1, +h2, +p { + font-weight: 400; + line-height: 1.5; + word-break: break-word; +} + +h1 { + font-size: 2rem; + text-align: center; + margin: 0 0 0.5em 0; +} + +h2 { + font-size: 1.25rem; + margin-top: 0; +} + +h2.title { + margin-bottom: 0.25em; +} + +h3 { + font-size: 1rem; + overflow-wrap: anywhere; +} + +strong { + font-weight: 700; +} + +@media (min-width: 780px) { + h1 { + font-size: 2.4rem; + } +} + +/* Links */ + +p a { + text-underline-offset: 3px; +} + +/* Buttons */ + +button { + font-size: inherit; + font-family: inherit; + border: none; + cursor: pointer; + text-decoration: none; +} + +button:focus:not(:focus-visible) { + outline: none; +} + +/* Forms */ + +label { + display: block; + margin-top: 1em; +} + +form { + margin-bottom: 2em; +} + +div.actions form { + margin-bottom: 0; +} + +input { + font-size: inherit; + font-family: inherit; +} + +form input:not([type="checkbox"]):not([type="radio"]), +form textarea { + display: block; + margin-top: 0.83em; + margin-bottom: 0.5em; + font-family: inherit; + font-size: 1rem; + font-weight: 400; + padding: 12px 10px; + border-radius: 3px; + border-width: 1px; + border-style: solid; +} + +form input:focus, +form textarea:focus { + outline: none; +} + +form textarea { + max-width: 25em; + min-width: 12.5em; + width: 40vw; + height: 10rem; +} + +/* Misc Elements */ + +hr { + margin-left: 0; + width: 15rem; +} + +hr.long { + width: 100%; + margin-top: 2em; + margin-bottom: 2em; +} + +pre { + font-size: 16px; + padding: 0.5em; + overflow-x: auto; + border-radius: 3px; +} + +ul { + padding-left: 1.5em; +} + +ul.item-list { + padding-left: 0; +} + +ul.item-list li { + list-style: none; + margin-bottom: 2em; +} + +iframe { + max-width: 100%; +} + +/* Rich Content */ + +post div.content img, +event div.event-description img { + max-width: 100%; + max-height: 65vh; + margin-bottom: 1em; +} + +event div.event-description p { + margin-top: 0; + margin-bottom: 0; +} + +/* Utility */ + +.inline { + display: inline; +} + +.opaque { + opacity: 0; + cursor: default; +} + +.font-size-small { + font-size: 0.8em; + font-weight: 200; +} + +.lucide { + vertical-align: text-bottom; + max-width: 1.25em; + max-height: 1.25em; + align-self: center; +} + +div.actions { + display: flex; + gap: 0.5em; + margin-bottom: 0.5em; +} + +div.meta-info { + display: flex; + flex-direction: row; + gap: 1.5em; +} + +div.center-block { + display: flex; + justify-content: center; + margin: 2em; +} + +div.text-block { + margin-bottom: 1em; +} + +p.align-right { + text-align: right; +} diff --git a/src/lib/styles/theme.css b/src/lib/styles/theme.css new file mode 100644 index 0000000..b79ec09 --- /dev/null +++ b/src/lib/styles/theme.css @@ -0,0 +1,150 @@ +/* theme.css + Visual styles only: color, background, border, shadow, font-family, + border-radius, text-decoration, opacity, transition. + Zero hardcoded values — everything references a token from tokens.css. + This file should read identically regardless of which theme is active. +*/ + +/* Base */ + +body { + font-family: var(--font-body); + color: var(--color-text); + background: var(--color-bg); +} + +/* Typography */ + +strong { + color: var(--color-text-2); +} + +/* Links */ + +a { + color: var(--link-color); + text-decoration: none; +} + +a:has(> strong) { + color: var(--color-text-2); +} + +a:hover:not(:has(img)) { + background: var(--color-text-2); + color: var(--link-hover-color); +} + +a:hover strong { + background: var(--color-text-2); + color: var(--link-hover-color); +} + +p a { + text-decoration: underline; +} + +/* Buttons */ + +button { + color: var(--color-text-2); + background-color: transparent; +} + +button:hover { + background: var(--color-text-2); + color: var(--color-bg); +} + +/* Forms */ + +input { + color: var(--color-text); + accent-color: var(--color-text-2); +} + +form input:not([type="checkbox"]):not([type="radio"]), +form textarea { + background-color: var(--form-bg-color); + border-color: var(--form-border-subtle); + color: var(--form-text-color); +} + +form input:not([type="checkbox"]):not([type="radio"]):focus, +form textarea:focus { + border-color: var(--form-border-color); +} + +::selection { + color: var(--link-hover-color); + background: var(--color-text-2); +} + +/* Misc Elements */ + +hr { + border-color: var(--color-text-2); + color: var(--color-text-2); +} + +hr.long { + border-color: var(--border-subtle); + color: var(--border-subtle); +} + +pre { + font-family: var(--font-mono); + color: var(--color-text-2); + background-color: var(--pre-bg); + box-shadow: 2px 2px 6px var(--pre-shadow); +} + +ul { + list-style: square; +} + +/* Rich Content */ + +post div.content img, +event div.event-description img { + border-radius: 10px; +} + +/* Utility */ + +.dim { + color: var(--color-text-3); +} + +/* Post Actions */ + +.post.action { + color: var(--color-text); + background-color: transparent; + border: none; + cursor: pointer; +} + +.post.action:hover { + color: var(--color-text-2); +} + +/* Third-party Date Picker */ + +.date-time-field input { + width: 16rem; + margin-top: 0.83em !important; + margin-bottom: 0.83em !important; + font-weight: 400 !important; + font-size: 1rem !important; + padding: 12px 10px !important; + border: 1px solid var(--form-border-subtle) !important; +} + +.date-time-field input:focus { + border-color: var(--form-border-color) !important; +} + +.date-time-field select { + background-color: var(--date-picker-background) !important; +} diff --git a/src/lib/styles/themes/dark.css b/src/lib/styles/themes/dark.css new file mode 100644 index 0000000..c6347a1 --- /dev/null +++ b/src/lib/styles/themes/dark.css @@ -0,0 +1,15 @@ +/* + Dark theme token overrides. + Apply by adding class="dark" to . + Only redefines tokens that differ from the default (light) theme. + */ + +html.dark { + --color-text: #090; + --color-text-2: #0f0; + --color-text-3: #050; + --link-color: var(--color-text-2); + --link-hover-color: var(--color-bg); + --form-border-color: var(--color-text-2); + --form-text-color: var(--color-text-2); +} diff --git a/src/lib/styles/tokens.css b/src/lib/styles/tokens.css new file mode 100644 index 0000000..32eb428 --- /dev/null +++ b/src/lib/styles/tokens.css @@ -0,0 +1,45 @@ +@import "@fontsource/courier-prime"; +@import "@fontsource/fira-mono"; + +/* + Single source of truth for all design tokens. + Light theme values are the default. + Each theme file (themes/*.css) overrides only what changes. +*/ + +:root { + /* Typography */ + --font-body: "Fira Mono", "Courier New", "Courier Prime", monospace; + --font-mono: "Fira Mono", "Courier Prime", monospace; + + /* Layout */ + --column-width: 42rem; + --column-margin-top: 4rem; + + /* Base colors */ + --color-bg: #040204; + --color-text: #ffff80; + --color-text-2: #af1a1a; + --color-text-3: #315563; + + /* Semantic tokens, components reference these, not base colors */ + --link-color: var(--color-text); + --link-hover-color: var(--color-text); + --form-border-color: var(--color-text-2); + --form-text-color: var(--color-text); + --form-bg-color: #1c1c1c; + --form-border-subtle: rgba(255, 255, 255, 0.075); + --border-subtle: rgba(255, 255, 255, 0.075); + --pre-bg: rgba(20, 20, 20, 0.45); + --pre-shadow: rgba(20, 20, 20, 0.25); + + /* Third-party date picker */ + --date-picker-foreground: var(--form-text-color); + --date-picker-background: var(--form-bg-color); + --date-picker-highlight-border: var(--form-border-color); + --date-picker-border-color: var(--form-border-subtle); + --date-picker-highlight-shadow: none; + --date-picker-selected-color: var(--link-hover-color); + --date-picker-selected-background: var(--color-text-2); + --date-input-width: fit-content; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 4d970a3..7d73d7b 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -65,25 +65,7 @@ From edb057a2b095308b8aadf71575af180e03d3afce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jok=C5=ABbas=20Banaitis?= Date: Wed, 13 May 2026 19:00:00 +0300 Subject: [PATCH 2/2] Code cleanup --- src/app.css | 17 +++++++---------- src/lib/styles/layout.css | 2 -- src/lib/styles/theme.css | 2 -- src/lib/styles/themes/dark.css | 5 ++--- src/lib/styles/tokens.css | 8 +------- 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/app.css b/src/app.css index 2bdf7d2..bd0e9e7 100644 --- a/src/app.css +++ b/src/app.css @@ -1,13 +1,10 @@ -/* ─── app.css ──────────────────────────────────────────────────────────────── - Entry point. Import order matters: - 1. tokens — variables must exist before anything references them - 2. themes — overrides must come after token defaults - 3. layout — structure (references tokens for sizing vars) - 4. theme — visuals (references tokens for all color/font values) - - To add a new theme, create src/lib/styles/themes/yourtheme.css - and add an @import here. Apply it with . -─────────────────────────────────────────────────────────────────────────────── */ +/* + Import order matters: + 1. tokens - variables must exist before anything references them + 2. themes - overrides must come after token defaults + 3. layout - structure (references tokens for sizing vars) + 4. theme - visuals (references tokens for all color/font values) + */ @import "$lib/styles/tokens.css"; diff --git a/src/lib/styles/layout.css b/src/lib/styles/layout.css index e041070..824f46f 100644 --- a/src/lib/styles/layout.css +++ b/src/lib/styles/layout.css @@ -1,7 +1,5 @@ /* Structural rules only: display, spacing, sizing, positioning. - No color values. No font-family. No box-shadow. - All visual properties live in theme.css. */ /* Base */ diff --git a/src/lib/styles/theme.css b/src/lib/styles/theme.css index b79ec09..26524b0 100644 --- a/src/lib/styles/theme.css +++ b/src/lib/styles/theme.css @@ -1,8 +1,6 @@ /* theme.css Visual styles only: color, background, border, shadow, font-family, border-radius, text-decoration, opacity, transition. - Zero hardcoded values — everything references a token from tokens.css. - This file should read identically regardless of which theme is active. */ /* Base */ diff --git a/src/lib/styles/themes/dark.css b/src/lib/styles/themes/dark.css index c6347a1..c827129 100644 --- a/src/lib/styles/themes/dark.css +++ b/src/lib/styles/themes/dark.css @@ -1,7 +1,6 @@ /* - Dark theme token overrides. - Apply by adding class="dark" to . - Only redefines tokens that differ from the default (light) theme. + Dark theme overrides. + Only redefines rules that differ from the default theme. */ html.dark { diff --git a/src/lib/styles/tokens.css b/src/lib/styles/tokens.css index 32eb428..e5618d4 100644 --- a/src/lib/styles/tokens.css +++ b/src/lib/styles/tokens.css @@ -1,12 +1,6 @@ @import "@fontsource/courier-prime"; @import "@fontsource/fira-mono"; -/* - Single source of truth for all design tokens. - Light theme values are the default. - Each theme file (themes/*.css) overrides only what changes. -*/ - :root { /* Typography */ --font-body: "Fira Mono", "Courier New", "Courier Prime", monospace; @@ -22,7 +16,7 @@ --color-text-2: #af1a1a; --color-text-3: #315563; - /* Semantic tokens, components reference these, not base colors */ + /* Semantic tokens used by components */ --link-color: var(--color-text); --link-hover-color: var(--color-text); --form-border-color: var(--color-text-2);