Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should shadow host have display: block by default? #426

Closed
rniwa opened this issue Mar 8, 2016 · 26 comments
Closed

Should shadow host have display: block by default? #426

rniwa opened this issue Mar 8, 2016 · 26 comments

Comments

@rniwa
Copy link
Collaborator

rniwa commented Mar 8, 2016

Continuing my proposal / question in #224 (comment) here:

Do most of custom elements that have display: block rule attach a shadow DOM? If so, should we make every shadow host default to use use display: block?

Making custom elements default to display: block is problematic because we have to do either:

  1. display value changes when an unresolved custom element becomes upgraded
  2. We change display value for all existing unknown elements with - in its name
@rniwa
Copy link
Collaborator Author

rniwa commented Mar 8, 2016

This would still result in a flush of contents when custom elements are upgraded but its behavior is a lot more easier to understand as simply having UA stylesheet with:

:host { display: block; }

and it's probably less confusing for authors.

@hayatoito
Copy link
Contributor

A couple of thoughts:

  • That might upset authors if we attach a shadow root to <span>. Authors would expect <span> is still 'display: inline'.
  • :host is a relatively cost-expensive selector because it is one of cross-scoping selectors. It is defined in a child tree, but it is applied to a shadow host in a parent tree.

How about having another pseudo class, like :custom-host, other than using :host?
:custom-host applies to an element which is (a custom element and a shadow host), in the same scope, of course. I think this is much cheaper.

If we prefer primitives, we might want to separate it into :custom and :shadowhost (any better name is welcome), and use them together, instead of :custom-host

e.g.

:custom:shadowhost {
  display: block;
}

@jarek-foksa
Copy link

For the sake of consistency, I would suggest using dash-case naming convention, like :shadow-host, :first-child, :last-of-type, etc.

@hayatoito
Copy link
Contributor

Yeah, :shadowhost is just a tentative name. I am aware that it is confusing to have both :host and :shadowhost (or :shadow-host).

@domenic
Copy link
Collaborator

domenic commented Mar 10, 2016

If we prefer primitives, we might want to separate it into :custom and :shadowhost (any better name is welcome), and use them together, instead of :custom-host

I don't really understand the difference between :shadowhost and :host? From a developer perspective, at least? That is, when would a developer ever use :shadowhost instead of :host?

@domenic
Copy link
Collaborator

domenic commented Mar 10, 2016

Would :custom apply to only custom elements, or would it also apply to undefined potentially-custom elements (i.e. any element whose name is a valid custom element name)?

@hayatoito
Copy link
Contributor

:host is a very special pseudo class, defined in https://drafts.csswg.org/css-scoping/#host-selector.

e.g.
It is declared in a shadow tree, A, but it does not match any element in A. Instead, it matches the shadow host which hosts A.

Would :custom apply to only custom elements, or would it also apply to undefined potentially-custom elements (i.e. any element whose name is a valid custom element name)?

I thought that :custom only checks the element name. It is :custom-element-name, in other words.

@TakayoshiKochi
Copy link
Member

@domenic
You should use :shadow-host in <style> in the same component tree as the target shadow host,
while :host has to be used from inside its shadow root.

Thus if a document has several shadow dom components, one :shadow-host rule can match all the shadow hosts, while :host has to be used in each component.

@domenic
Copy link
Collaborator

domenic commented Mar 10, 2016

I see. I wish we could use :host to select all nodes that are shadow hosts, and :ancestor-host or :closest-host or something to select the host element of the current shadow tree. Maybe that does not match the terminology well though.

In general I think these are good ideas and it would be best to just come up with names. I suggest :potentially-custom for the one that selects elements with valid custom element names, but do not have any good suggestions for the host situation. :host and :shadow-host together are too confusing IMO.

@annevk
Copy link
Collaborator

annevk commented Mar 10, 2016

How about :shadow-host to select the shadow hosts in your tree and :my-shadow-host to select the node hosting the tree you're in.

(We could shorten them and remove "shadow" but remember that template is also technically a host.)

@rniwa
Copy link
Collaborator Author

rniwa commented Mar 13, 2016

I don't think that distinction is clear. If anything, I'd call the former :has-shadow-root and the latter :shadow-host

@hayatoito
Copy link
Contributor

:host was born and shipped in Blink before we realize that we need a selector to select all nodes that are shadow hosts here. Given that, I am afraid that we can not have ideal naming sets here. That's the technical debt.

Thus, :potentially-custom and :has-shadow-root look promising to me.
However, I am okay with :potentially-custom and :shadow-host. Yeah, having :host and :shadow-host is confusing, but I would like to use :shadow-host here...

@dfreedm
Copy link

dfreedm commented Mar 24, 2016

These new pseudo classes splayed out across multiple comments are very confusing.
Let me try to summarize:

  1. :host / :shadow-host - style the host of a shadowroot from inside the shadowroot
  2. :has-shadow-root - matches any element in the "current" scope that has a shadowroot
  3. :potentially-custom - matches any element that has a dash in the tagname?

Does that sound right?

If so, I don't think there's a real usecase for :has-shadow-root. It is unlikely that an author would mean to use anything other than display, as the selector is very broad. In fact, it is far more likely that the author would rather encapsulate the display styling either inside the shadow root with :host or perhaps another option to attachShadowRoot that can set the default styling.

Would :potentially-custom only match custom element candidates that don't have a definition? That could potentially be useful for styling lazily-defined elements to speed up first paint. If it always matches custom elements, I'm less optimistic of its utility.

@hayatoito
Copy link
Contributor

