Skip to content

feat(theme): add 5 tinted-neutral families + adjustable gray base#211

Merged
ryota-murakami merged 7 commits into
mainfrom
feat/tinted-neutral-expansion
Jun 13, 2026
Merged

feat(theme): add 5 tinted-neutral families + adjustable gray base#211
ryota-murakami merged 7 commits into
mainfrom
feat/tinted-neutral-expansion

Conversation

@ryota-murakami

@ryota-murakami ryota-murakami commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Expands the theme catalog with 5 new tinted-neutral families and a tinted-only adjustable gray base, taking the catalog from 27 presets / 44 themes → 37 presets / 54 visual themes (2 pure neutral + 17 color hues + 18 tinted = 9 families × dark/light).

Theme feature

  • feat(theme) 6dcc596 — Add 5 tinted-neutral families (clay, olive, sage, steel, plum) to THEME_PRESETS, joining the existing zinc/slate/stone/mauve. All nine share TINTED_NEUTRAL_CHROMA = 0.05 with hues ≥25° apart for perceptual distinctness. Adds the .tone-tinted gray-base mechanism: deeper gray surfaces in Light, lighter gray surfaces in Dark — pure-neutral (0) and full-color (0.16) presets keep the crisp default ramp untouched. ThemeSelector becomes a 5×2 palette grid (10 families).
  • style(theme) 4c77c10 — Hold dark tinted --secondary/--muted lightness at L≤0.27 so the 10px ThemeSelector family labels keep AA 4.5:1 contrast on hover:bg-muted (verified live at 4.66:1).

Tests

  • test 53f0729 — Pin the inline tinted-gate upper bound to COLOR_PRESET_CHROMA so a constant bump cannot silently desync first paint.
  • test c615533 — Cover the bootstrap tinted-gate null-chroma arm (a stale .tone-tinted class must be stripped for an unclassifiable persisted theme).

Docs

  • docs 6ebb33f — Fix stale comments flagged by pre-landing review.
  • docs 691956a — Sync every count-bearing doc (README, SPEC + 9-family table, website llms.txt + Features.tsx) to 37 presets / 54 themes.
  • docs(design) 846d11d — Document the .tone-tinted gray-base mechanism in DESIGN.md (the binding visual source of truth).

Test Coverage

pnpm validate green — 1493 tests + lint (--max-warnings 0) + typecheck + dead-code + storybook build.

bootstrap tinted-gate:  26 → 29 tests (+3: upper-bound pin, null-arm strip, byte-identity)
listener .tone-tinted:  toggle covered for tinted / pure-neutral / full-color presets
contrast (AA 4.5:1):    dark tinted muted held at L≤0.27 → 4.66:1 live

Coverage audit: 94% (above the 80% gate). The one defensive gap (the bootstrap chromaVal === null arm) was closed in c615533 rather than waived. Theme tests count presets dynamically via Object.keys(THEME_PRESETS), so they auto-adapt to the new families.

Pre-Landing Review

/reviewapproved-with-followup.

  • F1 (drift-guard gap — the existing guard pinned only the quoted '0.16' setProperty arg, not the bare chromaVal < 0.16 comparison): multi-model confirmed, fixed in 53f0729.
  • Codex "Block" (4 HIGH) — C1 listener trusts hydrated chroma/mode, C2 bootstrap typeof==='number' accepts NaN/Infinity, C3 tone-tinted float boundary, C4 OKLCH gamut untested. All reduce to FS-staged spoofing of UI-only signals on the tamperer's own machine = the repo's documented threat model ("user-side FS is trusted; ship-as-is on theoretical FS-staged spoofing of UI-only signals"). C1/C2/C4 pre-existing; C3 unreachable (chroma is always an exact literal). Overridden ship-as-is; advisor concurred.

Design Review

/design-review ran for this branch and passed. DESIGN.md additionally refined (846d11d) to document the new .tone-tinted mechanism, per the project's living-document principle.

Adversarial Review

Claude adversarial subagent → evidence-based APPROVE. Verified no IIFE/listener desync (chroma distribution is exactly 2×0 / 17×0.16 / 18×0.05 — the open (0, 0.16) band contains only 0.05), and no v0→v1 tinted FOUC (the tinted-families commit postdates the v0→v1 chroma refactor and is not an ancestor of it, so no legacy payload can resolve into the tinted band).

