If you’re following the series, you definitely saw how ugly our page was at the end of the last post. Well, good news – we’re gonna fix it right now. Before you close the window, let me explain: I’m not going to start doing graphical design here, as I do not know enough about it. Instead, I’m going to show you how to use Yesod with one of the best design libraries in the world – Bootstrap. Yesod has kind of built-in support for Bootstrap – you might’ve guessed it by the import Yesod.Form.Bootstrap3() line in Projects.hs. It’s actually not exactly what we’re looking for, so let’s change it to import Yesod.Bootstrap() and add Yesod.Bootstrap to build dependencies in .cabal file. You might need to run stack solver --update-config && stack build before next run.
And that’s it – Bootstrap is already included in our files. Now let’s get rid of the ugly projects.cassius and let’s replace it with some nice styling!

But before this, you might be a little surprised that including Bootstrap didn’t require us to add any additional CSS files. That’s not actually true – we simply already have one. It’s located in static/css/bootstrap.css. If you are able to access the site after running stack exec -- yesod devel, you have this file – otherwise the site wouldn’t even compile.

Now you can delete all the classes and IDs we used in previous post and replace the .hamlet file with something like this:

<div>
  <h1 .page-header> Available projects:
  <div .jumbotron>
  <ul .list-group>
    <li .list-group-item> Project 1
    <li .list-group-item> Project 2
    <li .list-group-item> Super project

looks much better now, doesn’t it?

The hardcoded project names are quite a shame, we should get rid of them. Let’s make our Hamlet template an actual template that is filled with some data! For the first shot let it be a simple list of project names – typed [Text]. Wonder why not [String]? Short answer is: it’s more efficient. For a slightly longer one you can check out The Yesod Book. Anyway, our goal now is to have a list of Texts passed to the template and same output rendered. To do this, we need to add a little big of logic to our template. Lines starting with $ are treated as special instructions, virtually typical Haskell operations. #{someName} is used for variable interpolation. Check out the code for projects.hamlet:

<div>
  <h1 .page-header> Available projects:
  <div .jumbotron>
    $if null projects
      There are no ongoing projects
    $else
    <ul .list-group>
      $forall projectName <- projects
      <li .list-group-item> #{projectName}

Quite a change, isn’t it? But it’s quite simple – if there are no elements in projects container we show a message, otherwise – list all elements one-by-one.

But the code doesn’t compile again. To make it work you have to add projects variable to the scope of widgetFile invocation. This can be done for example by adding

    where
      projects = ["Project 1", "Project 2", "My extra project", "Project 10"]::[Text]

to getProjectsR function.

And that’s it! we can feed the template via the list in Projects.hs file. Nice, eh?

Actually, no. Right now it’s even worse than it was – previously it was sufficient to modify projects.hamlet and yesod runner automatically reloaded all the stuff. Now the compilation has to be done manually, by running stack build. Have no fear, we will change this in the future. but the next step is different. The next step will introduce Yesod’s killer feature – typesafe routes.

Stay tuned!