| name | ui-polish |
| description | Use for plugin-side UI implementation, screenshot matching, visual polish, styling changes, layout fixes, hover or focus states, component appearance changes, and visual regressions. Trigger when the owner names a UI component, attaches a screenshot, or asks to make an interface element look like a reference. |
UI polish
Implement one visual target at a time through a bounded screenshot-driven repair loop.
Goal
The owner should only need to provide:
- the component or visible UI area;
- a screenshot or short description of the desired appearance;
- an optional note about the current defect.
Match the owner's reference as closely as practical with the smallest maintainable patch.
Do not ask the owner to explain the implementation.
Visual sources of truth
Keep these roles clear.
Owner reference
The screenshot attached by the owner.
This is the target appearance.
Focused Playwright snapshot
The screenshot generated by the focused Playwright scenario through:
await expect(locator).toHaveScreenshot("<state>.png")
Store it in the standard Playwright snapshot folder next to the spec.
Use this snapshot as the visible working result during visual development.
After owner approval, keep it as the regression baseline for future tests.
Existing approved snapshots
Snapshots for other components and states are regression baselines.
Do not update them during the current task.
Canonical commands
Read package.json before working.
Use npm scripts as canonical entry points.
Expected scripts:
-
run the full visual suite:
npm.cmd run test:ui-visual
-
update snapshots:
npm.cmd run test:ui-visual:update
Use focused spec paths during local iteration, for example:
npm.cmd run test:ui-visual:update -- tests/visual/tags-toggle-off.spec.ts
npm.cmd run test:ui-visual -- tests/visual/tags-toggle-off.spec.ts
Never bulk-update unrelated snapshots merely to make the suite green.
If visual testing already exists and one required npm alias is missing, add only the missing alias.
Step 1: locate the real UI
Before editing:
-
Find where the named component is created.
-
Identify the control type:
- Obsidian component;
- native HTML control;
- existing custom component.
-
Find the relevant production CSS.
-
Find the closest existing project UI pattern.
-
Check whether a focused Playwright visual scenario already exists.
-
Identify the exact requested state:
- closed;
- open;
- hover;
- focus;
- selected;
- disabled;
- or another explicitly named state.
Keep this inspection internal and concise.
Report only blockers or decisions that require owner input.
Step 2: create or reuse one focused Playwright scenario
Reuse an existing focused visual scenario whenever possible.
If no suitable scenario exists, create the smallest faithful Playwright scenario for the named component and requested state.
The scenario must:
-
load real production CSS;
-
reproduce the relevant production markup or use a narrow shared renderer;
-
render only the smallest useful UI area;
-
reproduce the requested state deterministically;
-
test the production UI surface, not handmade substitute controls, decorative stand-ins, or invented helper elements.
-
use a standard Playwright screenshot assertion:
await expect(locator).toHaveScreenshot("<state>.png")
-
remain isolated from production behavior.
A small faithful markup mirror is acceptable when extracting shared production code would create a broad refactor.
Do not add fake visual details to make a preview look closer to the live app. Do not hand-draw browser, Obsidian, library, or production-owned controls such as clear buttons, search icons, scrollbars, dropdown arrows, focus rings, or other widget chrome unless the owner explicitly asks for a custom mock or illustration. If a real production detail cannot be reproduced faithfully in the harness, either omit that detail and narrow the test's stated contract, or stop and report that the visual state needs a real renderer/host-specific test.
Do not add broad UI infrastructure for a local visual task.
If the requested state cannot be reproduced faithfully, stop and report the limitation.
Step 3: generate the initial focused snapshot
For a new visual scenario:
- Add the focused Playwright spec.
- Run only that spec with snapshot update enabled.
- Let Playwright generate the initial snapshot in the standard snapshot folder.
- Inspect the generated snapshot.
- Compare it visually with the owner's attached reference.
Treat the snapshot as a provisional working result until the owner approves it.
Do not stop after generating the first snapshot.
Do not present the first generated snapshot as an approved design.
Step 4: run the screenshot-driven repair loop
When the owner provides a screenshot, treat it as the visual acceptance target.
For each iteration:
-
Inspect:
- the owner's attached reference;
- the current focused Playwright snapshot.
-
Identify concrete visible mismatches.
-
Apply one smallest local repair.
-
Prefer:
- local CSS;
- existing design tokens;
- existing project UI patterns;
- a small local markup adjustment only when required.
-
Regenerate only the focused snapshot:
npm.cmd run test:ui-visual:update -- tests/visual/<scenario>.spec.ts
-
Inspect the updated snapshot again against the owner's reference.
Repeat for at most three local repair iterations.
Do not ask the owner to compare intermediate results.
Do not infer visual success from passing tests alone.
After the bounded loop, report the best focused snapshot and any remaining mismatch.
Step 5: verify existing visual baselines
After the focused snapshot is visually close to the owner's reference:
-
Run the focused scenario normally.
-
Run the full visual suite without updating snapshots:
npm.cmd run test:ui-visual
-
Inspect failures and screenshot diffs.
-
Repair only regressions caused by the current patch.
-
Run relevant unit tests.
-
Run the plugin build when applicable.
-
Review the Git diff for unrelated changes.
Snapshots for unrelated components must remain unchanged.
Existing regression mode
If an already approved visual state has regressed and the owner wants to restore it:
- Do not update the existing focused snapshot.
- Run the focused visual test.
- Inspect Playwright actual and diff artifacts.
- Repair production code until the existing test passes.
- Run the full visual suite.
Update a snapshot only when the intended design itself has changed.
Scope guardrails
A UI-polish task may change:
- local CSS;
- existing design tokens;
- a small local markup detail;
- one narrow Playwright preview;
- one narrow Playwright spec;
- the focused Playwright snapshot.
Stop and request owner approval before:
- replacing the control type;
- creating a new dropdown, popup, listbox, or interactive component;
- adding UI state;
- adding document-level listeners;
- implementing keyboard-navigation logic;
- adding a new production dependency;
- changing more than two production files;
- adding more than approximately 30 lines of production TypeScript;
- restructuring a broad view;
- changing unrelated components.
Do not solve a cosmetic mismatch by silently building a new component.
Experiments
If a platform limitation or browser behavior needs investigation:
- Create one narrow experiment only.
- Keep it isolated under test files or a dedicated scratch area.
- Do not modify production code during the experiment.
- Generate and inspect the experimental Playwright snapshot.
- Report the result.
- Wait for explicit owner approval before adopting the approach in production.
Do not combine experiment creation, experiment adoption, and production polish in one task.
Platform-owned UI
Some browser-owned or operating-system-owned surfaces cannot be styled or captured reliably, for example parts of classic native controls.
When this happens:
-
Confirm the limitation with one narrow experiment.
-
Report the smallest options:
- accept the platform-native appearance;
- apply a small supported CSS-only enhancement with fallback;
- open a separate component-design task.
-
Stop.
Do not turn a visual-polish task into an open-ended browser experiment.
Preview boundary
The standalone Playwright preview is a fast local visual-development harness.
It is not a pixel-perfect substitute for the live Obsidian host.
Do not claim access to the live Obsidian DOM, theme state, or computed styles unless a dedicated tool is configured.
After automated checks, request one short owner smoke check in real Obsidian.
If the live Obsidian result differs from the preview:
- Ask for one screenshot of the live result.
- Apply at most one focused local repair.
- Stop if the mismatch requires host-specific investigation.
Iteration budget
Use at most three local visual-repair iterations per named defect.
Stop earlier if:
- a patch has no visible effect;
- a new regression appears;
- the next attempt would be speculative;
- the scope starts expanding;
- the requested result requires structural change;
- the task stops being cost-effective for a cosmetic detail.
Report the best snapshot and the remaining mismatch honestly.
Owner approval
Ask for owner approval only after:
- the bounded visual loop is complete;
- the focused snapshot is generated;
- the full visual suite has been checked;
- relevant tests and build have been run when applicable.
Show the owner the focused snapshot path.
After explicit owner approval:
- Keep the focused snapshot as the approved regression baseline.
- Run the full visual suite once more.
- Report the final Git diff.
Final owner smoke
Ask only for the exact changed visual state, for example:
Please open the Tags toggle in the off state once and confirm that it matches the reference.
Do not give the owner a long manual checklist.
Output format
Keep reports concise.
Snapshot
- snapshot path;
- visible mismatches fixed;
- remaining mismatch, if any.
Diff
- changed production files;
- changed visual-test files;
- scope check.
Checks
- focused visual scenario;
- full visual suite;
- relevant unit tests;
- build.
Owner decision
- approve the snapshot as the regression baseline;
- request one more bounded local iteration;
- stop because the remaining mismatch is not worth further complexity.