Jekyll

Jekyll is a static site generator for which CS50 has its own theme, which comes with its own layout, includes, configuration options, plugins, and custom syntax. It also supports custom CSS via Sass.

The theme uses Bootstrap as well as other third-party libraries.

To use CS50’s theme, it suffices to configure your Gemfile as follows:

source "https://rubygems.org/"

gem "jekyll-theme-cs50", group: :jekyll_plugins, git: "https://github.com/cs50/jekyll-theme-cs50", branch: "develop"

CS50’s theme automatically enables some third-party plugins as well, per PLUGINS in https://github.com/cs50/jekyll-theme-cs50/blob/develop/lib/jekyll-theme-cs50/constants.rb. You can enable other plugins in the Gemfile itself.

Layout

On larger screens (e.g., laptops and desktops), CS50’s theme lays out pages as follows, wherein aside represents the site’s sidebar, main represent’s a page’s content, and alert represents an optional alert:

+------------------------+
| alert                  |
+------------------------+
|        |               |
|        |               |
| aside  | main          |
|        |               |
|        |               |
+------------------------+

Within each aside is a header, a nav, and a footer:

+------------------------+
|                        |
+------------------------+
| header |               |
|        |               |
| nav    |               |
|        |               |
| footer |               |
+------------------------+

On smaller screens (e.g., phones), the aside is collapsed and relocated up top:

+---------------+
| alert         |
+---------------+
| aside         |
+---------------+
|               |
|               |
| main          |
|               |
|               |
+---------------+

When the aside is collapsed, only the site’s header is visible:

+---------------+
|               |
+---------------+
| header        |
+---------------+
|               |
|               |
|               |
|               |
|               |
+---------------+

But a button, when clicked, reveals the nav and footer:

+---------------+
|               |
+---------------+
| header        |
|               |
| nav           |
|               |
| footer        |
+---------------+
|               |
|               |
|               |
|               |
|               |
+---------------+

Includes

CS50’s layout automatically includes these files if they exist in _includes:

  • alert.md, the content of which will appear in a top-level alert, if site.cs50.alert is configured

  • header.md, the content of which will appear at the top of the theme’s aside

  • nav.md, the content of which will appear in the middle of the theme’s aside

  • footer.md, the content of which will appear at the bottom of the theme’s aside

Configuration Options

CS50’s theme can be configured via a cs50 key in _config.yml (or another YAML file), the value of which is an object with these keys:

Some of those keys have default values, as do other top-level keys, per DEFAULTS in https://github.com/cs50/jekyll-theme-cs50/blob/develop/lib/jekyll-theme-cs50/constants.rb.

And some top-level keys have fixed values that cannot be changed in _config.yml (or another YAML file), per OVERRIDES in https://github.com/cs50/jekyll-theme-cs50/blob/develop/lib/jekyll-theme-cs50/constants.rb.

alert

To position an alert at the top of every page in a site (so as to catch users’ attention), use an alert key like

cs50:
  alert: warning

(wherein warning can also be any other type of alert) and create _includes/alert.md, the contents of which will be rendered within the alert.

To configure the alert as dismissible, such that the user can dismiss it once read, configure _config.yml with YAML like

cs50:
  alert: warning dismissible

instead. A user’s localStorage will be used to remember which alerts the user has dismissed.

assign

To define a global variable (e.g., foo) with some value (e.g., bar), user an assign key like:

cs50:
  assign:
    foo: bar

You can then access that variable in pages with syntax like:

{{ foo }}

Or use it conditionally in pages with syntax like:

{% if foo == "bar" %}
    baz
{% endif %}

We use assign when generating multiple sites from one collection of pages (e.g., for Harvard College and Harvard Extension School), as by defining

cs50:
  assign:
    college: true

in one configuration file (e.g., _college.yml) and

cs50:
  assign:
    extension: true

in another configuration file (e.g., _extension.yml) and then using conditional logic in pages like:

{% if college %}
    This is CS50
{% elsif extension %}
    This is CSCI E-50
{% endif %}

We then build the sites separately with commands like:

bundle exec jekyll build --config _config.yml,_college.yml --destination _site/college/

and:

bundle exec jekyll build --config _config.yml,_extension.yml --destination _site/extension/

description

To define the site’s description, use a description key like:

cs50:
  description: Introduction to Computer Science

The value of description will then be used as the site’s og:description value.

locale

To define the site’s locale (e.g., French), use a locale key like:

cs50:
  locale: fr

wherein the value of locale is a language subtag. By default, the value of locale is assumed to be en.

title

To define the site’s title, use a title key like:

cs50:
  title: CS50

The value of title will then be used in the site’s title tags and og:title values, prefixed with each page’s own title.

tz

To define the site’s time zone (e.g., Pacific Time), use a tz key like

cs50:
  tz: America/Los_Angeles

wherein the value is a TZ database name. By default, the value of tz is assumed to be America/New_York.

The value of tz is used by CS50’s local tag.

Plugins

after

