Into the shadow (DOM)

A belated Happy New Year!

I’ve written a simple component (NinjaShadow :wink:) for encapsulating Anvil Form HTML in a shadow DOM (component and demo in the link below). Admittedly, I suspect that the wider interest for this may be limited as the component is really not of much use if you work exclusively with Anvil’s native UI components. However, if you are a heretic like me :blush: and dabble into creating custom components based on native HTML and JavaScript (or rather, with Anvil’s excellent hybrid Python-JavaScript capability), I believe this component can be useful.

As you probably know, the claim to fame of the shadow DOM is the ability to “encapsulate” CSS, i.e., to prevent both inside-out and outside-in style leakage across the confines of a given shadow DOM.

The NinjaShadow component can (more or less) be regarded as a regular Form. However, when you set the ‘html’ property of a NinjaShadow instance, that HTML automatically gets wrapped in its own shadow DOM (specifically, the DOM node of the component’s Anvil Form gets an open shadow DOM attached to it). This means that you can define “component-level encapsulated” styles within the component’s HTML. To do this, just include a style tag in the component’s HTML and prefix all CSS selectors with ‘:host’. Styles defined in this way only apply to the component’s HTML. If left at this, the component’s HTML is not affected by any global stylesheets. Global stylesheets can, however, be “brought in” by including link tags with appropriate references to global (or non-global for that matter) stylesheets in the component’s HTML (the demo app showcases this).

HTML is typically not much fun if you cannot access the elements (e.g., to add event listeners and to dynamically create content). Accessing the component’s HTML elements can be done via the component’s ‘root’ property (the demo app also does this).

Finally, a word on coding style… I’ve been looking into native web components recently (cool stuff, btw.). There, you often see the practice of including an HTML string inside a JavaScript class. Although you loose “editor linting” with this practice, I really like the “proximity” between the HTML and the JavaScript code that this approach offers. In consequence, I’ve started to adopt a similar practice in Anvil, i.e., setting the ‘html’ property to a multi-line Python string directly in Form code. However, if you prefer to set your ‘html’ by reference to an asset file or via the Anvil Designer, you can of course still do that with the NinjaShadow component.

Component and demo:

https://anvil.works/build#clone:2DMJUGIIKQ26AAGI=MX2MB6S7SYVHX5BNF4DFQXNF

PS

In a previous iteration of the component, I did in fact allow for inclusion of anvil-slot elements in the shadow DOM HTML. It did require overloading the HtmlTemplate-inherited methods, ‘add_component’, ‘get_component’, and ‘clear’ – and to simultaneously control added Anvil components in the slots as well as in internal containers. It actually worked. However, some properties of Anvil’s UI components are controlled via DOM classes – and these of course do not readily penetrate the shadow DOM (which is close to the entire rationale for using a shadow DOM in the first place). Long story short, the benefits of adding this extra code (to an otherwise simple component) didn’t really make sense to me, so I took it out.

5 Likes