CtrlK
BlogDocsLog inGet started
Tessl Logo

ui-eng-vision-local-lit-renderer

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

58

Quality

67%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Fix and improve this skill with Tessl

tessl review fix ./.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md
SKILL.md
Quality
Evals
Security

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.
    • 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:

    • Import the Lit rendering system inside the view module:

      import {html, render, nothing} from '../../ui/lit/lit.js';
  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. 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.
  13. 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. Tag Parity: Did I accidentally change any span to div (or vice versa)?
  2. Attribute Drop: Did I drop any aria-*, role, or custom data attributes?
  3. Clang-Off Boundary: Is the template wrapped in // clang-format off?
  4. Build: Does the code compile and do visual tests pass with 0% diff?
Repository
ChromeDevTools/devtools-frontend
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.