Skip to content

How a Spectate project works

Jason Kao edited this page Jan 2, 2021 · 10 revisions

Spectate has two main functions:

  1. Copy template files into a folder (this folder becomes a "Spectate project")
  2. Publish a Spectate project onto columbiaspectator.com

This page walks through the file structure of a Spectate project and where and how to code. Publishing instructions can be found on the main README.

The most important directory in a Spectate project is src/. It contains all your CSS, JavaScript, and HTML (including ai2html output).

The main driver of a Spectate web page is the src/index.html file. It uses data from an external source to create a web page. We reporters provide that data (we'll get to how later). The most important piece of data used to create the web page is an array of elements called body. (An element could be a paragraph, image, graphic, etc.) Spectate loops through the array, and for each element adds the appropriate HTML to the web page.

For example, suppose you want your article to contain a paragraph, an image, some HTML, and then another paragraph. Your body array would consist of those four elements in that order. When Spectate loops through body, it sees the paragraph element first, so it stacks the following HTML snippet onto the page:

<p class="paragraph">{ element data }</p>

When Spectate sees the image element, it adds this HTML:

<img src="{ element data }"/>

When Spectate sees the next element, it will add this HTML:

{ element data }

Finally, it adds another <p> element.

Somehow, we have to tell Spectate what the type of each element is. That's why each element is actually not just a paragraph or image URL, but an object with two keys: type and value. The value of type describes the kind of element we are trying to add (e.g. paragraph, image, graphic). The value of value is the actual data associated with that element (e.g. words, image URL, custom HTML).

To define the body array, Spectate uses a Google Doc that is written in a special syntax called ArchieML. ArchieML allows reporters to write readable and minimally-structured Google Docs that can be automatically parsed into an array of type-value objects. Read through the ArchieML introduction up to and including the "freeform arrays" section.

So what we want is body to be a freeform array. For example, a Google Doc with this content…

[+body]

Leadin. Something to draw the reader in. Graphics is the only visuals and words desk at the Spectator.

Nutgraf. What is the meat of the story? Why does this matter?

{.graphic}
  asset: cool-map.html
{}

[]

…would be converted into this data:

{
  "body": [
    {
      "type": "text",
      "value": "Leadin. Something to draw the reader in. Graphics is the only visuals and words desk at the Spectator."
    },
    {
      "type": "text",
      "value": "Nutgraf. What is the meat of the story? Why does this matter?"
    },
    {
      "type": "graphic",
      "value": {
        "asset": "cool-map.html"
      }
    }
  ]
}

As seen in the Spectate Template Doc, we customarily also define variables such as headline, byline, and date. The resulting data becomes:

{
  "headline": "The U.S. and Ivy League schools were late to respond to COVID-19.",
  "date": "October 17, 2019",
  "byline": "By <a href=\"https://www.columbiaspectator.com/contributors/Charlotte-Li\">Charlotte Li</a>\nEdited by <a href=\"https://www.columbiaspectator.com/contributors/Jun-Yi-Zhang\">Jun Yi Zhang</a>",
  "top": [],
  "body": [ ... ]
}

The spectate download command downloads and parses your Google Doc, exposing all the top-level keys (e.g. body, headline) as variables that are available in src/index.html.

Take a look at the midsection of src/index.html:

<each loop="item in body">
  <switch expression="item.type">
    <case n="'text'">
      <include src="partials/paragraph.html"></include>
    </case>
    <case n="'subhed'">
      <include src="partials/subhed.html"></include>
    </case>
    <case n="'image'">
      <include src="partials/image.html"></include>
    </case>
    <case n="'graphic'">
      <div class="graphic">
        <if condition="item.value.asset === 'graphic-name.html'">
          <include src="graphic-name.html"></include>
        </if>
      </div>
    </case>
  </switch>
</each>

We see that index.html loops through the body variable. Each element of the body array is called item. We use a switch statement on the type property of every item in body to include the HTML snippet that corresponds to each type of element. (Note that HTML doesn't have for loops or variables. index.html is actually written in PostHTML, an extension of HTML that makes life easier.)

Note that we barely include any of the actual HTML for a paragraph/image/etc. We instead reference partials: small snippets of HTML that can be dynamically reused. Generally speaking, when you write new code, you should include separate small HTML files rather than modifying index.html.

Note that the value of a graphic element needs to be an object with one field, "asset", which is the name of your custom HTML file. Also note that if an element of the body array has type "graphic", we can't just include a file with {{ item.value.asset }} as the value. This is due to some peculiarities about the order in which PostHTML transforms all this into normal HTML. Thus, you have to manually enter the filename of each HTML file.

Clone this wiki locally