Create a simple site in Origami

A hands-on walkthrough

You don’t need to install anything to complete this tutorial. If you prefer a conceptual introduction first, see Hello, world.

Note: The Glitch service that is used to host this tutorial project has had issues updating its servers so that they can run the latest version of Origami. Some notes below indicate places where the current Origami syntax has changed from what it shown.

Scenario

One day your team decides:

We need an “About Us” site! The main page should list the people on our team, with thumbnail photos and links to separate pages for each person. A person’s page should show their name and a full-size photo.

Open the sample About Us site and click on a few pages to get a feel for it. (This would typically be part of a larger site, but for illustration purposes we’ll consider it a site on its own.)

If you’re the kind of person who can write spreadsheet formulas, you can use the Origami language to build a site like that.

Start

Open the tutorial project on Glitch, a free web-based code editor.

You can use any web tool with Origami; Glitch is used here because it’s free and has no setup.

Click the Remix button (or Remix to Edit, either works) to create your own copy of the project.

You’ll see a list of files on the left and the currently-open file (the project’s ReadMe) in the center.

Click the Preview button at the bottom of the window, then Open Preview Pane to open a preview pane on the right.

The page in the preview pane says: Hello

Edit a simple page

You’ll define the complete set of pages and other resources your site will need in an Origami file with a .ori extension. This project is configured to serve the site defined in src/site.ori.

In the src folder, open site.ori:

{
  index.html = "Hello"
}

Everything between the { } curly braces defines the top level of the site. For now, this defines a single thing you can think of as a “file” even though it’s not stored anywhere. The name or key for this file is “index.html”. The contents or value of this file is the text “Hello”.

You’re going to work on this site.ori file so that it creates the About Us site. The index.html page will eventually become the main About Us page.

Try it: Edit the quoted text in the formula for index.html to give it more content, like: Hello, world!

After a moment, the Glitch preview window should refresh to show: Hello, world!

View your site as a tree

Origami lets you visualize and explore your site as a hierarchical tree of pages and other resources.

Click the Preview button at the bottom of the window, then click Preview in a new window. This will open your site in a new window (or tab).

In the browser address bar for that new window, add /!@svg to the end of the URL. The new URL should look like https://your-project-name.glitch.me/!@svg

You’ll see your site visually represented as a tree with just one branch:

g index.html Hello, world! ->index.html index.html

The little circle represents the overall tree, and the box represents the index.html file.

In the tree diagram, click the box for index.html to view it.

Navigate back to the tree diagram.

Leaving the tree diagram open, switch back to the main Glitch window. You’ll return to this tree diagram occasionally to view the structure of your evolving site and to explore the individual pages.

Use a template to create text

The index.html file is currently defined with a short quoted string. You can create larger, more realistic HTML pages using templates. A template is a document with placeholders that will be filled with data.

For this tutorial, you’ll use the template system built into Origami, but Origami can also work with other template systems.

View the file src/greet.ori. Glitch shows only a single file at a time, so opening greet.ori will replace site.ori in the editor window.

=`<p>Hello, <strong>{{ _ }}</strong>!</p>`

This Origami template starts with an = equals sign and encloses some HTML with ` ` backticks.

Inside the backticks, the placeholder marked with {{ }} curly braces contains an Origami expression. In this case, the _ underscore tells Origami to insert any text passed to the template into the HTML at that point.

Note: The Origami syntax for template placeholders recently changed from {{ }} to ${ }. For projects outside Glitch, use the newer ${ } style in templates.

You can call this template from an Origami formula.

Try it: In site.ori, update the formula for index.html to remove the quoted string, and instead call the greet.ori template and pass it the text "world".

{
  index.html = greet.ori("world")
}

When someone visits index.html, Origami will now generate the HTML for it:

<p>Hello, <strong>world</strong>!</p>

So the page ends up with “world” in bold: Hello, world!

When you call greet.ori in a formula like this, Origami searches the current scope for that name. Origami will find the src/greet.ori template file and use it to create the home page.

Defining the team data

Data in Origami projects can come from pretty much anything. This sample project stores the data for your team members in a file format called YAML, but it could just as easily use another format called JSON, or some other data file format, or data sitting on a server.

Open the team data file in src/teamData.yaml:

- name: Alice
  image: van.jpg
  location: Honolulu
  bio: After working as a manager for numerous startups over the years, I
    decided to take the plunge and start a business of my own.
