| name | ui-eng-vision-local-lit-renderer |
| description | Migrates legacy imperative DOM construction to local declarative Lit-html templates rendered inside existing containers. |
| allowed-tools | code_search open_urls |
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
-
Conserve Abstraction Topology:
- Maintain the existing class hierarchy, helper class boundaries, and
logical groups.
- 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.
-
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.
-
Import Modern Templating:
-
Import the Lit rendering system inside the view module:
import {html, render, nothing} from '../../ui/lit/lit.js';
-
Modern Component Mapping:
-
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.
-
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);
-
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}
``.
-
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.
-
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.
-
Visual Parity 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.
- 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").
-
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.
-
Wait for confirmation:
- Wait for an explicit confirmation from the user before proceeding to the
next step.