Sebb

Sebb

Motivated by the success @benkimpel had with shoelace (see: Improve Support for Web Components in Forms with "Element Adapters") and the thread by @adw632 (see: Adobe Spectrum 2 web components with LiveView) I tried it myself.

I had some success, but there are also some problems I do not know how to solve and if they are solvable.

Solved? Problem 1 - LV removes attributes set by shoelace

If you do not set all relevant attributes on an element, shoelace sets defaults, LV removes them. This can be easily solved by either wrapping all shoelace elements into function components (see Ben’s thread) or just:


SL_DEFAULTS = {
    "SL-BUTTON": { "variant": "default", "size": "medium" },
    "SL-AVATAR": { "shape": "circle" },
    "SL-BADGE": { "variant": "neutral" },
    "SL-ICON": { "library": "default" },
   ...
}

let liveSocket = new LiveSocket("/live", Socket, {
    params: { _csrf_token: csrfToken },
    dom: {
        onBeforeElUpdated(_from, to) {
            const sl_defaults_for_current = SL_DEFAULTS[to.tagName];
            if (sl_defaults_for_current) {
                Object.entries(sl_defaults_for_current).forEach(([key, value]) => {
                    if (!to.hasAttribute(key)) {
                        to.setAttribute(key, value);
                    }
                });
            }
        }
    }
})

this seems to work fine.

Solved? Problem 2 - LV removes state like in @open

Multiple components store their state in an @open, eg <sl-details>

<sl-details summary="Toggle Me">
      Lorem ipsum  ...
</sl-details>

So when you

  1. render → closed (@open not set)
  2. toggle → opens (@open set)
  3. rerender → closes (@open removed by LV)

This can be fixed by sth like

window.addEventListener("sl-after-show", (evt) => {
    liveSocket.execJS(evt.target, '[["set_attr", {"attr": ["open", true]}]]');
})

window.addEventListener("sl-after-hide", (evt) => {
    liveSocket.execJS(evt.target, '[["remove_attr", {"attr": "open"}]]');
})

Or sth more sophisticated (see Ben’s thread)
Seems to work fine.

Problem 3 - LV removes elements that shoelace places into the light-DOM

This happens with <sl-breadcrumb>, code:

<sl-breadcrumb>
  <sl-icon name="arrow-right" slot="separator" aria-hidden="true"></sl-icon>
  <sl-breadcrumb-item>
    <sl-icon slot="prefix" name="house"></sl-icon>First
  </sl-breadcrumb-item>
  <sl-breadcrumb-item>Second</sl-breadcrumb-item>
  <sl-breadcrumb-item>Third</sl-breadcrumb-item>
</sl-breadcrumb>

This is how the breadcrumbs look like after first render (correct):
image

Note the arrow-icon that shoelace dynamically put into the separator slot of the item:

after a rerender it looks like:
image

Note the missing arrow-icon:

Problem 4 - LV removes generated classes

happens with <button-group>, code:

<sl-button-group label="Alignment">
  <sl-button size="small">Left</sl-button>
  <sl-button size="small">Center</sl-button>
  <sl-button size="small">Right</sl-button>
</sl-button-group>

first render (correct):
image

note the classes:

after rerender:

classes missing:
image

Problem 5 - ARIA

Seems like shoelace puts some important aria attributes which LV removes, didn’t look into that.

Problem 6 - Forms

This does absolutely not work right now, see Ben’s thread.

Most Liked

Sebb

Sebb OP

sad story.

Thank’s for all the help so far, did put me on the right track.
We’ll see what comes of it.

benkimpel

benkimpel

This is great. Let’s see if we can fix these.

When we were testing it out we found that adding an ID to some of the Shoelace slots helped with patching. It triggers different behavior in morphdom since the id is the nodeKey so rather than patching as children it patches directly.

There’s a more drastic option as well…

// WARNING: Lazy example. There's probably much more to it.
onBeforeElUpdated: (from, to) => {
  // Effectively make sure Shoelace always wins the attr merge by assigning from
  // back over it.
  // 
  // This could be done better with a "Managed Attr" list where instead of this
  // all or nothing approach below one could say: 
  // "Ok, for attr ABC of element XYZ we never patch it."
  // or even handle some specifically...
  // "For class of element XYZ we can merge the classes, but never remove"
  //
  // BUT I expect this would result in unnecessary renders all the time?
  //
  // This is why I think there are ultimately low-level changes involved if we want
  // to support arbitrary WCs
  //
  if (from.tagName.startsWith("SL-")) {
    [...to.attributes, ...from.attributes].forEach((attr) => {
      to.setAttribute(attr.name, attr.value); 
    });
},
benkimpel

benkimpel

For example, this fixes the button group…

// onBeforeElUpdated

// Protect sl- classes
if (from.tagName.startsWith("SL-")) {
  from.classList.forEach((cls) => {
    if (cls.startsWith("sl-")) {
      to.classList.add(cls);
    }
  });
}

But how fragile is all of this? idk

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews