This repository contains the website explained-from-first-principles.com, which is built with Jekyll and published with GitHub Pages.
This document has the following sections:
- Setup with requirements and instructions to serve this website
- Development with requirements and dependencies to build, lint, and watch the source files with instructions to do the same just for:
- Documentation of custom features and common patterns:
- Articles lists the available front matter attributes
- Preview refers to websites to refresh cached previews
- Markdown documents various advanced Markdown features
- Images describes how to scale and embed images in articles
- Graphics explains how to generate and embed SVG graphics
- Math shows how to write both inline as well as block math
- PDF details how to generate a PDF of an article with a script
- Timestamps summarizes how to create and verify timestamps
- About section with information on how to contribute, used dependencies, copyright owner, chosen license, and how to contact me.
The following instructions assume that you roughly know how to use a Unix shell.
If you are using macOS,
open the Terminal located at /Applications/Utilities/Terminal.app
.
The required tools are easiest to install, upgrade and remove with a package manager.
Attention: You might have to perform some of the following steps with a user which has administrator rights at the operating system level, but I still need to verify this.
Homebrew is the most popular package manager for macOS.
To check whether Homebrew is already installed on your system,
run brew --version
.
If you have not the newest version,
you can update Homebrew with brew update
.
If Homebrew is not yet installed, install it by entering in your Terminal:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Git is a distributed version control system designed to track changes in text documents such as source code.
You can run git --version
to check whether Git is already installed
and download it otherwise.
You can also install Git with Homebrew:
brew install git
Install (or upgrade) Ruby.
In order to avoid file permission issues, install Ruby with Homebrew on macOS:
brew install ruby
Add the paths indicated by the Ruby install script to your ~/.bashrc
:
export PATH="/usr/local/opt/ruby/bin:/usr/local/lib/ruby/gems/2.6.0/bin:$PATH"
Reload your shell for the changes to take effect by opening a new window/tab
or by entering . ~/.bashrc
.
Depending on what shell you use, your configuration file can be different.
Navigate to the directory in which you would like to store this repository:
cd path/to/desired/directory
Clone this repository (or a fork thereof) either using SSH if you have an account at GitHub with an SSH key:
git clone [email protected]:KasparEtter/ef1p.git
or using HTTPS otherwise:
git clone https://github.com/KasparEtter/ef1p.git
Navigate to the root directory of this repository:
cd ef1p
Most of the following commands have to be called from this directory.
If you already cloned this repository, you can pull in the newest changes with:
git pull
Install Bundler and replace a potentially existing installation:
gem install bundler
Install all Ruby dependencies with:
bundle config set path 'vendor/bundle'
bundle install
You have to execute the first line only once.
The configuration is then stored in .bundle/config
.
This file is intentionally not under version control.
In order to run the same versions as the GitHub Pages server, update the dependencies from time to time:
bundle update
You can check the versions of your local dependencies with:
gem list
Serve the website from the root directory of this repository:
bundle exec jekyll serve --livereload
If you got no errors, you can open the website at http://localhost:4000.
If you want to serve the website in your local network:
bundle exec jekyll serve --livereload --host=0.0.0.0
Use npm run watch
as described below
to also watch for changes in scripts and styles.
The articles, scripts and styles need to be rebuilt after making changes to them.
You can check whether Node.js and npm are already installed with:
node -v
npm -v
If they are, you can update npm with:
npm install -g npm
You can also install Node.js and npm with Homebrew:
brew install node
Install the npm dependencies as specified in package.json
with:
npm install
Update all dependencies to their latest version respecting Semantic Versioning:
npm update
If you want to update all dependencies to their latest version ignoring the specified versions, you can use npm-check-updates as follows:
npm run npm-upgrade
npm install
If vulnerabilities were found, fix them with:
npm audit fix
npm run npm-upgrade
ignores upgrades for the following packages:
anchor-js
: v4.3.0 always shows the anchors in Chrome.bootstrap
: Too much effort to migrate to v5.bootswatch
: Linked to the version ofbootstrap
.react
andreact-dom
: React 18 causes the cursor to jump to the end when editing input fields. Clicking on a suggested input value no longer works either. Furthermore, losing focus on an input field no longer triggers a state update in Safari/WebKit.readable-stream
: Direct dependency only to avoid bundling issues.
Show the version of a particular direct or indirect dependency:
npm show [dependency] version
Show the tree of all direct and indirect dependencies:
npm list
All the following scripts are defined in package.json
.
Use the following script to build the articles, scripts and styles:
npm run build
Use the following script to lint the articles, scripts and styles in parallel:
npm run lint
Use the following script to watch the articles, scripts and styles simultaneously:
npm run watch
If you change a Markdown file,
it needs to be regenerated as HTML
and copied to the _site
folder:
npm run md-build
All Markdown files have to pass markdownlint
with the rules specified in .markdownlint.json
.
If you are using Visual Studio Code,
you can install this extension.
npm run md-lint
Please start a new line at least for each sentence. You can also include more line breaks according to the Semantic Line Breaks Specification.
If you want the website to reload automatically on any changes to Markdown files, you can run the following script:
npm run md-watch
Please note that all errors by the script md-watch
are ignored using 2>/dev/null
.
If you run into problems,
don't forget to remove this suppression for debugging.
I decided to do this in order to get rid of the following issues outside of my control:
vendor/bundle/ruby/2.7.0/gems/jekyll-3.8.5/lib/jekyll/convertible.rb:41: warning: Using the last argument as keyword parameters is deprecated
ERROR: directory is already being watched!
for directories innode_modules/puppeteer/
(see this page for more information).
If you change any of the Sass files in scss
,
you have to compile, prefix and minify the CSS again with:
npm run scss-build
The full and minified CSS for both the dark and the light theme are written to assets/styles
.
You have to run npm run md-build
to copy the CSS files to the _site
directory
from which the website is served
in order for them to take effect.
All Sass files have to pass stylelint
with the rules specified in .stylelintrc
.
If you are using Visual Studio Code,
you can install this extension.
npm run scss-lint
If you want to rebuild the styles automatically when you change a Sass file, you can run the following script:
npm run scss-watch
Please note that
in order for the rebuilt styles to take effect
you also have to run npm run md-watch
.
If you change any of the TypeScript files in typescript
or in one of the article folders,
you have to compile and minify them again with:
npm run ts-build
The minified JavaScript is written to assets/scripts
.
You have to run npm run md-build
to copy the JavaScript files to the _site
directory
from which the website is served
in order for them to take effect.
All TypeScript files have to pass TSLint
with the rules specified in tslint.json
.
If you are using Visual Studio Code,
you can install this extension.
npm run ts-lint
If you want to rebuild the scripts automatically when you change a TypeScript file, you can run the following script:
npm run ts-watch
Please note that
in order for the rebuilt scripts to take effect
you also have to run npm run md-watch
.
You can find circular dependencies in the TypeScript code with npm run ts-circular
.
If you want to inspect the generated bundles,
you can run npm run ts-analyze
,
which opens a locally hosted website,
or npm run ts-stats
,
which generates the file webpack-stats.json
,
which can be visualized with this website.
The inspectpack
plugin outputs during the build process whether there are any duplicate sources.
You can read more about this
here.
If you want to update the external JavaScript libraries in assets/scripts/external/
,
which are imported in _layouts/head.html
,
you can run the following scripts:
npm run fonts-copy
npm run katex-copy
npm run scripts-copy
npm run scripts-download
The favicons stored in assets/favicons
were generated with RealFaviconGenerator.
You can convert the logo in assets/images
from SVG to PNG with:
npm run logo-convert
Articles can have the following variables in their front matter:
title
: The title of the article as used at the top of the article and in the navigation.category
: The name of the category to which the article belongs.author
: The author of the article so that this information is included in timestamps of the file.published
: The date when the article was first published as YYYY-MM-DD. Omit this variable if the article shall not yet be added to the navigation.modified
: The date when the article was last modified as YYYY-MM-DD. Omit this variable if the article has not been modified since its publication.teaser
: A short text that shall be used when the article is shared on social media or indexed by search engines.icon
: The name of the Font Awesome icon used in the navigation without thefa-
prefix.math
: Set this totrue
if you want to activate KaTeX rendering for the article.
You can refresh the cached preview of an article on Twitter, Facebook, and LinkedIn. In case of Telegram, you need to send the URL to the @webpagebot.
Markdown is converted by kramdown according to this syntax specification.
{::comment}
This text is ignored by kramdown.
{:/comment}
{% comment %}
This text is ignored by Liquid.
{% endcomment %}
This statement requires a source.[^label]
[^label]: This can be written anywhere.
You can use an abbreviation like HTML anywhere and then provide a definition anywhere.
*[HTML]: Hyper Text Markup Language
---
You can declare a description list like this:
Term
: Description
This is transformed into:
<dl>
<dt>Term</dt>
<dd>Description</dd>
</dl>
| Default aligned | Left aligned | Center aligned | Right aligned
|-|:-|:-:|-:
| First body | Second cell | Third cell | Fourth cell
| Second line | a | b | c
| Third line | 1 | 2 | 3
|---
| Second body
| Second line
|===
| Footer row
See Bootstrap for styling options.
You can use HTML blocks in Markdown.
If you want the content of an HTML tag to be processed as Markdown as well,
you have to use markdown="1"
as an attribute
in order to parse its content with the default mechanism.
You can also use markdown="block"
or markdown="span"
if you want the content of the tag to be parsed explicitly as a block or span level element.
For example, this is how you can declare a collapsed details section with a summary, where the content is still rendered using Markdown.
<details markdown="block" open>
<summary markdown="span" id="appropriate-id">
Summary with *Markdown*.
</summary>
Details with *Markdown*.
</details>
If the information box shall not be open by default,
remove the open
attribute from the details
tag.
You can declare tabbed areas as follows after including bindTabbed()
in the script of the page:
<div class="tabbed" data-titles="One | Two | Three | All" data-default="All" markdown="block">
One
Two
Three
</div>
If you don't provide the attribute data-default
, the first area is displayed by default.
You can prevent undesirable line breaks by wrapping the text
in <span class="text-nowrap" markdown="span">โฆ</span>
.
You can provide the header ID yourself if you want:
## Title {#my-id}
You can use attribute lists to add attributes to the generated HTML elements:
{:#my-id .first-class .second-class attribute="value"}
Classes are appended to the current value of the class
attribute.
Attributes provided as name-value pairs,
on the other hand,
replace previous attributes with the same name.
You can apply the attributes to block elements by putting the list on a separate line directly before or after the block element:
This is a paragraph.
{:.class-of-paragraph}
You can apply the attributes to span elements by putting the list directly after the span element with no space in between:
A [link](test.html){:rel='something'} and some **tools**{:.tools}.
If you want to re-use the same attributes, you can declare an attribute list definition:
{:ref-name: #my-id .my-class}
{:other: ref-name #id-of-other .another-class title="Example"}
## Potentially very long title
{:data-toc-text="Short title"}
## Title omitted from the table of contents
{:data-toc-skip=""}
![Name of the image](image.png)
{% include image.md source="source.png" caption="Caption with *Markdown*." %}
The caption
string can span several lines.
You just need to escape all quotation marks in it.
It will be rendered with markdown="span"
as a single paragraph.
Images in the images
subfolder of articles which end on .jpg
are automatically scaled to a width of 910, 1820, and 2730 pixels
and stored in the subfolder generated
when running npm run build
, npm run watch
, or npm start
.
If you only want to build or watch these images,
you can execute npm run img-build
or npm run img-watch
instead.
A srcset
with the various sizes is generated when you use the include
statement.
{% include image.md source="source.jpg" caption="Caption with *Markdown*." %}
This example requires that images/source.jpg
exists.
The caption
is optional and can be skipped.
If you want to provide a different image for the dark and the light theme,
you can add themed="true"
to the include
statement.
Please note that the light image is used for printing and the PDF export.
{% include image.md source="source.png" caption="Caption with *Markdown*." themed="true" %}
This example requires that images/source.dark.png
and images/source.light.png
exist.
npm run svg-build
builds all SVGs
which end with .svg.ts
in the graphics
subfolder of articles.
You can also watch all SVG-related TypeScript files with npm run svg-watch
.
These scripts are also included in npm run build
and npm run watch
respectively.
{% include_relative generated/example.embedded.svg %}
<figure markdown="block">
{% include_relative generated/example.embedded.svg %}
<figcaption markdown="span">
Caption with *Markdown*.
</figcaption>
</figure>
If you want to use an SVG image for the social media preview,
you can generate a dark and properly scaled PNG
by adding the graphic to the script at code/node/thumbnail.ts
.
You can then run this script with:
npm run thumbnail
You can write both inline math such as $$f(x) = x^2$$ as well as block math:
$$
e^{i\pi} + 1 = 0
$$
Check out the list of supported functions with tons of examples.
Don't forget to add the following front matter at the beginning of the article:
math: true
If you start an inline math statement on a new line, you have to prefix it with a HTML comment in order to preserve the outline view of Visual Studio Code:
<!-- --> $$1 + 2$$ is a simple equation
The following command generates a PDF of the given article with Puppeteer:
npm run export <article>
Make sure that you are serving the website (e.g. with npm start
)
at http://localhost:4000/
before calling the above script.
Replace <article>
with the name of the article's directory.
The script runs headless Chromium
to generate the PDF.
Generate, verify, and upgrade timestamps using OpenTimestamps with the following commands:
npm run ots-remove <article>
npm run ots-stamp <article>
npm run ots-info <article>
npm run ots-verify <article>
npm run ots-upgrade <article>
npm run ots-remove-bak <article>
Make sure that the PDF has already been generated before calling the second command.
Please open an issue if you found a typographical error, a factual inaccuracy or a logical fallacy. Please also let me know if an explanation is difficult to follow. You are welcome to suggest interesting topics for me to write about. However, unless I explicitly asked for it, I will not accept any pull requests. By making a pull request, you transfer the copyright of your contribution without any limitations to me.
The most important third party projects used by this website are:
- Ruby
- Jekyll
- Bootstrap
- Darkly Theme
- Flatly Theme
- npm
- React
- TypeScript
- webpack
- jQuery
- AnchorJS
- Font Awesome
More dependencies are listed in package.json and Gemfile.
The copyright for the content of this repository, excluding the aforementioned dependencies, belongs to Kaspar Etter.
The content of this repository, excluding the aforementioned dependencies, is licensed under the Creative Commons Attribution 4.0 International License.
Please do not hesitate to contact me.