Open WebUI chat theme: house palette via native custom.css hook + Figtree fonts (v2 contrast fixes)

This commit is contained in:
Inferencebot committed 2026-09-29 21:22:21 -06:00
1 parent f38e0a683c
commit 3dfdfa6a48
6 files changed
+303

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
# Open WebUI chat theme
The member chat (chat.inference.coop) is styled to the co-op's house palette
using Open WebUI's **native** `/static/custom.css` hook — no image patching, no
license issues (the branding surface is untouched; we only load styles).
## Files
- `openwebui-custom.css` — the theme (copied to the chat app as `/app/data/coop-theme.css`, served as `/static/custom.css`)
- `figtree-*.woff2` — the four Figtree weights (OFL), copied into the chat app's static dir and served same-origin from `/static/fonts/` (avoids cross-origin font CORS problems)
## How it's deployed
State on the chat app (`50ae6c97-…` on Cloudron):
1. **Persistent copies** live in `/app/data/` (survives app updates):
- `/app/data/coop-theme.css`
- `/app/data/fonts/figtree-*.woff2`
2. **Startup hook** in `/app/data/env.sh` (appended, labeled "Co-op theme"):
```bash
if [ -f /app/data/coop-theme.css ]; then
cp /app/data/coop-theme.css /run/openwebui/static/custom.css 2>/dev/null || true
mkdir -p /run/openwebui/static/fonts
cp /app/data/fonts/*.woff2 /run/openwebui/static/fonts/ 2>/dev/null || true
fi
```
This is needed because Open WebUI's start script recreates
`/run/openwebui/static/` from pristine image files on every boot.
3. **Live serving** — Open WebUI's built `index.html` already contains
`<link rel="stylesheet" href="/static/custom.css">`, so no HTML changes.
## To update the theme
```bash
# 1. Push the new CSS to the app's persistent storage
cloudron push --app 50ae6c97-e851-4d17-b013-51ca7a4e5be0 \
openwebui-custom.css /app/data/coop-theme.css
# 2. Apply immediately (no restart needed)
cloudron exec --app 50ae6c97-e851-4d17-b013-51ca7a4e5be0 -- \
cp /app/data/coop-theme.css /run/openwebui/static/custom.css
```
Verify at `https://chat.inference.coop/static/custom.css`.
## Contrast notes (v2, 2026-09-29)
v1 remapped the full Tailwind gray scale, which drifted body text and
placeholders toward olive and hurt readability. v2 rules:
- **Tint surfaces, not text.** Backgrounds/borders (gray-50..300) get the
cream/olive treatment; text-side tokens (gray-600+) stay near Tailwind
defaults in light mode.
- **Explicit text contrast fixes** for message bodies (`.markdown-prose`),
placeholders, and secondary text, both modes.
- Links darkened to `#3f6e3e` on cream (AA); lightened in dark mode.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+247
View File
@@ -0,0 +1,247 @@
/* Inference Cooperative theme for Open WebUI
House style: cream #faf8f5, ink #2d3327, greens #5b8c5a/#6ba86b/#7ab87a,
bronze #a97a4a, earthy red #b3543a. Figtree via co-op-hosted fonts.
Loaded via /static/custom.css (native Open WebUI hook). */
/* ---------- Font (served same-origin from the chat app, no CORS needed) ---------- */
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/static/fonts/figtree-400.woff2') format('woff2');
}
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/static/fonts/figtree-500.woff2') format('woff2');
}
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/static/fonts/figtree-600.woff2') format('woff2');
}
@font-face {
font-family: 'Figtree';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/static/fonts/figtree-700.woff2') format('woff2');
}
:root {
--coop-cream: #faf8f5;
--coop-cream-2: #f1ede5;
--coop-ink: #2d3327;
--coop-muted: #6b7a62;
--coop-green-deep: #5b8c5a;
--coop-green-mid: #6ba86b;
--coop-green-soft: #7ab87a;
--coop-bronze: #a97a4a;
--coop-earth: #b3543a;
--coop-border: #e4e0d8;
}
/* Light mode base.
NOTE: base text colors stay near-Tailwind defaults for contrast; we tint
surfaces (backgrounds, borders) rather than pushing text colors around,
because remapping the whole gray scale made body text and placeholders
hard to read (gray-400/500 shifted too far toward olive). */
html, body {
font-family: 'Figtree', -apple-system, 'Segoe UI', system-ui, sans-serif !important;
}
/* ---------- Light theme: warm surfaces, readable text ---------- */
@media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
html:not(.dark) {
/* App background: cream. Text: unchanged (dark slate) for max contrast. */
--color-gray-50: #faf8f5;
--color-gray-75: #f6f3ee;
--color-gray-100: #f1ede5;
--color-gray-200: #e4e0d8;
--color-gray-300: #d4cfc3;
--color-gray-400: #9c978b; /* placeholders — darker than before, still muted */
--color-gray-500: #75705f;
/* gray-600 through 950 UNTOUCHED: body text keeps Tailwind's near-black
contrast instead of olive drift */
}
}
/* Dark mode: warm-tinted greens instead of pure gray.
Keep text-side tokens (600+) bright enough for contrast. */
html.dark {
--color-gray-50: #262b21;
--color-gray-75: #2d3327;
--color-gray-100: #333a2d;
--color-gray-200: #3e4636;
--color-gray-300: #545e4a;
--color-gray-400: #7d8875;
--color-gray-500: #97a28e;
--color-gray-600: #b2bca9;
--color-gray-700: #ccd3c3;
--color-gray-800: #e3e7db;
--color-gray-850: #eef1e7;
--color-gray-900: #f5f7ef;
--color-gray-950: #faf8f5;
}
/* ---------- Accent color: co-op green ---------- */
:root, html.dark {
--color-green-300: #7ab87a;
--color-green-400: #6ba86b;
--color-green-500: #5b8c5a;
--color-green-600: #4e7a4d;
}
/* Primary buttons */
.btn-primary, button.bg-black, button.bg-emerald-600, .bg-emerald-600 {
background-color: #5b8c5a !important;
}
.btn-primary:hover, button.bg-emerald-600:hover {
background-color: #4e7a4d !important;
}
/* Selected states, toggles, focus rings */
.bg-emerald-500, .peer:checked ~ .bg-emerald-500 {
background-color: #6ba86b !important;
}
.ring-emerald-500, .focus\:ring-emerald-500:focus, .border-emerald-500 {
--tw-ring-color: #6ba86b;
border-color: #6ba86b;
}
.text-emerald-500, .text-emerald-400, .text-emerald-300 {
color: #5b8c5a !important;
}
html.dark .text-emerald-500, html.dark .text-emerald-400, html.dark .text-emerald-300 {
color: #7ab87a !important;
}
/* ---------- Sidebar ---------- */
#sidebar {
background: linear-gradient(180deg, #f6f3ee 0%, #f1ede5 100%) !important;
border-right: 1px solid #e4e0d8;
}
html.dark #sidebar {
background: linear-gradient(180deg, #262b21 0%, #1f241b 100%) !important;
border-right: 1px solid #3e4636;
}
/* Sidebar: active conversation */
#sidebar a[id^="chat-item"].active {
background-color: #eaf3ea !important;
border-left: 3px solid #5b8c5a;
}
html.dark #sidebar a[id^="chat-item"].active {
background-color: #333a2d !important;
}
/* ---------- Chat area ---------- */
/* User message bubble: soft green tint */
.user-message .message, .chat-user .message, div[class*="user-message"] {
background-color: #eaf3ea !important;
border-radius: 14px;
}
html.dark .user-message .message, html.dark div[class*="user-message"] {
background-color: #333a2d !important;
}
/* Assistant message: cream card */
.assistant-message .message, div[class*="assistant-message"] {
border-radius: 14px;
}
/* ---------- Composer ---------- */
#chat-input, textarea[name="prompt"], .scrollbar-hidden.relative {
border-radius: 14px !important;
}
button[type="submit"].bg-black {
background-color: #5b8c5a !important;
}
/* ---------- Model selector chip ---------- */
button[aria-label="Select a model"], #model-selector {
border-color: #d4cfc3;
}
html.dark button[aria-label="Select a model"], html.dark #model-selector {
border-color: #545e4a;
}
/* ---------- Code blocks ---------- */
pre, code, .language-python, .hljs {
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, monospace;
}
.codeblock {
border-radius: 12px !important;
border: 1px solid #e4e0d8;
}
html.dark .codeblock {
border-color: #3e4636;
}
/* ---------- Scrollbars: thin, green ---------- */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: #c9d3c5; border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: #6ba86b; }
html.dark ::-webkit-scrollbar-thumb { background: #3e4636; }
html.dark ::-webkit-scrollbar-thumb:hover { background: #6ba86b; }
::-webkit-scrollbar-track { background: transparent; }
/* ---------- Selection ---------- */
::selection { background: #cfe4cf; }
html.dark ::selection { background: #3e5a3d; }
/* ---------- Links ---------- */
a:not(.no-underline):not([class*="btn"]) {
color: #3f6e3e; /* darkened for AA contrast on cream */
}
html.dark a:not(.no-underline):not([class*="btn"]) {
color: #8fc98f;
}
/* ---------- Text contrast fixes ---------- */
/* Assistant reply text: near-black in light mode regardless of remapped tokens */
.text-gray-800, .text-gray-900, div[id^="message-"] .markdown-prose {
color: #262b21;
}
html.dark .text-gray-800, html.dark .text-gray-900, html.dark div[id^="message-"] .markdown-prose {
color: #eef1e7;
}
/* Placeholder + secondary text: readable, not ghostly */
::placeholder, .placeholder\:text-gray-400::placeholder {
color: #8a8578 !important;
opacity: 1 !important;
}
html.dark ::placeholder {
color: #7d8875 !important;
}
.text-gray-400 { color: #75705f !important; }
html.dark .text-gray-400 { color: #97a28e !important; }
.text-gray-500 { color: #66604f !important; }
html.dark .text-gray-500 { color: #a5b09a !important; }
/* Input borders slightly stronger so fields are visible on cream */
input, textarea, select {
border-color: #d4cfc3 !important;
}
html.dark input, html.dark textarea, html.dark select {
border-color: #545e4a !important;
}
/* ---------- Modal / dropdown polish ---------- */
.modal, .dropdown-menu, [role="menu"] {
border-radius: 14px !important;
border: 1px solid #e4e0d8 !important;
}
html.dark .modal, html.dark .dropdown-menu, html.dark [role="menu"] {
border-color: #3e4636 !important;
}
/* ---------- Small serif-free caps for section labels (subtle) ---------- */
.settings-tab, [data-sidebar-label] {
letter-spacing: 0.02em;
}