This is a post about what I worked on this month, why I was motivated to work on it, and the lessons that I learned. As the title says, the project is a clone of , which is a web application for building and sharing nice looking questionnaires. Here is a . Here is the . Also here is a gif: Typeform demo source Why Typeform? It has an amazing User Experience:1. It looks great with its choice of colors and layout2. It is smooth. Navigating between questions is snappy.3. It has keyboard shortcuts for answering questions and navigating the form.4. There are dynamic questions that can change depending on what the user has entered. But so what? Why does that make it a good candidate for cloning? Dan Pink talks about 3 pillars of motivation: Autonomy, , and Purpose. Mastery “Why reach for something you can never fully attain? But it’s also a source of allure. Why not reach for it? The joy is in the pursuit more than the realization. In the end, mastery attracts precisely because mastery eludes.”― Daniel H. Pink, Drive: The Surprising Truth About What Motivates Us The act of creating an interface with the features listed above using Elm was on the (Mastery). In other words, I have never been able to achieve that fidelity in the past, but I knew I was capable of it and was therefore highly motivated. edge of my skillset Sketching with tachyons About 6 months ago, I was creating a new static website, but I didn’t want it to look like . I could have found a CSS theme that matched the given design, but those typically have edge cases to work around. Plus, if the design only used 20% of the CSS in the theme, the extra 80% is hard to get rid of because of complications like media queries and nesting rules. every bootstrap site ever I chose for both that project and this one because I can create a fully designed site without ever touching CSS. Literally the only CSS I wrote for the previous project was a couple colors. Tachyons Here is a quick example I made showing how quick the iteration process is with tachyons. Sketching with Tachyons Link to Jsbin It is also easier to debug a tachyon design. Normally your browser’s development tools show something like this when investigating how something looks: Not only is there a lot of classes to debug, but each one can have many properties! If there was a problem with the layout that needed to be debugged, one would have to open up the HTML and CSS code. This might just be 2 files, but it could be more if the CSS includes other files via a pre-processor, or if the HTML is controlled by JS. With Tachyons, the only code to worry about is the HTML. Even if the HTML is heavily controlled by JS, it is still easier to debug the element and test different solutions. For example, here is a similarly complex layout: Still many CSS classes on those elements, but each one only has one property. Here, we see all the properties affecting the element and its parents. While it might look just as complex as the previous example, each class only does , so debugging it is much easier. one thing Another benefit to not having to write CSS is not having to come up with class names. Naming stuff . Conventions like BEM and SMACCS are effective for standardizing names but you still have to come up with the name and they can get long: is hard sidebar__section sidebar__section — large Wouldn’t it be better if you didn’t have to name anything to begin with? One final note to choosing tachyons for this project: I know CSS better now. Bootstrap and similar frameworks make it very easy to put something together that looks nice in most environments, but instead of CSS, you learn those framework’s specific class names and idioms. learning For more examples of how using Tachyons can simplify a design, Simon Vrachliotis has about it where he rewrote the design of . a great series SocietyOne Modeling with Elm of has already on to Elm for development. If articles aren’t your style, then . Lots discussion happened why chose front-end YouTube is your friend Two strengths in particular are its flexible type system, and its compiler errors. Haskell users would be quick to point out that their type system is more flexible, and they would be right! While Elm does not have compared to JavaScript, it is streets ahead. type classes, Some types and compiler errors When I used to build Angular apps, I might have represented a list of questions in JSON like this: [{title: "Question 1 - Enter your name",type: "text",value: ""},{title: "Question 2 - Select a Country",type: "dropdown",choices: ["Abkhazia", "Afghanistan", "Aland", "..."]filteredChoices: [],selectedChoice: ""},{title: "Question 3 - Select a Gender",type: "select",choices: ["Male", "Female", "Other"],selectedChoice: ""}] But we can have Elm give us guarantees with stronger types: type alias Question ={ title : String, type: QuestionType, answer : String} type QuestionType= Text TextOptions| Select SelectOptions| Dropdown DropdownOptions type alias TextOptions ={ internalValue : String} type alias DropdownOptions ={ choices : List String, filteredChoices : List String, inputValue : String} type alias SelectOptions ={ choices : List String} Elm will make us do more work up front to write code that follows this schema, but if we mess up, we have nice errors: -- NAMING ERROR --------------------------------------------------- elm/Main.elm Cannot find variable `answr` 510| List.map (\q -> { q | answer = answr }) questions^^^^^^^^^Maybe you want one of the following? answer Easy mode Refactoring data effectively is a good skill to have as a developer. Having both of these features in Elm meant that when changing the application model: 1. Those changes were done correctly. 2. Everything that depends on those changes knows about it. “When it compiles, it works” — The Elm Community Stumbling blocks and my solutions to them There were a few parts of this project that I got hung up on for a day or so. I will attempt to cover most of them: Use , add a Msg, add some logic to route to different functions depending on what keys were pressed (Enter, Shift+Up/Down, Escape) How to get keyboard navigation? keyboard-extra case I used a Javascript library and added a port to tell it what to do. How to smoothly scroll between questions? smoothScroll Just make a type alias to store colors (primary, secondary, background, hover, etc.). Then in the view functions, simply set HTML style attributes. One gotcha is not being able to use CSS hover psuedo classes, so I used . How to do multiple color schemes without separate CSS files? elm-dynamic-style Easy: How to add Markdown to the question text? elm-markdown Use a Zipper structure…. a what? How to navigate Up and Down between Questions and between dropdown items? Zippers to the rescue I first heard about this data structure from Richard Feldman’s talk about . If you are new to Elm or learning it, I recommend watching the entire video as it is mind opening. Making Impossible States Impossible A is a technique for traversing and updating a data structure. In my case the data structure was a List, but it can also be applied to Trees. The type signature for a list-based Zipper is: The library implements an API to use the zipper technique. With it, I can change my Lists to Zippers and easily traverse them. The most common functions I used were , , , , and . Zipper type Zipper a = Zipper (List a) a (List a) elm-listzipper map mapCurrent next previous find For example, the list of questions in the demo is a “Zipper Question”, and the dropdown options in the demo is a “Zipper String”. This allowed common operations (next item, previous item, get selected item, update selected item) to be performed with one line of code. -- The updated model for the filterable dropdown, notice the Zippertype alias DropdownOptions ={ choices : List String, filteredChoices : Zipper String, inputValue : String, showList : Bool} Because of Elm’s compiler messages, you can simply change your definition and follow the trail of errors until the code compiles. Here is a snippet that demonstrates handling a navigation event like Up and Down keypresses. If I didn’t use a zipper, I would either have to set a “selected id” or an “isSelected” flag and handle the edge-case logic. With the zipper, I can just call or and give it a default in case there’s an error. next previous handleUpOrDown : Direction -> Model -> ModelhandleUpOrDown direction model =letnextZipper =case direction ofUp ->Zipper.previous model.filteredChoicesDown ->Zipper.next model.filteredChoicesin{ model | filteredChoices = nextZipper |> Zipper.withDefault "Not Found :(" } Updating the item that the zipper is focused on is also easy with mapCurrent. setQuestionIsFocused : Zipper Question -> Zipper QuestionsetQuestionIsFocused zipper =Zipper.mapCurrent (\x -> { x | isFocused = True }) zipper What’s next? The main goal of this project was to learn more Elm and use Tachyons with it. If there is enough interest to clone more of Typeform, I will build an interface to interactively build questionnaires and add user support. For now, the code needs refactoring, which is the easy part! I am in the market for remote web development work. If you’re interested in hiring me, send me an email! davidstreeterconsulting@gmail.com _Website Optimizations | Re-designs | New Sites | Webdev consulting_davidstreeterconsulting.com David Streeter Consulting