- name: Bob
  image: kingfisher.jpg
  location: Los Angeles
  bio: Having been an art student for 11 years, I constantly explore various
    disciplines to incorporate artistic pursuits into product design studies.
- name: Carol
  image: venice.jpg
  location: Venice
  bio: I open the line of communication between clients, customers, and
    businesses to get projects done.

This defines an array of person records but this data is too boring!

In teamData.yaml, replace the people’s names with your name and the names of family or friends.

Formulas can extract data

In Origami you can use slash-separated paths to extract information out of any hierarchical source, whether it’s a file system folder or data like your team information.

Try it: In site.ori, update your formula for index.html to pass the name of the first team member to greet.ori. Array indexes start with zero, so /0/name will get the name of the first person.

{
  index.html = greet.ori(teamData.yaml/0/name)
}

The preview should show something like: Hello, Alice!

Incorporate data into your site’s tree

You can incorporate folders and other sources of hierarchical data into your site’s tree. For example, you can include all the data in teamData.yaml into a browsable part of your site.

Try it: Update site.ori to add a formula that defines team as equal to teamData.yaml/ (with a trailing slash):

{
  index.html = greet.ori(teamData.yaml/0/name)
  team = teamData.yaml/
}

In the tree diagram window, refresh the page to confirm that the tree now includes an team area with all the data from teamData.yaml. But for the team area to be useful, you’ll need to transform that raw data into presentable HTML.

Creating a virtual folder with a map

There are several places in this web site where you want to transform one set of things and into a new set of things:

  1. For each team member in teamData.yaml, you want a page in the team area.
  2. For each team member in teamData.yaml, you want a tile on the main About Us page.
  3. For each image like images/van.jpg, you want a corresponding thumbnail image like thumbnails/van.jpg.

You can address all these situations in Origami with a map in the computer science sense of the word: an operation performed on every item in a collection to produce a new collection.

You can think of the result of a map as a virtual folder — a set of things you can browse and work with, but which aren’t stored anywhere. This is an efficient way to create an entire area of a site from existing data or files.

Let’s start by mapping the people defined in teamData.yaml: for each person, we’ll create a tiny page in the team area.

Try it: In the Glitch editor window, update the formula in site.ori for team to:

{
  index.html = greet.ori(teamData.yaml/0/name)
  team = @map(teamData.yaml, =_/name)
}

This formula calls a built-in function called @map. All built-in functions start with an @ sign.

This team formula says: starting with the tree of structured data in teamData.yaml, create a new tree. For each person in the data, evaluate the expression =_/name, which gets the name field of the person being operated on.

If you know JavaScript: the expression =_/name is like an arrow function: (_) => _.name

So the team formula transforms the team data into a corresponding tree of just the names:

g 0 ->0 0 1 ->1 1 2 ->2 2 0/name Alice 0->0/name name 0/image kingfisher.jpg 0->0/image image 1/name Bob 1->1/name name 1/image beach.jpg 1->1/image image 2/name Carol 2->2/name name 2/image venice.jpg 2->2/image image
g 0 Alice ->0 0 1 Bob ->1 1 2 Carol ->2 2
Tree structure of teamData.yaml
Mapped tree of names

In the tree diagram window, refresh the page to confirm that the tree now includes an team area with the names from teamData.yaml.

g index.html <p>Hello, <strong>Alice</strong>!</p> ->index.html index.html team ->team team team/0 Alice team->team/0 0 team/1 Bob team->team/1 1 team/2 Carol team->team/2 2

Use a template in a map

The formula you give to @map can be as complex as your situation requires.

Try it: In the Glitch editor window, in site.ori, update the expression =_/name so that, instead of just returning a name, it calls the greet.ori template and passes in that person’s name:

{
  index.html = greet.ori(teamData.yaml/0/name)
  team = @map(teamData.yaml, =greet.ori(_/name))
}

In the tree diagram window, refresh the page to see the updated team area.

g index.html <p>Hello, <strong>Alice</strong>!</p> ->index.html index.html team ->team team team/0 <p>Hello, <strong>Alice</strong>!</p> team->team/0 0 team/1 <p>Hello, <strong>Bob</strong>!</p> team->team/1 1 team/2 <p>Hello, <strong>Carol</strong>!</p> team->team/2 2

Pull in more resources

The src folder has two real subfolders you’ll want to include in the tree for your site:

  • assets contains a stylesheet and icon
  • images contains sample images you can use to represent your team members

You can pull a real folder or file into your tree by writing its name on a line by itself.

Try it: Update site.ori to add lines that pull in the assets and images folders.

{
  index.html = greet.ori(teamData.yaml/0/name)
  team = @map(teamData.yaml, =greet.ori(_/name))
  assets
  images
}

Switch to tree diagram window and refresh it to see the updated site structure.

Your site now includes both real files (the assets and images) and virtual files (the greetings in the team area).

Formulas can call JavaScript

You can do a lot in Origami without JavaScript, but JavaScript programmers can extend Origami with JavaScript. We’ll briefly look at that; you won’t need to know JavaScript to complete this step.

In the Glitch editor window, view the images in the src/images folder. Each person in teamData.yaml identifies one of these full-size images as a profile photo.

For each full-size image, you want to produce a corresponding thumbnail image for the main About Us page. Instead of using an image-editing app to create a real folder of thumbnail images, you can create virtual thumbnail images on demand.

View the file src/thumbnail.js. This contains a small JavaScript function which can invoke an image-processing library to generate a small thumbnail copy of an image.

import sharp from "sharp";

export default function thumbnail(imageBuffer) {
  return imageBuffer instanceof Buffer
    ? sharp(imageBuffer).resize({ width: 200 }).toBuffer()
    : undefined;
}

Try it: In site.ori, add a new formula for small.jpg that calls thumbnail.js as a function and passes in the file images/van.jpg.

{
  index.html = greet.ori(teamData.yaml/0/name)
  team = @map(teamData.yaml, =greet.ori(_/name))
  assets
  images
  small.jpg = thumbnail.js(images/van.jpg)
}

Switch to the tree diagram window and refresh it.

In the tree diagram, click the box for the real image in images/van.jpg to preview it.

Navigate back to the diagram and click the box for small.jpg to see the same image at a smaller size. The formula you created above produces this thumbnail on demand.

Navigate back to the tree diagram.

Create a virtual folder of thumbnails

You could write formulas to create a thumbnail for each image in the images folder — but the Origami @map function lets you define the transformation of all the images with a single line.

Try it: Switch to the Glitch editor window. In site.ori, delete the formula for small.jpg and replace it with the following thumbnails formula:

