We at Microsft used this to build apps.microsoft.com. We're quite happy with the results. Shoelace is customizable, accessible, extensible, easy to use.
This is nice and all, but when I see what the big companies do, I always have this cave-at in my head - "is this one of the teams with enough resources to boil the ocean?"
I.e. it's proof that Shoelace, in this case, is not inherently useless. But it's hard to get a hunch for how good it is to a mere non-corporate.
Gotta give props though, the apps site is fast and slick.
That's a fair point. Big companies do have the resources to go all out, but that doesn't always mean their solutions are the best fit for everyone. I think Shoelace is pretty neat because it's more approachable for folks who aren't part of those mega-teams.
I'm not happy with any of the solutions to prevent a flash of undefined components. Has anyone given up on web components for that reason? Or is there a best practice for this?
You basically write all the normal HTML and CSS so it's loaded in right away. But then you also make use of the advantages that defining a custom element gives you.
That's a nice workaround but still just a bandaid in my opinion. Instead of a "flash of undefined component" you get a flash of whatever the vanilla html is before your web component was loaded. Now you have to adjust the vanilla html to look as close to your web component as possible, at which point you may just want to use vanilla html and css altogether and ditch the web component.
I've started moving to this technique from my earlier technique of putting a data-module on wrapping div components, where I manually had to deal with a bunch of stuff which HTML Web Components just do for you for free. Super handy!
the doc site doesn't have this problem because it doesn't make a giant single archive of code that loads slowly on a single core. lighthouse doesn't even record the flash. most of the pages are getting 98/99 performance scores.
i'm sure if you webpack the crap out of it bundling hundreds of dependencies into the same parse, it'll perform poorly, but well, stop doing that.
I did. I can't accept that as a user, so I'm not going to make my users put up with it. The simplest "fade" solution or adding a loading dialog is a huge pain and I'd rather write vanilla html/css/js if it will allow me to avoid that.
Do I understand correctly, this only works when the size of the component is determined from "above" in the layout tree? That sounds not generally useful...
1) use a bundler (rollup or webpack) if your code is big so it can be chunked and loaded quickly
2) have a loading spinner by default
Some developers have a dogmatic zeal against loading spinners, but users are very accustomed to them.
In fact, it's possible for websites to load _too_ fast, seeming unnatural to users (who at this point in time are very much expecting the flow to be request->spinner->load).
Web Components in general: sure. But what Shoelace uses: no.
Specifically, Shoelace uses Shadow DOM and adoptedStyleSheets.
For Shadow DOM, there’s Declarative Shadow DOM, a way of serialising a Shadow DOM, which is extra work to support, but possible; it’s currently supported in Chromium and Safari, and last month Firefox landed an experimental implementation.
But the key feature and problem of Shadow DOM (in my opinion, more problem than feature) is how it isolates styles. You need to add the stylesheet to every shadow root, via a <style> or <link rel=stylesheet> element (and hope the browser is clever enough to deduplicate, which it should be in the latter case but I don’t think it will be in the former), or via adoptedStyleSheets, which is generally more efficient and allows shared mutation after construction (unlike the other approaches). Shoelace uses adoptedStyleSheets. Trouble is, that doesn’t work with Declarative Shadow DOM.
this looks great. I'm puzzled by something though: why does it duplicate components that already exist in standard HTML5 (Button, Checkbox, Dropdown etc.)? The whole idea of web components is being able to augment what's already built in, not have to replace it.
Maybe it's for consistency (styling/behaviour). Either way, it looks really well done.
It doesn't duplicate, it wraps. `sl-checkbox` is ultimately a `input type="checkbox"`, but wrapped in containers and exporting uniform props. For example `sl-checkbox` is a container with both a checkbox and a label container in it. And it exports a prop `size` which takes the values "small", "medium", "large", which is uniformly provided across Shoelace input components.
It's a combo of (a) HTML lacks the features/ergonomics and (b) need to provide a clean-syntax API that extends HTML.
That's a good thing in my opinion. Don't give me a set of 25 controls, instead make it easy to make my own. Because no matter what you include in default controls, I am going to need 12 more.
But the default set has been lacking for too long now too.
Webbrowsers should really get together and make a new default set of Application-based UI controls, and make them a bit more beautiful by default. :)
Like datatables, rich-text editing, toolbars,...
MOST applications are just simpler CRUD UIs.
Developing these is still too complex on the web platform.
Or: we as users can wish for the days of 90's web design and bring it back! The main content was so much more up front. Forms, buttons, and general layout were all the same.
And Progressive Enhancement has it that our bells and whistles gracefully degrade back to from whence they came.
I think it's also because every site wants their buttons etc. to look slightly different to every other site's, because branding. Being able to stand out from the crowd is a feature.
Personally, I wish that for accessibility and other reasons, all 10000 frameworks would produce buttons with some kind of metadata that lets a user configure their own settings. But we're quite far off that.
Because browsers offer very little styling and customization options even for the handful of built-in elements. That's why every library under the sun tries to re-invent them again, and again, and again.
I recently made an entire WebRTC p2p video player that worked seamlessly everywhere! Except iPhone Safari. And all iPhone (but not iPad!) browsers are Safari under the hood that couldn't do a few of the fundamentals needed.
I didn't like that the components are all web components with their own shadow Dom inside. Makes styling really hard and didn't work with tailwind for example.
Not just that, but it basically makes the library incompatible with serverside rendering a la Next.js. We actually explored using Shoelace for a project but ended up having to choose something else instead because of this.
True, it completely relies on client side rendering, and it's impossible to render it server side. With disabled Javascript it doesn't show anything at all.
I would really like to use it, but using it feels like limiting your options much more than necessary. Even the CSS-in-JS based libraries (like React MUI) allow SSR to some extent.
We settled on Chakra (https://chakra-ui.com/). Although we also abandoned our ambitions of a Next.js migration, so... I guess it didn't really end up mattering all that much anyway.
I guess the advantage is that you can drop them into any HTML page and they look and work correctly. And you can control them with plain JS, like `document.getElementByID(dialogId).open = false;`
Cory (the creator) actually works at FontAwesome now. FontAwesome purchased the library ~1yr ago and hired Cory to work on the library full time as well.
I'm impressed with the breadth of Shoelace's offerings, particularly its emphasis on accessibility and framework compatibility. For a project I'm working on, which involves creating an interactive e-learning platform, components like adaptable quizzes and responsive drag-and-drop interfaces are crucial. Could Shoelace be extended to include such interactive elements, maintaining its accessibility standards?
Haven't done frontend in a long time, but how is this different than something like Bootstrap? Is it the fact that it comes also as a React & co library and you can customize properties of a 'component' easier than building your own wrapper 'component' to modify properties which are set via 'data' or 'css' attributes? I guess this leads me to the next question, I saw in a recent project on my company's Github how everything was done with 'React-Bootstap', which to me looked very weird, that in every Jsx component there was barely any HTML tags, but only smaller components from this bootstap-react library, even for simple things like <h1>s. It all looked like a mess to me, but maybe my views are from older times.
It's like Bootstrap, but different. Main difference: it's based on Web Components and Javascript, Shoelace won't render anything without Javascript, without the possibility of Server Side Rendering (Shadow DOM requires Javascript)
Bootstrap got kind of stuck in the past. Vanilla Bootstrap is only really usable without a modern frontend framework, as it comes with many Javascript snippets that make the controls interactive. But it's just not really compatible with other UI libraries like React, Vue, etc.
Also Bootstraps feels quite antiquated from a user perspective. And for theming bootstrap you need to go through Sass hell.
I still use it for server side generated HTML, because it's really easy to use and just works.
Like bootstrap it's a set of standard behaviours and styles.
Bootstrap basically takes standard elements and when you add CSS classes and data properties, it styles them and adds some JS behaviours.
Shoelace builds HTML custom elements, using CSS variables for controlling the style, with the JS baked into the custom-element code. And Shoelace uses the shadow DOM, so any customisations do not "leak" outside of your components (although this has its own implications).
Custom elements basically do the same as React/Vue/whatever (reactive properties, slots etc) but in a manner that's built into the HTML spec and into all browsers. (React/Vue et al also have mechanisms for building your components as custom-elements, but you're then adding the dependency on React/Vue when you reuse them - although Shoelace has a dependency on lit.dev, as the actual custom-elements spec is quite low-level).
But Bootstrap and Shoelace are essentially solving the same problem - a reusable design system - but Shoelace does it in a way that's already built in to browsers and arguably cleaner to use.
I didn't try it out, but usually I'm unhappy with Tailwind based libraries. It just doesn't really fit the concept of Tailwind to use it for a UX library. On one hand those libraries are just a handbook of HTML snippets to copy&paste. On the other hand many of them need some support library, that does some magic in the background that's hard to understand.
The only one I liked so far was shadcn, which is more like a code generator for creating Tailwind based React components.
Just hover over that middle divider between the input>|<output panels to get a feeling of how customizable it is.
Also our menus do not follow their keyboard navigation logic at all and adds support for disabled, hidden, inert elements, as well as selection by typing the first letter of menu items.
All of these are possible because the author knows what NOT to do with HTML custom elements.
have been following the development of this very closely for this library, and love the fact that we can script load individual web component in html as bare bones.
On my phone, the whole page is covered by the left side menu. Tapping on the burger doesn’t hide the menu either. Not a good impression from a web design library.
It’s extremely common to support the two most recent major versions, especially for iOS. It’s great if things work on iOS 14, but it’s been explicitly out of support for over a year in a vast number of organisations.
It just barely supports Safari 15, on iOS only, and that’s likely to go away imminently because it’s under 1% usage.
Browserslist is used by a huge proportion of web developers and the tools that they use, with millions of downloads a week. It has nothing to do with native apps and everything to do with the web.
The issue here is the version of the webkit engine, which ships with iOS and that is 3 years old. Anyway a docs page is basic, it should be compatible regardless.
Yeah but flash of unstyled content? Plus you need to somehow adopt only related parts? Selectors wouldnt match easily necessarily, e.g. decision to adopt :hover selector must be made somehow
this seems like a great library, but it is missing a date picker. it’s hard to find UI libraries willing to take on the date picker, i guess because native pickers, esp on mobile, get the job done?
Serious question: if you're building an application, why do you want this? It used to be a huge thing to build Apple, Windows, Motif, whatever apps closely in accordance with their interface guidelines and idioms, but for some reason on the web everyone seems to need an idiosyncratic, opinionated set of basic controls.
I agree with you and I am sure I am not saying anything new, but the fact that on the web content, presentation and marketing sites heavily outnumber webapps, and that one needed to stand out - even if superficially - from countless other options, caused a much larger emphasis being put on customizability, looks and originality which didn't stop at layouts colors and typography, but seeped into interactions. So a lot of jobs got created, lots of users suffered from slow, confusing and buggy UI/UX but at least lots of originality and prettiness was had :)
Oh for sure if there was a web platform look, I would happily oblige and use that look. The point is that there isn't one so in that case I would rather have something different.
If we had gone down the path of smart documents instead of web apps for the internet, we would have had a better time for sure.
I don't see that these components use part/exportparts feature to do that. Am I looking at it the wrong way? Or do you mean that customization happens via CSS?
Yeah, sorry, it's using CSS parts for customization. So if a component is made up of multiple nested containers, you can target the inner containers with ::part(name).