See #468.

If #468 is going well, we do not need additional pseudo classes, I think.

@domenic
Copy link
Collaborator

domenic commented Apr 5, 2016

Sorry, I noticed I never replied to this:

Would :potentially-custom only match custom element candidates that don't have a definition? That could potentially be useful for styling lazily-defined elements to speed up first paint. If it always matches custom elements, I'm less optimistic of its utility.

Yes, it would, but you could do :potentially-custom:not(:defined) to get the effect you're going for.

@rniwa
Copy link
Collaborator Author

rniwa commented Apr 9, 2016

It looks like this issue morphed into something entirely different about defining a new pseudo element.

I think we should file a separate issue to track this rather than re-using this issue.

@hayatoito
Copy link
Contributor

Okay. Let me close this issue without any action because:

@trusktr
Copy link
Contributor

trusktr commented Apr 26, 2016

@hayatoito @domenic I like the idea of :selecting-hosts-somehow in order to possibly get references to those host in order to manipulate them, but not for applying any sort of style. It seems to me like shadow host elements can be entirely skipped from the rendering engine so that only the elements in the shadow root , are the child nodes of the element that contains the shadow host in the render tree, and therefore the shadow host never needing any styling at all. That's my initial hunch, but I might be missing details.

What I'm imagining is that the following

              div.container
                   |
              div:has-shadow-root --> entirely skipped from rendering, doesn't need any styling at all
                   |
              shadowroot
              /        \
            div        div

results in a render tree that is more along the lines of

              div.container
              /        \
            div        div

But, it seems like that's not the case based on this discussion. Why not?

EDIT: nevermind, after thinking about it, it makes sense that the host (while being display:block with a size for example) should be in the render tree because it can define it's own layout space (the space for it's shadow children) within some unforseeable future parent when it gets attached to one.

@smfr
Copy link

smfr commented May 18, 2017

Another question here is whether the shadow root should be a CSS stacking context. I don't think it should be possible to inter-layer shadow content and page content with z-index.

@trusktr
Copy link
Contributor

trusktr commented May 18, 2017 via email

@trusktr
Copy link
Contributor

trusktr commented May 18, 2017 via email

@trusktr
Copy link
Contributor

trusktr commented May 18, 2017 via email

@trusktr
Copy link
Contributor

trusktr commented Jan 27, 2019

One thing I don't think was covered much, is for example: if all custom elements have display: block, what happens when that custom element extends from HTMLSpanElement or similar?

Seems like defaults should be inherited from parent classes.

For example, I think it'd make sense that if we inherit from HTMLElement or HTMLDivElement we get display: block, and that if we inherit from HTMLSpanElement or similar we get display: inline.

So, instead of defining a single default for all custom elements in the spec, why not define a way in which certain features (like display) are inherited?

Maybe the display value is read from a static display property. HTMLElement might have a default value of "block". Someone extending from HTMLElement could change it:

class MyElement extend HTMLElement {
  static display = 'inline'
}

This particular style may appear as a "user agent stylesheet" (as seen in Chrome devtools for example).

Inheritance like this (with static, though a decorator could be an uglier option), seems to let us describe (or inherit) the correct display in a way that is intuitive.

@trusktr
Copy link
Contributor

trusktr commented Jan 28, 2019

In contrast to solutions like :host { display: block }, the above idea is easy to work with in terms of class inheritance. How would otherwise someone change an inherited value from a base class that puts this style into the shadow root (and imagine the shadow root is private)?

Also imagine elements that do not need shadow roots, but still want custom styling. #468 may solve it, but:

  • it works fine when library authors also call customElements.define('library-element') on behalf of users.
  • But for library authors that give users a class and tell them to register it under the name they choose, this becomes clunky
    • f.e. "to use the element, call customElements.define() with any name you desire. Oh, yeah, also don't forget to pass in all of these styles and options"

Class inheritance seems to be the best way to do it, considering that we're using classes.

Library author makes a class:

export default
class SomeEl extends HTMLElement {
  suppose custom elements can provide "user agent" styles, like builtins.
  static stylesheet = `
    display: block
  `

  // or, now that we're gonna have constructible stylesheets:
  static stylesheet = new CSSStyleSheet( ... )
}

Then the end user can use the element:

import SomeEl from 'some-lib'

// avoids a name clash with another custom element lib
customElements.define('somelib-el', SomeEl) // has default style

End user can also extend from it, inheriting the default style:

class NewEl extends SomeEl {
  connectedCallback() { ... }

  // etc...
}

customElements.define('new-el', NewEl) // has the same default style

@trusktr
Copy link
Contributor

trusktr commented Jan 28, 2019

I went down this thought path, because I thought we're using classes, so why not then take advantage of inheritance?

@padcom
Copy link

padcom commented May 10, 2023

:host { display: block; }

My problem with GrapesJS was that my custom webcomponent was by default displaying vertical rather than horizontal placeholders when dragging and dropping components on the editor canvas.
It turns out GrapesJS is aware of the display property (a tiny bit of surprise there) and uses it to decide which line (h or v) to display, and where.

Default (css display: inline)

image

With css display: block:

image

I was not aware that currently the default is inline, which kinda at the same time does and doesn't make sense. I guess the default needed to be somehow decided upon and it regardless of which one would be chosen it would still be just 50% of cases covered.

With custom webcomponents to address the top-level node one uses the :host {...} style - as mentioned by @rniwa. I just wish the documentation for GrapesJS would mention this.

Anyways, your suggestion saved my day. Thank you!

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

No branches or pull requests

10 participants