An after block can be used to hide content until a specific date and time. The content of the block can be HTML, Markdown, or text.

For instance,

{% after "2001-01-01 00:00:00" %}
    It is the 21st century
{% endafter %}

would not display “It is the 21st century” until it is the 21st century (in the site’s time zone).

Note that the content of the block is always present in the browser’s DOM and is only hidden via CSS, so this block should not be used to hide sensitive content (e.g., a link to an otherwise accessible exam).

alert

An alert block can be used to render an alert. The block expects one argument, the type of alert to render, which can be any of:

  • primary

  • secondary

  • success

  • danger

  • warning

  • info

  • light

  • dark

The content of the block can be HTML, Markdown, or text.

before

A before block can be used to show content until a specific date and time. The content of the block can be HTML, Markdown, or text.

For instance,

{% after "2001-01-01 00:00:00" %}
    It is the 20th century
{% endafter %}

would display “It is the 20th century” until it is no longer the 20th century (in the site’s time zone).

Note that the content of the block is always present in the browser’s DOM and is only hidden via CSS, so this block should not be used to hide sensitive content (e.g., a link to an otherwise accessible exam).

calendar

A calendar tag can be used to embed a Google Calendar in “agenda” mode. The tag expects one argument, the “Calendar ID” (i.e., src) of a Google Calendar, which appears under “Integrate calendar” in calendar settings. The calendar must be public. For instance,

{% calendar en.usa%23holiday@group.v.calendar.google.com %}

would embed the Google Calendar whose Calendar ID is en.usa%23holiday@group.v.calendar.google.com.

local

A local tag can be used to render a date and time in the user’s own time zone, based on their device’s clock. The block expects one argument, a quoted date and time in YYYY-MM-DD HH:MM format, which is assumed to be in the time zone specified by site.cs50.tz, the value of which is a TZ database name, the default value of which is America/New_York.

For instance, if the value of site.cs50.tz is America/New_York (by default or otherwise), then

{% local "1970-01-01 00:00" %}

would be parsed as representing midnight, Eastern Time, on January 1, 1970. But it would be rendered for users in their own time zone.

If tags should be assumed to be in some other time zone (e.g., Pacific Time), then _config.yml should be configured with YAML like:

cs50:
  tz: America/Los_Angeles

The format in which a date and time should be rendered can be configured with YAML like:

cs50:
  local:
    day: numeric
    hour: numeric
    minute: numeric
    month: long
    timeZoneName: short
    weekday: long
    year: numeric

Default values for those keys are defined in https://github.com/cs50/jekyll-theme-cs50/blob/develop/lib/jekyll-theme-cs50/constants.rb. Other possible values for those keys are defined by the ECMAScript Internationalization API.

Setting the value of a key to null will remove the key from the plugin’s output. For instance,

cs50:
  local:
    year: null

would remove the year from the default format for dates and times.

spoiler

A spoiler block can be used to present a spoiler (e.g., a hint) on which a user must click in order to see more. The block expects one argument, a string on which the user can click; the content of the block can be HTML, Markdown, or text that the user will then see.

For instance,

{% spoiler "Hint" %}
    42
{% endspoiler %}

would be rendered in such a way that a user has to click “Hint” in order to see “42”.

video

A video tag can be used to embed a YouTube video. The tag expects one argument, the URL of the video to embed. For instance,

{% video https://www.youtube.com/watch?v=xvFZjo5PgG0 %}

would embed https://www.youtube.com/watch?v=xvFZjo5PgG0.

The tag can also be used to embed the same video using CS50 Video Player instead. For instance,

{% video https://video.cs50.io/xvFZjo5PgG0 %}

would embed https://video.cs50.io/xvFZjo5PgG0 instead.

Syntax

CS50’s theme supports all of Jekyll’s and Kramdown’s own syntax and also some of its own for:

Lists

Normally, (unordered) lists can be implemented in Markdown with any of *, +, and -, but CS50’s theme treats those symbols as distinct:

  • a * will be rendered as a bullet as usual

  • a + will be rendered as a subtree that’s collapsed by default

  • a - will be rendered as a subtree that’s expanded by default

For instance, Markdown like

* foo
- bar
    * qux
+ baz
    * quux

would be appear initially has having three top-level bullets (foo, bar, and baz), with qux also visible, but if baz were clicked, quux would appear too.

Subtitle

So that pages can have not only titles but subtitles, CS50’s interprets a ## that immediately follows a # heading, with no content in between, as representing a subtitle. For instance, Markdown like

# Title
## Subtitle

would be rendered in such a way that “Subtitle” is clearly a subtitle.

Sass

CS50’s theme uses Sass, which means you can customize Bootstrap as well as CS50’s own CSS by creating assets/page.scss with, at least, these lines, along with your own:

---
---

@import "page";

For instance, to override the theme’s crimson colors with shades of blue, you could use:

---
---

$link-color: #286dc0;

@import "page";

aside {
    background-color: #00356b;
}

Acknowledgements

CS50’s theme is inspired by Hyde.