Shoelace: More flexibility for 3rd party component developers regarding icons

Created on 18 Apr 2021  路  7Comments  路  Source: shoelace-style/shoelace

The way Shoelace is handling icons may be as-is okay for Shoelace core and app developers, but it's far away from being flexible enough for 3rd party component developers.
Part of that has already been discussed in #297 but obviously my descriptions and comments must have been completely ambiguous as the final comment there was "we're at a good place with icons in general", but frankly, I disagree completely with that. I would not have spent hours and hours on that topic if we were already "at a good place with icons in general" (for example, none of what's said in that issue's last comment is applicable for 3rd party component developers).

A very concrete scenario: You want to write a large suite of components for developing admin panels and dashboards based on Shoelace components. One very important requirement is that those components should ALWAYS look good out of the box (if that means that the bundles of those components will be 10 kB larger then be it). If they are used in a Shoelace specific context they shall automatically have the icon set that has been configured there, should be shown in the theme that is used there and use the language that is currently used by the other shoelace components. Outside of the Shoelace world they may be using a default theme, a default icon set and use the en-US locale if not otherwise configured somehow. I think this is a very legitimate requirement (of course it would be possible to provide two versions for each component, but that would be quite a suboptimal solution).

The part with the icons is currently not possible with shoelace as it is currently not possible to find out whether icons are actual available in the default icon library.

