devtool-ui
Build dense developer-tool frontends with Svelte 5, semantic CSS custom properties, and focused dependencies. Use for session or log viewers, analytics dashboards, search interfaces, virtualized lists
SKILL.mdagent-marketplace/src/design/skills/devtool-ui/SKILL.md
GitHubDeveloper Tool UI
Use this skill to build a fast, keyboard-native interface for structured, high-volume data. The patterns come from production code handling 20,000+ item lists with sub-16ms frame budgets, dual themes, and zero component-library overhead. Adapt the sample thresholds to the app's measured workload; keep the principles below unless the user has a concrete reason to depart from them.
Build sequence
- Set the visual system. Define semantic CSS properties for surfaces, text, role-based backgrounds, and accents in both light and dark themes. Read visual design for color, typography, and interaction details.
- Choose the app structure. Group components by feature, decide whether a History API router is enough, and define typed content blocks where users need independent rendering or filtering. Read architecture for those choices and for Go embedding.
- Define data ownership. Split reactive state by domain, add a typed API boundary, and decide which state belongs in local storage. Read state management for stores, SSE, routing, and persistence.
- Protect responsiveness. Virtualize long lists, cancel stale requests, and cache expensive rendering where profiling supports it. Read performance for adapter, pagination, cache, and debounce patterns.
- Make interaction accessible. Centralize global shortcuts, keep list navigation based on data indices, and verify focus, ARIA, and narrow-screen behavior. Read keyboard and accessibility for implementation patterns.
- Integrate and check. Connect the layers, then verify theme switching, keyboard use, loading and cancellation, and representative large lists in the running app.
Core principles
- Semantic over descriptive. Name CSS variables by meaning (
--tool-bg), not appearance (--amber-50). Names survive theme changes. - Minimal dependencies. Three runtime deps (
@tanstack/virtual-core,marked,dompurify) and hand-written everything else. Each dependency is bundle size, upgrade churn, and security surface. - Performance through architecture. Virtual scrolling, LRU caching, AbortController on every async operation, progressive loading. Performance is a structural decision, not a late-stage optimization.
- Keyboard-first, mouse-supported. One centralized
keydownhandler with three tiers (modifier combos, Escape cascade, single-key shortcuts gated on input focus and modal state), vim-style j/k navigation on data indices, a Cmd+K command palette. - Two complete themes. Design light and dark simultaneously, not "light
first, then invert." Tint the neutrals (blue-shifted dark backgrounds);
pure grays such as
#1a1a1aread as sterile. - No component library. Scoped Svelte
<style>blocks plus CSS custom properties give full control over micro-interactions with no dead code or specificity battles. Tailwind fits forms and marketing pages, not dense information displays withcolor-mix()-driven state styling. - Embedded SPA. Plain Vite + Svelte 5, no SvelteKit. Build to static files
and embed them in a Go or Rust binary (
//go:embed) for single-binary deployment.
Choosing a reference
Each reference is independent. Load the one matching the current decision:
- Visual design: semantic color tiers, tinted dark palettes, typography, polish, and stable entity colors.
- Performance: virtualizer integration, LRU caching, request cancellation, progressive pages, debounce, and dependency trade-offs.
- State management: Svelte 5 stores, effect roots, SSE, a custom router, persistence, and a typed fetch client.
- Keyboard and accessibility: shortcut handling, vim-style navigation, command palette, ARIA, and responsive layout.
- Architecture: feature-based components, SPA choice, backend embedding, typed content blocks, resizable panes, and page rendering.
Anti-patterns
- One giant store. Every
$effectreruns on any field change. Split by data-ownership domain, but not per component; that scatters related state and forces prop drilling or event buses. - Scatter-registered keyboard handlers. Per-component listeners cause duplicate responses, orphaned listeners, and ordering ambiguity. One centralized handler is the single source of truth.
- Forgetting AbortController. Without cancellation, stale responses overwrite current data and stale closures write to outdated state. Every async store method gets one.
- Skipping virtual scrolling. "A few hundred items" becomes 20,000 faster than you think. Virtualize from the start; the adapter pattern makes it no harder than a plain list. Fixed row heights are simpler; measured variable heights need the adapter plus cache invalidation on context switch.
- Pure gray dark themes and Tailwind for dense displays. See principles 5 and 6.
- Unchecked hand-written styling. Owning the CSS means owning keyboard
focus (
:focus-visible), contrast,aria-labelon icon buttons, and touch targets. Check those explicitly.