| Title: | Create, Build and Serve 'Jekyll' Websites from 'R Markdown' and 'Quarto' |
|---|---|
| Description: | Do for 'Jekyll' what 'blogdown' does for 'Hugo': create sites from the most popular themes ('al-folio', 'Chirpy', 'Minimal Mistakes', 'minima'), write posts in R Markdown or 'Quarto' that are knitted to Markdown consumed natively by 'Jekyll', customize theme colors, fonts and elements declaratively from R, migrate 'blogdown'/'Hugo' sites, build and serve the site locally with live reload, and diagnose the Ruby/'Jekyll' toolchain. 'Jekyll' is invoked strictly via its command line interface; no R-Ruby language bridge is used. |
| Authors: | Allan Quadros [aut, cre] |
| Maintainer: | Allan Quadros <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.3.4 |
| Built: | 2026-10-14 19:00:29 UTC |
| Source: | https://github.com/cran/jekylldown |
The escape hatch under set_theme_style(), set_theme_font() and
set_element_style(): free-form CSS, but written into a
marker-delimited block in the site-local stylesheet, so it is
idempotent per id, easy to find, and removable – instead of hand
edits scattered through theme files. Works with any theme whose site
has (or can get) a local main.scss.
add_css(css, id = "custom", dir)add_css(css, id = "custom", dir)
css |
Character vector of CSS lines. |
id |
Identifier of the block (lowercase letters, digits, |
dir |
Site root, or any directory inside it. Like
|
Invisibly, the path of the stylesheet written.
site <- new_site(tempfile("my-blog")) add_css(".profile img { border-radius: 50%; }", id = "round-avatar", dir = site) # remove it later add_css(character(0), id = "round-avatar", dir = site) unlink(site, recursive = TRUE)site <- new_site(tempfile("my-blog")) add_css(".profile img { border-radius: 50%; }", id = "round-avatar", dir = site) # remove it later add_css(character(0), id = "round-avatar", dir = site) unlink(site, recursive = TRUE)
Writes a site-level copy of jekyll-feed's template to
_includes/atom-feed.xml and the feed pages that render it:
feed.xml with every post, plus feed/<category>.xml for each
category. A category feed carries the full text of the posts whose
categories front-matter entry contains that value. The match is
verbatim, so "R" and "r" are different categories. Aggregators
such as R-Bloggers require exactly that kind of feed; see
use_r_bloggers() for the one-call setup.
add_feed(category = NULL, dir, force = FALSE)add_feed(category = NULL, dir, force = FALSE)
category |
Optional character vector of post categories, one
feed each at |
dir |
Site root, or any directory inside it. Like
|
force |
Regenerate |
The copy differs from the plugin's template in one place. al-folio
sets title: blank in _config.yml to mean "build the site title
from the author's name". The theme's layouts follow that convention,
but jekyll-feed does not, and prints the literal word "blank" as the
feed title. The copy handles that case and leaves the title of every
other theme as it is.
The template is taken from the jekyll-feed gem installed for the
site, at the version pinned in Gemfile.lock. That is the version
the local build and the deploy workflow render with, so the include
follows the plugin instead of freezing a copy inside jekylldown. When
the gem is not installed yet, the template of that version is fetched
from GitHub. Offline, the copy shipped with the package is used. It is
a complete template, and the feed pages need no plugin at all. The
include records where it came from. Running the function again after
a gem upgrade regenerates it from the new version, and force = TRUE
regenerates it unconditionally. An atom-feed.xml you wrote yourself,
without the jekylldown marker, is never touched.
Chirpy ships a main feed of its own at the same address, and Jekyll
keeps the theme's version of feed.xml on that theme. The
per-category feeds are jekylldown's on every theme.
In the feeds, root-relative image and link paths in the post body
(/assets/img/..., which is what knit_post() writes) are made
absolute from url: in _config.yml. Feed readers are supposed to
resolve such paths against the entry's xml:base, but aggregators
such as R-Bloggers ask for absolute URLs, so the feed does not depend
on the reader.
new_site() and migrate_hugo() already call this on al-folio
sites. Call it yourself on an existing site, or on any theme where
you want per-category feeds.
Invisibly, the paths of the feed pages written or confirmed
(the main feed first), with the include's path as attribute
"include".
site <- new_site(tempfile("my-blog")) add_feed(category = "R", dir = site) # feed.xml plus feed/R.xml readLines(file.path(site, "feed", "R.xml")) unlink(site, recursive = TRUE)site <- new_site(tempfile("my-blog")) add_feed(category = "R", dir = site) # feed.xml plus feed/R.xml readLines(file.path(site, "feed", "R.xml")) unlink(site, recursive = TRUE)
R Markdown writers use LaTeX math, but not every Jekyll theme renders
it out of the box (on minima, $$...$$ shows up as literal text).
This turns math on the way each theme expects:
add_mathjax(dir)add_mathjax(dir)
dir |
Site root, or any directory inside it. Like
|
al-folio: sets its native enable_math: true;
Chirpy: enables its per-page math flag site-wide through a
front matter default;
Minimal Mistakes: loads MathJax through the theme's
head_scripts hook;
minima (and unknown themes): copies the theme's
_includes/head.html into the site and inserts the MathJax
<script> before </head>, in a managed, idempotent block.
kramdown (Jekyll's Markdown engine) emits MathJax-v2-flavored output for math, so the v2 bundle is loaded where jekylldown injects the script itself; al-folio and Chirpy ship their own MathJax setups.
Invisibly, a short description of what was changed.
## Not run: add_mathjax("my-site") ## End(Not run)## Not run: add_mathjax("my-site") ## End(Not run)
Knits every outdated _source/*.Rmd and renders every outdated
_source/*.qmd (Quarto) to _posts/*.md (see knit_post()), then runs
jekyll build if a usable Jekyll is available.
build_site(dir, local_jekyll = NULL, force = FALSE)build_site(dir, local_jekyll = NULL, force = FALSE)
dir |
Directory in (or under) the site. |
local_jekyll |
Run |
force |
Re-knit sources even when the output is up to date? |
Invisibly, the paths of the posts (re)knitted.
# knit only, no Jekyll needed: what a GitHub Actions setup uses site <- new_site(tempfile("my-blog")) build_site(site, local_jekyll = FALSE) list.files(file.path(site, "_posts")) unlink(site, recursive = TRUE) ## Not run: # knit/render outdated posts, then build with the local Jekyll build_site("my-site") # re-knit everything from scratch build_site("my-site", force = TRUE) ## End(Not run)# knit only, no Jekyll needed: what a GitHub Actions setup uses site <- new_site(tempfile("my-blog")) build_site(site, local_jekyll = FALSE) list.files(file.path(site, "_posts")) unlink(site, recursive = TRUE) ## Not run: # knit/render outdated posts, then build with the local Jekyll build_site("my-site") # re-knit everything from scratch build_site("my-site", force = TRUE) ## End(Not run)
Runs bundle install in the site root with the package's isolated gem
environment injected, so a theme's Gemfile (al-folio has one) resolves
without any manual GEM_HOME/PATH setup. Needed once per site (and
after editing the Gemfile); afterwards build_site() and
serve_site() automatically switch to bundle exec jekyll.
bundle_install(dir)bundle_install(dir)
dir |
Directory in (or under) the site. |
One external requirement: a Gemfile that installs gems straight from
git repositories (al-folio's does) needs git, because Bundler
shells out to it – the only step in the jekylldown workflow that
requires git. On Windows, install_git() puts a portable copy in
the isolated toolchain; elsewhere install it with your package
manager.
Invisibly, the processx::run() result.
Prints a situation report on everything jekylldown needs: Ruby, gem,
bundler and jekyll executables (looking first in the package's isolated
GEM_HOME, then on the PATH), plus the current site, if any.
check(dir = ".")check(dir = ".")
dir |
Directory in (or under) a Jekyll site. Defaults to the working directory; not finding a site is reported, not an error. |
Invisibly, a named list with the collected information.
jekylldown::check()jekylldown::check()
The one jekylldown step that needs git is bundle_install() on a
Gemfile that pulls gems straight from git repositories (al-folio's
does) – Bundler shells out to git for those. This function covers
machines without git: it downloads MinGit, the portable build that
Git for Windows publishes exactly for embedding (a plain .zip, no
installer, no registry entries, no admin rights), and unpacks it
under tools::R_user_dir("jekylldown", "data")/git, where every
jekylldown-run Ruby command finds it automatically. Nothing is
installed system-wide; delete the data directory to uninstall.
install_git(file = NULL, version = NULL, force = FALSE)install_git(file = NULL, version = NULL, force = FALSE)
file |
Optional path to an already-downloaded
|
version |
Optional Git for Windows release to install instead
of the latest, in asset notation (e.g. |
force |
Re-download even when the toolchain directory already
holds a working git? Default |
Like install_ruby(), the release lookup uses the GitHub API and
falls back to the releases/latest page on github.com (then to a
pinned known-good release) on networks where api.github.com is
unreachable. x86-64 only.
This is a private copy for the toolchain: it does not appear on your
system PATH, so it does not replace a real git installation for
everyday version-control work (for publishing a site with
git push, install git from https://git-scm.com/downloads/).
The path to the installed git executable, invisibly.
Downloads the latest Quarto release and unpacks it under
tools::R_user_dir("jekylldown", "data")/quarto, where build_site(),
serve_site() and check() find it automatically. Nothing is installed
system-wide and no shell configuration is touched; uninstall by deleting
that directory. Only needed to write posts in Quarto (.qmd) –
see new_post().
install_quarto(file = NULL)install_quarto(file = NULL)
file |
Optional path to an already-downloaded
|
Linux x86_64 only for now (the platform jekylldown's isolated toolchain
targets); on other platforms install Quarto from
https://quarto.org/docs/get-started/ and it is picked up from the
PATH.
The path to the installed quarto executable, invisibly.
Windows has no system package manager to get Ruby from, so this
function does the whole setup: it downloads the portable RubyInstaller
archive (a .7z, no graphical installer, no registry entries), unpacks
it under tools::R_user_dir("jekylldown", "data")/ruby, and installs
the jekyll, bundler and minima (the default theme) gems
into it. Nothing is installed system-wide and no shell configuration
is touched; uninstall by deleting the data directory. build_site(),
serve_site() and check() find this toolchain automatically.
install_ruby(file = NULL, devkit = TRUE, version = NULL, force = FALSE)install_ruby(file = NULL, devkit = TRUE, version = NULL, force = FALSE)
file |
Optional path to an already-downloaded
|
devkit |
Set up the MSYS2 toolchain (via |
version |
Optional RubyInstaller release to install instead of
the latest, as tagged upstream (e.g. |
force |
Re-download Ruby even when the toolchain directory
already holds a working one? Default |
Unpacking the .7z archive needs the archive package
(install.packages("archive")).
The MSYS2 build tools are set up by default (devkit = TRUE, via
RubyInstaller's ridk install): Jekyll depends on the
‘eventmachine’ gem, which has no precompiled binary for current
Rubies and must be compiled at install time. devkit = FALSE skips
that large download for the rare setups that already have a working
MSYS2 toolchain.
On Linux and macOS install Ruby with your package manager instead (see
the README's prerequisites); jekylldown picks it up from the PATH.
The path to the installed jekyll command, invisibly.
Knits a single .Rmd with the conventions inherited from Yihui Xie's
knitr-jekyll: front matter preserved, output fenced for kramdown/rouge
via knitr::render_jekyll(), figures written to
assets/img/posts/<post-name>/ and referenced through
{{ site.baseurl }} so they resolve wherever the site is mounted.
knit_post(input, output, root = NULL, method = NULL)knit_post(input, output, root = NULL, method = NULL)
input |
Path to the |
output |
Path of the |
root |
Site root; found from |
method |
For |
A .qmd input is rendered with the Quarto CLI (quarto render --to gfm) instead, and the result is adapted to the same conventions: the
front matter of the .qmd is carried over (minus Quarto-only keys,
with layout: post added), and figures are moved to
assets/img/posts/<post-name>/. Quarto is looked up in jekylldown's
isolated toolchain (see install_quarto()), then on the PATH;
options(jekylldown.quarto = "/path/to/quarto") overrides both.
In both cases Markdown pipe tables get a {: .table} kramdown IAL so
the theme styles them (al-folio only styles Bootstrap's .table
class; a bare <table> looks unstyled).
Mostly called for you by build_site() and serve_site() (the latter via
the generated build.R), but exported so those entry points and users can
share one implementation.
output, invisibly.
# normally called for you by build_site()/serve_site() site <- new_site(tempfile("my-blog")) src <- list.files(file.path(site, "_source"), full.names = TRUE)[1] out <- file.path(site, "_posts", xfun::with_ext(basename(src), "md")) knit_post(src, out) readLines(out, n = 8) unlink(site, recursive = TRUE) ## Not run: # force the pandoc pipeline for one post (citations, footnotes, ...) knit_post("my-site/_source/2026-01-01-paper.Rmd", "my-site/_posts/2026-01-01-paper.md", method = "pandoc") ## End(Not run)# normally called for you by build_site()/serve_site() site <- new_site(tempfile("my-blog")) src <- list.files(file.path(site, "_source"), full.names = TRUE)[1] out <- file.path(site, "_posts", xfun::with_ext(basename(src), "md")) knit_post(src, out) readLines(out, n = 8) unlink(site, recursive = TRUE) ## Not run: # force the pandoc pipeline for one post (citations, footnotes, ...) knit_post("my-site/_source/2026-01-01-paper.Rmd", "my-site/_posts/2026-01-01-paper.md", method = "pandoc") ## End(Not run)
Creates a fresh Jekyll site with new_site() and converts the mechanical
parts of a Hugo site into it. The Hugo config (config.toml/.yaml) is
read first and drives the migration:
migrate_hugo( from, to, theme = c("minima", "al-folio", "chirpy", "minimal-mistakes"), drafts = TRUE, only_referenced = FALSE, publications = c("bib", "html"), theme_color = NULL, rerender = TRUE )migrate_hugo( from, to, theme = c("minima", "al-folio", "chirpy", "minimal-mistakes"), drafts = TRUE, only_referenced = FALSE, publications = c("bib", "html"), theme_color = NULL, rerender = TRUE )
from |
Root of the existing Hugo site (must contain |
to |
Directory to create the Jekyll site in (must not exist or be empty). |
theme |
Passed to |
drafts |
Migrate draft posts (as |
only_referenced |
Copy from |
publications |
How to migrate a publications page found in the menu
(slug |
theme_color |
Accent color for al-folio, passed to
|
rerender |
Should |
posts under content/post/, content/posts/ or content/blog/ are
renamed to Jekyll's YYYY-MM-DD-slug convention; .Rmd/.Rmarkdown
sources go to _source/ (to be knitted by jekylldown), plain
.md/.markdown to _posts/;
every page referenced by the Hugo menu ([[menu.main]]) is migrated
to the theme's own convention, with permalink preserving the Hugo
URL and the menu order kept: al-folio gets _pages/<slug>.md with
nav_order, Chirpy gets _tabs/<slug>.md with order, Minimal
Mistakes gets _pages/<slug>.md plus an entry in
_data/navigation.yml, minima gets <slug>.md at the root; a menu
entry pointing at the posts section maps to the theme's blog page;
the home page (content/_index.md) becomes the body of the theme's
landing page (al-folio: _pages/about.md; Chirpy: _tabs/about.md;
Minimal Mistakes and minima: index.md); an avatar
{{< figure class="avatar" >}} becomes the theme's profile picture
(al-folio profile, Chirpy avatar:, Minimal Mistakes
author.avatar);
site identity from the config (title/author, baseURL, description)
is written into the new _config.yml (al-folio: first_name/
last_name, jekyll-scholar author names; minima: title);
social profile links found in the Hugo pages (GitHub, LinkedIn, ORCID,
Google Scholar, ResearchGate, StackOverflow, ...), the first e-mail
address and a link to a CV/resume PDF are written where the theme
shows them: al-folio's _data/socials.yml, Chirpy's social: config
block (plus its github/twitter buttons), or Minimal Mistakes'
author: sidebar links. Posts are not scanned (they may link to
other people's profiles), and only profile-shaped URLs count (a
GitHub repository link is ignored);
front matter (YAML --- or TOML +++) is converted to Jekyll YAML:
layout: post added, draft: true becomes published: false,
aliases becomes redirect_from (needs the jekyll-redirect-from
plugin), Hugo-only keys are dropped;
static/ is copied to the site root, so absolute asset URLs like
/images/foo.png keep working; with only_referenced = TRUE only the
files actually referenced by the migrated content are copied;
page-bundle resources (content/post/my-post/plot.png) are copied to
assets/img/posts/<post>/ and relative image links are rewritten;
{{< figure >}}, {{< youtube >}} and {{< vimeo >}} shortcodes
are converted;
any other shortcode is left in place and reported for manual porting.
The Hugo site is only read, never modified. Content not referenced by the menu, layouts and whatever else cannot be converted is listed in the final report.
The migrated site carries no leftovers from the theme's demo: the scaffold is scrubbed before your content lands, and a final pass removes any demo media (sample photos, videos, audio, notebooks, preview images) that nothing in the migrated site references – what remains is your content plus what Jekyll and the theme need.
Invisibly, a list report: converted posts, drafts, pages, files with remaining shortcodes, config keys set, social links written, static files skipped, and content left for manual migration.
## Not run: migrate_hugo( from = "blog-hugo", # existing blogdown/Hugo site (never written to) to = "blog-jekyll", # created here; must not exist yet theme = "al-folio", only_referenced = TRUE, # copy only the static files actually used theme_color = "red" ) # then, after bundle_install("blog-jekyll") once, preview locally: build_site("blog-jekyll") serve_site("blog-jekyll") ## End(Not run)## Not run: migrate_hugo( from = "blog-hugo", # existing blogdown/Hugo site (never written to) to = "blog-jekyll", # created here; must not exist yet theme = "al-folio", only_referenced = TRUE, # copy only the static files actually used theme_color = "red" ) # then, after bundle_install("blog-jekyll") once, preview locally: build_site("blog-jekyll") serve_site("blog-jekyll") ## End(Not run)
Creates _source/YYYY-MM-DD-slug.Rmd (default) or .qmd (Quarto), or,
with format = "md", a plain _posts/YYYY-MM-DD-slug.md, all with
Jekyll front matter.
new_post( title, date = Sys.Date(), format = c("Rmd", "qmd", "md"), tags = NULL, categories = NULL, dir, open = interactive(), rmd = NULL )new_post( title, date = Sys.Date(), format = c("Rmd", "qmd", "md"), tags = NULL, categories = NULL, dir, open = interactive(), rmd = NULL )
title |
Post title. |
date |
Post date; defaults to today. |
format |
|
tags, categories
|
Optional character vectors for the front matter. |
dir |
Directory in (or under) the site. |
open |
Open the file (via |
rmd |
Deprecated alias kept for compatibility: |
The path to the created file, invisibly.
site <- new_site(tempfile("my-blog"), sample = FALSE) new_post("Hello world", dir = site, open = FALSE) new_post("A Quarto post", format = "qmd", dir = site, open = FALSE) new_post("Plain Markdown, no R code", format = "md", dir = site, tags = c("news", "r"), open = FALSE) list.files(file.path(site, "_source")) unlink(site, recursive = TRUE)site <- new_site(tempfile("my-blog"), sample = FALSE) new_post("Hello world", dir = site, open = FALSE) new_post("A Quarto post", format = "qmd", dir = site, open = FALSE) new_post("Plain Markdown, no R code", format = "md", dir = site, tags = c("news", "r"), open = FALSE) list.files(file.path(site, "_source")) unlink(site, recursive = TRUE)
Scaffolds a Jekyll site ready for the jekylldown workflow: R Markdown
posts live in _source/, are knitted to _posts/*.md, and figures land
in assets/img/posts/. Four themes are supported first-class:
new_site( dir, theme = c("minima", "al-folio", "chirpy", "minimal-mistakes"), title = "A jekylldown site", sample = TRUE, demo = TRUE )new_site( dir, theme = c("minima", "al-folio", "chirpy", "minimal-mistakes"), title = "A jekylldown site", sample = TRUE, demo = TRUE )
dir |
Directory to create the site in. Must be empty or nonexistent. |
theme |
|
title |
Site title written to |
sample |
Create a sample |
demo |
Keep the theme's demo content? Only al-folio ships demo
content (sample posts, news, projects, books, an example biography
and tens of MB of sample media); default |
"minima" (default) – a minimal self-contained blog, generated
locally (no network needed);
"al-folio" – the academic theme
(https://github.com/alshedivat/al-folio), fetched from GitHub
(a shallow git clone when git is available, the template archive
otherwise – git is not required);
"chirpy" – the technical-blog theme
(https://github.com/cotes2020/jekyll-theme-chirpy): its starter
template is fetched the same way and stripped of the upstream repo
tooling, keeping the GitHub Pages deploy workflow;
"minimal-mistakes" – the general-purpose theme
(https://github.com/mmistakes/minimal-mistakes), generated
locally as a gem-based site (see set_theme_skin() for its skins).
On al-folio the feed is rendered from a site-level copy of
jekyll-feed's template (see add_feed()), so that the theme's
title: blank convention yields the author's name as the feed
title instead of the literal word "blank".
The normalized site path, invisibly.
# a minimal blog with the locally generated minima theme (no network) site <- new_site(tempfile("my-blog")) list.files(site) unlink(site, recursive = TRUE) ## Not run: # the al-folio academic theme, fetched from GitHub, starting empty new_site("my-site", theme = "al-folio", demo = FALSE) # the Chirpy starter, or a gem-based Minimal Mistakes site new_site("my-blog", theme = "chirpy") new_site("my-blog", theme = "minimal-mistakes", title = "My blog") ## End(Not run)# a minimal blog with the locally generated minima theme (no network) site <- new_site(tempfile("my-blog")) list.files(site) unlink(site, recursive = TRUE) ## Not run: # the al-folio academic theme, fetched from GitHub, starting empty new_site("my-site", theme = "al-folio", demo = FALSE) # the Chirpy starter, or a gem-based Minimal Mistakes site new_site("my-blog", theme = "chirpy") new_site("my-blog", theme = "minimal-mistakes", title = "My blog") ## End(Not run)
Builds the site and serves _site/ with live reload: keep the
preview tab open in the browser and every save of a watched file –
_source/*.Rmd and *.qmd, Markdown pages, _config.yml, data
files, styles – re-knits what is stale, reruns jekyll build and
reloads the page. No manual build_site() calls while writing.
serve_site(dir, background = interactive(), port = NULL, ...)serve_site(dir, background = interactive(), port = NULL, ...)
dir |
Directory in (or under) the site. |
background |
Run the watcher/server in a separate R process?
Default |
port |
Port to serve on; a random free port by default. |
... |
Passed on to |
By default the whole thing runs in a separate background R
process: your console stays free, nothing is printed there, and
rebuild output goes to a log file (.jekylldown-serve.log in the
site; the path is shown when the server starts). Stop it with
stop_server(). With background = FALSE the server runs in this
session instead (as a servr daemon), which keeps the console usable
but briefly busy during each rebuild.
The rebuild is triggered by the open preview tab (it pings the server about once a second); with no browser tab open, nothing rebuilds.
Invisibly, the preview URL.
Content-only edits (posts and pages) rebuild with jekyll's
incremental mode – only the changed pages are regenerated. Edits
with site-wide effects that incremental mode does not track (sass
partials, _config.yml, data files) still trigger a full build.
Jekyll is markedly slower on Windows, mostly because real-time
antivirus scanning intercepts the thousands of small file reads of
every build. If previews feel sluggish, excluding the site folder
and jekylldown's toolchain directory
(tools::R_user_dir("jekylldown", "data")) from real-time scanning
(Windows Security > Virus & threat protection > Exclusions) makes
the single biggest difference. Heavy themes also do less work per
build with the demo content removed (new_site(demo = FALSE)).
## Not run: serve_site("my-site") # console stays free; open the URL stop_server("my-site") ## End(Not run)## Not run: serve_site("my-site") # console stays free; open the URL stop_server("my-site") ## End(Not run)
Styles a single part of the site – the navbar, the footer, headings – without writing CSS: the element name is translated to the theme's own selectors and the properties are appended to the site-local stylesheet in a managed block (one per element; re-running replaces it).
set_element_style( element, color = NULL, background = NULL, size = NULL, ..., dir )set_element_style( element, color = NULL, background = NULL, size = NULL, ..., dir )
element |
One of the element names above. |
color, background, size
|
Text color, background color and
|
... |
Further CSS properties as named arguments, with |
dir |
Site root, or any directory inside it. Like
|
Elements: "navbar", "brand" (the site name in the navbar),
"socials" (the social icon row – e.g. its size; the sizing also
covers what font-size alone would miss: image and SVG icons, and
Chirpy's circular buttons, all fixed-size in the themes),
"footer", "headings", "post_title", "links", "code",
"buttons".
Invisibly, the CSS selector styled.
Unlike set_theme_style(), which goes through the CSS variables the
theme itself promises, this layer depends on a curated map of theme
selectors (.navbar, footer, ...) that upstream can rename at any
release. jekylldown checks the installed theme's sass and warns when a
mapped selector is no longer found, but the check needs the theme gems
installed and cannot catch everything. Prefer set_theme_style()
whenever a CSS variable covers your case; treat this function as the
middle ground before dropping to add_css().
site <- new_site(tempfile("my-blog")) set_element_style("navbar", background = "#222222", color = "white", dir = site) set_element_style("headings", color = "red", font_weight = "600", dir = site) unlink(site, recursive = TRUE)site <- new_site(tempfile("my-blog")) set_element_style("navbar", background = "#222222", color = "white", dir = site) set_element_style("headings", color = "red", font_weight = "600", dir = site) unlink(site, recursive = TRUE)
al-folio derives its accent (links, buttons, badges) from the
--global-theme-color CSS variable defined in the theme's
_sass/_themes.scss – purple in light mode, cyan in dark mode. This
function overrides it without touching the theme: the theme's
assets/css/main.scss is copied into the site (a local file shadows the
theme's) and a plain-CSS override block is appended, one rule per
selector in which the theme sets --global-theme-color, so the override
tracks the theme's own light/dark structure. Named colors are resolved
from the theme's palette ($red-color, $blue-color, ... in
_sass/_variables.scss); hex values are used as-is.
set_theme_color(color = NULL, dir)set_theme_color(color = NULL, dir)
color |
A named theme color (e.g. |
dir |
Site root, or any directory inside it. Like
|
The theme files live in the al_folio_core gem, so run
bundle_install() once before calling this. Calling it again replaces
the previous override, so the color can be changed at any time.
On a Chirpy site this delegates to set_theme_style(accent = color)
(the link color); on Minimal Mistakes it points you at
set_theme_skin() instead.
The hex color applied, invisibly.
set_theme_color(NULL) removes the color override. To drop
every jekylldown customization at once (colors, fonts, element
styles, custom CSS), delete the site-local stylesheet
(assets/css/main.scss; assets/main.scss on minima) – it only
holds the theme's copy plus the managed blocks, and is recreated on
the next customization call.
## Not run: set_theme_color("red", dir = "my-site") set_theme_color("#0057b7", dir = "my-site") set_theme_color(NULL, dir = "my-site") # back to the theme default ## End(Not run)## Not run: set_theme_color("red", dir = "my-site") set_theme_color("#0057b7", dir = "my-site") set_theme_color(NULL, dir = "my-site") # back to the theme default ## End(Not run)
Overrides the theme's font family and base size without touching the
theme: @font-face rules for Google Fonts are inlined into the
site-local stylesheet (they are valid anywhere in a stylesheet, unlike
@import), followed by font-family/font-size overrides. Re-running
replaces the previous override.
set_theme_font( family = NULL, size = NULL, headings = NULL, code = NULL, google = TRUE, dir )set_theme_font( family = NULL, size = NULL, headings = NULL, code = NULL, google = TRUE, dir )
family |
Body font family (e.g. |
size |
Base font size as a CSS length ( |
headings |
Font family for |
code |
Font family for code ( |
google |
Fetch the given families from Google Fonts and inline
their |
dir |
Site root, or any directory inside it. Like
|
Invisibly, the path of the stylesheet written.
# a system font, no download site <- new_site(tempfile("my-blog")) set_theme_font("Georgia", size = "17px", google = FALSE, dir = site) unlink(site, recursive = TRUE) ## Not run: # Google Fonts, fetched and inlined set_theme_font("Lora", size = "17px", dir = "my-site") set_theme_font(code = "JetBrains Mono", dir = "my-site") ## End(Not run)# a system font, no download site <- new_site(tempfile("my-blog")) set_theme_font("Georgia", size = "17px", google = FALSE, dir = site) unlink(site, recursive = TRUE) ## Not run: # Google Fonts, fetched and inlined set_theme_font("Lora", size = "17px", dir = "my-site") set_theme_font(code = "JetBrains Mono", dir = "my-site") ## End(Not run)
Minimal Mistakes styles the whole site through Sass "skins" compiled
into the CSS (there are no CSS variables to override). This sets
minimal_mistakes_skin in _config.yml; the skin list is read from
the installed theme gem when available ("default", "air",
"aqua", "contrast", "dark", "dirt", "mint", "neon",
"plum", "sunrise", ...).
set_theme_skin(skin, dir)set_theme_skin(skin, dir)
skin |
Skin name. |
dir |
Site root, or any directory inside it. Like
|
Invisibly, the skin set.
## Not run: set_theme_skin("dark", dir = "my-site") ## End(Not run)## Not run: set_theme_skin("dark", dir = "my-site") ## End(Not run)
al-folio and Chirpy define their palettes as CSS variables, with one block per color scheme (light and dark). This function overrides those variables from R – the most robust customization layer, because the theme's components all read from the variables, and light/dark mode keeps working.
set_theme_style(..., dir)set_theme_style(..., dir)
... |
Named style options from the list above. |
dir |
Site root, or any directory inside it. Like
|
Every option takes either a single color (applied to both modes) or a
vector like c(light = "#ffffff", dark = "#1c1c1d"). Colors can be a
#hex value or any CSS color (on al-folio, also a theme palette name
like "red" or "blue"). Re-running replaces the previous override.
Options on al-folio: accent and hover (the accent color –
set_theme_color() is shorthand for both), background, text,
text_light, divider, highlight, code_background,
card_background, footer_background, footer_text, footer_link.
Options on Chirpy: accent (the link color), background, text,
text_light, heading, divider, code_background,
card_background, sidebar_background, sidebar_active.
Minimal Mistakes has no CSS variables (its palette is compiled from
Sass skins): use set_theme_skin() to switch skins, and
set_element_style()/add_css() for finer control. The same applies
to minima.
This needs the theme gems: run bundle_install() once first.
Invisibly, the named list of CSS variables written.
## Not run: set_theme_style( accent = "red", footer_background = "#222222", background = c(light = "#fffdf7", dark = "#1c1c1d"), dir = "my-site" ) ## End(Not run)## Not run: set_theme_style( accent = "red", footer_background = "#222222", background = c(light = "#fffdf7", dark = "#1c1c1d"), dir = "my-site" ) ## End(Not run)
serve_site()
Stops the background serve process of dir (found through the
.jekylldown-serve.pid file the server wrote), plus any in-session
servr daemons.
stop_server(dir)stop_server(dir)
dir |
Directory in (or under) a site served with
|
NULL, invisibly.
## Not run: stop_server("my-site") ## End(Not run)## Not run: stop_server("my-site") ## End(Not run)
Writes the standard GitHub Actions workflow that builds the site with
Jekyll and publishes it to GitHub Pages on every push
(.github/workflows/jekyll.yml): checkout, Ruby with cached bundle,
jekyll build, upload, deploy. After pushing, enable it once in the
repository settings: Settings > Pages > Source: GitHub Actions.
use_pages_workflow(dir)use_pages_workflow(dir)
dir |
Site root. |
al-folio and Chirpy already ship their own deploy workflow – for
those sites this function leaves it alone and tells you so. It is
meant for the themes that do not (minima, Minimal Mistakes, most
others). Mind the Pages source for the bundled ones: Chirpy's
workflow publishes like this one (Source: GitHub Actions), but
al-folio's pushes the built site to a gh-pages branch, so its
Pages source must be Deploy from a branch > gh-pages.
Note the division of labor: the workflow only runs jekyll build.
Knitting stays local – run build_site() (with
local_jekyll = FALSE if you have no local Jekyll), then commit both
_source/ and the generated _posts/*.md and push.
The workflow caches gems, which requires a committed Gemfile.lock:
run bundle_install() once and commit the lockfile it produces.
Invisibly, the path of the workflow file (or of the theme's own workflow when one already exists).
site <- new_site(tempfile("my-blog")) use_pages_workflow(site) unlink(site, recursive = TRUE)site <- new_site(tempfile("my-blog")) use_pages_workflow(site) unlink(site, recursive = TRUE)
R-Bloggers (https://www.r-bloggers.com/) aggregates R posts from
a full-text feed that contains R content only, and asks for a link
back to it on the blog. This function does the site side of that
setup in one call. It writes an R-only feed at feed/<category>.xml
through add_feed(), which also fixes the feed title on al-folio
sites. On al-folio it adds a line under the blog header with links to
the category, to R-Bloggers and to the feed. That line is a
marker-delimited block in _pages/blog.md, replaced on re-runs; a
link to R-Bloggers you wrote there yourself is left alone. On other
themes the HTML snippet is printed for you to place. Finally it
prints the feed URL to submit at
https://www.r-bloggers.com/add-your-blog/ once the site is
published.
use_r_bloggers(category = "R", dir)use_r_bloggers(category = "R", dir)
category |
The category that marks R posts; |
dir |
Site root, or any directory inside it. Like
|
Only posts whose front matter carries the category enter the feed:
new_post("...", categories = "R"), or categories: R by hand.
Posts about anything else stay out, which is what R-Bloggers asks.
Invisibly, a list with feed (the feed page's path), url
(the public feed URL, from the site's url and baseurl) and
link (the page the link was added to, or NULL).
site <- new_site(tempfile("my-blog")) use_r_bloggers(dir = site) unlink(site, recursive = TRUE)site <- new_site(tempfile("my-blog")) use_r_bloggers(dir = site) unlink(site, recursive = TRUE)