Skip to content

Instantly share code, notes, and snippets.

@caroqliu
Last active January 5, 2021 17:43
Show Gist options
  • Save caroqliu/93ad857263c7bce17a3eb1a9e0ba38a1 to your computer and use it in GitHub Desktop.
Save caroqliu/93ad857263c7bce17a3eb1a9e0ba38a1 to your computer and use it in GitHub Desktop.

display: contents in Bento AMP

How do we use it?

PreactBaseElement creates a <c style="display: contents"> element in the shadow root appended to its associated custom element. Note: display: contents is not supported everywhere, and its accessibility is not up to spec. Given display: inline is an acceptable fallback for it, <c> is utilized because it is display: inline by default.

For a passthrough element, it looks like this:

amp-foo
  ├── #shadow-root 
  |    └── c > Foo({children: Slot})
  └── … user supplied children rendered via slot

What does it do?

display: contents removes an element's box while keeping its contents, essentially promoting its children up a level in the DOM with some interesting side effects.

What's important to know style-wise is all properties are calculated, but box-based rules are not painted. This means an ineffective border: 5px solid blue, though not visible to the end user, is still present and accounted for in style calculations.

<div style="border: 5px dashed red; color: red">      // red border, red font
  <c style="display: contents; 
            border: 5px solid blue; color: blue">     // not painted
    <div style="border: inherit">                     // blue border, blue font
      Hello world
    </div>
  </c>
</div>

Basically the style logic is completely separate from the painting rules.

When does this matter?

The following would be a reasonable, common, and intuitive way to style an amp-lightbox: <amp-lightbox style="background-color: cyan;" />

Because the underlying <Lightbox> element has a default background-color of rgba(0,0,0,0.9) (translucent black), opening it causes the end-user to see both: translucent black, and faintly underneath, cyan. We want a way for user provided styles on the amp-lightbox to override, not mix with, certain stylistic defaults provided by the Lightbox.

Accordingly, we set default styles in PreactBaseElement and amp-lightbox to do exactly that: (1) make box-styles from the custom element available but ineffective via inherit in the base implementation, and (2) propagate style values to the desired target inside the shadow via inherit and part. It is particularly important, for background, to make sure they are only visibly applied once, and not on intermediary layers (which would otherwise compound something like fractional opacities and mess up visibility-based animations in Preact).

Affected styles include: background, padding, border, box-sizing. These could all be set to inherit to give the underlying Preact components access to them, but we are holding off on applying them all until clear use cases come up for them.

Additional notes

display: flex parent treats display: contents element like a fragment, so we get things like the following where A, B, and C here are laid out based on display: flex, as if <c> wasn't there at all.

<div style="display: flex">
  <c style="display: contents">
     <div>A</div>
     <div>B</div>
     <div>C</div>
  </c>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment