/ui — Shift Interface Work
SkillMediaDesign, implement, audit, or review Shift user interfaces and shared UI components. Use for React components, Base UI primitives, Tailwind styling or theme tokens, Figma/reference matching, accessibility, icon-only actions, tooltips, menus, popovers, dialogs, forms, layout, and visible UI regressions.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the /ui — Shift Interface Work skill
What this skill tells your AI
The instructions your AI receives, as published by shift-editor/shift in .agents/skills/ui/SKILL.md and read by ahel’s review.
Build interfaces that match the supplied design, use Shift's shared primitives and semantic theme, and remain accessible in every interaction state.
Sources of truth
Read these before editing an unfamiliar UI surface:
docs/architecture/index.mdfor documentation routing.packages/ui/docs/DOCS.mdfor the shared component boundary.apps/desktop/src/renderer/index.cssfor Tailwind v4 theme tokens, fonts, and custom utilities.- The relevant existing component and its neighboring components for local composition and density.
- Any supplied Figma frame, screenshot, or product reference. Treat it as the visual target, not merely inspiration.
When library behavior or composition is unclear, inspect the installed Base UI types and current Base UI documentation. Do not guess from Radix, shadcn, or an older Base UI API.
Architecture boundary
- Check whether Base UI has a matching primitive before implementing an interactive control.
- Shared primitives live in
packages/ui/src/components/{component}/and wrap@base-ui-components/react/{component}. - Application code imports shared controls from
@shift/ui; never import Base UI directly in the desktop app. - Keep application state and domain behavior in the consuming app. Shared wrappers own primitive composition, reusable visual defaults, and widget-local behavior only.
- Use the Base UI component name for its Shift wrapper:
Button,Menu,Popover,Tooltip, and so on. - Re-export every shared component and public prop type through its component barrel and
packages/ui/src/index.ts. - Prefer
React.ComponentPropsWithoutRef,React.ElementRef, andReact.forwardRefso wrappers preserve the primitive contract. SetdisplayNameon forwarded components. - Compose Base UI triggers with its
renderprop. Produce exactly one interactive DOM element: no nested buttons, no trigger-only wrapper spans, and no duplicated event targets. - Use Base UI state attributes such as
data-[disabled],data-[highlighted],data-[active], anddata-[starting-style]instead of duplicating primitive state in React.
Tailwind and theme tokens
Shift uses Tailwind CSS v4. The renderer theme is declared in the @theme block in apps/desktop/src/renderer/index.css, and that stylesheet scans shared UI source with:
@source "../../../../packages/ui/src/**/*.tsx";
Follow these rules:
- Use semantic theme utilities before raw Tailwind palette colors or literals:
bg-surface,bg-panel,bg-input,bg-hover,text-primary,text-secondary,text-muted,border-line-subtle,ring-accent, and related tokens. - Remember that Shift overrides Tailwind's typography scale:
text-smis 12px,text-uiis 11px, andtext-xsis 10px. Check the theme instead of assuming Tailwind defaults. - Use opacity modifiers on semantic tokens when appropriate, such as
bg-hover/50. - Do not copy legacy hard-coded hex values or generic palette classes when an existing semantic token expresses the role.
- Add a new theme token only for a stable semantic role that will be reused or themed. Name the role, not the component or current color.
- Reserve literal colors and inline styles for genuinely data-driven graphics, canvas/SVG rendering, or platform-defined colors such as native window controls.
- Use
cnfrom@shift/uior the package-local utility for conditional classes and consumer overrides. Shared wrappers must mergeclassNamethroughcnsotailwind-mergeresolves conflicts. - Keep reusable visual defaults in the shared wrapper. Do not restyle the same primitive independently at many call sites.
Matching a visual reference
Before changing styles, identify the reference's:
- typography and density;
- foreground, background, border, and shadow roles;
- spacing, dimensions, radius, and alignment;
- hover, focus, pressed, selected, disabled, read-only, open, error, and empty states;
- popup side, offset, collision behavior, layering, and arrow treatment.
Map those roles to existing theme tokens first. Compare the finished implementation with the reference at the app's actual scale. Do not declare a match from class names alone.
If the reference is ambiguous or conflicts with an established interaction pattern, ask which behavior wins before inventing a new one.
Accessibility and interaction
- Every icon-only action needs an accessible name and a concise visible tooltip.
- Tooltip text always names the action. Keep the same tooltip and accessible name when the action is unavailable; disabled styling and behavior communicate availability without replacing the action name with an explanation.
- If an unavailable control must retain its tooltip, use
aria-disabled, guard its action, and style that state. Do not use nativedisabled, which removes focus and pointer interaction. - Preserve keyboard navigation supplied by Base UI. Verify
focus-visible, not only pointer hover. - Use semantic roles and labels for toolbars, navigation, dialogs, groups, sliders, and form controls.
- Keep focus indicators visible. Do not remove outlines without an equivalent token-based focus treatment.
- Portal popups above application content and give their positioner the shared layering class.
- Close, menu, popover, and dialog triggers are actions too; icon-only compound triggers follow the same label and tooltip rules.
Coverage audits
For broad UI work such as "add all missing tooltips," do not patch only the first reported control.
- Define the invariant being audited, such as “every icon-only user action has an accessible name and tooltip.”
- Trace the actual component tree for every requested surface.
- Search by primitive (
Button,MenuTrigger,PopoverTrigger,DialogClose, toolbar controls), icon names, and accessible labels. A single grep pattern is not a complete audit. - Include persistent and hover-revealed controls, compound triggers, read-only or disabled states, dialogs, and both home and editor views.
- Record a temporary coverage ledger while working: surface, control, shared owner, enabled state, unavailable state, accessible label, and tooltip copy.
- Centralize repeated behavior in the narrowest existing shared component when that removes omissions without hiding domain-specific copy.
- Recheck the complete inventory after edits. Do not equate “wrapper exists in source” with “tooltip works in the rendered app”; verify compound trigger composition and the active build.
Testing and review evidence
Test observable behavior, not styling implementation:
- Do not add unit or E2E tests that assert Tailwind classes, static tooltip wiring, component nesting, or Base UI behavior.
- Add tests when Shift adds meaningful behavior: unavailable controls remain tooltip-accessible, keyboard interaction changes state, focus is restored, a form validates, or a menu action changes application state.
- For thin wrappers, typechecking plus focused visual/manual verification is usually the right evidence.
- For materially visible work, capture the actual implementation in each affected state for review. Use the remote E2E workflow when automated Electron verification is warranted; do not open Electron on the user's current Mac.
- Never update a visual baseline without inspecting the diff and confirming it matches the intended design.
Validation
Run the smallest relevant checks, then the shared checks for cross-cutting UI changes:
pnpm format:files <changed files...>
pnpm lint:check
pnpm typecheck
Run repository commands inside the Nix dev shell as required by AGENTS.md. If behavior changed, run the focused owning test. If only visual defaults or declarative wiring changed, report the focused manual verification instead of inventing a low-value test.
Completion checklist
- Supplied reference matched at actual application scale.
- Existing Base UI primitive and
@shift/uiwrapper used. - Exactly one interactive element per composed trigger.
- Semantic theme tokens used; no avoidable raw colors.
- Pointer, keyboard, disabled/read-only, and open states considered.
- Icon-only actions have accessible names and tooltips.
- Requested surfaces were audited completely, not sampled.
- Shared styling lives in the shared wrapper.
- Formatting, lint, and typecheck pass.
- Visible changes have appropriate manual or screenshot evidence.
Signals
- GitHub stars
- 262
- Forks
- 7
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
ui-shift-editor- Source
- github.com/shift-editor/shift