Jekyll Theme Primer Spec

Primer Spec Dev Onboarding

Contents

Purpose

This document aims to help potential contributors (and curious developers) understand the structure of the repository and how all the code fits in together.

Jekyll

Jekyll is a static site generator built in Ruby — it features reusable layouts, extensible page generation and easy content maintenance. Most importantly:

Primer Spec is a remote Jekyll theme. The README describes how users can integrate Primer Spec as a remote theme with their Jekyll website.

Primer Spec makes the spec layout available to website pages. You can find the layout template at _layouts/spec.html — it generates scaffolding around the content, including required stylesheets and scripts. The spec layout also accepts a number of page-specific or site-wide configuration options, usually to make these available to the JavaScript that powers Primer Spec. To render a Sitemap, the spec layout exposes a list of all available Jekyll pages to the JavaScript as well.

Fun fact: The content on the page is actually always hidden when the page loads! The JavaScript uses this hidden content later while rendering the page.

The assets directory is versioned — each version directory contains compiled Primer Spec JavaScript and a basic CSS stylesheet that simply imports the Primer Spec SCSS partial from _sass/jekyll-theme-primer-spec.scss.

SCSS Styles

Primer Spec is built on top of the Primer theme, the default theme for GitHub Pages. Both themes use Primer CSS, GitHub’s design framework. The main Primer Spec SCSS partial at _sass/jekyll-theme-primer-spec.scss imports the base Primer CSS styles, the base Primer Spec styles (_sass/spec/base.scss) and the base styles for syntax highlighting (_sass/spec/rouge.scss).

If you’ve never used SCSS before: It’s like regular CSS (and in fact compiles to regular CSS), but provides features like selector-nesting, compile-time variables, mixins and utility methods. VSCode’s support for SCSS is quite nice. I especially like how hovering over any selector shows an example visualizing the nesting of selectors and what elements would be affected by a block of styles.

Primer Spec’s subtheme styling is achieved via dynamic CSS variables that can be easily manipulated by JavaScript. The variables are used throughout spec/base.scss, and a full list of CSS variables is defined in JavaScript (src_js/subthemes/Subtheme.ts). These variables are initialized to the default light theme’s values, with all other subtheme variable configurations defined in the JavaScript.

When subthemes are changed, the JavaScript updates CSS variable values on the body element, which affects the styling of the entire page.

Syntax highlighting styles

When content is stored as Markdown and has code blocks, the Markdown parser (kramdown) uses Rouge to convert the code blocks to semantic HTML to enable syntax highlighting. Rouge creates HTML <pre> blocks and encapsulates different semantic elements of the code (tokens, numbers, functions, classes, etc.) with different class names. A Rouge/Pygments stylesheet can then be used to color and style the code block.

The original Primer theme included default styles for Rouge syntax highlighting in _sass/rouge.scss. However, since Primer Spec supports a dark mode code block theme, these styles need to be specified as dynamic CSS variables instead.

A full list of Rouge class names can be found in _sass/spec/rouge.scss. The file uses fancy SCSS features like iteration and mixins to initialize CSS variables for each of the Rouge class names and style properties. The corresponding JS definition can be found in src_js/subthemes/Subtheme.ts.

Similar to other subtheme styles, when the subtheme is changed, Rouge CSS variables are updated on the body element, which affects code block styling on the page.

JavaScript

The JavaScript in src_js powers the many dynamic features of Primer Spec, including generating the Sidebar, switching subthemes, enabling a decent mobile viewing experience and making pages printable.

Primer Spec uses Preact, a lightweight alternative to React. The scripts are written in TypeScript and are compiled by Webpack into a single JavaScript bundle in the assets directory. We also use ESLint and Prettier to keep the code clean and formatted.

The entry point is in src_js/main.tsx. It injects necessary stylesheets, performs some initialization, and inserts the Primer Spec Preact component. Preact components are located in the components directory, subtheme-changing logic are in the subthemes directory and constants in Config.ts. Hopefully, the directory structure and the inline comments should help navigate the code.

Don’t forget to open the VSCode Workspace in this repository and use VSCode’s powerful features.

Jest is also available for writing unit and integration tests.

To analyze JS dependencies and investigate how much each dependency and file contributes to the final JS bundle size, run script/server, then visit http://localhost:4000/report.html.

Demo pages

To test Primer Spec functionality, the repository features several different demo pages. (This repository is itself a Jekyll site!)

index.md features a mock project specification — a long page with several sections.

The demo directory contains several other pages that can be used to test other behaviors of Primer Spec, like handling of long headings, page configuration options and task lists.

(The links above take you to the source Markdown files. To actually view the pages rendered with Primer Spec, you’ll need to build the site locally or visit one of the deployments.)

Scripts

The script directory contains various utility scripts for use during development. Most importantly:

Stable and Nightly builds

Since the develop branch is ahead of the main branch for months at a time, Primer Spec deploys to two different websites to preview each branch.

Every open PR is also deployed to the private server via Primer Spec Preview to dynamically interact with proposed changes. The link is made available in a PR comment.

Dev setup

See the CONTRIBUTING docs for actual setup instructions.

Contributing to Primer Spec requires a Ruby environment and a NodeJS environment setup. script/bootstrap installs the Ruby dependencies from Gemfile and the JS dependencies from package.json. The script also installs the pre-commit hook, which ensures that the assets directory is not accidentally modified by commits. (We update the pre-committed JavaScript bundles just before deploying the main branch for use by external websites.)

For everyday development, use script/server, which builds and serves the site at https://localhost:4000. Open the VSCode workspace primer-spec.code-workspace and install the recommended extensions for easy ESLint and Prettier formatting.

For every Pull Review and commit to develop/main, GitHub Actions runs script/cibuild to sanity-check that Jekyll can build the website, and also runs linters and any Jest tests. Testing is currently manual — efforts to improve our automated testing of Primer Spec would be appreciated.

Versioning & backwards compatibility

Starting with version v1.2, Primer Spec guarantees backwards-compatibility — when an external website is built using a specific version of Primer Spec, pages are guaranteed to appear visually similar as long as the site is not rebuilt. Primer Spec’s versioning system is further described in the CONTRIBUTING docs.

When an external site is built, the spec layout is used so that the HTML never changes. However, the pages use JS and CSS assets that are hosted on https://eecs485staff.github.io/primer-spec. Hence, to guarantee backwards-compatibility:

To facilitate the above requirements, the assets directory is versioned. Before adding minor or major changes, the previous version’s assets must be archived by using script/version bump.