Jekyll Theme Primer Spec
Primer Spec Dev Onboarding
Contents
- Purpose
- Jekyll
- SCSS Styles
- Syntax highlighting styles
- JavaScript
- Demo pages
- Scripts
- Stable and Nightly builds
- Dev setup
- Versioning & backwards compatibility
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:
- Content is typically stored as either HTML or Markdown. Markdown files are automatically converted to HTML using kramdown.
- Content can specify “front matter” which can contain page configuration data. For instance, a page can specify that it should be generated using a specific “layout”.
- Layouts are templates available in a site’s
_layouts
directory. They can specify additional front matter and can use powerful Liquid templating logic. - Static assets like CSS, JS and images are customarily stored in an
assets
directory. Any SCSS files in theassets
directory will be compiled to CSS at build time. - SCSS partials are stored in the
_sass
directory. - When a Jekyll site uses an external “theme”, its assets, layouts and SCSS partials are merged with those of the theme.
- Jekyll themes are traditionally distributed as Ruby gems. However, with the introduction of GitHub Pages, Ben Balter created
jekyll-remote-theme
, a Jekyll plugin that allows any public GitHub repository to be used as a Jekyll theme. - Jekyll Site configuration options are specified in
_config.yml
.
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:
bootstrap
initializes your dev environment. It also installs the Git pre-commit hook from.githooks/pre-commit
, which ensures that the assets directory is not accidentally modified.server
starts the Jekyll server at http://localhost:4000 and rebuilds whenever files change (with some exceptions).cibuild
is what’s run by GitHub Actions for every Pull Request. It can also be run locally.version
bumps or freezes the Primer Spec version (further reading)ci-site-preview-build
is used to generate previews of each Pull Request. You would normally never run this script locally.
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.
- The
main
branch hosts the latest stable version, and is deployed via GitHub Pages to https://eecs485staff.github.io/primer-spec/. This site also hosts the CSS and JS assets used by most external Primer Spec pages. - The
develop
branch has all the latest changes and is deployed to a private server at https://preview.sesh.rs/previews/eecs485staff/primer-spec/develop-preview/. (This link is also available from the project README page.) It’s automatically updated on every push to thedevelop
branch and is rebuilt at least once a month.
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:
- The CSS files must not change drastically for a given minor version.
- Changes to the JS/CSS files must not depend on changes to the HTML templates.
- The URL for those assets must remain stable and unchanged.
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
.