UICards: create UIs without the laborious reload-navigation-input test loop

Crafting a nice UI can suck up a lot of time due to its stateful nature. We're all familiar with this cycle: make some tweaks, reload the app, navigate to the relevant page, click around and type for a while to get the right state, only to see that the spacing in one of the dropdowns still doesn't look quite right. Rinse and repeat.

If you use something like elm-live, you can cut out the reload step and have it done automatically on every code change. But you still need to do the rest of the steps to get to the particular widget and put it into a specific state.

But what if you could instead lay out your UI elements on a single page, so that you can see them instantly and compare them side-by-side? What if you could show them in any particular states that you want, without clicking around pages and manipulating widgets to get them into the right condition? UICards help you do just that!

With it, you can design a consistent, good-looking UI with less effort. You can also avoid the time consuming retrofits that are likely if your UI largely evolves page by page without an overarching plan.

Say you have a registration form with some validation. It can be in different states, and UICards allows you to seem them side by side:

You can tweak the font weight and see how all these states look after the change:

Combine this with live reloading (eg using elm-live), and you can now have instant feedback as you make changes to the UI widgets.

Interactive cards

Of course, static widgets are just one part of the story. What about all those interactive aspects of the UI? Say you have interactive widgets, like a menu that slides out from the side. Do you have to go back to the app to test it and get the animation just right? Not really! Thanks to the fact that all the state is in the model, and all the messages are handled by one update function, UICards allows you to have fully interactive cards. For example, we can test the functionality and user experience of a color picker which requires state and message handling:

How to make cards

You need to install UICards with elm install alexkorban/uicards.

This is what the code for the registration form cards shown above could look like, assuming the application code defines Main.registrationForm:

module Cards exposing (main)

import Main
import UiCards exposing (..)

initialModel =
    let 
        flags = ()
    in
    Tuple.first <| Main.init flags

main = 
    show Main.update
        [ deck "Forms"
            [ card "Blank registration" initialModel <|
                \_ ->
                    Main.registrationForm initialModel
            , card "Invalid registration" initialModel <|
                \_ ->
                    let 
                        failureModel = 
                            Main.register 
                                initialModel { userName = "InigoMontoya", password = "" }
                    in
                    Main.registrationForm failureModel
            ]
        ]

The cards are organised into decks. You view one deck at a time, and can switch between them in the UI.

The entry point for UICards is the show function. It takes an update function (in this case, we're using the application's update function) and a list of decks.

The card function takes a name, the initial model value, and a Model -> Html msg function as its last argument. The reason that it takes a function and not just a Html msg value is the support for interactive cards which get updated as the model changes.

Since the cards above are showing static UI elements, I've ignored the model argument and used initialModel and failureModel to render different versions of the registration form.

You can find more details in the documentation.

Inspiration & ideas for the future

Several years ago, I stumbled upon Bruce Hauman's post about his devcards project for ClojureScript. It stuck with me because it was such a great idea, and I wanted to see whether I could implement something similar in Elm. Typically this type of tool is simpler to implement in a dynamically typed language like ClojureScript.

At this point, UICards are experimental, and I just got to the point where I thought that it might be useful to other people. I have a lot of ideas for improving UICards:

  • Displaying the changes to the model caused by manipulating the UI widgets in the cards.
  • Keeping the history of UI states and allowing to rewind them.
  • Handling more complex scenarios like issuing commands on initialisation (currently each card only takes the initial model, so commands aren't possible).
  • Simplifying the setup with a custom index.html.
  • Perhaps more options for controlling the display area in the card, like specifying a fixed height or a reduced width.

If only I had a corresponding amount of time to turn these ideas into code!

Finally, I'm also going to write a post explaining how UICards are implemented. If you'd like to get notified when it's out, pop your email into the form in the footer ↓.

Comments or questions? I'm @alexkorban on Twitter.