Eval Results

No prompt-related files changed — evals skipped.

Plan Completion

/autoplan plan (feat-tinted-neutral-expansion-plan.md) — all items complete. Electron e2e run fresh this session: 58 passed.

Documentation

No documentation changes were needed beyond those already in this branch. Commit 691956a synced every count-bearing doc the tinted-neutral expansion made stale (README.md, SPEC.md + the 9-family table, website/public/llms.txt, website/src/components/Features.tsx), and 846d11d added the .tone-tinted mechanism to DESIGN.md. An independent /document-release audit of DESIGN.md, TODOS.md, .storybook, and all of website/src confirmed nothing else was invalidated by this branch's diff.

Notes

  • No version bumppackage.json stays 0.23.0. Versioning + release are owned exclusively by the project's /electron-release pipeline (Phase 13). PR title follows the repo's conventional-commit convention (no version prefix).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • 拡張されたティント付きニュートラルテーマセット:新しいカラーファミリー(Clay、Olive、Sage、Steel、Plum)を追加し、利用可能なテーマプリセットを44個から54個に増加。
    • テーマメニューUIを改善:ティント付きニュートラルセクションを5列グリッドで整理し、最大10個のファミリーを効率的に表示。
  • Documentation

    • デザイン仕様とREADMEを更新:テーマシステムの説明とプリセット数を最新化。
  • Tests

    • テーマ機能のテスト範囲を拡張:新規ティント機能とテーマ選択の動作をカバー。

Part A — 5 new Tinted Neutral families (clay/olive/sage/steel/plum),
hues spaced ≥25° from the existing stone/slate/zinc/mauve set so they
stay perceptually distinct at TINTED_NEUTRAL_CHROMA. ThemeSelector now
lays the families out as a 5-column grid (2 rows) instead of one flex row.

Part B — tinted presets get a softened gray base: lighter in dark mode,
deeper in light mode. Surface lightness is extracted to per-token --*-l
CSS variables so the new .dark.tone-tinted / .light.tone-tinted blocks
override only L (uniform +0.05 dark / -0.05 light); foreground / primary /
ring stay fixed so WCAG AA contrast and surface separation hold. The
tone-tinted class is scoped to 0 < chroma < COLOR_PRESET_CHROMA, so the
pure-neutral default and full-color presets keep the crisp ramp — the
default app appearance is unchanged. Toggled in redux/listener.ts and the
pre-hydration bootstrap (both index.html + settings/index.html) to avoid
a first-paint flash for persisted tinted themes.

Tests: contrast.test.ts evaluates tinted presets against the shifted L
(dark muted/muted-fg stays 4.2:1 > AA UI floor) and drift-guards the
tone-tinted blocks; bootstrap.test.ts runs against both windows and
asserts they stay byte-identical; themeSlice/listener cover the new
families and the tone-tinted toggle.
Codex design review flagged that .dark.tone-tinted lifted --muted-l to 0.3,
dropping muted-foreground on bg-muted to 4.21:1. The 10px ThemeSelector
family labels sit on hover:bg-muted, so that pair is WCAG normal text and
must clear 4.5:1, not the 3.0:1 UI floor. Verified live in the running app
(canvas-resolved oklch): 0.3 = 4.21:1, 0.27 = 4.63:1.

- Cap the dark-tinted secondary/muted lift at +0.02 (L 0.27) instead of
  +0.05; headline surfaces (background/card/popover/border/input) keep the
  full +0.05 lighter-gray-base lift. Light tinted muted (0.9 = 5.53:1) is
  already safe and unchanged.
- Raise the muted/muted-foreground contrast assertion to AA 4.5:1 for all
  presets (all pass: base 4.95, tinted 4.66, color 4.94) so the test
  encodes the real normal-text requirement the 10px label demands.
- Fix two stale comments: --theme-chroma now documents the 0.05 tinted
  value; the .tone-tinted note no longer claims border-l stays fixed.
