11 February 2020

This tutorial will demonstrate a workflow for creating a landing page (with email signup form) for a new project. Part of The Solo Hacker's Guide To Clojure. Prerequisites: First Steps With Clojure.

Demo

Clone the Mystery Cows repository and checkout the landing-page branch. Run ./task setup to install a couple npm deps and download the Bootstrap source (if you don't have npm installed, do that first). Then run ./task dev live to start a development server.

(I'm going to assume your environment has the ability to run shell scripts. I figure this is a reasonable assumption since Windows 10 has the Windows Subsystem for Linux. But if you can't run shell scripts, you can just look inside the files and run the individual commands yourself. I won't be doing much shell scripting anyway.)

A landing page (like this one) should open in your web browser. Whenever a Clojure file changes on disk, the task we ran will regenerate the public/index.html file and reload the page in your web browser. Try it out: open src/cows/core.clj and change the nav bar so it says "Mystery Sows" instead of "Mystery Cows" (which is a real thing in case you're wondering).

(By the way, the email form will give you console errors if you try to use it because it has a Firebase dependency which we haven't set up yet. More on that later.)

The code

src/cows/core.clj

This file uses Rum server-side rendering to generate the html. Things you may not have seen yet:

deps.edn

Always worth a look.

task

Note that clj -m cows.core calls the -main function from src/cows/core.clj .

bootstrap/custom.scss

This is where you can customize Bootstrap's CSS.

Do it yourself

Now, create a separate project for your own landing page:

Set up a skeleton for the project. Copy over deps.edn , task and bootstrap/custom.scss , then add a bare-bones src/yourproject/core.clj file with just a "hello world" div. Also include a head section with the title and meta elements. Run ./task setup , then start the dev server and make sure it works. Generate a favicon (favicon.io). Place the generated files in public/ and link to them from yourproject.core . Add links to Bootstrap from yourproject.core . Change the theme colors in bootstrap/custom.scss (unless you want to leave it as "cowpie brown"). You can use use coolers.co or something similar to help you pick some colors. Write the HTML/CSS in cows.core . You could do it from scratch without too much effort, but I searched for "free bootstrap themes" and ended up on this one. If you take that route, use this site to convert the HTML to Clojure data structures. Wrap the output in a (defc landing-page [] ...) as in cows.core . You'll have to do a little editing. For one thing, Rum is slightly different from Hiccup. Hiccup uses only strings for inline CSS, so you can't write something like [:p {:style {:background-color "green"}} ...] . Instead, you'd have to write [:p {:style "background-color: green;"}] . Rum is the opposite way, so if you get any compilation errors, you may need to convert the strings to maps by hand. Also stick any images and CSS files from the theme you copied somewhere under public/ , adding the appropriate links to the head section. I'd recommend rewriting the CSS into yourproject.core in most cases, but some things (like media queries) can't be written inline, so feel free to leave those in a separate CSS file. (See Garden if you want a 100% Clojure solution.) Finally, you can break up the HTML structures into separate variables and components like I've done in cows.core . For example, the "testimonial items" were originally three separate blocks of HTML, but I consolidated them into a component and a for . For stock photos, I recommend pexels.com (that's where I got all the cows).

Once your landing page is looking good, the next step is to make the signup form actually work. We'll need to set up Firebase as we'll be using that for storing the email addresses (and for hosting).

Go to firebase.google.com and set up a new project. From that website, create a Firestore database. After it's provisioned, click "Start collection" and set the ID to signups . Add a single string field called email . Follow this guide to install the Firebase CLI. Once you get past the firebase login part, run firebase init firestore . Replace the the new firestore.rules file with the one from Mystery Cows. Those rules will let your signup form add new email addresses, and it will prevent users from reading or modifying the saved addresses. Run firebase deploy --only firestore:rules . In cows.core , you'll notice three Firebase :script elements. Add those elements to yourproject.core . The :src values are special "Hosting URLs" that Firebase can use to add the dependencies for you along with project-specific configuration. See here. Run ./task dev (not ./task dev live ). This will serve your app using the Firebase dev server instead of the npm live server. This is necessary so that the Hosting URLs will work. Unfortunately, you'll have to manually refresh the page whenever you regenerate the index.html file.

Now all that's left is to hook up some JS to your signup form and then deploy the landing page. See public/js/main.js and the signup form elements in cows.core to see how I did the former. Once you set that up, test out the signup form. You can check the Firebase console to make sure that the email was saved.

Finally, run firebase init hosting and then firebase deploy . Your landing page should be live. You can hook up a custom domain now if you want, or just use one of the provided domains. Later, when you set up a mailing list, you can import the emails you get from Firebase. (I use Mailgun with some custom code myself, but Firebase somewhat recently made a convenient extension for adding new users to Mailchimp, so that could be a decent option. We'll go more into that eventually.)

Next, add authentication.