{
  index.html = greet.ori(teamData.yaml/0/name)
  team = @map(teamData.yaml, =greet.ori(_/name))
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

This thumbnails formula applies the thumbnail.js function to each of the images. In that @map function, the second parameter is just the file name thumbnail.js, which is a shorthand for writing the longer form =thumbnail.js(_)

Because Origami treats real folders and virtual folders the same, you can browse your virtual folder of thumbnails.

Switch to the tree diagram window and refresh it to view your site’s updated structure.

The virtual thumbnails folder in the diagram now contains a set of thumbnail images that do not exist in any persistent form. They are potential images. If you click on one, it will be created at that moment.

Use a map inside a template

The main About Us page should display a tile for each member that links to their individual page.

In the src folder, open index.ori. This template will form the basis of the final home page.

=`<h1>About Us</h1>
<ul>
  {{ @map(_, =`
    <li>{{ _/name }}</li>
  `) }}
</ul>
`

In site.ori, you’ve already created a map of images to thumbnails, and a map of team data to a set of greetings. The index.ori template uses the same kind of map to transform the team data into corresponding bits of HTML.

The index.ori file defines two templates, an outer template and an inner template:

  • The outer template spans all lines and defines the overall page. This outer template will accept the entire collection of team data as input; that’s what the _ underscore immediately following @map will receive.
  • The inner, nested template is defined on the middle line as part of the @map. That inner template will receive a single team member at a time as input; that’s what the _ underscore in _/name will refer to. This template generates a list item containing that person’s name.

Try it: In site.ori, update your index.html formula to remove the call to greet.ori and instead invoke the index.ori template, passing in the teamData.yaml data.

{
  index.html = index.ori(teamData.yaml)
  team = @map(teamData.yaml, =greet.ori(_/name))
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

The preview now shows: the heading About Us and a bulleted list of names.

A nested template can span multiple lines

The text inside a template can be as complex as you want.

Try it: Copy and paste this fuller template into index.ori:

=`<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>About Us</title>
    <link rel="stylesheet" href="assets/styles.css">
  </head>
  <body>
    <header>
      <img class="icon" src="assets/personIcon.svg">
      <h1>About Us</h1>
    </header>
    <ul class="tileGrid">
    {{ @map(_, =`
      <li class="tile">
        <a href="team/{{ _/name }}.html">
          <img class="avatar" src="thumbnails/{{ _/image }}" alt="{{ _/name }}">
          <h2 class="name">{{ _/name }}</h2>
          <div class="location">{{ _/location }}</div>
        </a>
      </li>
    `) }}
    </ul>
  </body>
</html>
`

(Note: the last line contains a backtick character; be sure to copy that too.)

Functionally speaking, this is no more complex than the earlier template; it just has more elements.

The preview for index.html now shows a tile for each team member that includes their name and location. It also shows a thumbnail image pulled from the virtual thumbnails folder you created earlier. As far as the <img> tag above knows, that thumbnail is a real image — but actually that image is being created on demand.

Use a person template

You can use a template for the people pages in the team area too.

In the src folder, view the person.ori template:

=`<h1>{{ _/name }}</h1>`

This template displays a person’s name in a header. You can use this in the @map that defines the team area.

Try it: In site.ori, edit the team formula to replace the =greet.ori(_/name) with person.ori.

{
  index.html = index.ori(teamData.yaml)
  team = @map(teamData.yaml, person.ori)
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

Refresh the tree diagram window to see that the pages in the team area now use your person.ori template.

g 0 <h1>Alice</h1> ->0 0 1 <h1>Bob</h1> ->1 1 2 <h1>Carol</h1> ->2 2

Use people names as file names

As you’ve seen, the top-level keys in teamData.yaml are numbers, like 0 for the first person, so at the moment the team area pages are identified with numbers too. But in your final website tree, you’d like the keys in the team area to include the person’s name, like Alice.html.

So you want to transform both the keys and values of the team data. You can do this with an expanded form of the @map function.

Try it: In site.ori, update the team formula so that the second parameter is a set of options in { } curly braces. Turn the existing person.ori reference into a valueMap option.

{
  index.html = index.ori(teamData.yaml)
  team = @map(teamData.yaml, {
    valueMap: person.ori
  })
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

This will use person.ori to transform values just as before.

Now add a keyMap option that will change the keys (names) of the team pages:

{
  index.html = index.ori(teamData.yaml)
  team = @map(teamData.yaml, {
    keyMap: =_/name
    valueMap: person.ori
  })
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

Note: The Origami syntax for @map recently changed. For projects outside Glitch, use key instead of keyMap and value instead of valueMap.

Switch to the tree diagram window and refresh it to confirm that the team area is now using names instead of numbers:

g 0 <p>Hello, <strong>Alice</strong>!<p> ->0 0 1 <p>Hello, <strong>Bob</strong>!<p> ->1 1 2 <p>Hello, <strong>Carol</strong>!<p> ->2 2
g Alice <p>Hello, <strong>Alice</strong>!<p> ->Alice Alice Bob <p>Hello, <strong>Bob</strong>!<p> ->Bob Bob Carol <p>Hello, <strong>Carol</strong>!<p> ->Carol Carol
Before: pages have numbers
After: pages have names

Add an HTML extension

We want the pages in the team area to end in a .html extension because that helps indicate the type of data the files contain. One way you can do that in an Origami map is defining a keyMap with a small template.

In site.ori, update the team formula’s keyMap option to add a.html extension to the keys.

{
  index.html = index.ori(teamData.yaml)
  team = @map(teamData.yaml, {
    keyMap: =`{{ _/name }}.html`
    valueMap: person.ori
  })
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

Switch to the tree diagram window and refresh it to confirm that the team pages now have names that end in .html:

g Alice.html <p>Hello, <strong>Alice</strong>!<p> ->Alice.html Alice.html Bob.html <p>Hello, <strong>Bob</strong>!<p> ->Bob.html Bob.html Carol.html <p>Hello, <strong>Carol</strong>!<p> ->Carol.html Carol.html

Fill out the person template

The only thing left to do is complete the person.ori template.

Replace the contents of person.ori with:

=`<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>{{ _/name }}</title>
    <link rel="stylesheet" href="../assets/styles.css">
  </head>
  <body>
    <a class="headerLink" href="..">
      <header>
        <img class="icon" src="../assets/personIcon.svg">
        <h1>About Us</h1>
      </header>
    </a>
    <main class="person">
      <img class="avatar large" src="../images/{{ _/image }}" alt="{{ _/name }}" />
      <h2 class="name">{{ _/name }}</h2>
      <div class="location">{{ _/location }}</div>
      <p class="bio">{{ _/bio }}</p>
    </main>
  </body>
</html>
`

(Note: the last line contains a backtick character; be sure to copy that too.)

In the site preview, you can now click a person tile on the main About Us page to navigate to the specific page for that team member.

View the tree of the completed site

The site is now complete.

Switch to the tree diagram window and refresh it to view your site’s final structure. In that diagram (not the one below) you can click on the circles or boxes to explore what you’ve made.

g index.html <h1>About Us</h1> ->index.html index.html team ->team team assets ->assets assets images ->images images thumbnails ->thumbnails thumbnails team/Alice.html <h1>Alice</h1> team->team/Alice.html Alice.html team/Bob.html <h1>Bob</h1> team->team/Bob.html Bob.html team/Carol.html <h1>Carol</h1> team->team/Carol.html Carol.html assets/personIcon.svg assets->assets/personIcon.svg personIcon.svg assets/styles.css assets->assets/styles.css styles.css images/kingfisher.jpg [binary data] images->images/kingfisher.jpg kingfisher.jpg images/van.jpg [binary data] images->images/van.jpg van.jpg images/venice.jpg [binary data] images->images/venice.jpg venice.jpg thumbnails/kingfisher.jpg [binary data] thumbnails->thumbnails/kingfisher.jpg kingfisher.jpg thumbnails/van.jpg [binary data] thumbnails->thumbnails/van.jpg van.jpg thumbnails/venice.jpg [binary data] thumbnails->thumbnails/venice.jpg venice.jpg

To review, you’ve created this entire site with a few resources, a couple of templates, and a concise site.ori with a handful of formulas:

{
  index.html = index.ori(teamData.yaml)
  team = @map(teamData.yaml, {
    keyMap: =`{{ _/name }}.html`
    valueMap: person.ori
  })
  assets
  images
  thumbnails = @map(images, thumbnail.js)
}

Building static files

You have been viewing your About Us site using a small Origami server running in the background. Since the members of your team aren’t going to change every minute, you can turn the whole site into static files: regular files whose contents aren’t expected to constantly change.

Defining a site as static files is generally faster and cheaper than running a live web server. On services like Glitch, static file websites are free!

Glitch will build your site’s static files automatically after you stop editing the site, but you can manually trigger the build process to see it in action.

In the main Glitch editor window, click the Terminal button in the toolbar at the bottom of the Glitch window.

In the Glitch terminal, type the following command. (The $ dollar sign comes from the terminal — don’t type it.)

$ npm run build

That copies everything in your running website to a real folder called build.

In the Glitch terminal, type:

$ refresh

This refreshes the files shown in the main portion of the Glitch window.

Close the Glitch terminal.

Click the build folder on the left side of the Glitch window and view the files it contains.

In addition to copies of the real files in the assets and images folders, the build folder now contains real copies of all the virtual files you defined in site.ori:

  • A real thumbnails folder with real thumbnail versions of each image.
  • A real index.html page with HTML that includes a tile for each team member.
  • A real team folder with real HTML pages for each team member.

At some point after you close the Glitch window, Glitch will rebuild and serve these static files instead of using the Origami server. Because the static files all use native web formats, your site will be extremely fast.

View your final site

In the tree diagram window, edit the URL to remove the !@svg part.

This is how your site will look to visitors.

If you’d like to keep your site, you can create a Glitch account. Your site will have a permanent URL like https://<something>.glitch.me that you can share with other people.

If you don’t want to create an account, Glitch will keep the site for some time before removing it.

Done!

This concludes the Origami tutorial. If you’d like to try working with Origami on your own machine, you can copy the origami-start project.

You can continue exploring related topics:

  • The Origami expression language you used to write formulas and template expressions has additional features not covered in this tutorial.
  • As you were creating the About Us site, the Origami command-line interface and its included web server was working behind the scenes to let you view the site during development and to copy the virtual files to real files.
  • The conceptual framework is built on an async-tree library that lets you do everything that you did here with formulas using JavaScript instead.
  • You can implement sites completely from scratch using the async tree pattern and no library or framework at all, an approach may appeal to people who want to work as close to the metal as possible. That pattern is also a useful reference if you want to understand how Origami works under the hood.

 

Back to Overview