by Adam Brett

Getting Started With Sculpin

This article was published on Friday, February 07, 2014 which was more than 18 months ago , this means the content may be out of date or no longer relevant. You should verify that the technical information in this article is still current before relying upon it for your own purposes.

Ever since I really started using Jekyll (which powers this blog) I've been increasingly fascinated with the world of static site generators. Whilst at first they seem like a massive step back to 1994, what we have with this modern crop is nothing like the horrific CGI scripts we had back then.

The more I've used Jekyll the more I want to generate all of my sites with it. If the data isn't dynamic, why bother with a CMS or PHP? I can just write a markdown file and re-generate the site. As a programmer the work-flow is extremely appealing.

The main problem I've run in to with Jekyll is that it's designed as a blogging engine, not a static site generator. That means if you want to create something that's not a blog, you have to find a way to hack around it.

Sculpin1 is a static site generator first and foremost, and it is written in PHP using Symfony22, which is why when I first saw it I became very interested.

In it's own words:

Sculpin is a static site generator written in PHP. It converts Markdown files, Twig templates or standard HTML into a static HTML site that can be easily deployed.

The Getting Started section on the Sculpin website is entirely based on building a blog from a skeleton repository. What I needed was a really simple step-by-step guide to building a minimal Sculpin site from scratch, not using a skeleton that has a bunch of files I can't tell if I can remove or not.

So here is a highly opinionated guide to the absolute minimum you need to start working with Sculpin.

Install Sculpin

First, you need to download and install Sculpin. I like it globally available, like composer. This is also the recommended way on the Sculpin website.

wget https://download.sculpin.io/sculpin.phar
chmod +x sculpin.phar
mv sculpin.phar ~/bin/sculpin

If you don't have ~/bin on your path, edit ~/.bash_profile or ~/.bashrc and add this at the end:

PATH=$PATH:$HOME/bin

Then you need to re-load that file in your shell, so type (replace .bash_profile with .bashrc if that's what you edited):

source ~/.bash_profile

You should now be able to type sculpin from any directory on your system and get output similar to:

Sculpin Output

Directory Structure

Now that works, we need to create a project. I'll assume you're working in ~/Projects.

cd ~/Projects
mkdir my-first-sculpin-project
cd my-first-sculpin-project

Now, to get started there is only one directory you need to create:

mkdir source

This directory is where the source of your website lives. This is basically the markdown files and assets that you want Sculpin to convert to HTML.

Site Source

Inside source create an index.md file and include some YAML Front-matter blocks, just like in Jekyll. Leave the YAML Front-matter empty for now and add some markdown content:

---
---
# Hello World

This is a test

Generate The Site

Now from the root of your project run:

sculpin generate --watch --server

Here there are 3 important parts. First, we're telling Sculpin we want to generate the site. Next we're telling it to --watch for any changes, and if anything changes, re-generate automatically, and finally, we're telling it to create a --server for us to view the site on.

Open http://localhost:8000 in your browser and you should now be able to see your markdown converted to HTML.

This is the quickest and dirtiest way to get up and running with Sculpin. In reality, you're going to want more than raw markdown to HTML conversion, you're going to want to add a layout and some styles, so let's take a look at that.

Adding Some Style

There are two ways to do this in Sculpin, the basic way, which involves putting your layouts/views/images/css in the source folder directly, and then there's the theme way, which involves grouping them into easily swappable themes. The theme way is much nicer, so we're going do that.

Themes

Themes group together layouts, views, partials, includes, and assets (such as images and css) into an easily distributable package. Every time you reference an asset Sculpin will look for it first in your theme directory and then in your source directory. This means that you can override any theme files by re-creating them and customising them directly in your source directory.

Creating a theme involves two steps. First, we need to create a directory to hold your Sculpin configuration. From the root of your project type:

mkdir -p app/config

In here, you can add two files, the first, sculpin_kernel.yml customises the behaviour of Sculpin itself, and the second, sculpin_site.yml is used to customise your site, and has no effect on Sculpin's behaviour.

To add a theme, we want to use sculpin_kernel.yml so create that inside app/config and add the following:

sculpin_theme:
    theme: yourname/themename

In Sculpin, all themes are namespaced, just like PSR-0 packages. The first part of the theme name is the vendor namespace, and the second part is the theme name. Really simple.

In the second step, the theme namespace and theme name translate to a physical directory on disk inside source/themes, so let's create the directory to hold our new theme now:

mkdir -p source/themes/yourname/themename

Any assets and templates we create will now be loaded from your theme directory. You can create multiple themes and easily switch between them using the theme setting in app/config/sculpin_kernel.yml.

Layouts

The first thing you probably want to do is add a layout file. This will let you add some default navigation, a header and footer and so on. Sculpin will look in 8 different places to attempt to find your layout, but that can be confusing, so let's keep it simple and say there are two places you can put your layout:

source/_layouts
source/themes/yourname/yourtheme/_layouts

It's always best to store your layout in your theme, and overwrite it in your source directory on a site by site basis if you need too. This keeps themes nice and portable, and most importantly, re-usable. Let's create a directory to hold our first layout:

mkdir -p source/themes/yourname/yourtheme/_layouts

Now create a file in that directory called default.twig.html, and add some basic structure:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    {% block content %}{% endblock%}
</body>
</html>

This is a really standard HTML5 template, the only difference being some special mark-up in the body tags:

{% block content %}{% endblock %}

This is because Sculpin uses Twig3 as its template engine. This defines an area in the layout for Sculpin to inject the content from your rendered markdown files.

Finally, open up index.md and update your YAML Front-matter to look like this:

---
layout: default
---

Here, we're telling Sculpin that this file will use a layout file called default. Next time we run generate Sculpin will find that in our theme's _layout directory and render our markdown where the content block is placed. Let's give that a go:

sculpin generate --watch --server

If you load up http://localhost:8000 in your browser now and view source you should see your HTML and markdown rendered together. Any changes you make to either the layout or the markdown should be reflected when you refresh the page.

Templates

As Sculpin uses Twig as a templating engine, this means it can do anything Twig4 can do. Sculpin will look for templates/layouts etc in one of eight directories, firstly, in your theme:

  1. source/themes/yourname/yourtheme/_includes
  2. source/themes/yourname/yourtheme/_layouts
  3. source/themes/yourname/yourtheme/_partials
  4. source/themes/yourname/yourtheme/_views

And then the same again in your source directory:

  1. source/_includes
  2. source/_layouts
  3. source/_partials
  4. source/_views

The files in your source directory will take precedence over the files in your theme, so you can use this to overwrite any theme files you want to change without having to edit the theme.

Sculpin will allow you to use one of 4 file extensions for your Twig template files:

  1. .html
  2. .twig.html
  3. .html.twig
  4. .twig

I like to use .twig.html, but use whatever you're happiest with.

Sculpin extends Twig with 3 methods:

  • theme_path
  • theme_path_exists
  • theme_paths

Theme Path

This method will return the path to the file you pass in, relative to your current theme. e.g.:

// echos /themes/yourname/yourtheme/assets/css/style.css
{{ theme_path('assets/css/style.css') }}

Theme Path Exists

This method will return true or false depending on whether or not an asset exists. It is primarily for use with Twig's if tag:

{% if theme_path_exists('assets/css/ie.css') %}
    <!--[if IE]>
        <link rel="stylesheet" type="text/css" href="{{ theme_path('assets/css/ie.css') }}" />
    <![endif]-->
{% endif %}

Theme Paths

This will NOT do what you expect it to. Logically, you would expect a method called theme_paths to take a path then loop through all files in that path. This method doesn't do anything like that. Instead this method will find all paths in a theme for a single file. I'll explain that a bit more, because it doesn't make much sense.

Remember earlier we said that you could overwrite a theme file in your source directory? Well a theme itself can also overwrite a parent theme (set using a parent key in a theme.yml in the root directory of the theme), and what theme_paths will do is return all instances of a file from all locations in an iterable form.

{% for stylesheet in theme_paths('assets/css/style.css') %}
// stylesheets
// 1. /themes/parentname/parenttheme/assets/css/style.css
// 2. /themes/yourname/yourtheme/assets/css/style.css
// 3. /source/assets/css/style.css
{% endfor %}

theme_paths will only include the file if it exists, and will return an empty array if none of the paths exist.

At first glance, this doesn't seem very useful, however it allows for quite a powerful feature, in that it allows you to build on a parent file rather than overload it entirely, so if you were to use this for a css file, you could add or overwrite only the styles you need to and leave the rest of the file untouched as it will be included from the parent file.

Example

Going back to our example, let's demonstrate this functionality. Inside themes/yourname/yourtheme create a new directory css:

mkdir -p source/themes/yourname/yourtheme/css

And add a single css file inside that directory called style.css with a single rule:

body {
    background: #000;
    color: #fff;
}

This file will swap your background and foreground colours so you now have white text on a black background. Save the file, then open up your layout source/themes/yourname/yourtheme/_layouts/default.twig.html and add the following before the </head> tag:

{% for stylesheet in theme_paths('css/style.css') %}
    <link rel="stylesheet" type="text/css" href="{{ site.url }}/{{ stylesheet }}" />
{% endfor %}

Make sure you've saved all of your changes, then re-run sculpin generate --watch --server if it's not already running.

When you visit the site in your browser now you should see our css has been loaded from your theme. Let's demonstrate the theme_paths functionality though, and overwrite something in our source folder. Create a css directory in your source folder:

mkdir source/css

And add a css file with the same name (style.css) inside your new directory with the following contents:

h1 {
    color: red;
}

You may need to stop the Sculpin server and re-run it to see the changes, but once you do and then refresh the page you should see both stylesheets are now loaded.

Conclusion

Hopefully this has given you a thorough and simple introduction to building websites using Sculpin. Sculpin is already an extremely powerful tool, and as the documentation grows and the software matures it is going to become an even more outstanding product.

If you want to take a next step, I strongly recommend reading about and playing with Sculpin's Content Types5.

For exclusive content, including screen-casts, videos, and early beta access to my projects, subscribe to my email list below.


I love discussion, but not blog comments. If you want to comment on what's written above, head over to twitter.