Frog
Frog image by @Goug8888, used under Creative Commons license Attribution-NonCommercial-ShareAlike 2.0 Generic.
Overview
Frog is a static web site generator written in Racket.
You write content in Markdown or Scribble. You generate files. To deploy, you push them to a GitHub Pages repo (or copy them to Amazon S3, or whatever).
Posts get a variety of automatic blog features.
You can also create non-post pages.
The generated site uses Bootstrap, which is responsive, automatically adapting to various screen sizes.
Yes, it's very much like Octopress and others. But Frog doesn't require installing Ruby. Installing Racket is typically waaaay simpler and faster.
The only non-Racket part is optionally using Pygments to do syntax highlighting.
Q: "Frog"?
A: Frozen blog.
Quick Start
Installing Frog
-
Install Racket 6.0 or newer.
TIP: On OS X you will need to add
/Applications/Racket\ 6.1/bin
(or similar) to your OS XPATH
in order to be able to run things likeracket
orraco
at the command line. -
Install Frog:
$ raco pkg install frog
. -
Optional: Install Pygments if you want syntax highlighting for fenced code blocks:
$ sudo easy_install --upgrade Pygments
.NOTE: If that fails, first install
easy_install
-- e.g.$ sudo apt-get install python-setuptools
-- and try again.NOTE: Why
--upgrade
? You probably want the most recent version of Pygments because new languages are constantly being added. For example, Racket is supported starting in Pygments 1.6.
Updating Frog
To update Frog and its dependencies:
raco pkg update --update-deps frog
Starting a new blog project
Creating a new blog project is 3 easy steps:
# 1. Create a subdir
$ mkdir frog-project
# 2. Go there
$ cd frog-project
# 3. Tell Frog to create default files and dirs
$ raco frog --init
Configuration /tmp/frog-project/.frogrc not found; using defaults.
Creating files in /tmp/frog-project/:
/tmp/frog-project/.frogrc
/tmp/frog-project/_src/About.md
/tmp/frog-project/_src/page-template.html
/tmp/frog-project/_src/post-template.html
/tmp/frog-project/_src/posts/2012-01-01-a-2012-blog-post.md
/tmp/frog-project/css/
/tmp/frog-project/js/
/tmp/frog-project/img/
Project ready. Try `raco frog -bp` to build and preview.
You can go ahead and build/preview this to get a feel for the default starting point:
# Build and preview it
$ raco frog -bp
Using configuration /tmp/frog-project/.frogrc
13:28 Done generating files
Your Web application is running at http://localhost:3000/.
Stop this program at any time to terminate the Web Server.
# The home page should launch in your web browser.
# Switch back to the command prompt and type C-c to quit:
^C
Web Server stopped.
Project file tree
Here is the file tree that raco frog --init
creates for you and Frog
expects:
project/
# Files provided by you:
.frog/build # a cache to support minimal rebuilds
.frogrc # see next section of README for details
_src/ # default. see `source-dir` in .frogrc
page-template.html # lets you define the page layout
post-template.html # lets you define the <article> layout
posts/
# You'll create these using `raco frog -n <post-title>`
2013-01-01-a-blog-post.md
2013-02-15-another-blog-post.md
...
# Zero or more other .md files for non-post pages.
# May be in subdirs.
css/
bootstrap.css #\
bootstrap.min.css # get these files
bootstrap-theme.css # from getbootstrap.com
bootstrap-theme.min.css #/
pygments.css # used to style code elements from Pygments
custom.css # other styles you provide; may be empty
js/
bootstrap.js # get these files
bootstrap.min.js # from Bootstrap
img/
feed.png
favicon.ico
Here are the files created by Frog when you run raco frog -b
to
(re)build the blog:
project/ # default. see `output-dir` in .frogrc
index*.html
sitemap.txt
tags/
feeds/
# Post pages in subdirs.
# Exact layout depends on `permalink` in `.frogrc`.
blog/
2013/
01/
a-blog-post-title/
index.html
...
2013/
02/
another-blog-post-title/
index.html
...
...
NOTE: Although the Frog
/example
project has copies for example purposes, for your own project you should get the official/latest Bootstrap 3 files directly from Bootstrap.
TIP: To design a Bootstrap 3 "theme", try Bootstrap Magic.
Also, http://bootswatch.com/ has some ready-made themes.
TIP: For examples of
pygments.css
code highlighting styles see https://github.com/richleland/pygments-css.
Per-project configuration: .frogrc
raco frog --init
creates a .frogrc
file in your project directory:
# Required: Should NOT end in trailing slash.
scheme/host = http://www.example.com
# A path prepended to URIs, including those specified here in .frogrc
# such as `permalink` and `posts-index-uri`. Defaults to `/`. This is
# useful when you want to embed your blog in another web site.
uri-prefix = /
# The title of the blog. Used when generating feeds.
title = My Awesome Blog
# The author. Used when generating feeds, and provided to
# `page-template.html` as the template variable `@author`.
author = The Unknown Author
# What editor to launch with --edit. $EDITOR means to use $EDITOR from
# the environment
editor = $EDITOR
# The command to run, in case you need to customize how the editor is
# called. For example, {editor} {filename} will call:
# (system "$EDITOR 2012-01-01-a-blog-post.md")
# See the test submodule in paths.rkt for more examples
editor-command = {editor} {filename}
# Whether to show the count of posts next to each tag in the
# `page-template` variable `tags/feeds`.
show-tag-counts? = true
# Pattern for blog post permalinks
# Optional: Default is "/{year}/{month}/{title}.html".
# Here's an example of the Jekyll "pretty" style:
permalink = /blog/{year}/{month}/{day}/{title}/index.html
# There is also {filename}, which is the `this-part` portion of
# your post's YYYY-MM-DD-this-part.md file name. This is in case
# you don't like Frog's encoding of your post title and want to
# specify it exactly yourself, e.g. to match a previous blog URI.
# Should index page items contain full posts -- more than just the
# portion above "the jump" <!-- more --> marker (if any)?
index-full? = true
# Should feed items contain full posts -- more than just the portion
# above "the jump" <!-- more --> marker (if any)?
feed-full? = true
# How many posts per page for index pages?
posts-per-page = 10
# How many items to include in feeds?
# Older items in excess of this will not appear in the feed at all.
max-feed-items = 20
# Decorate feed URIs with Google Analytics query parameters like
# utm_source ?
decorate-feed-uris? = true
# Insert in each feed item an image bug whose URI is decorated with
# Google Analytics query parameters like utm_source ?
feed-image-bugs? = true
# Replace links to tweets with embedded tweets?
# In Markdown, must be auto-links alone in a pargraph (blank lines
# above and below), for example:
#
# <https://twitter.com/racketlang/status/332176422003163138>
#
auto-embed-tweets? = true
# When embedding tweets that are replies, show the parent tweet along
# with the reply?
embed-tweet-parents? = true
# Try to automatically link symbols in Markdown ```racket fenced code
# blocks, to Racket documentation?
racket-doc-link-code? = true
# Try to automatically link Markdown of the form `symbol`[racket] to
# Racket documentation? i.e. This is similar to the @racket[] form in
# Scribble.
racket-doc-link-prose? = true
# The source directory. Defaults to "_src".
#
# If you deploy to GitHub pages then it is simplest to keep this under
# the repo/project top directory.
#
# This may be an absolute or relative path. If relative, it's relative
# to the project top directory, i.e. to where this .frogrc file is
# located.
source-dir = _src
# The output directory where generated HTML and other files should go.
#
# If you deploy to e.g. GitHub pages then it is simplest to put the
# output in the repo/project top directory, which is why this defaults
# to ".". But you may change it if you prefer to copy the output
# files to their final destination.
#
# This may be an absolute or relative path. If relative, it's relative
# to the project top directory, i.e. to where this .frogrc file is
# located.
output-dir = .
# Options controlling Pygments' HTML format.
## Whether to use line numbers.
pygments-linenos? = true
## CSS class for the wrapping <div> tag (default: 'highlight').
pygments-cssclass = source
Creating blog posts
A typical workflow:
-
Create a new post with
raco frog -n "My Post Title"
. The name of the new.md
file is displayed to stdout. -
Edit the
.md
file in your preferred plain text editor. -
Regenerate your site and preview it with
raco frog -bp
. (You might repeat steps 2 and 3 a few times until you're satisfied.) -
Deploy. If you're using GitHub Pages, you can commit and push to deploy to your real site. If you're using some other method, you can copy or rsync the files to your static file server.
TIP: If you use Emacs, try markdown-mode. Although Markdown is really simple, markdown-mode makes it even more enjoyable.
Posts
You create new posts in _src/posts
. There are several source formats.
Markdown source files
Post source files in markdown format should be named
YYYY-MM-DD-TITLE.md
and need to have some meta-data in the first few
lines.
You can do raco frog -n "My Title"
to create such a file
easily. This will also fill in the required meta-data section. The
markdown file starts with a code block (indented 4 spaces) that must
contain these three lines:
Title: A blog post
Date: 2012-01-01T00:00:00
Tags: foo, bar, tag with spaces, baz
Everything from here to the end is your post's contents.
If you put `<!-- more -->` on a line, that is the "above-the-fold"
marker. Contents above the line are the "summary" for index pages and
Atom feeds.
<!-- more -->
Contents below `<!-- more -->` are omitted from index pages and Atom
feeds. A "Continue reading..." link is provided instead.
Title
can be anything.
Date
must be an ISO-8601 datetime string: yyyy-mm-ddThr:mn:sc
.
Tags
are optional (although you have to include the Tags:
part).
The tag
DRAFT
(all uppercase) causes the post.html
file not to be generated.
Markdown template files (experimental)
Files with a .mdt
extension are first evaluated as
templates. The resulting text is fed to the markdown
parser, as for a .md
plain markdown source.
Such files may be used as the source for both posts and non-post pages.
There are no template variables -- not even when the file is being used as the source of a post. The template evaluation occurs prior to the extraction of the post meta-data.
Code blocks in markdown files
Frog optionally uses Pygments to do syntax highlighting. When using fenced code blocks, you can specify a language (as on GitHub):
```language
some lines
of code
```
That language
is given to Pygments as the lexer to use.
For example this:
```js
/**
* Some JavaScript
*/
function foo()
{
if (counter <= 10)
return;
// it works!
```
Yields this using the js
lexer:
/**
* Some JavaScript
*/
function foo()
{
if (counter <= 10)
return;
// it works!
And this:
```racket
#lang racket
;; Finds Racket sources in all subdirs
(for ([path (in-directory)])
(when (regexp-match? #rx"[.]rkt$" path)
(printf "source file: ~a\n" path)))
```
Yields this using the racket
lexer:
#lang racket
;; Finds Racket sources in all subdirs
(for ([path (in-directory)])
(when (regexp-match? #rx"[.]rkt$" path)
(printf "source file: ~a\n" path)))
The colors are controlled by your css/pygments.css
file. There are
examples of many styles.
If you use larger font sizes, code may wrap and get out of alignment
with the line numbers. To avoid the wrapping, add the following to
your css/custom.css
:
/* When highlighted code blocks are too wide, they wrap. Resulting in the */
/* line numbers column's rows not lining up with the code rows. Prevent */
/* wrapping. */
pre {
white-space: pre;
width: inherit;
}
NOTE: Pygments has lexers for many, many languages. Plus, it fits the spirit of static web site generation better than JavaScript options like SyntaxHighlighter.
Scribble source files
Post source files in Scribble format should be named
YYYY-MM-DD-TITLE.scrbl
and need to have some meta-data in the first
few lines.
You can do raco frog -N "My Title"
to create such a file
easily. This will also fill in the required meta-data section.
See the example Scribble post and example Scribble non-post page for more information.
Automatic post features
Posts are automatically included in various index pages and feeds.
Posts with any tag go on the home page /index.html
, in an Atom feed
/feeds/all.atom.xml
, and in an RSS feed /feeds/all.rss.xml
.
Posts for each tag go on an index page /tags/<tag>.html
, in an Atom
feed /feeds/<tag>.atom.xml
, and in an RSS feed
/feeds/<tag>.rss.xml
.
The default template post-template.html
provides:
- Twitter and Google+ sharing buttons.
- Disqus comments.
The default template page-template.html
(used for all pages, not
just post pages) also provides:
- Twitter follow button.
- Google Analytics tracking.
Non-post pages
You can put .md
, .mdt
, and .scrbl
files in _src
and its
subdirs (except _src/posts
). They will be converted to HTML pages.
For example, _src/About.md
will be /About.html
in the site.
NOTE: Non-post pages are not included in any automatically generated index pages or feeds. You can manually add links to them in the nav bar by editing that portion of
page-template.html
.
sitemap.txt
A /sitemap.txt
file (for web crawlers) is automatically generated
and includes all post and non-post pages. (It does not include index
pages for tags.)
Templates
Frog uses the Racket
web-server/templates
system based on scribble/text
@
-expressions. This means that the
files are basically normal HTML format, with the ability to use @
to
reference a template variable --- or indeed to "escape" to arbitrary
Racket code.
In contrast to most templating systems, you have a full programming
language available -- Racket -- should you need it. However most of
what you need to do will probably be very simple, such as the
occasional if
or when
test, or perhaps defining a helper function
to minimize repetition.
NOTE: If you need to require another module in your template, you must use
local-require
. Plainrequire
won't work because the template is not evaluated at a module level or top level.
TIP:
@"@"
evaluates to a literal@
. For example if you need to write an email address:foo@"@"bar.com
becomes"foo@bar.com"
.Understanding why this works may help you remember it:
@
means "evaluate the following s-expression as Racket code"."@"
is the Racket code for the string literal"@"
.- Therefore
@"@"
evaluates to the string"@"
.
_src/page-template.html
Page template: The _src/page-template.html
template specifies an <html>
element
used by Frog to generate every page on your site.
Anything in the file that looks like @variable
or @|variable|
is a
template variable supplied by Frog. Most of these should be
self-explanatory from their name and from seeing how they are used in
the default template. Specifically:
contents
: The contents of the page.title
: The title of the page (for<title>
)description
: The description of the page (for<meta>
content element)keywords
: The keywords for the page (for<meta>
keywords element)uri-path
: The path portion of the URI, e.g./path/to/file.html
full-uri
: The full URI, e.g.http://example.com/path/to/file.html
atom-feed-uri
: The full URI to the Atom feedrss-feed-uri
: The full URI to the RSS feedtag
: If this an index page,tag
is the name of the index (such as "All Posts") or ("Posts tagged foo"), elsetag
is#f
.tags-list-items
: HTML with a<li>
for every tag on the blog, suitable for putting in a<ul>
. Each<li>
has a link to that tag's index page.tags/feeds
: HTML that has, for each tag, a link to its index page and a link to its Atom feed.rel-prev
:@(when rel-prev @list{<link rel="next" href="@|rel-next|">})
rel-next
:@(when rel-next @list{<link rel="prev" href="@|rel-prev|">})
_src/post-template.html
Post template: The _src/post-template.html
template determines how blog posts are
laid out on pages that are dedicated to one post. The default template
defines an <article>
element.
For pages that are blog posts, the result of post-template.html
becomes most of the @|contents|
variable in page-template.html
. In
other words, the post template is effectively nested in the page
template. (They are two separate templates so that the page template
can also be used for pages that are not blog post pages.)
+---------------------------+
| page-template |
| |
| +---------------+ |
| | post-template | |
| +---------------+ |
| |
+---------------------------+
NOTE: This template does not control how a blog post is laid out on an index page like
/index.html
or/tags/<some-tag>.html
. For that, see_src/index-template.rkt
.The main purpose of this template is to specify things like Disqus comments, Tweet and +1 sharing buttons, and older/newer links --- things that only make sense in the context of pages dedicated to one blog post.
Anything in the file that looks like @variable
or @|variable|
is a
template variable supplied by Frog. Most of these should be
self-explanatory from their name and from seeing how they are used in
the default template. Specifically:
title
: The title of the posturi-path
: The path portion of the URI, e.g./path/to/file.html
full-uri
: The full URI, e.g.http://example.com/path/to/file.html
date-8601
: The post date as a string, "YYYY-MM-DD".date-struct
: The post date as aracket/date
date
struct.date
: HTML to show the date of the post in a<time>
element.tags
: HTML to show the tags of the post as links.date+tags
: HTML to show the date and tags of the post.content
: The content of the postolder-uri
: The URI of the next older post, if any, or#f
older-title
: The title of the next older post, if any, or#f
newer-uri
: The URI of the next newer post, if any, or#f
newer-title
: The title of the next newer post, if any, or#f
_src/index-template.html
Index template: The _src/index-template.html
template determines how blog posts are
laid out on index pages.
Typically it would be similar to your _src/post-template.rkt
, but
without some "footer" items like comments or previous/next post
buttons.
+----------------------------+
| page-template |
| |
| +----------------+ |
| | index-template | |
| +----------------+ |
| |
| +----------------+ |
| | index-template | |
| +----------------+ |
| |
| +----------------+ |
| | index-template | |
| +----------------+ |
| . . . |
| |
+----------------------------+
Anything in the file that looks like @variable
or @|variable|
is a
template variable supplied by Frog. Most of these should be
self-explanatory from their name and from seeing how they are used in
the default template. Specifically:
title
: The title of the posturi-path
: The path portion of the URI, e.g./path/to/file.html
full-uri
: The full URI, e.g.http://example.com/path/to/file.html
date-8601
: The post date as a string, "YYYY-MM-DD".date-struct
: The post date as aracket/date
date
struct.date
: HTML to show the date of the post in a<time>
element.tags
: HTML to show the tags of the post as links.date+tags
: HTML to show the date and tags of the post.content
: The content of the post plus a "More.." link when needed.content-only
: The content of the post, only.more?
: Is the content just a blurb?
Template Example
Let's say you want to customize the date display format of your posts.
Instead of the default ISO-8601 YYYY-MM-DD format, you want it to be
the default of the date->string
function from the racket/date
module. Here is what you could do in your index-template.rkt
:
@(local-require racket/date)
<article>
<header>
<h2><a href='@|uri-path|'>@|title|</a></h2>
<p class='date-and-tags'>
<time datetime='@|date-8601|' pubdate='true'>
@(date->string date-struct)
</time>
:: @|tags|</p>
</header>
@|content|
</article>
NOTE: If you need to require another module in your template, you must use
local-require
. Plainrequire
won't work because the template is not evaluated at a module level or top level.
Widgets
In addition to the variables described above for each template, some predefined functions are available for templates to use: "widgets".
Anything a widget can do, you could code directly in the
template. There's no magic. But widgets minimize clutter in the
templates. Plus they make clearer what are the user-specific
parameters (as opposed to putting stuff like <!-- CHANGE THIS! -->
in the template).
For example, @google-universal-analytics["UA-xxxxx"]
returns
text for a <script>
element to insert Google Analytics tracking
code. You supply it the two user-specific pieces of information, which
it plugs into the boilerplate and returns.
Likewise there are widgets for things like Twitter and Google+ share buttons, Twitter follow button, Disqus comments, older/newer post links.
See widgets.rkt
for the complete list. See the
example page template and example post template for usage
examples.
NOTE: If you'd like to add a widget, pull requests are welcome!
Embedding a blog in an existing site
If you want to embed the entire blog in an existing site, one way is
to use a subdomain, e.g. blog.example.com
.
Another way is to embed your blog "under" the existing site's URI path
structure, e.g. example.com/blog/
. To do so:
-
In
post-template.html
change URIs from/
to/blog/
as appropriate. -
In
.frogrc
seturi-prefix = /blog
. This causes URIs generated by Frog to be prefixed with/blog
. (Other URIs in.frogrc
-- such asposts-index-uri
andpermalink
-- will automatically be prefixed withblog/
, so don't change those.)
The --preview
flag should open on your blog's index page at
/blog/index.html
, automatically. (But there's also a --root
flag
in case you need to control it more specifically.)
TIP: Are you a "Tilde Club" member -- your blog will be hosted at
http://example.com/~user
? In your Frog project directory, create an output directory named~user
:mkdir \~user
(note the\~
to allow using~
in the name). Then follow the steps above, including settingoutput-dir = ~user
anduri-prefix = /~user
in.frogrc
, and adjusting yourpage-template.html
and so on.
MathJax
To use MathJax:
-
Add configuration to the
<head>
of yourpage-template.html
. For a standard MathJax configuration simply add@math-jax[]
(call the themath-jax
function fromwidgets.rkt
). -
In your markdown source files, use
\\( some math \\)
for inline and\\[ some math \\]
for display. (Note the double backslashes,\\
, because in markdown\
already has a meaning.)
The serve function
If you wish to create your own static website, you can still take advantage
of continuous building when files change.
Place (require frog)
in your module, and use the serve
function
(found in frog/frog.rkt
) to continuously build your website.
Bug reports? Feature requests?
Please use GitHub Issues.