Session 5 - Creating the Landing Page Part 1 - Organize the Project

Goal

So far, we are able to serve static files and converting markdown contents to html outputs. However, we have been only serving self-contained test files, and it is still far from serving dynamically generated files that are combined from templates and data.

To get there, we'll work from a more complex example - we'll serve the landing page of appfromscratch.com.

Breaking Down The Landing Page

The following image shows the breakdown of the existing landing page:

Landing Page Components

Which has the following visual components:

The Signup Form is reused twice. Although none of the other contents are repeated, it's good to go ahead and compose the landing page out of the above components, since:

So let's get started - we'll start with trying to re-create the above structure in a template.

Using Pug As Template

Express has a built-in mechanism for handling template-based view generation.

The way templates works in Express is that you need to define a configuration setting called views that points to the template directory, and then you need to define another configuration setting called view engine with the name of the view engine module, which needs to be written to specifically work with Express.

The default template engine for express is Pug, so we'll leverage pug as well.

npm install --save pug

Then we add the following to our index.ts:

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

Let's also make sure that the views directory exists:

# in the project's root directory.
mkdir views

Now that we have the root directory, let's create an index.pug as the starting point of the template:

doctype
html
  head
    title Index page
  body
    div Header Section
    div Body Section
      div Jumbotron
        div Signup
      div Proposition
      div
        div Benefit 1
        div Benefit 2
        div Benefit 3
      div
        div Topics 1
        div Topics 2
        div Topics 3
      div Call To Action
        div Signup
    div Footer Section

Then we need to add a route for the index page.

app.get('/', function (req, res, next) {
  return res.render('index', {});
});

res.render is how Express leverage the created templates. The section parameter is an object holding the values for substitution (called locals in Express), but since we aren't defining any variables in our template right now, it's left empty.

Remember to keep tsc running in the background so any changes you have in your .ts files will get incrementally compiled, so you just need to continue to restart the service (we'll fix this annoyance later).

./node_modules/.bin/tsc --watch

Then you can restart the web server:

npm start

Open http://localhost:800/ and you'll see a plain looking html being created from the above template.

Landing Page from the template

The plain look is due to the lack of custom styling, so let's tackle that as well!

Creating a Style Sheet with Stylus and Bootstrap

Anyone who has work with CSS for a while would quickly recognize that it can be difficult to reuse CSS snippets, and some people have come up with solutions to tackle this issue - instead of writing in CSS directly, write in a "better CSS" that can be compiled into the final CSS. This is the same solution for CSS as the HTML template engines are for HTML, and they are called CSS preprocessors, as they focus on "extending" the CSS language with features, rather than creating a completely different language (though some of them do look quite different).

The most popular CSS preprocessors today are:

They all extend CSS with variables, mixins, etc. Though there are tradeoffs among them, you can't really go wrong with any of them today, so the choice is largely subjective, and for Module Core we'll going with Stylus.

Correspondingly, Bootstrap is a very popular CSS framework and is fine as a starting point, so we'll leverage that as well. There is a stylus-based bootstrap package, so we'll use that.

npm install --save stylus nib bootstrap-styl 
mkdir stylus # for holding the source stylus
mkdir static/css # for holding 

Then let's create a main.styl in the stylus folder, and add the following:

@require 'bootstrap/index' // requires the whole bootstrap package.

Let's then compile the stylus spreadsheet into the final CSS (and we'll keep it running in the background as well):

./node_modules/.bin/stylus -o static/css/ -u nib -u bootstrap-styl --watch --compress ./stylus/main.styl 

And let's add the stylesheet reference to index.pug:

    // under head
    link(rel='stylesheet',href='/css/main.css')

With the addition of the stylesheet, the page now looks a bit different (fonts and margins):

Landing page with Bootstrap

That's nice, but not enough for what we are looking for. To fully take advantage of bootstrap, we'll need to convert our template to utilize bootstrap styles, and add additional styles to match what we are looking for.

Let's update our template to the following:

doctype
html
  head
    title Index page
    link(rel='stylesheet',href='/css/main.css')
    // for mobile
    meta(name='viewport',content='width=device-width, initial-scale=1')/
  body
    // header section
    nav.top-nav
      div.container-fluid
        div.navbar-header
          a.navbar-brand(href='/') Header Section
    // body section
    div.container-fluid
      div.jumbotron Jumbotron section
        div.sign-up Signup
      div.proposition Proposition section
      div.benefits
        div.benefit Benefit 1
        div.benefit Benefit 2
        div.benefit Benefit 3
      div.topics
        div.topic Topics 1
        div.topic Topics 2
        div.topic Topics 3
      div.call-to-action Call to Action
        div.sign-up Signup
    // footer section
    div.footer Footer Section

Then we can also update our stylesheet with:

body
  nav.top-nav
    @extends .navbar
    @extends .navbar-default
    background: #ffffff
    background-size: cover
    margin-bottom: 0
  
  > .container-fluid

    .jumbotron
      @extends .row
      background: #ffffff
      background-image: radial-gradient(farthest-corner at 45px 45px , #ace 0%, rgba(255, 255, 255, 0) 100%)
      background-size: cover
      border-radius: 0
      margin: 0
    
    .proposition
      @extends .row
    
    .benefits
      @extends .row

      .benefit
        @extends .col-sm-4 
        @extends .col-md-4

    .topics
      @extends .row

      .topic
        @extends .col-sm-4 
        @extends .col-md-4 

    .call-to-action
      @extends .row 
      margin-left: 5%
      margin-right: 5%
      margin-bottom: 3em

  .footer
    @extends .row
    background: #ffffff
    background-image: radial-gradient(farthest-corner at 45px 45px , #ace 0%, rgba(255, 255, 255, 0) 100%)
    background-size: cover
    border-radius: 0
    margin: 0
    height: 8em

With the above changes, our page starts to look a bit like the original landing page:

Landing Page with additional styles

There are more things we can adjust, but to do so, we should start to consider how to populate the content to each of the components. We'll tackle that in the next session.

Conclusion

In this session, we have start the larger goal of creating a landing page. To get there, we have accomplished the following: