Files
gerbeur/src/themes/melange.css
khannurien 62e51f1622
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 55s
Build and Deploy Static Documentation Site / build-and-push (push) Successful in 26s
v4: the six styles are reworked, each around one idea of its own, and the tab row stops painting the page — a pass over every theme, then the faults it showed in what they share. Brutalist draws every box in ink with a solid offset shadow and sets the palette's colors as flat blocks (a teal tag, an apricot quote, an inked label); its dark scheme keeps that ink darker than the page, on Twilight Indigo, where eggshell lines glared across every frame. GeoCities goes to the desktop of its day: the header is the taskbar, the site's name its Start button, the controls grey bevels, and a marquee crawls under the header (Marquee.tsx, drawn in that style alone), with Comic Neue where the system has no Comic Sans. Melange leaves its browns and Fraunces for a sorbet blend in Bricolage Grotesque, run along the header, through the site's name, where it drifts, and between a journal's blocks; its two schemes are one print and its negative, trading ink and paper while the pink and the plum written on it stay, and they share one fine grain where the light one had a coarse tile that greyed the page. Kanagawa opens the comments and the related dumps on a status line and reads on Lotus's brightest paper; its page is flat. Times draws its heavy rules in a mid grey at night, where ink is near white, and sets a journal cell with no picture as text. In what the styles share: a row of tabs (TabBar) hid the tabs scrolled under its scope switcher, and faded its ends, by painting the page's color over them, which showed as a patch on any page that is a gradient or a picture. The switcher now sits before the row that scrolls, which clips the tabs at its edge, and the fades are a mask (--tab-fade-start and --tab-fade-end); useRoomBeside measures the row through that one more level. A page's title keeps a word whole unless it alone is wider than the line: Firefox sizes a title set with negative letter-spacing a hair narrower than it lays it out, which dropped its last letter to a second line in any style that tightened it, so the rule is the base's and the two styles' copies go. A card holding one paragraph, such as the instances a new account hasn't signed in to on /settings, is spaced by its padding and not by the paragraph's margins as well. The site's name answers the pointer as a card's title does in each style, where two of them left it still. The emails follow the styles' tokens, Melange's blend among them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:30:57 +00:00

375 lines
14 KiB
CSS

