TunkShif

TunkShif

Sprout UI - unstyled and accessible components for Phoenix Framework

Sprout UI provides a collection of common UI components. All of these components are completely unstyled, and they are built following the WAI-ARIA guide to be accessible.


Currently, it is not ready for a stable release yet, but I just can’t wait to share the progress I’ve made and get some feedback. Check out the links below for more information.

Website (including documentation and component demos) | Github Repo


Design goals for Sprout UI:

renderless function component

If you’ve never heard about this concept, it’s highly recommended to check out this informative blog post by @LostKobrakai . Also check out libraries like Downshift JS and Zag JS in front-end world.

A renderless function component does not render any visible HTML tags, but just provides necessary context information for rendering. And it is the user’s responsibility to decide what component structure should be rendered like.

For example, a simple dialog component is used like this:

<.dialog :let={api}>
  <button {api.trigger_attrs}>Open modal</button>

  <div {api.container_attrs}>
    <div {api.backdrop_attrs}></div>
    <div {api.panel_attrs}>
      <h2 {api.title_attrs}>Dialog</h2>
      <p {api.description_attrs}>...</p>
    </div>
  </div>
</dialog>

The component itself won’t render any HTML structures for your dialog, but just takes care of all the needed attributes for user interactions and accessibility.

no unnecessary round-trip to server for user interactions

Under the hood, Sprout UI components are using custom elements for handling user interactions like clicking or keyboard events. You won’t need to communicate to the server-side just for opening a modal or toggling an element.

And it can be used with LiveView or just plain server-side rendered templates withour LiveView.

styling and transition made easy

Generally, a Sprout UI component is composed of multiple component parts. For example, the dialog example above may eventually rendered like some HTML structures like this:

<sp-dialog data-state="open">
  <button data-part="trigger">Open modal</button>

  <div data-part="container">
    <div data-part="backdrop"></div>
    <div data-part="panel" aria-attributes>
      <h2 data-part="title">Dialog</h2>
      <p data-part="description">...</p>
    </div>
  </div>
<sp-dialog>

sp-dialog is a custom element for handling dialog opening/closing interactions and managing focus. Each component part is rendered with a data-part attribute. And for components with multiple UI states, like dialog can be open or closed, the state is presented in a data-state attribute as well. You can use CSS attribute selector to style a component part or a specific state.

[data-part="backdrop"] {
  background-color: #000;
  opacity: 0.6;
}

:where([data-state="open"]) [data-part="backdrop"] {
  /* ... */
}

Also, Sprout UI comes with a Tailwind CSS plugin that makes it easier for styling different UI states using variants. So you can just do things like ui-open:opacity-100 ui-closed:opacity-0.

And Sprout UI provides a transition utility to help you add transitions when a component part entering or leaving in a declarative way. Here is an example of how you can add transitions for a dialog backdrop:

  <div
    data-transition
    data-enter="transition duration-300 ease-in-out"
    data-enter-from="opacity-0"
    data-enter-to="opacity-100"
    data-leave="transition duration-300 ease-in-out"
    data-leave-from="opacity-100"
    data-leave-to="opacity-0"
    {api.backdrop_attrs}
  >
  </div>

Currently I’m still having lots of work to do: writing tests and docs, fixing some existing issues with these components, implementing more common component patterns.

So feel free to try it out yourself and share your ideas!

Most Liked

teamon

teamon

I took great inspiration from Sprout when deciding on how to build headless, thank you @TunkShif :purple_heart:

Where Next?

Popular in Announcing Top

jakub-zawislak
Hi everyone, I’m coming from the Symfony (PHP) framework. I like Phoenix, but it has a one thing that was build much better in the Symfo...
New
mplatts
With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components. We also have a boi...
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New
riverrun
I’ve just released version 3 of Comeonin, a password hashing library. The following small changes have been made: changes to the NIF c...
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_orde...
New
cjen07
parameterized pipe in elixir: |n&gt; edit: negative index in |n&gt; and mixed usage with |&gt; are supported example: use ParamPipe ...
New
mindok
What is ContEx? A pure Elixir server-side data plotting/charting library outputting SVG. It has nice barcharts in particular and works g...
New
Flo0807
Hello everyone! I am excited to share our heart project Backpex with you. After building several Phoenix applications, we realized that...
New
versilov
Could not wait for the missing Elixir ML libraries to appear, so, I wrote one myself, taking https://github.com/sdwolfz/exlearn as a foun...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New

Other popular topics Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

We're in Beta

About us Mission Statement