- name
- deslop-ui
- description
- Clean up canvas UI code generated by AI in packages/review/app. Use after writing or changing canvas components or styles, before review.
Review every canvas file the branch changed (`git diff origin/main --stat -- packages/review/app`). Assume each new style or component is a copy of something the kit already has, and prove otherwise before you keep it.
## The kit
Everything below lives in `packages/review/app/src/ui/` unless a path says otherwise. Kit modules never import app code.
- Controls: `Button` (`variant`, `tone`; `buttonLook` for an anchor drawn as a button), `IconButton` (`aria-pressed` for toggles), `SegmentedControl`, `Checkbox`, `Radio`, `CheckboxMark` (a row that is itself the checkbox), `Switch`, `TextField`, `TextArea`, `Select`, `fieldStyles.bare`, `Kbd`/`Shortcut`.
- Overlays: `Popover` (placed with `menuStyles.popover` or `shellStyles.topbarPopover`), `useDismissOnOutside`, `Dialog`, `OptionMenu` in `src/option-menu.tsx`. Menu rows use `menuStyles.item`, groups split with `menuStyles.separator`, arrows move with `moveMenuFocus`. Disclosures use `DisclosureChevron` with `disclosureStyles.toggle` and `chevronMarker` on the toggle.
- Feedback: `Spinner` (`spinStyles.turning` for a turning icon), `ProgressRing`, `EmptyState`, `StatusBanner`, `StatusDot`, `useCopy`/`CopyButton`, `useTooltip`, `useNotify` in `src/notify.ts`.
- Glyphs: `icons.tsx` (24 grid, sized with `iconStyles`), board and PR-state marks in `board-icons.tsx`, agent marks through `AgentLogo` in `brand-icons.tsx`. Icons are SVG, never Unicode characters.
- Fragments: `textStyles` (`truncate`, `clamp2`, `error`, `eyebrow`, `sectionTitle`, `count`), `focusStyles.ring`/`ringInset`, `buttonStyles.reset`, `dividerStyles.vertical`, `surfaceStyles` (`card`, `popover`, `dialog`), `listRowStyles` for sidebar rows, `pillStyles`/`Chip`, `CrumbSeparator`, `OverlayScrollbar`.
- Settings use `Section`/`Row` from `src/settings-section.tsx`. Times, durations and counts use `src/format.ts`.
## Checklist
`pnpm lint` already rejects off-scale literals, `!important`, bare `transparent`, raw colors, glyph icons, `<select>` outside the kit, unused `stylex.create` keys, Code OSS imports and app imports from `src/ui/`. The checklist is what lint cannot see.
- **Hand-rolled controls.** Search the kit before keeping any of these, and swap in the kit piece:
- a `<button>` with its own padding, border, background and font: `Button`, `IconButton` or `buttonStyles.reset`
- a local `<input>` or `<textarea>` look: `TextField`, `TextArea`, `Checkbox`, `Radio` or `Switch`
- a click-outside effect: `Popover` or `useDismissOnOutside`
- a fixed overlay: `Dialog`
- a single-choice menu: `OptionMenu`
- a rotating chevron: `DisclosureChevron` with `chevronMarker`
- a border-ring spinner: `Spinner`
- a copied-state timer: `useCopy`
- a sidebar row with its own hover wash: `listRowStyles`
- a rounded chip with an inset ring: `pillStyles` or `Chip`
- a notification dot: `StatusDot`
- a styled `<kbd>`: `Kbd` or `Shortcut`
- `title=` on an icon control: `useTooltip`
- **Menu and list rows.** A row rendered as a `<button>` stacks `focusStyles.ringInset`, `buttonStyles.reset`, then `menuStyles.item` (or `listRowStyles.row` + `wash`). Without the reset the browser's 2px border shows and the row grows.
- **Locked checkboxes.** `Checkbox` dims to half opacity when disabled. A locked-on option (a required item) is `checked` + `disabled` with a `useTooltip` on its wrapper that says why, as `onboarding/install-step.tsx` does. A dimmed box with no reason reads as broken.
- **Local icons.** An inline `<svg>` belongs in `icons.tsx`, drawn on its 24 grid and sized with `iconStyles`. Agent marks go through `AgentLogo`.
- **Repeated fragments.** These become kit fragments:
- `overflow: hidden` + `textOverflow: ellipsis` + `whiteSpace: nowrap`: `textStyles.truncate`
- two-line clamps: `textStyles.clamp2`
- error colors: `textStyles.error`
- focus outlines: `focusStyles.ring` / `ringInset`
- 1px vertical rules: `dividerStyles.vertical`
- bordered figures: `surfaceStyles.card`
- hairlines between menu groups: `menuStyles.separator`
A pattern that appears a third time goes in the kit.
- **Off-scale values.** Don't silence lint with a new step or an inline `calc()`. Snap to the nearest step, and name a geometry-coupled offset once. Reduced-motion and narrow-width media queries become `stylex.env.reducedMotion` / `stylex.env.narrow`; a one-off layout breakpoint may stay local. Replace local `spin` keyframes and easings with `motion.*` and `spinStyles`.
- **Every animation stills under reduced motion.** Check each `animationName` and `transition` has a `stylex.env.reducedMotion` branch.
- **Focus.** Look for anything that sets `disabled` on the button that was just pressed (Send, Retry, an opener while it loads). Replace it with a guard in the handler. Popovers and dialogs must close on Escape and give focus back to their trigger.
- **Scrollbars.** `global.css` already styles every native scrollbar in the canvas. Delete local `::-webkit-scrollbar` rules. Setting `scrollbarWidth` or `scrollbarColor` turns that styling off, and `scrollbarColor` inherits, so set them only for a scroller that hides its bar on purpose. A main scroller whose bar must not take layout width gets `OverlayScrollbar` and `overlayScrollbarStyles.scroller`.
- **Portals.** UI portaled outside `.review-app` needs `themeStyles.vars`, or kit controls lose their chrome tokens and collapse.
- **Feedback placement.** Errors inside a modal render inline with `role="alert"`. Transient success after a surface closes uses `useNotify`. Don't do both.
- **Slop in styles.** Delete:
- comment-per-property styles
- descendant selectors (use markers or variants)
- `zIndex` literals that stack across surfaces (use `layer`; order inside one component may stay local)
- shadows outside `elevation`
- duplicate `stylex.create` blocks that differ by one property (use a variant)
- **Tests.** Don't add tests that assert class names, CSS text or style objects. Test behaviour (roles, focus, dismissal) and check the visuals in the Desktop.
- **Style-only refactors.** Prove no pixel changed with computed-style diffs before and after, with the pointer moved off the page. Not by eye.
Then:
1. Run `pnpm format`, `pnpm lint` and `pnpm -w exec tsc --noEmit -p packages/review/app` from the repo root.
2. Run the canvas tests for the files you touched.
3. Check the changed surfaces in an isolated Desktop (`DEV_FAST_REVIEW_DESKTOP_BACKGROUND=1`): dark, light, a narrow width, keyboard focus and reduced motion.
4. Report what you replaced, and anything you kept local and why.
Ver no GitHub