Asked whether these could follow the theme too. They are the three most visible things
on screen and were the three least reachable: `Background.tsx` hardcoded
`url('/landscape1.webp')`, the header carried `rgba(255,255,255,0.25)` and `#1d2724`
in an inline style, and the logo was an `<img src>`.
Between them that is most of what "the app looks like Officer" actually meant. The
Plain theme turned every panel white and all three stayed exactly as they were, which
is how they were found — the leak detector doing its job on its first outing.
Verified across all three themes:
duck-dark wallpaper=landscape1.webp header=rgba(255,255,255,0.25)
duck-light wallpaper=landscape1.webp header=rgba(255,255,255,0.25)
plain wallpaper=none header=rgb(255,255,255)
── Images could not be done the obvious way ──
`--logo-image: url('/officer-logo.svg')` fails the build. Bun's CSS bundler resolves
`url()` at BUILD time and these files are served from `public/` at RUNTIME, so it goes
looking for a module that does not exist. Colours apply themselves; images cannot.
So a theme publishes a quoted PATH — an ordinary string token the bundler ignores —
and `useThemeAsset` reads it and wraps it in `url()`. One hook rather than the read
inline in two components, because the quote-stripping is the kind of detail that gets
forgotten the second time.
The logo is a `<div role="img">` with a background image rather than an `<img>`, since
CSS cannot set a `src` and a white-labelled build has to replace the mark from its
theme rather than by editing the header.
The header's title colour goes through a `.header-ink` class: `EditableTitle` accepts
a className and not a style, and the token stays the source of truth either way.
Panel radius and the ~985 raw palette usages are still outstanding — see
THEMES-NOTES.md.
tsgo clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
86 lines
2.6 KiB
CSS
86 lines
2.6 KiB
CSS
/*
|
|
App furniture: the rules that are neither Tailwind nor a component, expressed in theme variables.
|
|
|
|
These came out of `globals.css` with hardcoded greys baked in — `#444` scrollbars, `rgb(128 128 128 / .3)`
|
|
thumbs. Small, but exactly the kind of thing that makes a light theme look broken in the code editor and
|
|
that a theme author has no way to reach. Now every value here is a variable a theme owns.
|
|
*/
|
|
|
|
/* Sonner renders at the document root, above everything the app draws. */
|
|
[data-sonner-toaster] {
|
|
z-index: 700 !important;
|
|
}
|
|
|
|
/* Code editor — thin scrollbars. Dark by default under Duck, because the editor surface is dark in both
|
|
Duck themes; a theme with a light editor sets these to suit. */
|
|
.code-editor-scrollable {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--scrollbar-thumb) transparent;
|
|
}
|
|
|
|
.code-editor-scrollable::-webkit-scrollbar {
|
|
width: 6px;
|
|
height: 6px;
|
|
}
|
|
|
|
.code-editor-scrollable::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.code-editor-scrollable::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thumb);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.code-editor-scrollable::-webkit-scrollbar-thumb:hover {
|
|
background: var(--scrollbar-thumb-hover);
|
|
}
|
|
|
|
/* Chat — the background-task strip. A default horizontal scrollbar is ~15px tall and is laid out INSIDE
|
|
the row, so it stole most of the chips' breathing room and made the tray read as squeezed. */
|
|
.task-strip {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--scrollbar-thin-thumb) transparent;
|
|
}
|
|
|
|
.task-strip::-webkit-scrollbar {
|
|
height: 4px;
|
|
}
|
|
|
|
.task-strip::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.task-strip::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thin-thumb);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.task-strip:hover::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thin-thumb-hover);
|
|
}
|
|
|
|
/* File browser — while a context menu is open, the rest of the grid stops responding. Behaviour, not
|
|
appearance, but it lives with the other furniture rather than in a component's stylesheet. */
|
|
.file-grid:has([data-state='open']) [data-file-item]:not([data-state='open']) {
|
|
pointer-events: none;
|
|
}
|
|
|
|
.file-grid:has([data-state='open']) [data-file-item]:not([data-state='open']) .file-item-ellipsis {
|
|
opacity: 0 !important;
|
|
}
|
|
|
|
.file-grid [data-file-item][data-state='open'] .file-item-ellipsis {
|
|
opacity: 1 !important;
|
|
}
|
|
|
|
/* The header's own ink. A class rather than a variable at the call site, because `EditableTitle` accepts a
|
|
className and not a style — the token is still the source of truth. */
|
|
.header-ink {
|
|
color: var(--header-fg);
|
|
}
|
|
|
|
.header-ink-border {
|
|
border-color: color-mix(in srgb, var(--header-fg) 40%, transparent);
|
|
}
|