One Morandi design language — two themes, and the tool that keeps their colors honest.
同一套莫兰迪设计语言:两个宿主的主题,加一个守住它们配色的工具。
中文 | English
Bringing the Morandi feel of Bloom (a 90★ Typora theme)
to DeepSeek Harness.
Glass + Morandi: 8 light/dark palettes, frosted-glass panels, one-click switching from the top bar —
even the waiting state breathes with the theme.
## The gist
A **glass + Morandi** theme for DeepSeek Harness.
Eight light/dark palettes, adaptive by light/dark; panels are real frosted glass
(semi-transparent + backdrop blur + glass edges); colors are low-saturation Morandi
(OKLCH, WCAG AA); restrained, consistent micro-interactions; zero runtime deps; TypeScript front-end.
> The Morandi character does not live in `--accent`. It lives in `--accent-rgb`.
## Screenshots
Sage · light — frosted-glass panels + the selected session row tints with the active theme
**8 palettes (light)**: click any one and the whole accent / background / glass / motion hue switches together.
☁️ Mist
 |
🧧 Cinnabar
 |
🌸 Petal
 |
🌊 Ripple
 |
🌿 Sage
 |
🧱 Stone
 |
🔷 Lapis
 |
🍯 Amber
 |
**Every palette has a dark variant too** (example: Amber · dark):
## Install
```bash
# via the dsh CLI (recommended)
dsh plugin add @kubor/dsh-bloom-theme
# or global install + enable
npm i -g @kubor/dsh-bloom-theme
dsh plugin enable @kubor/dsh-bloom-theme
```
Refresh the page and a "Mist ▾" theme button appears top-right. Click it to switch between
**8 palettes**; the current version sits at the bottom of the dropdown, and a `↑ vX` badge
lights up when a newer version is on npm (jump to the Releases page).
## Features
| Feature | Description |
| :-- | :-- |
| **Frosted-glass panels (on by default)** | Semi-transparent fill + `backdrop-filter` blur + glass edges (top highlight / translucent stroke / soft outer glow), adaptive opacity per light/dark |
| **8 Morandi palettes** | Mist / Cinnabar / Petal / Ripple / Sage / Stone / Lapis / Amber, each with light & dark, one-click switch |
| **Two-track colors** | A readable track for contrast (text/buttons) and a mood track for ambient gradients — never mixed |
| **Micro-interactions** | Menu entrance, selected-row bar slide-in, hover / press feedback; shared duration & easing tokens, consistent everywhere |
| **Theme-colored thinking animation** | `Deep diving…` flows through the current theme's three-color spectrum; auto-still under `prefers-reduced-motion` |
| **Version / update hints** | Current version at the bottom of the dropdown; `↑ vX` chip when npm is newer; update banner for very stale installs |
| **OKLCH + WCAG AA** | Perceptually uniform color space, no jumps on light/dark switch; all accent/background pairs measured ≥ 4.5:1 |
| **Zero deps · CSS-variable driven** | Only CSS variables + a little switch logic; near-zero runtime cost |
| **Respects native controls** | Switcher mounts into the DSH header utilities area, side-by-side with native buttons |
| **TypeScript build** | `src/` 10 modules → `tsc` (ESM) for node + `esbuild` (single-file IIFE) for browser; one-command `build / deploy / check / preflight` |
## Design: two-track colors
Bloom's point was never "swap the colors" — it is a complete **Morandi design language**:
low-saturation ambient gradients, cool-toned hairlines, long soft shadows, restrained radii and spacing.
The easiest thing to get wrong (and most often done wrong) is the **two-track color**:
- **Readable track `--bloom-accent`** — deliberately deepened to pass WCAG; for text / buttons / selected states;
- **Mood track `--accent-rgb`** — the real Morandi color (low-saturation, grayed); used only for large ambient gradients and cool hairlines.
All 14 gradient usages in the original use the mood track, never the readable track. When you paint a
large area with the readable color, Petal goes from lotus pink to fluorescent magenta and the Morandi feel dies.
This plugin ports both tracks intact, and the sibling tool [contrast-guard](https://github.com/webkubor/contrast-guard)
keeps accent/background contrast honest.
## Develop / build (TypeScript)
```bash
npm install
npm run typecheck # tsc --noEmit, full type check
npm run build # typecheck → tsc emits lib/index.js → esbuild emits lib/client.js
npm run deploy # one-click deploy to local DSH (sync-version → build → rsync)
npm run preview # deploy + open http://127.0.0.1:3080
npm run dev # build + watch (recompile+deploy on src change)
npm run package # one-click package (npm pack → .tgz)
npm run check # 6 static gates + contrast-guard (runs on every commit)
npm run preflight # pre-release: version consistency / git state / listing sync
```
`src/` is 10 modules (`meta` · `palette` · `tokens` · `css/` · `dom` · `version` ·
`switcher` · `client`); DSH loads the artifacts in `lib/`.
**The build is dual-track** — the two halves want opposite module formats:
| Artifact | Compiler | Format | Why |
|---|---|---|---|
| `lib/index.js` (node side) | `tsc` | **ESM** | cordis' plugin loader reads it as ESM |
| `lib/client.js` (browser side) | `esbuild` | **single-file IIFE** | DSH runs plugin client.js as a *classic script* — a top-level `import`/`export` is a syntax error |
So the browser-side source can be split freely, but the artifact must bundle back
into one self-contained file.
The version number is bumped by release-please inside the release PR, alongside
`package.json` (via the `x-release-please-version` marker at the end of the line in
`src/meta.ts`). `scripts/sync-version.mjs` is only a local-dev fallback and is not
part of the publish path. See [CONTRIBUTING](./CONTRIBUTING.md).
## FAQ
**Theme not applying?** Hard-refresh once (`Cmd/Ctrl + Shift + R`). If the page says `Failed to load plugins`,
the client failed to register — re-run `dsh plugin add` or upgrade to the latest version.
**Can I customize colors?** For now you pick a whole palette via variants; per-color customization is on the roadmap.
## Contribute / open source
- Add a new palette: add one entry to `PALETTE` / `VARIANTS` / `VARIANT_LABELS` in `src/client.ts`
— glass, motion, selected state and the contrast guardrail all follow automatically.
- Run `npm run build && npm run check` and make sure `contrast-guard` passes before opening a PR.
- Issues, PRs and ideas are all welcome.
## Support
If Bloom makes your DSH nicer to use, drop a ⭐ or buy me a coffee (sponsor link below and one entry in the panel menu).
## License
[MIT](LICENSE) — keep the copyright notice on borrow/modify. Palette inspiration from [typora-Bloom-theme](https://github.com/webkubor/typora-Bloom-theme).