SkillAgentSearch skills...

Serif

Serif is a static site generator and blogging system powered by markdown files.

Install / Use

/learn @aprescott/Serif
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Serif

Build Status Code Climate Coverage Status

Serif is a static site generator and blogging system powered by markdown files.

First time use

To get started with Serif based on a site skeleton:

gem install serif     # install serif
cd path/to/some/place # go to where you'll be creating your site directory
serif new             # create an initial site skeleton

# ... edit your files how you want them ...

serif generate        # generate the site based on the source files
serif dev             # serve up the site for local testing purposes

Now visit http://localhost:8000/ to view the site.

Contents of this README

Basics

Installing

Installation is via RubyGems. If you don't have Ruby installed and are new to Ruby, I recommend using RVM.

$ gem install serif

Generating the site

$ cd path/to/site/directory
$ serif generate

Serving up the site for development

This runs a very simple web server that is mainly designed to test what the site will look like and let you make changes to stuff like CSS files without having to regenerate everything. Changes to post content will not be detected (yet).

$ cd path/to/site/directory
$ serif dev

Once this is run, visit http://localhost:8000.

Generate a skeleton application

You can generate a skeletal directory to get you going, using the new command.

$ cd path/to/site/directory
$ serif new

Content and site structure

The structure of a Serif site is something like this:

.
├── _site
├── _layouts
│   └── default.html
├── _drafts
│   ├── some-draft
│   └── another-unfinished-post
├── _posts
│   ├── 2012-01-01-a-post-you-have-written
│   ├── 2012-02-28-another-post
│   └── 2012-03-30-and-a-third
├── _templates
│   ├─── post.html
│   └─── archive_page.html
├── _trash
├── _config.yml
├── css
│   └── ...
├── js
│   └── ...
├── images
│   └── ...
├── 404.html
├── favicon.ico
├── feed.xml
└── index.html

_site

This is where generated content gets saved. You should serve files out of here, be it with Nginx or Apache. You should assume everything in this directory will get erased at some point in future. Don't keep anything in it!

_layouts

This is where layouts for the site go. The file default.html is used by default, and individual files can override this by setting a Layout: foo header, which will use _layouts/foo.html instead.

_drafts and _posts

Drafts go in _drafts, posts go in _posts. Simple enough.

Posts must have filenames in the format of YYYY-MM-DD-your-post. Drafts do not have a date part, since they're drafts and not published.

All files in these directories are assumed to be written in Markdown, with simple HTTP-style headers. The Markdown renderer is Kramdown, with Smarty for punctuation tweaks, and Pygments to get syntax highlighting for GitHub-flavoured fenced code blocks (although you'll need your own CSS).

Here's an example post:

Title: A title of a post
Created: 2012-01-01T14:30:00+00:00

Something something.

1. A list
2. Of some stuff
3. Goes here

End of the post

Headers

The headers are similar to Jekyll's YAML front matter, but here there are no formatting requirements beyond Key: value pairs. Header names are case-insensitive (so title is the same as Title), but values are not.

File headers like Title: Some title can contain any header, but certain headers have special meaning in Serif.

Header name | Meaning ----------- |:------- Created | For a post, timestamp for when it was first published. Must be a string that Ruby's Time class can parse. Means nothing for drafts. Updated | For a post, timestamp for when it was last updated. Must be a string that Ruby's Time class can parse. Means nothing for drafts. Title | Title for the draft or post. Update | For a post, When given the value of now (i.e., Update: now), the Updated timestamp will be updated on the next site generation. Means nothing for drafts. Publish | For a draft, when given the value of now (i.e., Publish: now), the draft will be published on the next site generation (and the Created set appropriately). Means nothing for a post. Permalink | For a post, overrides the default permalink value defined in _config.yml. Note that this is interpolated, so :title in the permalink value will be replaced according to regular permalink rules. Means nothing for a draft.

Note that while it is possible for you to manually handle timestamp values, it is recommended that you rely on using the value of now for Update and Publish.

If you change the permalink value for a published post, you will break any inbound URLs as well as, e.g., any feeds that rely on the URL as a unique persistent ID.

_templates

Two templates are available:

  • post.html, which will be used to render individual post pages.
  • archive_page.html, which will be used to render individual archive pages.

Both must be valid Liquid templates.

_trash

Deleted drafts go in here just in case you want them back.

_config.yml

Used for configuration settings.

Here's a sample configuration:

permalink: /blog/:title
archive:
  enabled: yes
  url_format: /blog/:year/:month

If a permalink setting is not given in the configuration, the default is /:title. There are the following options available for permalinks:

Placeholder | Value ----------- |:----- :title | URL "slug", e.g., "your-post-title" :year | Year as given in the filename, e.g., "2012" :month | Month as given in the filename, e.g., "01" :day | Day as given in the filename, e.g., "28"

<b>NOTE</b>: if you change the permalink value, you will break existing URLs for published posts, in addition to, e.g., any feed ID values that depend on the post URL never changing.

Other files

Any other file in the directory's root will mostly be copied over exactly as-is.

An exception is any file ending in .html or .xml. These files are assumed to contain Liquid markup and will be processed as such. Header values will be available on page.

For example, this would work as an about.html:

<h1>All about me</h1>
<p>Where do I begin? {{ 'Well...' }}</p>

And so would this:

x: y

<h1>All about me</h1>
<p>Where do I begin? Well... First, x is {{ page.x }}.</p>

If you have a file like feed.xml that you wish to not be contained within a layout, specify layout: none in the header for the file:

layout: none

<?xml version="1.0" encoding="utf-8"?>
<!-- ... -->

Publishing drafts

To automatically publish a draft, add a publish: now header to the draft:

title: A draft that will be published
publish: now

This is a draft that will be published now.

On the next site generation (serif generate) this draft will be automatically published, using the current time as the creation timestamp.

Updating posts

When you update a post, you need to remember to change the updated time. As luck would have it, Serif takes care of timestamps for you! Just use a header of update: now at the top of your published post after making your changes:

title: My blog post
Created: 2013-01-01T12:01:30+00:00
update: now

Now the next time the site is generated, the timestamp will be updated:

title: My blog post
Created: 2013-01-01T12:01:30+00:00
Updated: 2013-03-18T19:03:30+00:00

Archive pages

By default, archive pages are made available at /archive/:year/month, e.g., /archive/2012/11. Individual archive pages can be customised by editing the _templates/archive_page.html file, which is used for each month.

Within the archive_page.html template, you have access to the variables month, which is a Ruby Date instance, and posts for the posts within that month.

To disable archive pages, or configure the URL format, see the section on configuration.

Linking to archive pages

To link to archive pages, there is a site.archive template variable available in all pages. The structure of site.archive is a nested map starting at years:

{
  "posts" => [...],
  "years" => [
    {
      "date" => Date.new(2012),
      "posts" => [...],
      "months" => [
        { "date" => Date.new(2012, 12), "archive_url" => "/archive/2012/12", "posts" => [...] },
        { "date" => Date.new(2012, 11), "archive_url" => "/archive/2012/11", "posts" => [...] },
        ...
      ]
    }
  ]
}

Using site.archive, you can iterate over years, then iterate over months and use archive_url to link to the archive page for that given month within the year.

Configuration

Configuration goes in _config.yml and must be valid YAML. Here's a sample configuration with available options:

permalink: /posts/:title
archive:
  enabled: yes
  url_format: /archive/:year/:month

permalink is the URL format for individual post pages. The default permalink value is `/:ti

Related Skills

View on GitHub
GitHub Stars113
CategoryDevelopment
Updated9mo ago
Forks11

Languages

Ruby

Security Score

87/100

Audited on Jul 5, 2025

No findings