v3: tracked down many visual inconsistencies, added two new themes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 43s

This commit is contained in:
khannurien
2026-06-27 14:28:33 +00:00
parent 01940e14be
commit 96c41f01da
11 changed files with 1299 additions and 359 deletions

View File

@@ -20,7 +20,7 @@
font-style: italic;
}
.md code {
font-family: monospace;
font-family: var(--font-mono, monospace);
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: 3px;
@@ -131,10 +131,14 @@
.dump-title-view {
position: relative;
display: inline-flex;
align-items: baseline;
align-items: center;
gap: 0.4rem;
border-radius: 6px;
max-width: 100%;
/* Padding gives the hover box breathing room (incl. right of the pencil);
the matching negative margin keeps the title's visual position unchanged. */
padding: 0.1rem 0.45rem;
margin: -0.1rem -0.45rem;
}
.dump-title-view--editable {
@@ -147,7 +151,9 @@
.dump-title-edit-btn {
font-size: 0.85rem;
color: var(--color-muted);
line-height: 1;
flex-shrink: 0;
color: var(--color-text-muted);
opacity: 0;
transition: opacity 0.1s;
pointer-events: none;
@@ -164,18 +170,34 @@
gap: 0.5rem;
}
/* Shared chrome for inline-edit inputs (the editors that live outside `.form`).
Each rule below adds only its own size / weight / width deltas. */
.dump-title-input,
.profile-email-input,
.playlist-edit-input,
.playlist-edit-textarea {
border: 2px solid var(--color-border);
border-radius: 8px;
background: var(--color-bg);
color: var(--color-text);
font-family: inherit;
outline: none;
transition: border-color 0.2s;
}
.dump-title-input:focus,
.profile-email-input:focus,
.playlist-edit-input:focus,
.playlist-edit-textarea:focus {
border-color: var(--color-accent);
}
.dump-title-input {
flex: 1;
min-width: 12rem;
padding: 0.4rem 0.6rem;
border-radius: 8px;
border: 2px solid var(--color-border);
background-color: var(--color-bg);
color: var(--color-text);
font-size: 1.25rem;
font-weight: 700;
font-family: "Saira", sans-serif;
outline: none;
}
.dump-title-error {
@@ -294,7 +316,7 @@
background-color: var(--color-bg);
color: var(--color-text);
font-size: 1rem;
font-family: "Saira", sans-serif;
font-family: var(--font-body);
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
@@ -384,41 +406,12 @@
color: var(--color-text);
}
/* ── New dump form ── */
.dump-create-wrapper {
width: 100%;
max-width: 520px;
display: flex;
flex-direction: column;
gap: 0.75rem;
animation: page-enter 0.2s ease both;
}
.dump-create-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0 0.25rem;
}
.dump-create-title {
margin: 0;
font-size: 1.4rem;
font-weight: 700;
}
.dump-create-form {
width: 100%;
display: flex;
flex-direction: column;
gap: 1.25rem;
background: var(--color-surface);
border-radius: 14px;
border: 1.5px solid var(--color-border);
padding: 1.5rem;
}
/* ── Mode toggle — segmented control ── */
.visibility-toggle {
display: flex;
@@ -452,12 +445,6 @@
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
}
.file-input-info {
margin: 0.25rem 0 0;
font-size: 0.85rem;
opacity: 0.7;
}
/* ── FileDropZone ── */
.fdz-wrapper {
display: flex;
@@ -600,37 +587,6 @@
display: block;
}
.local-preview-generic {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.85rem 1rem;
border-radius: 10px;
background: var(--color-bg);
border: 2px solid var(--color-border);
}
.local-preview-icon {
font-size: 1.5rem;
flex-shrink: 0;
}
.local-preview-name {
font-weight: 600;
font-size: 0.95rem;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.local-preview-size {
margin-left: auto;
font-size: 0.8rem;
opacity: 0.5;
flex-shrink: 0;
}
.preview-loading {
margin: 0;
font-size: 0.85rem;
@@ -693,44 +649,6 @@
background: var(--color-accent-hover);
}
.file-preview-audio-btn {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem 1.25rem;
border: 2px solid var(--color-border);
border-radius: 10px;
background: var(--color-bg);
cursor: pointer;
color: var(--color-text);
width: 100%;
font-size: 0.95rem;
transition: border-color 0.15s;
}
.file-preview-audio-btn:hover {
border-color: var(--color-accent);
}
.file-preview-audio-icon {
font-size: 1.5rem;
flex-shrink: 0;
}
.file-preview-audio-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: left;
}
.file-preview-audio-play {
flex-shrink: 0;
opacity: 0.5;
}
/* ── Video player ── */
.video-player {
width: 100%;
@@ -1030,6 +948,14 @@
background: var(--color-soundcloud);
}
/* Brand badges sit on a saturated brand colour — use the fixed white
foreground rather than the accent-tuned `on-accent` of the generic badge. */
.rich-content-card--youtube .rich-content-badge,
.rich-content-card--bandcamp .rich-content-badge,
.rich-content-card--soundcloud .rich-content-badge {
color: var(--color-on-brand);
}
/* ── Global persistent player ── */
.global-player {
position: fixed;
@@ -1090,6 +1016,13 @@
min-height: 0;
border-radius: 0 0 8px 8px;
}
/* Inside the global player the shell already draws the border — drop the
nested media player's own border so it isn't doubled up. */
.global-player .audio-player,
.global-player .video-player {
border: none;
}
.global-player iframe {
width: 100%;
border: none;
@@ -1118,9 +1051,6 @@
body.has-player {
padding-bottom: var(--player-height, 0px);
}
body.has-player .fab-new {
bottom: calc(var(--player-height, 0px) + 1rem);
}
.rich-content-thumbnail-btn {
position: relative;
padding: 0;
@@ -1179,10 +1109,12 @@ body.has-player .fab-new {
min-width: 0;
}
/* Generic badge (unknown providers) — themed accent. Brand providers override
the background below and switch to the fixed white `on-brand` foreground. */
.rich-content-badge {
display: inline-block;
background: var(--color-youtube);
color: var(--color-text);
background: var(--color-accent);
color: var(--color-on-accent);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.04em;
@@ -1200,15 +1132,25 @@ body.has-player .fab-new {
word-break: break-word;
}
/* Multi-line truncation boilerplate. Each owner sets its own
`-webkit-line-clamp` line count; this group carries the rest. */
.rich-content-description,
.dump-card-comment,
.playlist-card-description,
.journal-card-title,
.journal-card--image .journal-card-comment,
.journal-card-quote-body {
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
}
.rich-content-description {
margin: 0;
font-size: 0.85rem;
opacity: 0.75;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.rich-content-url {
@@ -1322,24 +1264,30 @@ body.has-player .fab-new {
cursor: default;
}
.vote-count-clickable {
/* Clickable counts. Padding + matching negative margin enlarge the click
target without shifting layout — the box grows, its visual position holds. */
.vote-count-clickable,
.like-count-clickable {
display: inline-block;
cursor: pointer;
/* Padding+matching negative margin enlarges the click target without
shifting layout — the box grows but its visual position is unchanged. */
padding: 0.4rem;
margin: -0.4rem;
}
.vote-count-clickable:hover {
.vote-count-clickable:hover,
.like-count-clickable:hover {
text-decoration: underline;
}
.vote-count-clickable {
padding: 0.4rem;
margin: -0.4rem;
}
/* ── Dump OP line ── */
.dump-op {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
gap: 0.25rem 0.5rem;
font-size: 0.85rem;
opacity: 0.7;
}
@@ -1348,6 +1296,8 @@ body.has-player .fab-new {
font-weight: 600;
color: var(--color-text);
text-decoration: none;
min-width: 0;
overflow-wrap: anywhere;
}
.dump-op-link:hover {
@@ -1616,18 +1566,8 @@ body.has-player .fab-new {
width: min(260px, 100%);
padding: 0.35rem 0.6rem;
border-radius: 6px;
border: 2px solid var(--color-border);
background-color: var(--color-bg);
color: var(--color-text);
font-size: 0.85rem;
font-family: inherit;
line-height: 1.5;
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
}
.profile-email-input:focus {
border-color: var(--color-accent);
}
.profile-email-actions {
@@ -1742,7 +1682,7 @@ body.has-player .fab-new {
}
.invite-url {
font-size: 0.75rem;
font-family: monospace;
font-family: var(--font-mono, monospace);
color: var(--color-text-muted);
flex: 1;
overflow: hidden;
@@ -1807,9 +1747,7 @@ body.has-player .fab-new {
color: var(--color-accent);
}
/* ── Border-tinted buttons ── */
.btn-border-danger,
.btn-border-success,
/* ── Border-tinted button ── */
.btn-border {
display: inline-flex;
align-items: center;
@@ -1825,19 +1763,11 @@ body.has-player .fab-new {
transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.btn-border-danger:hover,
.btn-border-danger.active,
.btn-border:hover {
border-color: var(--color-danger);
color: var(--color-danger);
}
.btn-border-success:hover,
.btn-border-success.active {
border-color: var(--color-success);
color: var(--color-success);
}
/* ── ErrorCard ── */
.error-card-wrap {
display: flex;
@@ -1910,12 +1840,6 @@ body.has-player .fab-new {
width: 100%;
}
.form-error {
color: var(--color-danger);
margin: 0;
font-size: 0.9rem;
}
/* ── Shared layout ── */
.page-shell {
display: flex;
@@ -1957,45 +1881,6 @@ body.has-player .fab-new {
border-bottom: 2px solid var(--color-border);
}
/* ── Floating "+ New" button ── */
.fab-new {
position: fixed;
bottom: 2rem;
right: max(1.5rem, calc((100vw - 860px) / 2 + 1.5rem));
z-index: 20;
background: var(--color-accent);
color: var(--color-on-accent);
border: none;
border-radius: 999px;
padding: 0.6rem 1.4rem;
font-size: 0.95rem;
font-weight: 700;
cursor: pointer;
box-shadow: 0 4px 20px
color-mix(in srgb, var(--color-accent) 50%, transparent);
opacity: 0;
pointer-events: none;
transform: translateY(10px) scale(0.94);
transition:
opacity 0.2s ease,
transform 0.2s ease,
background 0.15s,
box-shadow 0.15s;
}
.fab-new--visible {
opacity: 1;
pointer-events: auto;
transform: none;
}
.fab-new:hover {
background: var(--color-accent-hover);
box-shadow: 0 6px 24px
color-mix(in srgb, var(--color-accent) 60%, transparent);
transform: translateY(-1px);
}
@media (min-width: 860px) {
.app-header--has-center {
display: grid;
@@ -2066,22 +1951,6 @@ body.has-player .fab-new {
display: none;
}
.app-header-center:has(.search-bar--expanded) .header-center-slot {
flex: 1;
justify-content: center;
}
/* Expanded: pin center absolutely so it aligns with the feed's visual center */
@media (min-width: 860px) {
.app-header-center:has(.search-bar--expanded) {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 420px;
pointer-events: auto;
}
}
/* As the center column shrinks (viewport narrow, search collapsed),
shed content in order: presence first, then tabs (still in .index-below-header) */
@container (max-width: 460px) {
@@ -2116,18 +1985,38 @@ body.has-player .fab-new {
flex-wrap: nowrap;
}
/* Normalize all buttons in the header nav to the same height */
.app-header-nav button {
padding: 0.35rem 0.85rem;
/* Normalize every header-nav control to one height (search / notifications /
avatar / + new were all different sizes) and centre their contents — flex +
line-height:1 keeps text and emoji glyphs vertically centred in the box. */
.app-header-nav button,
.app-header-user {
height: 2.25rem;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.95rem;
font-family: inherit;
line-height: inherit;
line-height: 1;
}
.app-header-nav button {
padding: 0.35rem 0.85rem;
}
.app-header-nav .btn-primary {
padding: 0.35rem 1rem;
}
/* Icon-only controls are square, so the row reads as a tidy set. */
.app-header-nav .nav-search-btn,
.app-header-nav .notification-bell,
.app-header-nav .user-menu-trigger {
width: 2.25rem;
padding: 0;
justify-content: center;
}
/* Ghost search button — always visible except when the center search bar is shown */
.nav-search-btn {
display: flex;
@@ -2548,6 +2437,26 @@ body.has-player .fab-new {
background: var(--color-danger-hover);
}
/* ── Ghost button — chromeless icon/toggle action (e.g. global player) ── */
.btn--ghost {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: none;
padding: 0.3rem 0.55rem;
border-radius: 6px;
color: var(--color-text);
font-family: inherit;
opacity: 0.7;
transition: opacity 0.15s, background 0.15s;
}
.btn--ghost:hover {
opacity: 1;
background: color-mix(in srgb, var(--color-text) 8%, transparent);
}
/* ── Ghost icon buttons (shared base) ── */
.modal-close-btn,
.playlist-remove-btn,
@@ -2937,9 +2846,6 @@ body.has-player .fab-new {
opacity: 0.65;
word-break: break-word;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
}
.dump-card-comment {
@@ -3043,7 +2949,8 @@ body.has-player .fab-new {
.playlist-card-count {
opacity: 0.7;
}
.playlist-card-owner {
.playlist-card-owner,
.playlist-detail-owner {
color: inherit;
text-decoration: none;
font-weight: 600;
@@ -3300,11 +3207,6 @@ body.has-player .fab-new {
gap: 0.1rem;
}
}
.playlist-detail-owner {
color: inherit;
text-decoration: none;
font-weight: 600;
}
.playlist-detail-owner:hover {
color: var(--color-accent);
opacity: 1;
@@ -3330,7 +3232,8 @@ body.has-player .fab-new {
transition: opacity 0.15s, border-color 0.15s, color 0.15s;
}
.playlist-edit-btn:hover {
.playlist-edit-btn:hover,
.playlist-cancel-btn:hover {
opacity: 1;
border-color: var(--color-accent);
color: var(--color-accent);
@@ -3338,17 +3241,9 @@ body.has-player .fab-new {
.playlist-edit-input,
.playlist-edit-textarea {
background: var(--color-bg);
border: 2px solid var(--color-border);
border-radius: 8px;
color: var(--color-text);
width: 100%;
font-size: 0.95rem;
padding: 0.4rem 0.65rem;
font-family: inherit;
width: 100%;
box-sizing: border-box;
outline: none;
transition: border-color 0.2s;
}
.playlist-edit-textarea {
@@ -3368,11 +3263,6 @@ body.has-player .fab-new {
gap: 0.7rem;
}
.playlist-edit-input:focus,
.playlist-edit-textarea:focus {
border-color: var(--color-accent);
}
.playlist-detail-meta .playlist-edit-toggle {
opacity: 1;
}
@@ -3431,12 +3321,6 @@ body.has-player .fab-new {
white-space: nowrap;
}
.playlist-cancel-btn:hover {
opacity: 1;
border-color: var(--color-accent);
color: var(--color-accent);
}
.drag-handle {
cursor: grab;
opacity: 0.35;
@@ -3472,20 +3356,6 @@ body.has-player .fab-new {
color: var(--color-accent);
}
/* ── My Playlists page ── */
.my-playlists-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.my-playlists-title {
margin: 0;
font-size: 1.5rem;
font-weight: 700;
}
/* ── New playlist toggle (profile page header) ── */
.new-playlist-toggle {
background: none;
@@ -3520,28 +3390,15 @@ body.has-player .fab-new {
color: var(--color-text-muted);
}
/* ── Comments ── */
.comment-section {
margin-top: 2.5rem;
padding-top: 1.75rem;
border-top: 2px solid var(--color-border);
}
.comment-section-title {
font-size: 0.8rem;
font-weight: 700;
color: var(--color-text-muted);
margin: 0 0 1.25rem;
text-transform: uppercase;
letter-spacing: 0.08em;
}
/* ── Comments / related (shared section chrome) ── */
.comment-section,
.related-section {
margin-top: 2.5rem;
padding-top: 1.75rem;
border-top: 2px solid var(--color-border);
}
.comment-section-title,
.related-section-title {
font-size: 0.8rem;
font-weight: 700;
@@ -3672,12 +3529,7 @@ body.has-player .fab-new {
background: color-mix(in srgb, var(--color-text) 6%, transparent);
}
.comment-delete-btn:hover {
color: var(--color-danger);
border-color: color-mix(in srgb, var(--color-danger) 40%, transparent);
background: color-mix(in srgb, var(--color-danger) 10%, transparent);
}
.comment-delete-btn:hover,
.comment-action-btn--liked {
color: var(--color-danger);
border-color: color-mix(in srgb, var(--color-danger) 40%, transparent);
@@ -3691,18 +3543,10 @@ body.has-player .fab-new {
}
.like-count-clickable {
display: inline-block;
cursor: pointer;
/* Padding+matching negative margin enlarges the click target without
shifting layout — the box grows but its visual position is unchanged. */
padding: 0.3rem;
margin: -0.3rem;
}
.like-count-clickable:hover {
text-decoration: underline;
}
.comment-replies {
padding-left: max(0.4rem, calc(1.25rem - var(--depth, 0) * 0.1rem));
margin-left: max(0.25rem, calc(1.1rem - var(--depth, 0) * 0.09rem));
@@ -3954,7 +3798,8 @@ body.has-player .fab-new {
transition: background 0.15s, color 0.15s;
}
.follow-btn:hover {
.follow-btn:hover,
.follow-btn--following {
background: var(--color-accent);
color: var(--color-on-accent, #fff);
}
@@ -3964,11 +3809,6 @@ body.has-player .fab-new {
cursor: not-allowed;
}
.follow-btn--following {
background: var(--color-accent);
color: var(--color-on-accent, #fff);
}
.follow-btn--following:hover {
background: transparent;
color: var(--color-accent);
@@ -4056,11 +3896,8 @@ body.has-player .fab-new {
text-decoration: none;
line-height: 1.3;
word-break: break-word;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
.journal-card-title:hover {
@@ -4143,11 +3980,8 @@ body.has-player .fab-new {
line-height: 1.4;
margin: 0;
color: rgba(255, 255, 255, 0.85);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
.journal-card--feature .journal-card-title {
@@ -4171,7 +4005,7 @@ body.has-player .fab-new {
position: absolute;
top: -0.5rem;
right: 0.4rem;
font-family: Georgia, "Times New Roman", serif;
font-family: var(--font-display, Georgia, "Times New Roman", serif);
font-size: 4.5rem;
line-height: 1;
color: var(--color-accent);
@@ -4186,11 +4020,8 @@ body.has-player .fab-new {
font-size: 0.92rem;
line-height: 1.45;
color: var(--color-text);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
overflow: hidden;
}
.journal-card--wide.journal-card--quote .journal-card-quote-body,
@@ -4706,7 +4537,7 @@ body.has-player .fab-new {
.notif-icon--mention {
font-weight: 700;
font-family: monospace;
font-family: var(--font-mono, monospace);
}
/* ── Tooltip ── */

View File

@@ -1,6 +1,6 @@
import { createContext } from "react";
export type StyleName = "smooth" | "brutalist";
export type StyleName = "smooth" | "brutalist" | "geocities" | "nyt";
export type ColorScheme = "auto" | "light" | "dark";
export interface ThemeContextValue {

View File

@@ -157,7 +157,7 @@ msgstr "Appearance"
msgid "At least {0} characters"
msgstr "At least {0} characters"
#: src/pages/UserPublicProfile.tsx:1146
#: src/pages/UserPublicProfile.tsx:1160
msgid "Auto"
msgstr "Auto"
@@ -177,8 +177,8 @@ msgstr "Can't connect to the live updates server. Upvotes and notifications may
#: src/pages/Dump.tsx:357
#: src/pages/DumpEdit.tsx:433
#: src/pages/PlaylistDetail.tsx:908
#: src/pages/UserPublicProfile.tsx:1482
#: src/pages/UserPublicProfile.tsx:1552
#: src/pages/UserPublicProfile.tsx:1496
#: src/pages/UserPublicProfile.tsx:1566
msgid "Cancel"
msgstr "Cancel"
@@ -212,7 +212,7 @@ msgstr "Checking invite…"
msgid "Close"
msgstr "Close"
#: src/pages/UserPublicProfile.tsx:1138
#: src/pages/UserPublicProfile.tsx:1152
msgid "Color scheme"
msgstr "Color scheme"
@@ -263,7 +263,7 @@ msgstr "Creating…"
msgid "Current password"
msgstr "Current password"
#: src/pages/UserPublicProfile.tsx:1160
#: src/pages/UserPublicProfile.tsx:1174
msgid "Dark"
msgstr "Dark"
@@ -417,8 +417,8 @@ msgid "Failed to post reply"
msgstr "Failed to post reply"
#: src/pages/PlaylistDetail.tsx:896
#: src/pages/UserPublicProfile.tsx:1485
#: src/pages/UserPublicProfile.tsx:1554
#: src/pages/UserPublicProfile.tsx:1499
#: src/pages/UserPublicProfile.tsx:1568
msgid "Failed to save"
msgstr "Failed to save"
@@ -538,7 +538,7 @@ msgstr "Invitees"
msgid "Journal"
msgstr "Journal"
#: src/pages/UserPublicProfile.tsx:1153
#: src/pages/UserPublicProfile.tsx:1167
msgid "Light"
msgstr "Light"
@@ -644,7 +644,7 @@ msgstr "New"
#: src/components/DumpCreateModal.tsx:292
#: src/pages/UserDumps.tsx:115
#: src/pages/UserPublicProfile.tsx:1209
#: src/pages/UserPublicProfile.tsx:1223
msgid "New dump"
msgstr "New dump"
@@ -709,8 +709,8 @@ msgstr "Not following anyone yet."
#: src/pages/Notifications.tsx:374
#: src/pages/UserDumps.tsx:125
#: src/pages/UserPublicProfile.tsx:1220
#: src/pages/UserPublicProfile.tsx:1342
#: src/pages/UserPublicProfile.tsx:1234
#: src/pages/UserPublicProfile.tsx:1356
#: src/pages/UserUpvoted.tsx:194
msgid "Nothing here yet."
msgstr "Nothing here yet."
@@ -885,8 +885,8 @@ msgstr "Retry"
#: src/pages/Dump.tsx:349
#: src/pages/DumpEdit.tsx:436
#: src/pages/PlaylistDetail.tsx:915
#: src/pages/UserPublicProfile.tsx:1474
#: src/pages/UserPublicProfile.tsx:1544
#: src/pages/UserPublicProfile.tsx:1488
#: src/pages/UserPublicProfile.tsx:1558
msgid "Save"
msgstr "Save"
@@ -895,8 +895,8 @@ msgstr "Save"
#: src/pages/Dump.tsx:348
#: src/pages/PlaylistDetail.tsx:911
#: src/pages/ResetPassword.tsx:124
#: src/pages/UserPublicProfile.tsx:1471
#: src/pages/UserPublicProfile.tsx:1541
#: src/pages/UserPublicProfile.tsx:1485
#: src/pages/UserPublicProfile.tsx:1555
msgid "Saving…"
msgstr "Saving…"
@@ -1044,8 +1044,8 @@ msgstr "Users"
#: src/pages/UserPublicProfile.tsx:963
#: src/pages/UserPublicProfile.tsx:1006
#: src/pages/UserPublicProfile.tsx:1048
#: src/pages/UserPublicProfile.tsx:1241
#: src/pages/UserPublicProfile.tsx:1372
#: src/pages/UserPublicProfile.tsx:1255
#: src/pages/UserPublicProfile.tsx:1386
msgid "View all →"
msgstr "View all →"
@@ -1059,7 +1059,7 @@ msgid "What makes it worth it?"
msgstr "What makes it worth it?"
#: src/pages/UserPublicProfile.tsx:850
#: src/pages/UserPublicProfile.tsx:1533
#: src/pages/UserPublicProfile.tsx:1547
msgid "Who am I?"
msgstr "Who am I?"

View File

@@ -157,7 +157,7 @@ msgstr "Apparence"
msgid "At least {0} characters"
msgstr "Au moins {0} caractères"
#: src/pages/UserPublicProfile.tsx:1146
#: src/pages/UserPublicProfile.tsx:1160
msgid "Auto"
msgstr "Auto"
@@ -177,8 +177,8 @@ msgstr "Impossible de se connecter au serveur de mises à jour en direct. Les vo
#: src/pages/Dump.tsx:357
#: src/pages/DumpEdit.tsx:433
#: src/pages/PlaylistDetail.tsx:908
#: src/pages/UserPublicProfile.tsx:1482
#: src/pages/UserPublicProfile.tsx:1552
#: src/pages/UserPublicProfile.tsx:1496
#: src/pages/UserPublicProfile.tsx:1566
msgid "Cancel"
msgstr "Annuler"
@@ -212,7 +212,7 @@ msgstr "Vérification de l'invitation…"
msgid "Close"
msgstr "Fermer"
#: src/pages/UserPublicProfile.tsx:1138
#: src/pages/UserPublicProfile.tsx:1152
msgid "Color scheme"
msgstr "Thème de couleur"
@@ -263,7 +263,7 @@ msgstr "Création…"
msgid "Current password"
msgstr "Mot de passe actuel"
#: src/pages/UserPublicProfile.tsx:1160
#: src/pages/UserPublicProfile.tsx:1174
msgid "Dark"
msgstr "Sombre"
@@ -417,8 +417,8 @@ msgid "Failed to post reply"
msgstr "Impossible de publier la réponse"
#: src/pages/PlaylistDetail.tsx:896
#: src/pages/UserPublicProfile.tsx:1485
#: src/pages/UserPublicProfile.tsx:1554
#: src/pages/UserPublicProfile.tsx:1499
#: src/pages/UserPublicProfile.tsx:1568
msgid "Failed to save"
msgstr "Enregistrement échoué"
@@ -538,7 +538,7 @@ msgstr "Invités"
msgid "Journal"
msgstr "Journal"
#: src/pages/UserPublicProfile.tsx:1153
#: src/pages/UserPublicProfile.tsx:1167
msgid "Light"
msgstr "Clair"
@@ -644,7 +644,7 @@ msgstr "Nouveau"
#: src/components/DumpCreateModal.tsx:292
#: src/pages/UserDumps.tsx:115
#: src/pages/UserPublicProfile.tsx:1209
#: src/pages/UserPublicProfile.tsx:1223
msgid "New dump"
msgstr "Nouvelle reco"
@@ -709,8 +709,8 @@ msgstr "Aucun abonnement pour le moment."
#: src/pages/Notifications.tsx:374
#: src/pages/UserDumps.tsx:125
#: src/pages/UserPublicProfile.tsx:1220
#: src/pages/UserPublicProfile.tsx:1342
#: src/pages/UserPublicProfile.tsx:1234
#: src/pages/UserPublicProfile.tsx:1356
#: src/pages/UserUpvoted.tsx:194
msgid "Nothing here yet."
msgstr "Rien ici pour l'instant."
@@ -885,8 +885,8 @@ msgstr "Réessayer"
#: src/pages/Dump.tsx:349
#: src/pages/DumpEdit.tsx:436
#: src/pages/PlaylistDetail.tsx:915
#: src/pages/UserPublicProfile.tsx:1474
#: src/pages/UserPublicProfile.tsx:1544
#: src/pages/UserPublicProfile.tsx:1488
#: src/pages/UserPublicProfile.tsx:1558
msgid "Save"
msgstr "Enregistrer"
@@ -895,8 +895,8 @@ msgstr "Enregistrer"
#: src/pages/Dump.tsx:348
#: src/pages/PlaylistDetail.tsx:911
#: src/pages/ResetPassword.tsx:124
#: src/pages/UserPublicProfile.tsx:1471
#: src/pages/UserPublicProfile.tsx:1541
#: src/pages/UserPublicProfile.tsx:1485
#: src/pages/UserPublicProfile.tsx:1555
msgid "Saving…"
msgstr "Enregistrement…"
@@ -1044,8 +1044,8 @@ msgstr "Utilisateurs"
#: src/pages/UserPublicProfile.tsx:963
#: src/pages/UserPublicProfile.tsx:1006
#: src/pages/UserPublicProfile.tsx:1048
#: src/pages/UserPublicProfile.tsx:1241
#: src/pages/UserPublicProfile.tsx:1372
#: src/pages/UserPublicProfile.tsx:1255
#: src/pages/UserPublicProfile.tsx:1386
msgid "View all →"
msgstr "Tout voir →"
@@ -1059,7 +1059,7 @@ msgid "What makes it worth it?"
msgstr "Pourquoi on en voudrait ?"
#: src/pages/UserPublicProfile.tsx:850
#: src/pages/UserPublicProfile.tsx:1533
#: src/pages/UserPublicProfile.tsx:1547
msgid "Who am I?"
msgstr "Qui suis-je ?"

View File

@@ -8,6 +8,8 @@ import { i18n, loadCatalog } from "./i18n.ts";
import "./index.css";
import "./themes/smooth.css";
import "./themes/brutalist.css";
import "./themes/geocities.css";
import "./themes/nyt.css";
await loadCatalog();

View File

@@ -1131,6 +1131,20 @@ export function UserPublicProfile() {
>
Brutalist
</button>
<button
type="button"
className={style === "geocities" ? "active" : ""}
onClick={() => setStyle("geocities")}
>
GeoCities
</button>
<button
type="button"
className={style === "nyt" ? "active" : ""}
onClick={() => setStyle("nyt")}
>
Times
</button>
</div>
</div>
<div className="profile-appearance-row">

View File

@@ -137,7 +137,8 @@
/* ── Typography ──────────────────────────────────────────────────── */
[data-style="brutalist"] {
font-family: "Space Grotesk", "Saira", sans-serif;
--font-body: "Space Grotesk", "Saira", sans-serif;
font-family: var(--font-body);
font-weight: 500;
}
@@ -347,11 +348,19 @@
box-shadow: 3px 3px 0 var(--color-border);
}
/* Neutralise provider brand border-colors so they don't fight the hard shadow */
[data-style="brutalist"] .rich-content-card--youtube,
[data-style="brutalist"] .rich-content-card--bandcamp,
/* Carry the provider brand colour through both the border and the hard
offset shadow, so the brand reads instead of being flattened to grey. */
[data-style="brutalist"] .rich-content-card--youtube {
border-color: var(--color-youtube);
box-shadow: 3px 3px 0 var(--color-youtube);
}
[data-style="brutalist"] .rich-content-card--bandcamp {
border-color: var(--color-bandcamp);
box-shadow: 3px 3px 0 var(--color-bandcamp);
}
[data-style="brutalist"] .rich-content-card--soundcloud {
border-color: var(--color-border);
border-color: var(--color-soundcloud);
box-shadow: 3px 3px 0 var(--color-soundcloud);
}
[data-style="brutalist"]
@@ -470,16 +479,12 @@
}
/* ── btn-border ──────────────────────────────────────────────────── */
[data-style="brutalist"] .btn-border,
[data-style="brutalist"] .btn-border-danger,
[data-style="brutalist"] .btn-border-success {
[data-style="brutalist"] .btn-border {
border-radius: 0;
box-shadow: 2px 2px 0 var(--color-border);
}
[data-style="brutalist"] .btn-border:hover,
[data-style="brutalist"] .btn-border-danger:hover,
[data-style="brutalist"] .btn-border-success:hover {
[data-style="brutalist"] .btn-border:hover {
box-shadow: none;
transform: translate(2px, 2px);
}

504
src/themes/geocities.css Normal file
View File

@@ -0,0 +1,504 @@
/* ══════════════════════════════════════════════════════════════════════
GeoCities / NeoCities theme — turn-of-the-millennium personal-homepage
maximalism: tiled starfield wallpaper, Comic Sans, ridged 3D boxes,
clashing neon accents, rainbow dividers, blinking badges.
Dark scheme → NeoCities "neon on black" revival.
Light scheme → classic GeoCities pastel-on-wallpaper.
══════════════════════════════════════════════════════════════════════ */
/* ── Dark variant (default for this theme) ────────────────────────── */
:root[data-style="geocities"] {
--color-text: #f2f0ff;
--color-text-secondary: #7fdbff; /* cyan */
--color-text-muted: #a99ad6; /* faded violet */
--color-on-accent: #000000; /* black on hot magenta */
--color-bg: #0a0a2e; /* deep indigo night */
--color-surface: #1b1b46; /* raised panel */
--color-border: #00ffff; /* neon cyan */
--color-border-subtle: rgba(0, 255, 255, 0.28);
--color-accent: #ff00ff; /* hot magenta */
--color-accent-hover: #ff5cff;
--color-link: #00ffff; /* classic bright link */
--color-link-hover: #ffff00; /* yellow on hover, very 1999 */
--color-danger: #ff3860;
--color-danger-bg: #3a0a1a;
--color-danger-hover: #ff6b8a;
--color-success: #39ff14; /* neon green */
--color-success-bg: #0a2e0a;
--color-success-hover: #6fff52;
--color-overlay: rgba(8, 0, 28, 0.72);
--color-header-user-bg: rgba(0, 255, 255, 0.1);
--color-header-user-bg-hover: rgba(0, 255, 255, 0.2);
--color-option-bg: #14143a;
--color-option-border: #00ffff;
/* Theme-local helpers */
--geo-rainbow: linear-gradient(
90deg,
#ff004d,
#ff8a00,
#ffee00,
#00d11a,
#00b3ff,
#b300ff
);
/* Starfield: white pinpricks plus the odd coloured star */
--geo-pattern:
radial-gradient(1px 1px at 20px 30px, #ffffff, transparent),
radial-gradient(1px 1px at 130px 80px, #ffffff, transparent),
radial-gradient(1.5px 1.5px at 80px 150px, #00ffff, transparent),
radial-gradient(1.5px 1.5px at 170px 40px, #ff00ff, transparent),
radial-gradient(1px 1px at 50px 190px, #ffee00, transparent);
--geo-pattern-size: 200px 200px;
}
/* ── Light via system preference (auto color-scheme) ──────────────── */
@media (prefers-color-scheme: light) {
:root[data-style="geocities"]:not([data-color-scheme]) {
--color-text: #2a1a4e;
--color-text-secondary: #aa00aa;
--color-text-muted: #7a6a9a;
--color-on-accent: #ffffff;
--color-bg: #fff0f8; /* pale lavender wallpaper */
--color-surface: #ffffff;
--color-border: #d000d0;
--color-border-subtle: rgba(208, 0, 208, 0.25);
--color-accent: #d000d0;
--color-accent-hover: #a000a0;
--color-link: #0000ee; /* unvisited blue */
--color-link-hover: #551a8b; /* visited purple */
--color-danger: #cc0033;
--color-danger-bg: #ffe0e8;
--color-danger-hover: #990022;
--color-success: #008800;
--color-success-bg: #e0ffe0;
--color-success-hover: #006600;
--color-overlay: rgba(40, 0, 40, 0.4);
--color-header-user-bg: rgba(208, 0, 208, 0.08);
--color-header-user-bg-hover: rgba(208, 0, 208, 0.16);
--color-option-bg: #fff0fb;
--color-option-border: #d000d0;
--geo-rainbow: linear-gradient(
90deg,
#ff004d,
#ff8a00,
#ffcc00,
#00aa1a,
#0077dd,
#8800cc
);
--geo-pattern:
radial-gradient(rgba(208, 0, 208, 0.16) 1.5px, transparent 1.5px),
radial-gradient(rgba(0, 179, 255, 0.14) 1.5px, transparent 1.5px);
--geo-pattern-size: 22px 22px, 22px 22px;
}
}
/* ── Manual dark override ─────────────────────────────────────────── */
:root[data-style="geocities"][data-color-scheme="dark"] {
color-scheme: dark;
--color-text: #f2f0ff;
--color-text-secondary: #7fdbff;
--color-text-muted: #a99ad6;
--color-on-accent: #000000;
--color-bg: #0a0a2e;
--color-surface: #1b1b46;
--color-border: #00ffff;
--color-border-subtle: rgba(0, 255, 255, 0.28);
--color-accent: #ff00ff;
--color-accent-hover: #ff5cff;
--color-link: #00ffff;
--color-link-hover: #ffff00;
--color-danger: #ff3860;
--color-danger-bg: #3a0a1a;
--color-danger-hover: #ff6b8a;
--color-success: #39ff14;
--color-success-bg: #0a2e0a;
--color-success-hover: #6fff52;
--color-overlay: rgba(8, 0, 28, 0.72);
--color-header-user-bg: rgba(0, 255, 255, 0.1);
--color-header-user-bg-hover: rgba(0, 255, 255, 0.2);
--color-option-bg: #14143a;
--color-option-border: #00ffff;
--geo-rainbow: linear-gradient(
90deg,
#ff004d,
#ff8a00,
#ffee00,
#00d11a,
#00b3ff,
#b300ff
);
--geo-pattern:
radial-gradient(1px 1px at 20px 30px, #ffffff, transparent),
radial-gradient(1px 1px at 130px 80px, #ffffff, transparent),
radial-gradient(1.5px 1.5px at 80px 150px, #00ffff, transparent),
radial-gradient(1.5px 1.5px at 170px 40px, #ff00ff, transparent),
radial-gradient(1px 1px at 50px 190px, #ffee00, transparent);
--geo-pattern-size: 200px 200px;
}
/* ── Manual light override ────────────────────────────────────────── */
:root[data-style="geocities"][data-color-scheme="light"] {
color-scheme: light;
--color-text: #2a1a4e;
--color-text-secondary: #aa00aa;
--color-text-muted: #7a6a9a;
--color-on-accent: #ffffff;
--color-bg: #fff0f8;
--color-surface: #ffffff;
--color-border: #d000d0;
--color-border-subtle: rgba(208, 0, 208, 0.25);
--color-accent: #d000d0;
--color-accent-hover: #a000a0;
--color-link: #0000ee;
--color-link-hover: #551a8b;
--color-danger: #cc0033;
--color-danger-bg: #ffe0e8;
--color-danger-hover: #990022;
--color-success: #008800;
--color-success-bg: #e0ffe0;
--color-success-hover: #006600;
--color-overlay: rgba(40, 0, 40, 0.4);
--color-header-user-bg: rgba(208, 0, 208, 0.08);
--color-header-user-bg-hover: rgba(208, 0, 208, 0.16);
--color-option-bg: #fff0fb;
--color-option-border: #d000d0;
--geo-rainbow: linear-gradient(
90deg,
#ff004d,
#ff8a00,
#ffcc00,
#00aa1a,
#0077dd,
#8800cc
);
--geo-pattern:
radial-gradient(rgba(208, 0, 208, 0.16) 1.5px, transparent 1.5px),
radial-gradient(rgba(0, 179, 255, 0.14) 1.5px, transparent 1.5px);
--geo-pattern-size: 22px 22px, 22px 22px;
}
/* ══════════════════════════════════════════════════════════════════════
Structural overrides
══════════════════════════════════════════════════════════════════════ */
/* ── Typography — the one true homepage font ──────────────────────── */
[data-style="geocities"] {
--font-body: "Comic Sans MS", "Comic Sans", "Chalkboard SE", "Comic Neue",
"Marker Felt", cursive;
font-family: var(--font-body);
font-weight: 400;
}
/* ── Tiled wallpaper, fixed so content scrolls over it ────────────── */
[data-style="geocities"] body {
background-color: var(--color-bg);
background-image: var(--geo-pattern);
background-size: var(--geo-pattern-size);
background-attachment: fixed;
}
/* ── Loud headings with a soft glow ──────────────────────────────── */
[data-style="geocities"] h1,
[data-style="geocities"] .app-header-brand {
font-weight: 700;
letter-spacing: 0.02em;
text-shadow: 0 0 6px color-mix(in srgb, var(--color-accent) 60%, transparent);
}
/* ── Links — always underlined, bold ─────────────────────────────── */
[data-style="geocities"] a,
[data-style="geocities"] .md a {
text-decoration: underline;
font-weight: 700;
}
/* …except navigation chrome and avatar links, which aren't prose. */
[data-style="geocities"] a:has(.avatar-img),
[data-style="geocities"] a:has(.avatar-initials),
[data-style="geocities"] .index-presence-avatar,
[data-style="geocities"] .app-header-brand,
[data-style="geocities"] .app-header-user,
[data-style="geocities"] .nav-search-btn,
[data-style="geocities"] .user-menu-item,
[data-style="geocities"] .user-list-popover-item {
text-decoration: none;
}
/* ── Blink + marquee-ish keyframes ───────────────────────────────── */
@keyframes geo-blink {
50% {
opacity: 0;
}
}
/* ── Buttons — chunky Win95 outset bevel, press = inset ──────────── */
[data-style="geocities"] button {
border-radius: 0;
border: 3px outset var(--color-border);
box-shadow: none;
font-weight: 700;
transition: border-color 0.1s, background 0.12s;
}
[data-style="geocities"] button:active:not(:disabled) {
border-style: inset;
}
[data-style="geocities"] .btn-primary,
[data-style="geocities"] .btn-secondary,
[data-style="geocities"] .btn-danger,
[data-style="geocities"] .btn-border,
[data-style="geocities"] .follow-btn,
[data-style="geocities"] .vote-btn,
[data-style="geocities"] .feed-sort-btn,
[data-style="geocities"] .invite-btn,
[data-style="geocities"] .btn-add-playlist,
[data-style="geocities"] .load-more-btn,
[data-style="geocities"] .comment-submit-btn,
[data-style="geocities"] .comment-cancel-btn,
[data-style="geocities"] .search-bar-btn {
border-radius: 0;
border: 3px outset var(--color-border);
box-shadow: none;
}
/* Ghost / icon-only buttons stay flat */
[data-style="geocities"] .modal-close-btn,
[data-style="geocities"] .playlist-remove-btn,
[data-style="geocities"] .playlist-card-delete-btn,
[data-style="geocities"] .emoji-picker-float [frimousse-emoji],
[data-style="geocities"] .emoji-picker-close-btn,
[data-style="geocities"] .new-playlist-toggle,
[data-style="geocities"] .btn--ghost,
[data-style="geocities"] .audio-player-btn,
[data-style="geocities"] .audio-player-vol-btn,
[data-style="geocities"] .rich-content-thumbnail-btn,
[data-style="geocities"] .user-menu-trigger,
[data-style="geocities"] .nav-search-btn,
[data-style="geocities"] .auth-link-btn,
[data-style="geocities"] .form-cancel,
[data-style="geocities"] .form-label-action {
border: none;
box-shadow: none;
}
/* ── Segmented controls ──────────────────────────────────────────── */
[data-style="geocities"] .visibility-toggle {
border-radius: 0;
border: 3px ridge var(--color-border);
}
[data-style="geocities"] .visibility-toggle button {
border-radius: 0;
border: none;
}
/* ── Cards & panels — ridged "table border" with a soft neon halo ─── */
[data-style="geocities"] .dump-card,
[data-style="geocities"] .playlist-card,
[data-style="geocities"] .dump-post-header,
[data-style="geocities"] .auth-card,
[data-style="geocities"] .notification-item,
[data-style="geocities"] .user-result-item,
[data-style="geocities"] .journal-card {
border-radius: 0;
border: 3px ridge var(--color-border);
box-shadow: 0 0 9px color-mix(in srgb, var(--color-border) 38%, transparent);
}
/* Comments nest deeply, so the full frame piled up. Keep the squared retro
edge but lighten it: thin flat border, just a whisper of the same halo. */
[data-style="geocities"] .comment-node-inner,
[data-style="geocities"] .comment-top-form {
border-radius: 0;
border: 2px solid var(--color-border-subtle);
box-shadow: 0 0 6px var(--color-border-subtle);
}
[data-style="geocities"] .dump-card:hover,
[data-style="geocities"] .playlist-card:hover,
[data-style="geocities"] .user-result-item:hover,
[data-style="geocities"] .journal-card:hover {
border-color: var(--color-accent);
box-shadow: 0 0 16px color-mix(in srgb, var(--color-accent) 75%, transparent);
}
/* ── App header — rainbow divider ────────────────────────────────── */
[data-style="geocities"] .app-header,
[data-style="geocities"] .header-search-below {
border-bottom: 4px solid;
border-image: var(--geo-rainbow) 1;
}
[data-style="geocities"] .app-header-user {
border-radius: 0;
border: 3px outset var(--color-border);
}
/* ── Rainbow dividers — on each element's real edge ──────────────── */
[data-style="geocities"] .profile-section > h2,
[data-style="geocities"] .profile-section-header,
[data-style="geocities"] .profile-tabs-scroller {
border-bottom: 3px solid;
border-image: var(--geo-rainbow) 1;
}
[data-style="geocities"] .comment-section,
[data-style="geocities"] .related-section {
border-top: 3px solid;
border-image: var(--geo-rainbow) 1;
}
[data-style="geocities"] .comment-section-title,
[data-style="geocities"] .related-section-title {
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* ── Inputs / textareas — sunken inset field ─────────────────────── */
[data-style="geocities"]
input:not([type="range"]):not([type="file"]):not([type="checkbox"]):not(
[type="radio"]
),
[data-style="geocities"] textarea {
border-radius: 0 !important;
border: 2px inset var(--color-border) !important;
background-color: var(--color-bg);
}
[data-style="geocities"] .search-bar-input {
border-radius: 0;
}
/* ── Modals — heavy ridged frame ─────────────────────────────────── */
[data-style="geocities"] .modal-card,
[data-style="geocities"] .confirm-modal,
[data-style="geocities"] .user-menu-dropdown,
[data-style="geocities"] .user-list-popover,
[data-style="geocities"] .mention-dropdown,
[data-style="geocities"] .emoji-picker-float {
border-radius: 0;
border: 3px ridge var(--color-border);
box-shadow: 0 0 18px color-mix(in srgb, var(--color-border) 45%, transparent);
}
[data-style="geocities"] .modal-header {
border-bottom: 2px solid var(--color-border-subtle);
}
/* ── Avatars — framed snapshots ──────────────────────────────────── */
[data-style="geocities"] .avatar-img {
border: 2px ridge var(--color-border);
}
/* ── Rich content cards — keep brand borders, add neon hover glow ── */
[data-style="geocities"] .rich-content-card {
border-radius: 0;
border-width: 3px;
border-style: ridge;
box-shadow: 0 0 9px color-mix(in srgb, var(--color-border) 38%, transparent);
}
[data-style="geocities"] .rich-content-card:has(.rich-content-body:hover),
[data-style="geocities"]
.rich-content-card--youtube:has(.rich-content-body:hover),
[data-style="geocities"]
.rich-content-card--bandcamp:has(.rich-content-body:hover),
[data-style="geocities"]
.rich-content-card--soundcloud:has(.rich-content-body:hover) {
box-shadow: 0 0 10px color-mix(in srgb, var(--color-accent) 70%, transparent);
}
[data-style="geocities"] .rich-content-badge {
border-radius: 0;
}
/* ── Global player — neon console ────────────────────────────────── */
[data-style="geocities"] .global-player {
border-radius: 0;
border: 3px ridge var(--color-accent);
box-shadow: 0 0 16px color-mix(in srgb, var(--color-accent) 55%, transparent);
}
[data-style="geocities"] .global-player-iframe-wrap,
[data-style="geocities"] .global-player-media-wrap,
[data-style="geocities"] .global-player .audio-player,
[data-style="geocities"] .global-player .video-player {
border-radius: 0;
}
[data-style="geocities"] .global-player .audio-player-btn {
color: var(--color-on-accent);
}
[data-style="geocities"] .global-player .audio-player-vol-btn,
[data-style="geocities"] .global-player .btn--ghost {
color: var(--color-text);
}
/* ── File drop zone ──────────────────────────────────────────────── */
[data-style="geocities"] .fdz {
border-radius: 0;
border-style: dashed;
}
/* ── Pills / badges — squared, and the unread bits blink ─────────── */
[data-style="geocities"] .notifications-unread-pill,
[data-style="geocities"] .playlist-badge,
[data-style="geocities"] .dump-card-private-badge,
[data-style="geocities"] .notif-icon,
[data-style="geocities"] .md code,
[data-style="geocities"] .md pre,
[data-style="geocities"] .error-card,
[data-style="geocities"] .feed-tab {
border-radius: 0;
}
[data-style="geocities"] .notification-badge {
border-radius: 0;
animation: geo-blink 1.1s step-start infinite;
}
[data-style="geocities"] .notif-dot,
[data-style="geocities"] .unread-dot {
border-radius: 0;
animation: geo-blink 1.3s step-start infinite;
}
/* ── Floating "new" affordance / FAB-style accents ───────────────── */
[data-style="geocities"] .fdz__upload-icon {
opacity: 0.7;
}
/* ── Casual web-1.0 voice: lowercase everything (code excepted) ──── */
[data-style="geocities"],
[data-style="geocities"] * {
text-transform: lowercase;
}
[data-style="geocities"] input,
[data-style="geocities"] textarea,
[data-style="geocities"] .md code,
[data-style="geocities"] .md pre,
[data-style="geocities"] .invite-url {
text-transform: none;
}

576
src/themes/nyt.css Normal file
View File

@@ -0,0 +1,576 @@
/* ══════════════════════════════════════════════════════════════════════
"Old Grey Lady" theme — New York Times editorial styling.
Serif typography, paper-and-ink palette, hairline rules, sharp corners,
restrained NYT-blue links, inverted ink-on-paper primary buttons.
Light scheme → newsprint (white page, black ink).
Dark scheme → NYT night mode (near-black page, warm-grey ink).
══════════════════════════════════════════════════════════════════════ */
/* ── Dark variant (default for this theme) ────────────────────────── */
:root[data-style="nyt"] {
--color-text: #e6e6e6;
--color-text-secondary: #ababab;
--color-text-muted: #8a8a8a;
--color-on-accent: #121212;
--color-bg: #121212; /* NYT night */
--color-surface: #1e1e1e;
--color-border: #3a3a3a;
--color-border-subtle: rgba(255, 255, 255, 0.12);
--color-accent: #ececec; /* monochrome: interactive = paper-on-ink */
--color-accent-hover: #ffffff;
--color-link: #ececec;
--color-link-hover: #ffffff;
--color-danger: #ff6b6b;
--color-danger-bg: #2a1414;
--color-danger-hover: #ff9a9a;
--color-success: #6cc070;
--color-success-bg: #122a14;
--color-success-hover: #95d699;
--color-overlay: rgba(0, 0, 0, 0.6);
--color-header-user-bg: rgba(255, 255, 255, 0.06);
--color-header-user-bg-hover: rgba(255, 255, 255, 0.12);
--color-option-bg: #181818;
--color-option-border: #3a3a3a;
}
/* ── Light via system preference (auto color-scheme) ──────────────── */
@media (prefers-color-scheme: light) {
:root[data-style="nyt"]:not([data-color-scheme]) {
--color-text: #121212; /* ink */
--color-text-secondary: #5a5a5a;
--color-text-muted: #767676; /* NYT timestamp grey */
--color-on-accent: #ffffff;
--color-bg: #ffffff; /* white page — content sits directly on it */
--color-surface: #ffffff; /* paper */
--color-border: #e2e2e2; /* hairline rule */
--color-border-subtle: rgba(0, 0, 0, 0.08);
--color-accent: #121212; /* monochrome: interactive = ink-on-paper */
--color-accent-hover: #000000;
--color-link: #1a1a1a;
--color-link-hover: #000000;
--color-danger: #b10000;
--color-danger-bg: #fdecec;
--color-danger-hover: #8a0000;
--color-success: #1a7f37;
--color-success-bg: #e6f4ea;
--color-success-hover: #136a2c;
--color-overlay: rgba(0, 0, 0, 0.4);
--color-header-user-bg: rgba(0, 0, 0, 0.05);
--color-header-user-bg-hover: rgba(0, 0, 0, 0.09);
--color-option-bg: #f2f2f2;
--color-option-border: #e2e2e2;
}
}
/* ── Manual dark override ─────────────────────────────────────────── */
:root[data-style="nyt"][data-color-scheme="dark"] {
color-scheme: dark;
--color-text: #e6e6e6;
--color-text-secondary: #ababab;
--color-text-muted: #8a8a8a;
--color-on-accent: #121212;
--color-bg: #121212;
--color-surface: #1e1e1e;
--color-border: #3a3a3a;
--color-border-subtle: rgba(255, 255, 255, 0.12);
--color-accent: #ececec;
--color-accent-hover: #ffffff;
--color-link: #ececec;
--color-link-hover: #ffffff;
--color-danger: #ff6b6b;
--color-danger-bg: #2a1414;
--color-danger-hover: #ff9a9a;
--color-success: #6cc070;
--color-success-bg: #122a14;
--color-success-hover: #95d699;
--color-overlay: rgba(0, 0, 0, 0.6);
--color-header-user-bg: rgba(255, 255, 255, 0.06);
--color-header-user-bg-hover: rgba(255, 255, 255, 0.12);
--color-option-bg: #181818;
--color-option-border: #3a3a3a;
}
/* ── Manual light override ────────────────────────────────────────── */
:root[data-style="nyt"][data-color-scheme="light"] {
color-scheme: light;
--color-text: #121212;
--color-text-secondary: #5a5a5a;
--color-text-muted: #767676;
--color-on-accent: #ffffff;
--color-bg: #ffffff;
--color-surface: #ffffff;
--color-border: #e2e2e2;
--color-border-subtle: rgba(0, 0, 0, 0.08);
--color-accent: #121212;
--color-accent-hover: #000000;
--color-link: #1a1a1a;
--color-link-hover: #000000;
--color-danger: #b10000;
--color-danger-bg: #fdecec;
--color-danger-hover: #8a0000;
--color-success: #1a7f37;
--color-success-bg: #e6f4ea;
--color-success-hover: #136a2c;
--color-overlay: rgba(0, 0, 0, 0.4);
--color-header-user-bg: rgba(0, 0, 0, 0.05);
--color-header-user-bg-hover: rgba(0, 0, 0, 0.09);
--color-option-bg: #f2f2f2;
--color-option-border: #e2e2e2;
}
/* ══════════════════════════════════════════════════════════════════════
Structural overrides
══════════════════════════════════════════════════════════════════════ */
/* ── Typography — one serif everywhere ───────────────────────────── */
[data-style="nyt"] {
--font-body: "Instrument Serif", Georgia, "Times New Roman", serif;
--font-display: "Instrument Serif", Georgia, "Times New Roman", serif;
font-family: var(--font-body);
font-weight: 400;
line-height: 1.5;
}
/* Sharp, rectilinear corners throughout — newsprint has no rounding. */
[data-style="nyt"] button,
[data-style="nyt"] .dump-card,
[data-style="nyt"] .playlist-card,
[data-style="nyt"] .journal-card,
[data-style="nyt"] .dump-post-header,
[data-style="nyt"] .auth-card,
[data-style="nyt"] .notification-item,
[data-style="nyt"] .user-result-item,
[data-style="nyt"] .comment-node-inner,
[data-style="nyt"] .comment-top-form,
[data-style="nyt"] .rich-content-card,
[data-style="nyt"] .rich-content-badge,
[data-style="nyt"] .modal-card,
[data-style="nyt"] .confirm-modal,
[data-style="nyt"] .user-menu-dropdown,
[data-style="nyt"] .user-list-popover,
[data-style="nyt"] .global-player,
[data-style="nyt"] .global-player-media-wrap,
[data-style="nyt"] .global-player-iframe-wrap,
[data-style="nyt"] .fdz,
[data-style="nyt"] .visibility-toggle,
[data-style="nyt"] .feed-tab,
[data-style="nyt"] .md code,
[data-style="nyt"] .md pre,
[data-style="nyt"]
input:not([type="range"]):not([type="file"]):not([type="checkbox"]):not(
[type="radio"]
),
[data-style="nyt"] textarea {
border-radius: 0;
}
/* ── Headlines — display serif ───────────────────────────────────── */
[data-style="nyt"] h1,
[data-style="nyt"] .app-header-brand,
[data-style="nyt"] .dump-title,
[data-style="nyt"] .dump-card-title,
[data-style="nyt"] .playlist-card-title,
[data-style="nyt"] .journal-card-title,
[data-style="nyt"] .playlist-detail-title,
[data-style="nyt"] .form-page-title,
[data-style="nyt"] .auth-card-title,
[data-style="nyt"] .profile-subpage-title,
[data-style="nyt"] .notifications-title,
[data-style="nyt"] .dump-create-title {
font-family: var(--font-display);
font-weight: 400; /* Instrument Serif is single-weight; size carries it */
letter-spacing: 0;
line-height: 1.14;
}
/* A confident broadsheet type scale */
[data-style="nyt"] .app-header-brand {
font-size: 1.45rem;
letter-spacing: 0;
}
[data-style="nyt"] .dump-title {
font-size: 2.1rem;
}
[data-style="nyt"] .playlist-detail-title {
font-size: 1.9rem;
}
[data-style="nyt"] .dump-card-title,
[data-style="nyt"] .playlist-card-title {
font-size: 1.2rem;
line-height: 1.2;
}
[data-style="nyt"] .journal-card-title {
font-size: 1.15rem;
line-height: 1.18;
}
[data-style="nyt"] .journal-card--feature .journal-card-title {
font-size: 1.6rem;
}
/* Headline links underline on hover (ink stays ink). */
[data-style="nyt"] .dump-card-inner:hover .dump-card-title,
[data-style="nyt"] .playlist-card-inner:hover .playlist-card-title,
[data-style="nyt"] .journal-card-title:hover {
color: var(--color-text);
text-decoration: underline;
text-underline-offset: 2px;
}
/* ── Section labels — sentence case, no shouty caps ──────────────── */
[data-style="nyt"] .comment-section-title,
[data-style="nyt"] .related-section-title,
[data-style="nyt"] .form-page-eyebrow,
[data-style="nyt"] .feed-sort-btn,
[data-style="nyt"] .follow-btn,
[data-style="nyt"] .playlist-badge,
[data-style="nyt"] .dump-card-private-badge,
[data-style="nyt"] .notif-group-label,
[data-style="nyt"] .dump-card-date,
[data-style="nyt"] .journal-card-meta {
text-transform: none;
font-variant-caps: small-caps;
font-size: 90%;
letter-spacing: 0;
}
[data-style="nyt"] .comment-section-title,
[data-style="nyt"] .related-section-title,
[data-style="nyt"] .form-page-eyebrow {
font-weight: 700;
}
/* ── Lead note — a standfirst with an editorial drop cap ─────────── */
[data-style="nyt"] .dump-comment {
border-left: none;
padding: 0;
margin: 0.35rem 0 0;
opacity: 1;
color: var(--color-text-secondary);
font-size: 1.2rem;
line-height: 1.6;
}
[data-style="nyt"] .dump-comment p:first-of-type::first-letter {
float: left;
font-family: var(--font-display);
font-weight: 400;
font-size: 3.4rem;
line-height: 0.78;
padding: 0.12em 0.14em 0 0;
color: var(--color-text);
}
/* ── Generous editorial leading for the lead note; comments stay tight ── */
[data-style="nyt"] .dump-comment,
[data-style="nyt"] .dump-comment p {
line-height: 1.7;
}
/* Tight, ruled comment thread — small padding, snug leading, no extra gaps. */
[data-style="nyt"] .comment-body {
line-height: 1.45;
}
[data-style="nyt"] .comment-node-inner {
padding: 0.5rem 0;
gap: 0.6rem;
}
[data-style="nyt"] .comment-meta {
margin-bottom: 0.1rem;
}
[data-style="nyt"] .comment-list,
[data-style="nyt"] .comment-replies {
gap: 0;
}
/* ── Content isn't boxed — it sits on the page, divided by hairline
rules (à la nytimes.com). No surface fill, no card shadow. ─────────── */
[data-style="nyt"] .dump-card,
[data-style="nyt"] .playlist-card,
[data-style="nyt"] .user-result-item,
[data-style="nyt"] .comment-node-inner {
background: transparent;
border: none;
border-bottom: 1px solid var(--color-border);
border-radius: 0;
box-shadow: none;
}
/* The dump detail is a single article, not a list — no dividing rules. */
[data-style="nyt"] .dump-post-header,
[data-style="nyt"] .dump-rich-content {
background: transparent;
border: none;
border-radius: 0;
box-shadow: none;
}
/* Notifications keep their left bar (the unread indicator), lose the box. */
[data-style="nyt"] .notification-item {
border: none;
border-left: 3px solid transparent;
border-bottom: 1px solid var(--color-border);
border-radius: 0;
box-shadow: none;
}
/* No box-hover; the headline underline is the affordance. */
[data-style="nyt"] .dump-card:hover,
[data-style="nyt"] .playlist-card:hover,
[data-style="nyt"] .user-result-item:hover {
background: transparent;
border-color: var(--color-border);
}
/* Genuine containers (forms, embeds, the compose box) keep a hairline box. */
[data-style="nyt"] .auth-card,
[data-style="nyt"] .comment-top-form,
[data-style="nyt"] .rich-content-card {
background: transparent;
border: 1px solid var(--color-border);
box-shadow: none;
}
/* Journal view: a section-front grid of stories divided by hairline rules,
not boxed cards. Close the gaps and rule each cell on its top & left edge so
the rules read as continuous row + column delimiters (à la nytimes.com).
The left-most column's rule is masked off (below) so there's no outer frame
on the left — CSS grid can't target "first column", hence the mask. */
[data-style="nyt"] .journal-grid {
gap: 0;
position: relative;
}
[data-style="nyt"] .journal-card {
background: transparent;
border: none;
border-top: 1px solid var(--color-border);
border-left: 1px solid var(--color-border);
box-shadow: none;
}
/* Page-coloured strip over the grid's content-left edge, hiding the first
column's left rule (left value tracks the grid's padding-left). */
[data-style="nyt"] .journal-grid::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 1.25rem;
width: 1px;
background: var(--color-bg);
z-index: 1;
pointer-events: none;
}
@media (max-width: 460px) {
[data-style="nyt"] .journal-grid::before {
left: 1rem;
}
}
[data-style="nyt"] .journal-card:hover {
border-color: var(--color-border);
transform: none;
box-shadow: none;
}
/* Inset content from the rules so the grid breathes — photos get a margin
frame, text stories sit a little further off the lines. */
[data-style="nyt"] .journal-card--image {
padding: 0.55rem;
}
[data-style="nyt"] .journal-card--image .journal-card-image {
inset: 0.55rem;
}
[data-style="nyt"] .journal-card--quote,
[data-style="nyt"] .journal-card--text {
padding: 1.05rem;
}
/* ── App header — crisp masthead rule ────────────────────────────── */
[data-style="nyt"] .app-header {
border-bottom: 2px solid var(--color-text);
}
[data-style="nyt"] .header-search-below {
border-bottom: 1px solid var(--color-border);
}
[data-style="nyt"] .app-header-user {
border-radius: 0;
}
/* ── Primary button — inverted ink-on-paper (auto-flips per scheme) ─ */
[data-style="nyt"] .btn-primary {
background: var(--color-text);
color: var(--color-bg);
border: 1px solid var(--color-text);
box-shadow: none;
}
[data-style="nyt"] .btn-primary:hover:not(:disabled) {
background: color-mix(in srgb, var(--color-text) 82%, var(--color-bg));
box-shadow: none;
transform: none;
}
/* Secondary / outline buttons — plain hairline box */
[data-style="nyt"] .btn-secondary,
[data-style="nyt"] .btn-border,
[data-style="nyt"] .btn-add-playlist,
[data-style="nyt"] .vote-btn,
[data-style="nyt"] .feed-sort-btn,
[data-style="nyt"] .follow-btn {
border-radius: 0;
}
/* ── Inputs — clean field, ink focus ─────────────────────────────── */
[data-style="nyt"] .form input:focus,
[data-style="nyt"] .form textarea:focus,
[data-style="nyt"] .mention-textarea-wrap textarea:focus {
border-color: var(--color-accent);
box-shadow: none;
}
/* ── Modals — paper sheet with a soft editorial shadow ───────────── */
[data-style="nyt"] .modal-card,
[data-style="nyt"] .confirm-modal,
[data-style="nyt"] .user-menu-dropdown,
[data-style="nyt"] .user-list-popover {
border: 1px solid var(--color-border);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
}
[data-style="nyt"] .modal-header {
border-bottom: 1px solid var(--color-border);
}
/* ── Rich content cards — keep brand cue, square it, drop the glow ── */
[data-style="nyt"] .rich-content-card {
box-shadow: none;
}
/* ── Global player — framed strip ────────────────────────────────── */
[data-style="nyt"] .global-player {
border: 1px solid var(--color-border);
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.2);
}
/* ── Drop the reply "tree" rules; nesting reads from indentation alone ── */
[data-style="nyt"] .comment-replies {
border-left: none;
margin-top: 0;
}
/* ══════════════════════════════════════════════════════════════════════
Monochrome UI chrome — colour is reserved for meaning (danger, the
notification badge, brand-provider cues). Everything else is ink & paper.
══════════════════════════════════════════════════════════════════════ */
/* Feed sort — section-front tabs: an underline, not a filled chip. */
[data-style="nyt"] .feed-sort-btn {
border: none;
border-bottom: 2px solid transparent;
border-radius: 0;
background: none;
color: var(--color-text-muted);
padding: 0.2rem 0.15rem;
}
[data-style="nyt"] .feed-sort-btn:hover {
background: none;
color: var(--color-text);
}
[data-style="nyt"] .feed-sort-btn.active {
background: none;
color: var(--color-text);
border-bottom-color: var(--color-text);
}
/* Square off the remaining pills & chips. */
[data-style="nyt"] .playlist-badge,
[data-style="nyt"] .dump-card-private-badge,
[data-style="nyt"] .notifications-unread-pill,
[data-style="nyt"] .notification-badge,
[data-style="nyt"] .vote-btn,
[data-style="nyt"] .follow-btn,
[data-style="nyt"] .btn-add-playlist,
[data-style="nyt"] .invite-btn {
border-radius: 0;
border: none;
}
/* Links are now ink, so give the user-link classes a hover underline
to keep them findable without colour. */
[data-style="nyt"] .dump-op-link:hover,
[data-style="nyt"] .playlist-card-owner:hover,
[data-style="nyt"] .playlist-detail-owner:hover,
[data-style="nyt"] .comment-author:hover {
text-decoration: underline;
text-underline-offset: 2px;
}
/* Desaturate decorative UI emoji/icons (the header truck, notification
type icons, card glyphs…). Real content — avatars, uploaded thumbnails,
media — is left in colour. grayscale() also greys the notif icon tints. */
[data-style="nyt"] .app-header-brand,
[data-style="nyt"] .nav-search-btn,
[data-style="nyt"] .search-bar-btn,
[data-style="nyt"] .notification-bell-icon,
[data-style="nyt"] .notif-icon,
[data-style="nyt"] .notifications-empty-icon,
[data-style="nyt"] .notifications-title-bell,
[data-style="nyt"] .dump-card-preview-icon,
[data-style="nyt"] .playlist-card-icon,
[data-style="nyt"] .rich-content-compact-icon,
[data-style="nyt"] .journal-card-glyph,
[data-style="nyt"] .fdz__file-icon,
[data-style="nyt"] .empty-state {
filter: grayscale(1);
}
/* ── Tighter, continuous ruled feed (no inter-item gap; the hairline
bottom rules read as one column). Even vertical padding per item. ── */
[data-style="nyt"] .dump-feed {
gap: 0;
}
[data-style="nyt"] .dump-card-inner,
[data-style="nyt"] .playlist-card-inner {
padding-top: 0.9rem;
padding-bottom: 0.9rem;
}
/* ── Card thumbnail — square & flat box (no pop/shadow), but keep a
hover effect adapted to the theme: the image zooms gently inside its
fixed, clipped frame, the editorial way. ───────────────────────── */
[data-style="nyt"] .dump-card-preview,
[data-style="nyt"] .playlist-card-preview {
border-radius: 0;
transition: none;
}
[data-style="nyt"] .dump-card-inner:hover .dump-card-preview,
[data-style="nyt"] .playlist-card-inner:hover .playlist-card-preview {
transform: none;
box-shadow: none;
}
[data-style="nyt"] .dump-card-preview img,
[data-style="nyt"] .playlist-card-preview img {
transition: transform 0.3s ease;
}
[data-style="nyt"] .dump-card-inner:hover .dump-card-preview img,
[data-style="nyt"] .playlist-card-inner:hover .playlist-card-preview img {
transform: scale(1.09);
}

View File

@@ -7,7 +7,12 @@
/* ── Dark variant (default) ───────────────────────────────────────── */
:root,
:root[data-style="smooth"] {
font-family: "Saira", sans-serif;
/* Global font defaults — other themes override --font-body; App.css styles
consume these variables rather than hard-coding font names. */
--font-body: "Saira", sans-serif;
--font-mono: monospace;
font-family: var(--font-body);
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
@@ -56,13 +61,16 @@
--color-option-bg: #0d2340;
--color-option-border: #001427;
/* Service brand colors (fixed, not theme-dependent) */
/* Service brand colors (fixed, not theme-dependent). `on-brand` is the
foreground for text sitting on any of these — also fixed, so the contrast
never flips with the theme's text colour. */
--color-youtube: #c00;
--color-youtube-hover: #f00;
--color-bandcamp: #1da0c3;
--color-bandcamp-hover: #25c8f0;
--color-soundcloud: #f50;
--color-soundcloud-hover: #f73;
--color-on-brand: #fff;
}
/* ── Light via system preference (auto color-scheme) ──────────────── */