Close the /review F1 drift-guard gap: the existing bootstrap guard only
pinned the QUOTED setProperty arg ('0.16'), not the BARE `chromaVal < 0.16`
comparison literal in the .tone-tinted gate. If COLOR_PRESET_CHROMA is
retuned but the bare literal is not, the pre-hydration bootstrap and the
post-hydration listener disagree at the chroma boundary -> flash of the
wrong gray base on first paint. Multi-model confirmed (Claude + Codex).

Test-only, zero runtime change.
Close the last coverage gap on the .tone-tinted pre-hydration gate: the
`chromaVal === null` arm (parseable theme with hue/mode but no numeric
chroma and no recognized presetType) reaching the gate was untested. New
test seeds tone-tinted then asserts the else arm strips it, proving a stale
tinted base can't survive an unclassifiable theme. Runs across both the
main and settings windows via describe.each.

Test-only, zero runtime change. Branch coverage now 100%.
Three comment-accuracy fixes from the /ship specialist pass (all
INFORMATIONAL, comment-only, zero runtime change):
- listener.ts: applyThemeToDOM summary now names the .tone-tinted class
  it toggles (was only .light/.dark)
- ThemeSelector.tsx: family-grid docblock says 'grid' not 'horizontal row'
  after the flex->grid layout change
- listener.test.ts: split a mislabeled AAA comment so the zinc-light
  (light tinted) and neutral-dark (pure neutral) assertions each read true
The +5 tinted families (clay/olive/sage/steel/plum) raised the catalog to
37 presets / 54 visual themes (2 pure neutral + 17 color + 18 tinted). This
updates every count-bearing doc the feature left stale:

- README.md: 44 -> 54 themes
- SPEC.md: total line, type-table row, and the Tinted Neutral Families
  table (4 -> 9 families, sorted by hue with characters from constants.ts)
- website/public/llms.txt + Features.tsx: 27 -> 37 presets, 44 -> 54 themes

Counts derive from THEME_PRESETS in src/shared/constants.ts; theme tests
already count dynamically via Object.keys(THEME_PRESETS), so no test churn.
DESIGN.md is the binding visual source of truth, and the tinted-neutral
gray base shipped in this branch had no entry there. Add a Color System
subsection covering: which presets trigger `.tone-tinted` (the open
(0, 0.16) chroma band), the per-mode effect (deeper gray in Light, lighter
gray in Dark), where it is implemented (globals.css overrides + listener.ts
runtime toggle + the no-FOUC bootstrap IIFEs), and the AA 4.5:1 dark-muted
L<=0.27 contrast guardrail for the 10px ThemeSelector labels.
@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
skills-desktop Ready Ready Preview, Comment Jun 13, 2026 7:01pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

このPRは、新しいティント付きニュートラルテーマを 5 ファミリー追加し、CSS の光度変数抽出、ランタイム DOM クラストグル、HTML プリハイドレーション、および包括的なテスト・ドキュメント更新を含む。総テーマ数は 44 から 54 に拡張。

Changes

Tinted Neutral Theme Implementation