The following simple changes would make this much more flexible (I don't say, it must be exactly those changes ... it's about the pattern):

  1. Get rid of those 4 lines (now every app has to invoke registerIconLibrary explicitly -> minor breaking change):
    https://github.com/shoelace-style/shoelace/blob/bf2a4ac6cb4e6cd4bb0a1126d349dd72d220dfaa/src/components/icon/library.ts#L14-L17

  2. Export getIconLibrary as part of Shoelace's public API
    https://github.com/shoelace-style/shoelace/blob/bf2a4ac6cb4e6cd4bb0a1126d349dd72d220dfaa/src/components/icon/library.ts#L29

  3. Optional: Just in case the app developers think that invoking the registerIconLibrary function once per app would be too much work then maybe this will make it less "painful" (I personally don't think that this is necessary, but anyway...) - minor breaking change as in this form the URLs of the default icons slightly differ:

    export function registerIconLibrary(
      name: string,
      options?: { resolver: IconLibraryResolver; mutator?: IconLibraryMutator }
    ) {
      if (!options) {
        registerIconLibrary(name, {
           resolver: (iconName) => `${getBasePath()}/assets/icons/${name}/${iconName}.svg`
        });
    
        return;
      }
    
      // ... starting from here, same as before ...
    }
    
    
feature

Most helpful comment

I've implemented this idea in #422. There are some key benefits that I really like about this approach. If it works for you, I'm happy to merge.

All 7 comments

I'm not sure what you're asking for. Technical how-to aside, what are you fundamentally trying to achieve here?

Is your concern based around components using <sl-icon> instead of hard coded SVGs?

I removed this section of the docs as to not encourage users to override the default icon set with other icons.

Alternatively, you can replace the default icons with a completely different icon set. Just keep in mind that some of the default icons are used by components so you'll want to make sure those names resolve to an appropriate alternative.

This isn't how I envisioned this working and I'm not sure why I even wrote that section. The default icon library should always point to Bootstrap Icons, and it's only customizable so you can point it to a different folder locally or via CDN. To add alternate icon sets, they should register them as such.

The default icon library should always point to Bootstrap Icons [...]

First of all, this is an important point ... thanks for the info.

what are you fundamentally trying to achieve here?

(tl;dr => just read the last section at the end)

The main goal is to provide a set of 3rd party components to the public that work out of the box and do not need any pre-configuration to look and work properly.
It's about providing the best possible DX to the user of my component library ... but also very important is to provide the best possible DX to myself by developing a bunch of helper functions to make my own life easier.
Let's say you develop a complex 3rd party component (not an app!) like for example a data grid like the following http://w2ui.com/web/demos/#!grid/grid-3 based on shoelace components (one big component as composite of a lot of smaller components). How do you make it possible that for this component the icons in the shoelace components are shown properly, a nice looking theme is used out-of-the-box and that the texts are shown in the proper language independent who is using that component in which context? Almost all of my comments and questions in the Shoelace project are about this topic .... and just because 99% of the Shoelace community do not care about this question(s), may only be because most of them are app developers and not 3rd party component developers and therefore have complete different requirements. It's perfectly fine if 99,99% of the Shoelace community agree that (quote from the doc) "Some components rely on assets (icons, images, etc.) and Shoelace needs to know [...]. This assumes assets are colocated with shoelace.js and will 'just work' for most users." this is the best way to handle icons ... but I am one of the 0.01% developers who think that "just work" means literally "just work" and not "it will work after you have done this or that". Also even if slots are available for each single icon this will not be a satisfying solution. If Shoelace does not ensure that all core icons are shown properly out of the box, then I will try to take care of that on my own in my projects, that's all.

If getIconLibrary was available for the public and by default no defaulticon library was registered by showlace itself, then I could first check whether somebody has already registered a resolver for the default icon library in userland => if no, then I register my own resolver which would return data uris for all ten currently used core icons and and empty strings for all others ... if a resolver for the default icon library was already registered (and I assume that an icon resolver is allowed to return an empty string if it does not know a URL for a given icon name - this is new, I guess) then I can register a new resolver that first asks the old resolver (retrieved beforehand by getIconLibrary) whether it knows the URL of the requested icon, otherwise in case of that 10 core icons it would return a data uri for the icon, otherwise an empty string. Of course that means that the data uris of all 10 core items are non-treeshakeable bundled with my component suite ... but I do not care at all about these additional 2 kB as DX has a much higher prio for me. That's why I've suggested the above changes.

The main goal is to provide a set of 3rd party components to the public that work out of the box and do not need any pre-configuration to look and work properly.

So to accommodate your goal (which you confess is a ".01%" need), all Shoelace users should have to register the default icon set? This is counterintuitive to Shoelace's mission. 馃槙

It sounds like you're jumping on the defensive, but I'm trying to understand what you're asking for. Between the last issue and this one, it seems like you want something that very much accommodates your use case and, to achieve it, hinders a large majority of the rest of Shoelace's user base.

So help me understand a couple things. It sounds like your library consumes Shoelace as a peer dependency 鈥斅爄s that correct?

And if that's true your concern is that, for whatever reason (e.g. misconfiguration, assets not available, etc.) the core icons used by certain Shoelace components may not be be loaded. Is that also correct?

If so, perhaps the key is to revisit the "system" library idea which provides a resolver with hard coded data URIs _only_ for those used in core components. The components would then be updated to use <sl-icon library="system">. Then, whether or not the default icon set (Bootstrap Icons) is loaded or overridden is irrelevant because we can guarantee that the "system" library will be available. (This would also allow us to "reuse" icons since they won't be baked into each individual component.)

As a bonus, you could still override the system library to provide custom icons, but they would _only_ affect those core icons.

Does that sound like a workable solution?

Also even if slots are available for each single icon this will not be a satisfying solution.

I agree, it's way too verbose and will lead to inconsistent appearances. This is one of the reasons the library registry was created.

I've implemented this idea in #422. There are some key benefits that I really like about this approach. If it works for you, I'm happy to merge.

I LOOOOOOOVE IT!!! 馃憤 馃憤 馃憤
Thanks a 1000 times ....

Merged. This will be available in beta.39.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

trailsnail picture trailsnail  路  4Comments

Xyz3R picture Xyz3R  路  7Comments

claviska picture claviska  路  5Comments

SheepFromHeaven picture SheepFromHeaven  路  7Comments

kevinlandsberg picture kevinlandsberg  路  4Comments