Skip to content

feat: preserve shadowroot* attrs on component template wrapper#288

Draft
janechu wants to merge 1 commit intomainfrom
users/janechu/ensure-shadowroot-attrs-on-template
Draft

feat: preserve shadowroot* attrs on component template wrapper#288
janechu wants to merge 1 commit intomainfrom
users/janechu/ensure-shadowroot-attrs-on-template

Conversation

@janechu
Copy link
Copy Markdown
Contributor

@janechu janechu commented May 1, 2026

Summary

When DomStrategy::Shadow is active, the WebUI parser now preserves any shadowroot-prefixed attributes the user wrote on their wrapping <template> and applies these rules:

  • Modeshadowrootmode value (or, if absent, a bare legacy shadowroot= attribute) is captured. Both attributes are then always emitted on the wrapper as shadowrootmode="<mode>" shadowroot="<mode>" (the legacy shadowroot attribute is emitted as a paired alias for older user agents / tooling that look for either form). Defaults to "open" when neither is supplied.
  • Other shadow-root attrsshadowrootclonable, shadowrootdelegatesfocus, shadowrootserializable, etc. are preserved verbatim on the emitted <template>.
  • shadowrootadoptedstylesheets — when the user supplies a value AND the CSS Module strategy would also generate one, the values are merged into a single space-separated list (user-supplied specifiers first in their original order; parser-generated specifiers appended only if not already present, deduplicated).
  • Runtime / non-shadowroot user attrs on <template>@event, :prop, ?bool, and any other non-shadowroot* attribute on the user's <template> is stripped from the emitted wrapper (existing behaviour).

Under the FAST v2 / v3 parser plugins those shadowroot* attributes are placed on the outer <f-template> element rather than the inner <template>. <f-template> is the framework-owned transport element that the FAST runtime consumes; the inner <template> always has all shadowroot* attributes stripped so the browser does not auto-activate it as a declarative shadow root inside <f-template>. The same shadowrootadoptedstylesheets merge rule applies on <f-template>.

Bug fix included

Also fixes a pre-existing bug in generate_f_template (fast-v2 / fast-v3) that used find('>') to locate the end of the outer <template> opening tag — this broke on attribute values containing > (e.g. data-note="a > b"). The new implementation uses the existing find_tag_close helper which respects quoted attribute values, with a regression test in both plugins.

Backward compatibility

Light-DOM mode is unaffected: the <template> wrapper continues to be stripped entirely. The default shadow-DOM behaviour (no user shadowroot* attrs supplied) emits <template shadowrootmode="open" shadowroot="open">, which is backward-compatible with existing apps that rely on shadowrootmode="open".

Tests

  • 8 new tests in the parser core (crates/webui-parser/src/lib.rs) covering: default paired emission, user-supplied shadowrootmode="closed", bare legacy shadowroot= promoted to mode, shadowrootclonable/shadowrootdelegatesfocus preservation, adopted-stylesheets merge with user value, dedup against module specifier, runtime attrs stripped from wrapper, and the light-DOM regression.
  • 7 new tests in each of fast_v2.rs and fast_v3.rs (14 total) covering: shadowrootmode moved to <f-template>, user closed mode preserved, other shadow-root attrs on <f-template>, no shadowroot* on <f-template> when user did not supply any, module CSS strategy puts shadowrootadoptedstylesheets on <f-template>, merged with user value, and the > in quoted attr regression test. Plus updated existing tests to assert the new placement and a coverage test for the renamed strip_shadowroot_attrs helper.
  • All 386 parser tests pass.
  • Full cargo xtask check passes (license-headers, fmt, clippy, deny, test, build, build (wasm), build (examples), bench, docs).

Documentation

  • DESIGN.md — new "Shadow DOM <template> wrapper" subsection documenting the preservation, paired-alias, merge, and FAST-plugin placement rules.
  • docs/guide/concepts/components/index.md — updated component-author guidance.
  • docs/guide/cli/index.md — updated DOM strategy table.
  • docs/ai.md — updated AI-targeted guidance.
  • packages/webui-framework/README.md and RENDERING.md — example output reflects paired shadowrootmode/shadowroot.
  • Doc-comments on process_component_template, generate_f_template, convert_btr_to_fast, and strip_shadowroot_attrs describe the new behaviour.

When DomStrategy::Shadow is active, the WebUI parser now preserves any
shadowroot-prefixed attributes the user wrote on their wrapping
<template> (shadowrootmode, shadowrootclonable, shadowrootdelegatesfocus,
shadowrootserializable, shadowrootadoptedstylesheets, or a bare legacy
shadowroot=). Both shadowrootmode and the legacy shadowroot attribute
are always emitted as a paired alias on the wrapper for older user
agents and tooling. shadowrootadoptedstylesheets values are merged
(user specifiers first, parser-generated specifiers appended only if
not already present, no duplicates).

Under the FAST v2 / v3 parser plugins those shadowroot* attributes are
moved to the outer <f-template> element rather than the inner
<template>: <f-template> is the framework-owned transport element that
the FAST runtime consumes; the inner <template> always has all
shadowroot* attributes stripped so the browser does not auto-activate
it as a declarative shadow root inside <f-template>. The same
shadowrootadoptedstylesheets merge rule applies on <f-template>.

Also fix a pre-existing bug in generate_f_template that used find('>')
to locate the end of the outer <template> opening tag — this broke on
attribute values containing '>' (e.g. data-note="a > b"). The new
implementation uses the existing find_tag_close helper which respects
quoted attribute values.

Light-DOM mode is unaffected: the <template> wrapper continues to be
stripped entirely. Default behaviour for shadow DOM (no user
shadowroot* attrs) emits <template shadowrootmode="open"
shadowroot="open">, which is backward-compatible with existing apps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
/// `shadowroot`-prefixed attributes so they can be re-emitted on the
/// declarative-shadow-DOM wrapper.
#[derive(Default, Debug)]
struct ExtractedTemplateAttrs {
Copy link
Copy Markdown
Contributor

@mohamedmansour mohamedmansour May 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want us to capture each individual user supplied attributes, we should preserve ALL the attributes if the webui component supplied them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants