Catalog
fauziyasin-s2m4qw/ui-eng-vision-local-lit-renderer

fauziyasin-s2m4qw

ui-eng-vision-local-lit-renderer

Migrates legacy imperative DOM construction to local declarative Lit-html templates rendered inside existing containers.

v1.0LATEST
NewUpdated Oct 20, 2026

Subskill: Local Lit-HTML Renderer (Pass 2)

This subskill converts consolidated imperative DOM construction helper methods into reactive, declarative Lit-html templates, rendering them locally inside the existing element containers.


1. Declarative Technology Migration

  1. Conserve Abstraction Topology:

    • Maintain the existing class hierarchy, helper class boundaries, and logical groups.
    • Preserve existing standalone/module-level DOM helper functions (e.g., linkifyIcon, maybeCreateLinkToElementsPanel) and class helper methods in their original locations, converting DOM-returning helpers in place to return LitTemplate (or Promise<LitTemplate|null>) rather than deleting and inlining them into performUpdate() or DEFAULT_VIEW.
    • Do not aggressively "flatten" classes or collapse helper abstractions into the main view just to coerce a single master template. Pass 2 should only change the implementation details of rendering (imperative -> declarative) within existing compartments, not the public API or responsibilities of objects.
  2. Determine Render Lifecycle per Layer:

    • For UI.Widget classes: Define or update performUpdate() to execute the main lit render call, and use this.requestUpdate() to queue updates.
    • For Delegate/Helper classes: Identify the custom update hook (e.g., update() or scheduleUpdate()) and invoke render() directly inside that method. Do not introduce Widget lifecycle methods (performUpdate) on classes that do not inherit them.
  3. Import Modern Templating (Named Imports Only):

    • Always use named imports from ../../ui/lit/lit.js:

      import {html, nothing, render, Directives, type LitTemplate} from '../../ui/lit/lit.js';
      
    • NEVER use namespace imports like import * as Lit or reference Lit.nothing / Lit.render. Named imports enable better tree-shaking and maintain consistent codebase style.

  4. Modern Component Mapping:

    • MANDATORY: You MUST read and load ui_engineering.md first to understand specific component mappings (e.g., Toolbar vs devtools-toolbar). Do not assume standard Lit defaults.
    • If unsure, also consult the automatic migration code
    • Prefer Component Built-Ins Over Legacy Boilerplate: Before porting legacy event handlers, comparators, or state variables, check if the modern component handles them natively. Drop host-level workarounds in favor of declarative component attributes.
  5. Local Modular Renders:

    • If the class contains multiple separate legacy container fields (e.g., fields generated by appendField or createChild on a parent layout), prefer local modular renders (rendering templates individually into each container) to preserve the legacy layout framework. Do not force a monolithic template if doing so violates the existing topology.
  6. Resolve TypeScript Type Friction:

    • Legacy DevTools elements may be typed as Element. Lit's render() function expects HTMLElement | DocumentFragment.
    • When rendering into legacy containers, cast the container using as HTMLElement or update its class property type definition from Element to HTMLElement to satisfy the TypeScript compiler.
    • Example: render(this.template(), this.containerField as HTMLElement);
  7. Delegate to Unmigrated Rendering Engines:

    • Do not attempt to rewrite complex historical subsystems (like Linkifiers or specialized UI utilities) to be "pure Lit" during this pass.
    • Capitalize on Lit's ability to interpolate standard HTMLElement or Element instances directly. Treat unmigrated utilities as "black boxes" that generate DOM, and embed their output in standard template expressions: html${this.legacyElement}``.
    • CRITICAL: Before treating any component as "unmigrated," you MUST follow steps in Section 4. You are only allowed to use this escape hatch if no declarative migration instructions exist.
  8. Handle Asynchronous DOM Updates:

    • For asynchronous callback-driven updates (e.g., elements updated in a .then() callback), render the parent container or a placeholder template first. Once the data is retrieved, execute a local render() inside the callback targeting the specific sub-container.
  9. Template Factorization Strategies:

    • Prefer nothing: Use Lit's nothing sentinel instead of empty strings '' or empty templates for conditional rendering.
    • Parameterized Fragment Factories: For repeated UI patterns or intermediate layout branches, extract them into parameterized helper functions inside the class scope returning LitTemplate. This avoids monoliths, preserves readability, and reuses template cache strategies.
  10. Visual Parity, CSS Adaptation, and Accessibility:

    • Zero-Tolerance Regression: Screenshot tests are the ground truth for this phase. Any visual diff (above 0%) in the generated screenshots is unacceptable and must be resolved before proceeding.
    • CSS Selector Migration: When replacing legacy imperative components/widgets with modern web components (e.g., replacing legacy DataGrid with <devtools-data-grid>), inspect and update the associated .css file. Replace legacy class selectors (.data-grid) with tag selectors (devtools-data-grid).
    • Strict Tag/Class Parity: Do not change tag types (e.g., span to div) or drop class names during template translation, as CSS may depend on them.
    • Explicit Component Variants: Modern Custom Element (e.g. <devtools-button>) defaults may not match legacy appearance. Explicitly bind .data=${{variant: Buttons.Button.Variant.OUTLINED}} (or appropriate variant) rather than relying on defaults.
    • Accessibility Preservation: Convert custom accessibility instrumentation (like ARIAUtils.markAsAlert()) to native ARIA attributes in the template (e.g., role="alert", aria-live="polite").
  11. Render Templates Syntax & Code Movement:

    • Surround the expressions containing lit template literals with // clang-format off and // clang-format on to prevent clang-format from corrupting template indentation.
    • Minimize Code Movement: Avoid moving existing code to a different location in the file. Put the function signature around the existing code to minimize the diff.
    • The Gerrit Code Review UI cannot identify moved blocks of unchanged code.
    • If blocks of code need to be reordered, create a "prefactoring" change that first extracts the code that needs to be reordered into a named helper functions and pause the migration, waiting user confirmation.
  12. Composing Child UI.Widgets & Pre-Existing Instances:

    • Strongly Prefer Two-Argument widget(ChildWidgetClass, {params}): When instantiating child widgets in a Lit template, strongly prefer the two-argument form widget(ChildWidgetClass, {propA: input.a, propB: input.b}) over the single-argument factory widget(element => new ChildWidgetClass(element)). The two-argument form automatically propagates updated parameters and schedules requestUpdate() on re-renders, whereas a factory callback only runs once on initial creation. If the child widget currently uses setFoo() methods instead of setters, convert them into set foo() setters on the child widget so you can use the two-argument widget() directive.
    • Pre-Existing Widget Instances (UI.Widget.WrapperWidget): Use ${widget(UI.Widget.WrapperWidget, {widget: input.childWidget})} when embedding widgets owned by UI.ViewManager (TabbedViewLocation.tabbedPane(), StackLocation.widget()) or when conditionally moving a shared widget instance between containers ({widget: shouldShow ? this.#sharedWidget : null}).
    • WrapperWidget automatically handles showing and detaching the inner widget and forwards calculateConstraints without requiring custom wrapper classes.
  13. Declarative <devtools-split-view> Guidelines:

    • CSS Sizing: Ensure <devtools-split-view> has flex: auto; height: 100%; in CSS so it fills available container space and doesn't collapse to 0x0.
    • Reuse Existing Settings for showMode: When binding show=${...} declaratively on <devtools-split-view name="...">, reuse the existing SplitWidget setting ({vertical?: {size, showMode}, horizontal?: {size, showMode}}) for showMode rather than creating new boolean settings (which loses user preferences and requires KnownContextValues.ts updates).
    • Keep Environment Overrides in Templates: Keep environment overrides (like !input.isTraceApp) in the DEFAULT_VIEW template (e.g., show=${!input.isTraceApp && input.isSidebarOpen ? 'both' : 'main'}) rather than baking them into the presenter's setting state.
    • Clean Up Resizers: Do not leave unused resizer-widget classes in templates; <devtools-split-view> manages its own built-in splitter unless custom elements are explicitly registered with installResizer.
  14. Declarative Long-Press Action Buttons (bindToAction):

    • Legacy UI.Toolbar.Toolbar.createLongPressActionButton(action, [subButtons]) maps declaratively to nesting option <devtools-button> elements directly inside <devtools-button ${bindToAction('action-id')}>...</devtools-button>.
  15. Audit Removed UIStrings:

    • If a UIStrings entry becomes unused during template migration, verify that a conditional UI branch (such as a dynamically shown checkbox, button, or tooltip) wasn't accidentally dropped before deleting the string.
  16. Micro-Commit Strategy:

    • Do not attempt to convert every helper method and UI component in a single monolithic commit. Break the template extraction into small, self-contained micro-commits (e.g., migrating one helper method, toolbar button, or list item render at a time).
    • Verify with build and run tests after each micro-commit to guarantee zero regressions and 100% visual parity before moving to the next compartment.
    • Provide the exact list of files and commit message when presenting progress so the change history remains atomic and easy to review.
  17. Wait for confirmation:

    • Wait for an explicit confirmation from the user (or Parent Orchestrator Agent) before proceeding to the next step.

🔍 Mental Audit (Internal Self-Correction)

Before reporting back or committing, re-read the instructions and verify:

  1. ❓ Named Imports: Did I use named imports {html, nothing, render} from ui/lit/lit.js (and no import * as Lit)?
  2. ❓ Tag Parity: Did I accidentally change any span to div (or vice versa)?
  3. ❓ Attribute Drop: Did I drop any aria-*, role, or custom data attributes?
  4. ❓ Clang-Off Boundary: Is the template wrapped in // clang-format off?
  5. ❓ Split Views: Does <devtools-split-view> have flex: auto; height: 100%; CSS and reuse existing split settings?
  6. ❓ String Audit: Did I verify that any removed UIStrings weren't from accidentally dropped UI branches?
  7. ❓ Build: Does the code compile and do visual tests pass with 0% diff?

Overall Score

88/100

Grade

A

Excellent

Grades are signals, not a certification. Always review a skill yourself before use.

Safety

88

Quality

90

Clarity

87

Completeness

84

Summary

This skill teaches an agent how to migrate legacy imperative DOM construction code to declarative Lit-html templates within existing container structures. It emphasizes preserving class hierarchies, avoiding monolithic template refactoring, and maintaining visual parity during the migration process. The skill provides detailed guidance on type handling, component mapping, async updates, and micro-commit strategy.

Detected Capabilities

code reading and analysisfile modification (TypeScript/JavaScript)CSS updatesgit operations (micro-commits)

Trigger Keywords

Phrases that agents use to match this skill to user intent.

migrate to lit-htmlconvert imperative domlit template refactordevtools ui migrationdeclarative template conversion

Use Cases

  • Migrate imperative DOM code to Lit-html templates
  • Convert helper methods from DOM-returning to template-returning
  • Maintain visual parity during declarative refactoring
  • Update CSS selectors when replacing legacy components with web components
  • Handle async DOM updates in callback-driven code
  • Refactor DevTools UI layers while preserving abstraction topology

Quality Notes

  • Comprehensive scope definition with clear preservation principles (section 1 enforces existing class hierarchies)
  • Strong emphasis on visual regression testing as ground truth (section 10 zero-tolerance requirement)
  • Detailed lifecycle guidance tailored to two class types (Widget vs Delegate/Helper) prevents incorrect API usage
  • Excellent micro-commit strategy (section 16) reduces risk of monolithic refactoring mistakes
  • Well-structured internal audit checklist (section 🔍) enables agent self-validation
  • Mandatory documentation reference (ui_engineering.md and eslint rules) ensures component mapping accuracy
  • Clear guardrails against aggressive refactoring that would violate existing topology
  • TypeScript casting guidance (section 6) addresses real type friction points in DevTools codebase
Model: claude-haiku-4-5-20251001Analyzed: Oct 20, 2026

Reviews

Add this skill to your library to leave a review.

No reviews yet

Be the first to share your experience.

Use fauziyasin-s2m4qw/ui-eng-vision-local-lit-renderer in your dev environment

Command Palette

Search for a command to run...