Skip to content

Build1 publisher2 min readPublished

Eighteen CSS declarations build a badge component out of an undefined custom element

Custom-element markup that never reaches customElements.define() is still valid HTML a stylesheet can target. A dev.to write-up shows the CSS for a badge, including the display declaration the pattern cannot skip.

The Engineer · Build desk

Illustration accompanying Eighteen CSS declarations build a badge component out of an undefined custom element

What happened

  • A dev.to write-up describes developers shipping custom-element-shaped markup that never calls customElements.define(): no class, no lifecycle callbacks, and no component JavaScript at all.
  • An undefined custom-element name is still valid HTML markup, and the browser can select, style and compose it with nothing but CSS.
  • The same markup can be handed to customElements.define() later, and the post says that changes the element's behavior and lifecycle, so the tag and attributes should be treated as a deliberate contract.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint The pattern buys styling. The tag has no semantics of its own, so anything assistive technology needs must come from semantic children or from ARIA the author writes.
  • decision Choosing the tag name and its attributes becomes a versioning decision, because a later define() call inherits both with no markup edit available as an escape.
  • capability A design system can publish its knobs as custom properties that inherit into descendants and that a consumer overrides from their own stylesheet, with no shadow root standing between them.

Count the declarations. The base rule in the post's badge example sets three custom properties and nine ordinary declarations, one of which is `display: inline-flex` [13]. Each variant block overrides two of the custom properties [14]. Each slot rule sets one [15]. Eighteen declarations total for a badge with a danger state, a success state and two icon positions, and nothing to execute before the CSS applies [20][4].

The pattern depends on one of those lines. Undefined custom elements default to inline layout, and the post says a component that needs flex, grid or block will not lay out correctly without an explicit `display` [16]. The browser treats the tag as an ordinary undefined element: selector matching and layout complexity cost what they normally cost [5].

The write-up is also honest about the part of its own example that is decorative. The `order` rules put the start icon at -1, leave the text at its default 0, and push the end icon to 1 [17]. In the markup shown, source order already agrees with that, so the rules only matter when the markup is authored out of order [17].

Because the element has no shadow root, the custom properties behave like any other element's: set once, inherited by descendants, and a consumer can override them [10]. Someone who wants a purple badge sets `--badge-bg-color` inline or in their own stylesheet, and no new variant attribute is needed [18].

Tag names start to matter on the upgrade path. The same markup can be passed to `customElements.define()` later without being changed, and the post warns that doing so changes the element's behavior and lifecycle, so the tag and its attributes should be treated as a deliberate contract [7]. One attribute in the example deserves particular care. The `slot` attribute here is a convention that CSS targets, not the shadow-DOM slotting mechanism [11].

The element itself has no implicit semantics, while semantic children such as headings and links keep theirs [6]. Accessibility therefore comes from the children or from ARIA the author writes, which is why the example SVGs are marked `aria-hidden="true"` as decorative [19].

On adoption, the post offers an assertion. It describes "a quiet trend picking up steam in the web components world" [2] without counting projects or libraries. Two stated conditions govern whether the code transfers: the CSS nesting in the examples has to be flattened for older browsers [12], and a component that needs interactivity gets defined after all [7].

What to watch

  • Whether a library actually ships an undefined tag and then defines it later, and whether consumer CSS written against the light-DOM children survives that change.
  • A measured count of projects styling undefined custom-element tags; the post asserts a trend without one.
  • Accessibility guidance for undefined-tag components beyond aria-hidden on decorative icons, given the tag has no implicit semantics.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories