feat: preserve shadowroot* attrs on component template wrapper#288
Draft
feat: preserve shadowroot* attrs on component template wrapper#288
Conversation
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>
mohamedmansour
requested changes
May 3, 2026
| /// `shadowroot`-prefixed attributes so they can be re-emitted on the | ||
| /// declarative-shadow-DOM wrapper. | ||
| #[derive(Default, Debug)] | ||
| struct ExtractedTemplateAttrs { |
Contributor
There was a problem hiding this comment.
I don't want us to capture each individual user supplied attributes, we should preserve ALL the attributes if the webui component supplied them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When
DomStrategy::Shadowis active, the WebUI parser now preserves anyshadowroot-prefixed attributes the user wrote on their wrapping<template>and applies these rules:shadowrootmodevalue (or, if absent, a bare legacyshadowroot=attribute) is captured. Both attributes are then always emitted on the wrapper asshadowrootmode="<mode>" shadowroot="<mode>"(the legacyshadowrootattribute is emitted as a paired alias for older user agents / tooling that look for either form). Defaults to"open"when neither is supplied.shadowrootclonable,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).<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 allshadowroot*attributes stripped so the browser does not auto-activate it as a declarative shadow root inside<f-template>. The sameshadowrootadoptedstylesheetsmerge rule applies on<f-template>.Bug fix included
Also fixes a pre-existing bug in
generate_f_template(fast-v2 / fast-v3) that usedfind('>')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 existingfind_tag_closehelper 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 usershadowroot*attrs supplied) emits<template shadowrootmode="open" shadowroot="open">, which is backward-compatible with existing apps that rely onshadowrootmode="open".Tests
crates/webui-parser/src/lib.rs) covering: default paired emission, user-suppliedshadowrootmode="closed", bare legacyshadowroot=promoted to mode,shadowrootclonable/shadowrootdelegatesfocuspreservation, adopted-stylesheets merge with user value, dedup against module specifier, runtime attrs stripped from wrapper, and the light-DOM regression.fast_v2.rsandfast_v3.rs(14 total) covering:shadowrootmodemoved to<f-template>, user closed mode preserved, other shadow-root attrs on<f-template>, noshadowroot*on<f-template>when user did not supply any, module CSS strategy putsshadowrootadoptedstylesheetson<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 renamedstrip_shadowroot_attrshelper.cargo xtask checkpasses (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.mdandRENDERING.md— example output reflects pairedshadowrootmode/shadowroot.process_component_template,generate_f_template,convert_btr_to_fast, andstrip_shadowroot_attrsdescribe the new behaviour.