/* ══════════════════════════════════════════════════════════════════════
Melange theme — a blend, which is all the name means now. It began as a
port of savq/melange-nvim and kept that scheme's idea rather than its
browns: a handful of hues that only make sense mixed. Here they are a
sorbet's (pink, coral, lilac, sky, mint), run together as one gradient
along the header, through the site's name and between a journal's blocks,
on grounds of berry at night and of blush paper by day.
The rest is a print of it: film grain over the whole page, pillowy
borderless cards that sit in a tinted shadow rather than inside a line,
a glow under whatever is active, and one quirky grotesque for all of it.
The two schemes are one print and its negative. They trade ink and paper:
the night's lilac-white type is the day's page, the night's plum ground
the day's type. What doesn't trade is the sorbet: the accent is the same
pink in both, steeped a little deeper by day so that it holds on paper,
and what is written on it is the same plum. A pill, a button, the stripe
under the header are the same objects in either.
Dark scheme → berry (plum grounds, lilac-white ink).
Light scheme → the same on paper (lilac-white grounds, plum ink).
══════════════════════════════════════════════════════════════════════ */
/* ── Palette (light-dark: see tokens.css) ─────────────────────────── */
:root[data-style="melange"] {
/* One family. fonts.css loads its weight axis alone, so it is the same
cut at every size. */
--font-body: "Bricolage Grotesque Variable", system-ui, sans-serif;
--font-display: var(--font-body);
/* Nothing here has a sharp corner. */
--radius-xs: 5px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 22px;
/* Text — by day, the night's card colour. On the accent it is plum in
both schemes. */
--color-text: light-dark(#2b2338, #f1e8f3);
--color-text-secondary: light-dark(#564769, #c8b8d2);
--color-text-muted: light-dark(#746489, #a898b6);
--color-on-accent: #2a1830;
/* Surfaces — sunken < bg < surface < raised: the page is the tinted
ground in both schemes and a card the lighter sheet on it. By day the
page is the night's type colour. */
--color-bg: light-dark(#f1e8f3, #211a2b);
--color-surface: light-dark(#fbf7fc, #2b2338);
--color-surface-raised: light-dark(#fffcff, #372d47);
--color-surface-sunken: light-dark(#e3d6e7, #19131f);
/* Borders — mostly unused: this theme separates by tint and shadow, not
by line. */
--color-border: light-dark(rgb(117 98 131 / 0.34), rgb(168 152 182 / 0.36));
--color-border-subtle: light-dark(
rgb(117 98 131 / 0.16),
rgb(168 152 182 / 0.18)
);
/* Accent — one pink, deeper on paper than at night. Hovering walks it one
step along the blend, to the coral. */
--color-accent: light-dark(#e2568f, #f48fb1);
--color-accent-hover: light-dark(#ea6a45, #ff9e80);
--color-accent-soft: light-dark(
rgb(226 86 143 / 0.12),
rgb(244 143 177 / 0.14)
);
--color-focus-ring: light-dark(
rgb(226 86 143 / 0.4),
rgb(244 143 177 / 0.42)
);
/* Links — a line of text needs more ink than a pill: the pink again,
steeped until it reads at any size on paper. */
--color-link: light-dark(#b62d6b, #f48fb1);
--color-link-hover: light-dark(#c2452b, #ff9e80);
--color-danger: light-dark(#c0303a, #ff8a80);
--color-danger-bg: light-dark(#f8dde0, #4d2030);
--color-danger-hover: light-dark(#c0303a, #c9404f);
--color-success: light-dark(#1b7f66, #86d3b4);
--color-success-bg: light-dark(#d5efe6, #1d3a33);
--color-success-hover: light-dark(#17705a, #a3e0c8);
--color-warning: light-dark(#b5541f, #ffb38a);
/* Overlays */
--color-overlay: light-dark(rgb(54 38 63 / 0.3), rgb(15 10 20 / 0.72));
--color-header-user-bg: transparent;
--color-header-user-bg-hover: light-dark(
rgb(54 38 63 / 0.07),
rgb(241 232 243 / 0.08)
);
/* Misc */
--color-option-bg: light-dark(#eee3ef, #2b2338);
--color-option-border: light-dark(
rgb(117 98 131 / 0.16),
rgb(168 152 182 / 0.18)
);
/* The rest of the blend, beside the accent's pink: the theme's own
furniture colours. By day each is steeped as far as the accent is, a
sorbet still and not a flag's primary. */
--mel-coral: light-dark(#ea6a45, #ff9e80);
--mel-lilac: light-dark(#8262d8, #b9a2f2);
--mel-sky: light-dark(#3a8fd4, #86bdea);
--mel-mint: light-dark(#27a583, #86d3b4);
/* The mélange: every hue, blended once, left to right in the order of
the wheel so no two neighbours mix to mud. */
--mel-spice: linear-gradient(
90deg,
var(--mel-coral),
var(--color-accent) 26%,
var(--mel-lilac) 54%,
var(--mel-sky) 78%,
var(--mel-mint)
);
/* Shadows are tinted with the ground and wide. Light needs more of them
than dark: on paper, shadow is the only thing holding a card off the
page. */
--shadow-color: light-dark(rgb(96 52 118 / 0.2), rgb(8 4 12 / 0.5));
--shadow-color-soft: light-dark(rgb(96 52 118 / 0.13), rgb(8 4 12 / 0.32));
--shadow-color-strong: light-dark(rgb(96 52 118 / 0.32), rgb(8 4 12 / 0.7));
--shadow-1: 0 2px 10px -2px var(--shadow-color-soft);
--shadow-2: 0 14px 34px -10px var(--shadow-color),
0 2px 6px var(--shadow-color-soft);
--shadow-3: 0 32px 70px -16px var(--shadow-color-strong),
0 3px 10px var(--shadow-color-soft);
/* Grain — one fine tile for both schemes, laid differently (see
body::after). Defaults are the night's; the day overrides them by
selector. */
--mel-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='0.22'/%3E%3C/svg%3E");
--mel-grain-blend: soft-light;
--mel-grain-opacity: 0.55;
/* Glow: the accent catches light where it's interactive. */
--mel-glow: 0 3px 16px -4px
light-dark(rgb(226 86 143 / 0.5), rgb(244 143 177 / 0.38));
/* Component skins — no lines anywhere; tint and shadow do the work. */
--border-width: 1px;
/* The header is a board laid over the page, lighter than it in both
schemes. */
--header-bg: light-dark(#fffcff, #2b2338);
--input-focus-bg: var(--color-surface-raised);
--overlay-filter: blur(6px);
--modal-border: transparent;
--card-border: transparent;
--card-border-width: 1px;
--card-shadow: var(--shadow-1);
--card-hover-border: transparent;
--panel-ring: var(--shadow-1);
--tab-border: transparent;
--btn-primary-shadow: var(--mel-glow);
--btn-primary-shadow-hover: 0 5px 22px -4px
light-dark(rgb(226 86 143 / 0.6), rgb(244 143 177 / 0.48));
}
/* ══════════════════════════════════════════════════════════════════════
Structural overrides
══════════════════════════════════════════════════════════════════════ */
/* ── The page is lit from above: the accent pools faintly at the top,
the ground below is flat. ────────────────────────────────────── */
[data-style="melange"] body {
background-image: radial-gradient(
120% 55% at 50% -12%,
var(--color-accent-soft),
transparent 70%
);
background-repeat: no-repeat;
background-attachment: fixed;
}
/* ── Film grain over everything, so no surface reads as flat pixels.
Sits above the page, below modals and toasts.
It is the same fine tile in both schemes, and the blend is what
differs. On berry, `soft-light` grits the surface without lifting the
blacks. On paper that blend does nothing, pale grey noise on a pale
ground having nothing to work against, so the day multiplies it:
paper tooth, which is tiny shadows, kept faint enough that the page
stays the colour it is given. Neither blend nor opacity takes
light-dark(), so the two are custom properties and the scheme is
picked by selector below. ───────────────────────────────────────── */
[data-style="melange"] body::after {
content: "";
position: fixed;
inset: 0;
z-index: calc(var(--z-modal) - 1);
pointer-events: none;
opacity: var(--mel-grain-opacity);
mix-blend-mode: var(--mel-grain-blend);
background-image: var(--mel-grain);
}
/* Auto (no forced scheme) follows the OS; the attribute forms win over
it because they come later and are more specific. */
@media (prefers-color-scheme: light) {
:root[data-style="melange"] {
--mel-grain-blend: multiply;
--mel-grain-opacity: 0.4;
}
}
:root[data-style="melange"][data-color-scheme="light"] {
--mel-grain-blend: multiply;
--mel-grain-opacity: 0.4;
}
/* Forcing dark under a light OS would otherwise inherit the day's two from
the media query above. */
:root[data-style="melange"][data-color-scheme="dark"] {
--mel-grain-blend: soft-light;
--mel-grain-opacity: 0.55;
}
@media (prefers-reduced-transparency: reduce) {
[data-style="melange"] body::after {
display: none;
}
}
/* ── Header — a board over the page: no rule under it, just its own
shade, a tinted shadow, and the blend laid along its bottom edge
as a single stripe. ──────────────────────────────────────────── */
[data-style="melange"] .app-header {
border-bottom: none;
box-shadow: var(--shadow-1);
z-index: var(--z-raised);
}
[data-style="melange"] .app-header::after {
content: "";
position: absolute;
inset: auto 0 0 0;
height: 3px;
background: var(--mel-spice);
}
/* Brand — the site's name is cut out of the blend itself. The fill takes
the place of the text's colour, so the emoji beside it, which sits
outside the name's span, keeps its own. The blend is laid twice as wide
as the name and drifts under it, from its warm end to its cool one and
back, so the name is never the same two colours for long. Pointed at, the
name fills with the accent over the blend, as a card's title turns to it. */
[data-style="melange"] .app-header-brand {
font-weight: 800;
letter-spacing: -0.02em;
}
[data-style="melange"] .app-header-brand-name {
background: var(--mel-spice) 0 0 / 200% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: color var(--dur-base);
animation: mel-drift 7s ease-in-out infinite alternate;
}
[data-style="melange"] .app-header-brand:hover .app-header-brand-name {
color: var(--color-accent);
}
@keyframes mel-drift {
to {
background-position: 100% 0;
}
}
/* Held still, the name shows the whole blend. */
@media (prefers-reduced-motion: reduce) {
[data-style="melange"] .app-header-brand-name {
background-size: 100% 100%;
animation: none;
}
}
/* Headlines are heavy and set a little tight, as the face likes. */
[data-style="melange"] h1,
[data-style="melange"] .dump-title,
[data-style="melange"] .page-title,
[data-style="melange"] .form-page-title {
font-weight: 800;
letter-spacing: -0.02em;
}
/* Tabs keep the shared uppercase cut, but the active one glows. */
[data-style="melange"] .feed-sort-btn.active {
box-shadow: var(--mel-glow);
}
[data-style="melange"] .feed-sort-btn:hover:not(.active) {
background: var(--color-accent-soft);
}
/* ── Cards sit in the light rather than inside a line; they rise a
little when pointed at. ───────────────────────────────────────── */
[data-style="melange"] .dump-card,
[data-style="melange"] .journal-card,
[data-style="melange"] .playlist-card {
transition:
box-shadow var(--dur-slow) var(--ease-out),
transform var(--dur-slow) var(--ease-out);
}
[data-style="melange"] .dump-card:hover,
[data-style="melange"] .journal-card:hover,
[data-style="melange"] .playlist-card:hover {
box-shadow: var(--shadow-2);
transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
[data-style="melange"] .dump-card:hover,
[data-style="melange"] .journal-card:hover,
[data-style="melange"] .playlist-card:hover {
transform: none;
}
}
/* A pointed-at headline over a photo sits on a pillow of the night ground
— the dark scheme's in either scheme, since the scrim under it is dark in
both — lit in the dark scheme's pink. */
[data-style="melange"] .journal-card--image .journal-card-title {
--journal-title-hover-bg: rgb(33 26 43 / 0.86);
--journal-title-hover-color: #f48fb1;
--journal-title-hover-radius: var(--radius-sm);
--journal-title-hover-shadow: 0 4px 14px -4px rgb(0 0 0 / 0.5);
}
/* ── A quote is a printed pull-quote: big mark, in the coral ─────── */
[data-style="melange"] .journal-card--quote {
background: var(--color-surface);
}
[data-style="melange"] .journal-card-quotemark {
color: var(--mel-coral);
opacity: 0.3;
}
[data-style="melange"] .journal-card-quote-body {
font-size: var(--text-lg);
line-height: 1.35;
}
/* The blend runs along the header's bottom edge; between sub-blocks it
returns as a softer band of the same — the page divided by its own
palette rather than by a line. A rule can't carry a gradient, so this one
is a filled bar and the base break's border stands down. */
[data-style="melange"] .journal-break {
--journal-break-space: 2rem;
--journal-break-rule: none;
height: 3px;
border-radius: 999px;
background: var(--mel-spice);
opacity: 0.6;
}
/* Vote tallies are the blend's lilac, apart from the accent's pink. */
[data-style="melange"] .vote-btn:not(.vote-btn--active) {
color: var(--mel-lilac);
}
/* A dump's note is read at length: full ink, where the base dims it. */
[data-style="melange"] .dump-comment {
opacity: 1;
}