Layer / File(s) Summary
Theme Preset Constants & Type Expansion
src/shared/constants.ts
THEME_PRESETS に clay/olive/sage/steel/plum の各ダーク/ライト(計 10 プリセット)を追加。TINTED_NEUTRAL_CHROMA の共通定義により、ThemePresetName 型は自動的に新規キーを包含。コメントで 9 ファミリー・18 個のティント付きニュートラルを明記。
CSS Lightness Variable Refactoring & Tinted Block Definitions
src/renderer/src/styles/globals.css
OKLCH 色を --*-l 光度変数に抽出。.dark.light で背景・カード・ポップオーバー・セカンダリ・ミューテッド・ボーダーを var(--*-l) 参照に置き換え。.dark.tone-tinted.light.tone-tinted で表面トークンの L 値をシフト。コントラスト維持(AA 4.5:1)のため、ダークティント時 --secondary-l/--muted-l0.27 以下に制限。
Runtime Theme DOM Application & tone-tinted Class Toggle
src/renderer/src/redux/listener.ts
applyThemeToDOM()COLOR_PRESET_CHROMA を import し、0 < chroma < 0.16 のときのみ <html>tone-tinted クラスを付与。CSS 変数と組み合わせてティント付きニュートラル見た目を実現。
Pre-hydration Bootstrap Scripts (Main & Settings Windows)
src/renderer/index.html, src/renderer/settings/index.html
localStorage から t.chroma(v1) または p.presetType(v0) を解決し、--theme-chroma を設定。0 < chromaVal < 0.16 で事前に tone-tinted クラス付与。Reactマウント前にティント状態をプリキャッシュして FOUC 回避。
ThemeSelector Component Layout & Label Update
src/renderer/src/components/theme/ThemeSelector.tsx
グリッドレイアウトを 5 カラム(10 ファミリー、2 段)に統一(flex → grid)。ボタンから flex-1 削除。ラベルを「現在のモードに基づきパートナー解決、再度モード選択不要」へ更新。
Comprehensive Test Coverage
src/renderer/src/bootstrap.test.ts, src/renderer/src/redux/listener.test.ts, src/renderer/src/redux/slices/themeSlice.test.ts, src/renderer/src/styles/contrast.test.ts, src/renderer/src/components/theme/ThemeSelector.browser.test.tsx
bootstrap.test.ts で main/settings 両 HTML をパラメータ化検証。listener.test.ts で tone-tinted クラス切り替え動作を検証。contrast.test.ts で DARK_TINTED_TOKENS/LIGHT_TINTED_TOKENS--*-l 整合性を drift guard で検証。themeSlice.test.ts で新プリセットのモード切り替え、ThemeSelector.browser.test.tsx で 10 ファミリー UI 表示を検証。
Design, Specification & Public Documentation
DESIGN.md, SPEC.md, README.md, website/public/llms.txt, website/src/components/Features.tsx
DESIGN.md に「Tinted-neutral gray base」セクション追加(色味シフト方針、実装参照、コントラストルール)。SPEC.md でテーマシステム概要と 9 Tinted Neutral Families を更新。README.md・website/* で公開テーマ数(44→54)とティント付きニュートラル数(8→18)を反映。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • laststance/skills-desktop#85: Core persisted theme bootstrap と DOM/CSS theming パイプラインの統一・フォールバック処理を基盤として、本PR が新しい tone-tinted/tinted-neutral 挙動を積層。
  • laststance/skills-desktop#169: モード優先テーマセレクタリファクターにより ThemeSelector.tsx/listener.ts の統合ポイントを整備し、本PR がそれを拡張して tinted-neutral 表示を実装。
  • laststance/skills-desktop#199: テーマ「ティント付きニュートラル」の UI スウォッチボタン classNames(min-h-11 削除等)変更と、本PR の ThemeSelector tinted-neutral セクション更新が同じコード経路に作用。
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PRタイトルは主な変更を明確に要約しており、5つのティント付きニュートラルファミリー追加と調整可能なグレーベース機構の導入という中核的な変更を正確に反映している。
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tinted-neutral-expansion

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.4.16)
src/renderer/src/styles/globals.css

File contains syntax errors that prevent linting: Line 2: Tailwind-specific syntax is disabled.; Line 4: Tailwind-specific syntax is disabled.; Line 199: Tailwind-specific syntax is disabled.; Line 203: Tailwind-specific syntax is disabled.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.16%. Comparing base (6ebb865) to head (846d11d).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #211      +/-   ##
==========================================
+ Coverage   67.15%   67.16%   +0.01%     
==========================================
  Files         199      199              
  Lines        6165     6167       +2     
  Branches     1390     1391       +1     
==========================================
+ Hits         4140     4142       +2     
  Misses       1603     1603              
  Partials      422      422              
Flag Coverage Δ
unittests 67.16% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
SPEC.md (1)

918-920: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Landing Page セクションのテーマプリセット数が旧値のままです。

Line 919 の 27 theme presets が、同ファイル Line 310 の 37 presets と不整合です。仕様書内の数値を統一してください。

🔧 修正案
-- Feature grid (68 agents, symlink status, 27 theme presets)
+- Feature grid (68 agents, symlink status, 37 theme presets)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SPEC.md` around lines 918 - 920, The spec contains inconsistent theme preset
counts: update the textual occurrences so they match (change "27 theme presets"
to "37 theme presets" or vice versa depending on the approved canonical value);
locate the two offending strings ("27 theme presets" in the landing page section
and "37 presets" elsewhere) and make them identical so the specification is
consistent across the document.
website/public/llms.txt (1)

9-10: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

ダウンロードURLが最新リリースを指していません。

Line 9-10 が v0.22.2 固定です。GitHub Releases では Latest が v0.23.0(2026年6月12日公開)なので、両アーキテクチャURLを最新へ更新してください。

🔧 修正案
-- [Download for macOS (Apple Silicon)](https://github.com/laststance/skills-desktop/releases/download/v0.22.2/skills-desktop-0.22.2-arm64.dmg): DMG installer for M1/M2/M3 Macs.
-- [Download for macOS (Intel)](https://github.com/laststance/skills-desktop/releases/download/v0.22.2/skills-desktop-0.22.2-x64.dmg): DMG installer for Intel-based Macs.
+- [Download for macOS (Apple Silicon)](https://github.com/laststance/skills-desktop/releases/download/v0.23.0/skills-desktop-0.23.0-arm64.dmg): DMG installer for M1/M2/M3 Macs.
+- [Download for macOS (Intel)](https://github.com/laststance/skills-desktop/releases/download/v0.23.0/skills-desktop-0.23.0-x64.dmg): DMG installer for Intel-based Macs.

As per coding guidelines, "website/**: Download URLs must point to the latest release version on GitHub Releases. Both ARM64 and x64 DMG links must be updated together."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/public/llms.txt` around lines 9 - 10, Update the two hard-coded DMG
download links that reference "skills-desktop-0.22.2-arm64.dmg" and
"skills-desktop-0.22.2-x64.dmg" so they point to the current latest release
(v0.23.0) on GitHub Releases by replacing "0.22.2" with "0.23.0" in both ARM64
and x64 URLs; ensure both links are changed together and keep the same filename
pattern (skills-desktop-0.23.0-arm64.dmg and skills-desktop-0.23.0-x64.dmg).

Sources: Coding guidelines, MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@SPEC.md`:
- Around line 918-920: The spec contains inconsistent theme preset counts:
update the textual occurrences so they match (change "27 theme presets" to "37
theme presets" or vice versa depending on the approved canonical value); locate
the two offending strings ("27 theme presets" in the landing page section and
"37 presets" elsewhere) and make them identical so the specification is
consistent across the document.

In `@website/public/llms.txt`:
- Around line 9-10: Update the two hard-coded DMG download links that reference
"skills-desktop-0.22.2-arm64.dmg" and "skills-desktop-0.22.2-x64.dmg" so they
point to the current latest release (v0.23.0) on GitHub Releases by replacing
"0.22.2" with "0.23.0" in both ARM64 and x64 URLs; ensure both links are changed
together and keep the same filename pattern (skills-desktop-0.23.0-arm64.dmg and
skills-desktop-0.23.0-x64.dmg).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8ee36099-8efa-4ae2-97db-7a50211981ed

📥 Commits

Reviewing files that changed from the base of the PR and between 6ebb865 and 846d11d.

📒 Files selected for processing (16)
  • DESIGN.md
  • README.md
  • SPEC.md
  • src/renderer/index.html
  • src/renderer/settings/index.html
  • src/renderer/src/bootstrap.test.ts
  • src/renderer/src/components/theme/ThemeSelector.browser.test.tsx
  • src/renderer/src/components/theme/ThemeSelector.tsx
  • src/renderer/src/redux/listener.test.ts
  • src/renderer/src/redux/listener.ts
  • src/renderer/src/redux/slices/themeSlice.test.ts
  • src/renderer/src/styles/contrast.test.ts
  • src/renderer/src/styles/globals.css
  • src/shared/constants.ts
  • website/public/llms.txt
  • website/src/components/Features.tsx

@ryota-murakami ryota-murakami merged commit 9c65e06 into main Jun 13, 2026
11 checks passed
@ryota-murakami ryota-murakami deleted the feat/tinted-neutral-expansion branch June 13, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants