Hyperlight

4. Theming

Disclaimer: This page assumes you know some PHP and HTML.

To start making your own theme, all you need to do is create a directory in the themes folder and create an index.php file.

For example, here is a basic folder structure at the very minimum. You can add whatever files you like with the theme as long as index.php is present:

themes/NAME/
    index.php

4.1 Metadata

As of version 1.2.2, there is no formally defined place to put metadata, as there is no theme selection nor admin panel. This will change in later versions.

If you would like to include some metadata about your theme, such as author information and copyright details, create a text file or readme document.

4.2 API Documentation

Everything you will need is either in the $Blog object, as an $Entry object, or in the Config class, although there are also a few helper functions. All will make sense in a moment.

4.2.1 Config class:

  • Properties:

  • Functions:

    • get_theme_css_dir()

      Echo's the URL pointing towards the css directory for your theme.

    • get_theme_js_dir()

      Echo's the URL pointing towards the js directory for your theme.

    • get_home_link()

      Echo's the URL pointing towards the blog's homepage.

    • get_page_url():

      Returns the current page, including whether a tag was included

    • get_post_link($post_slug):

      Returns a link to a specific blog post

    • get_tag_link($tag_slug):

      Returns a link to the archive of a specific tag

    • using_parsedown():

      Returns true/false depending on whether we are using markdown conversion

4.2.2 $Blog object structure:

  • Properties:

    • posts: An array of posts to display on the screen at once. When viewing a single page or post, the length of this array is only 1.

    • pages: An array of pages in the same format as posts.

    • url: An enum describing what page you are currently viewing. Can be one of the following:

      • Url::Archive: Viewing a list of posts, whether that contains a tag or not

      • Url::Post: Viewing a single post

      • Url::Page: Viewing a page

      • Url::Error404: When attempting to view a Post or Page and it doesn't exist, it will default to this instead.
  • Functions:

    • get_title()

      Returns the title of the currently viewed page, depending on what URL you're on. Usually used in the <title> tag in the HTML's header.

    • get_page_num()

      Returns the current page you're on. Used on the Archive URL.

    • get_page_prev()

      Returns a URL the user can visit to visit the previous page.

    • get_page_next()

      Returns a URL the user can visit to visit the next page.

    • has_page_prev()

      Returns true/false depending on whether there is a previous page to actually go to.

    • has_page_next()

      Returns true/false depending on whether there is a next page to actually go to.

    • has_pagination()

      Returns true/false depending on whether there are even enough posts to use pagination

    • get_page_total()

      Returns the total number of pages

4.2.3 $Entry object structure (Post or Page):

  • Properties:

    • title: The title of an entry.

    • slug: The URL friendly version of the title, taken from the filename of the entry.

    • summary: A short summary of the entry

    • image: A URL pointing towards a featured image.

    • tags: An array of tags used to group an entry with others.

    • content: The actual body of a post, containing HTML.

    • timestamp: The unix timestamp of when the entry's file was created.

    • edited: The unix timestamp of when the entry's file was last modified.
  • Functions:

    • has_image()

      Returns true/false depending on whether there is a featured image for this entry or not.

    • has_tags()

      Returns true/false depending on whether an entry has any tags or not.

    • date_pretty()

      Returns the date in the format specified in the config.php file (see 2. Configuration for more info.)

    • date_datetime()

      Returns the date in the HTML5 datetime format (Y-m-d\TH:i:s)

4.3 Writing your first theme

Now that you've created your index.php file and read through the above documentation, you're ready to start with the basics.

4.3.1 The <head> tag

This will contain some metadata such as the page title and CSS styling. Below is an example of how you may use the API to set these values:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><?php echo $Blog->get_title(); ?></title>
    <link rel="stylesheet" href="<?php get_theme_css_dir(); ?>/style.css">
    <script type="text/javascript" src="<?php get_theme_js_dir(); ?>/jquery.min.js"></script>
</head>

4.3.2 Site Header

Should you want to include the blog's name at the top of every page, it's as simple as including the following line anywhere inside the <body> section:

<?php echo Config::Title; ?>

This can then be used inside a anchor and heading tag so it always links to the homepage:

<a href="<?php echo Config::Root; ?>">
    <h1><?php echo Config::Title; ?></h1>
</a>

4.3.3 List Pages

To display a list of clickable pages, simple put $Blog->pages inside a foreach loop:

<ul>
    <?php foreach ($Blog->pages as $Page) {
        $link = Config::Root . $Page->slug;
        $title = $Page->title;
        echo "<li><a href='{$link}'>{$title}</a></li>";
    } ?>
</ul>

4.3.4 Main Content

This is where the magic happens. The easiest way to display the correct content is to use a switch case statement on $Blog-url, this is described as below:

switch ($Blog->url) {
    case Url::Error404:
        // Put your error code here
        break;

    case Url::Archive:
        // Display a number of things here
        break;

    case Url::Page:
    case Url::Post:
        // Display a single post or page
        // here, using `$Blog->posts[0]`
        break;

    default:
        // It's here just in case
        break;
}

4.3.5 Post Archive

In the case of Url::Archive, you probably want to do a couple of checks, such as whether there are actually any posts to display, and including pagination if necessary. To make this much easier to explain, we can separate this into a separate function called "archive()", so our Url::Archive case now looks like this:

// ...
    case Url::Archive:
        archive();
        break;
// ...

In our archive function, we will do as we said above, check for posts and include pagination.

Checking for posts is easy, as we simply count the number of entries in the posts array. Then, if there are valid posts, we can loop through them and display them. Afterwards, we will display any pagination if there needs to be (we'll get onto that a bit later on):

function archive() {
    if (count($Blog->posts) == 0) {
        echo "There are no posts!";
    } else {
        foreach ($Blog->posts as $Entry) {
            // Display post
        }
        if ($Blog->has_pagination()) {
            // Display pagination
        }
    }
}

4.3.6 Displaying Posts

In the previous section, we put in a placeholder comment that said // Display post. We'll write another PHP function to do that for us because it will come in very handy later on.

Our display_post function will take in two parameters: the entry, and whether it appears in the archive or by itself. Here is the function definition:

//                   $Entry, boolean
function display_post($entry, $archive) {

}

Inside this function, we want to use the properties of the $Entry object to display our post. We can put the title inside a <h2> tag, and add the featured image, tags, and main content too.

function display_post($entry, $archive) {
    // Creating an article tag to put everything
    echo "<article id='{$entry->slug}'>";

    // Entry Title
    echo "<h2>{$entry->title}</h2>";

    // Featured Image
    echo "<img src='{$entry->image}' />";

    // Main Content
    echo "<div>{$entry->content}</div>";

    // Print out the tags
    echo "<div>Tags: "
    foreach ($entry->tags as $tag) {
        echo "{$tag}; ";
    }
    echo "</div>";

    // Don't forget to close the article tag!
    echo "</article>";
}

Now, obviously this can be customised by adding links to the tags, and adding classes and styles to each element, but that's something you should be able to figure out easily enough using all the information given to you so far on this page!

Let's make use of that $archive variable, as we mentioned earlier. The idea is that this function can not only be used for the archive, but for displaying single posts too, to save writing duplicate code! Let's say we want to include a link to the entry and display the summary instead of the content when viewing the post on the archive. That's simple, just add an if statement here or there:

// ...
    if ($archive === true) {
        $link = get_post_link($entry->slug);
        echo "<h2><a href='{$link}'>{$entry->title}</a></h2>";
    } else {
        echo "<h2>{$entry->title}</h2>";
    }
// ...
    if ($archive === true) {
        echo "<div>{$entry->summary}</div>";
    } else {
        echo "<div>{$entry->content}</div>";
    }
// ...

4.3.7 Pagination

Now we have a function to print posts, but what happens when we have too many? We need to add buttons for pagination!

So, we'll need to check if we even need pagination (that was done earlier in section 3.3.5), then we'll need to check what page we're on so to put links to the right places. That looks like this:

function pagination() {
    echo "<div>";
    if ($Blog->has_page_prev()) {
        echo "<a href='{$Blog->get_page_prev()}'>< Newer</a>";
    }
    if ($Blog->has_page_next()) {
        echo "<a href='{$Blog->get_page_next()}'>Older ></a>";
    }
    echo "</div>";
}

4.3.8 Footer Information

The last thing we may want on our page is the footer. That's just as easy as 3.3.2 (adding the title to the start of every page)!

<footer>
    <?php echo Config::Footer; ?>
</footer>

4.3.9 Putting all the pieces together!

Alrighty, now we have the main switch case statement, the archive(), display_post($entry, $archive), and pagination() functions written, so it's time to piece them all together so it actually works! This is what the final index.php file should look like at the end:

<?php
/// 4.3.1 The `<head>` tag
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><?php echo $Blog->get_title(); ?></title>
    <link rel="stylesheet" href="<?php get_theme_css_dir(); ?>/style.css">
    <script type="text/javascript" src="<?php get_theme_js_dir(); ?>/jquery.min.js"></script>
</head>
<body>

<?php
/// 4.3.2 Site Header
?>
<a href="<?php echo Config::Root; ?>">
    <h1><?php echo Config::Title; ?></h1>
</a>

<?php
/// 4.3.3 List Pages
echo "<ul>";
foreach ($Blog->pages as $Page) {
    $link = Config::Root . $Page->slug;
    $title = $Page->title;
    echo "<li><a href='{$link}'>{$title}</a></li>";
}
echo "</ul>";

/// 4.3.4 Main Content
switch ($Blog->url) {
    case Url::Error404:
        echo "Error 404: Page/Post Not Found!";
        break;

    case Url::Archive:
        archive();
        break;

    case Url::Page:
    case Url::Post:
        display_post($Blog->posts[0], false);
        break;

    default:
        // It's here just in case
        echo "Something went wrong?";
        break;
}

/// 4.3.5 Post Archive
function archive() {
    if (count($Blog->posts) == 0) {
        echo "There are no posts!";
    } else {
        foreach ($Blog->posts as $Entry) {
            display_post($Entry, true);
        }
        if ($Blog->has_pagination()) {
            pagination();
        }
    }
}

/// 4.3.6 Displaying Posts
function display_post($entry, $archive) {
    // Creating an article tag to put everything
    echo "<article id='{$entry->slug}'>";

    // Entry Title
    if ($archive === true) {
        $link = get_post_link($entry->slug);
        echo "<h2><a href='{$link}'>{$entry->title}</a></h2>";
    } else {
        echo "<h2>{$entry->title}</h2>";
    }

    // Featured Image
    echo "<img src='{$entry->image}' />";

    // Main Content
    if ($archive === true) {
        echo "<div>{$entry->summary}</div>";
    } else {
        echo "<div>{$entry->content}</div>";
    }

    // Print out the tags
    echo "<div>Tags: "
    foreach ($entry->tags as $tag) {
        echo "{$tag}; ";
    }
    echo "</div>";

    // Don't forget to close the article tag!
    echo "</article>";
}

/// 4.3.7 Pagination
function pagination() {
    echo "<div>";
    if ($Blog->has_page_prev()) {
        echo "<a href='{$Blog->get_page_prev()}'>< Newer</a>";
    }
    if ($Blog->has_page_next()) {
        echo "<a href='{$Blog->get_page_next()}'>Older ></a>";
    }
    echo "</div>";
}

/// 4.3.8 Footer Information
?>
<footer>
    <?php echo Config::Footer; ?>
</footer>
</body>
</html>

4.4 Outro

Obviously, you can go as fancy or as minimal as you like, these are only the absolute basics to making a theme for Hyperlight. For example, the default theme is a bit more of a mess than this example, but that's because it has lots of styles, including code highlighting like you've seen here.