How to Add an Unsplash or Custom Hero Image to a Jigsaw Article

by webmaster 2020-03-10 #jigsaw #laravel
Photo by Esteban Lopez on Unsplash (https://unsplash.com) Photo by Esteban Lopez on Unsplash

The fashion these days dictates that every developer blog post come with a hero image. While not entirely true, I started this pattern with the blog and I'll be damned if I end it.

The blog engine itself is Tighten's Jigsaw, a Laravel static site builder that is perfect for my needs. Because it is Laravel/PHP based, the base template can be customized to a high extent, something that I have done here with some success.

Today I'll go into more detail about one customization in particular: the hero image featured at the top of every article.

Image types

Whether you've read my articles before or not, there are two types of hero images I typically use at the top of a blog post. There's the generic Unsplash image that bears a resemblance to the subject matter, like this very article for example. Then there's the more technical image that I create myself, such as this article about having Fun at the Laravel Console.

This boils down to either self-hosted or Unsplash images. The way each of these is generated differs slightly.

The blog post metadata

Disclaimer I write all articles in Markdown and I'm not sure how (and if) this would work in other formats.

Each Jigsaw Markdown post (located in {project}/source/_posts) has a metadata section at the top, written in YAML front-matter. This defines various article-specific parameters.

The current article, for example, would have the following as built-in defaults:

---
extends: _layouts.post
section: content
title: How to Add an Unsplash or Custom Hero Image to a Jigsaw Article
date: 2020-03-10
description: A guide for adding a custom hero image programatically to a Jigsaw blog post.
tags: [jigsaw, laravel]
featured: false
---

It's a very clean and simple format that is self-explanatory in what it accomplishes, so I won't go into further detail here, but you can read more on the official documentation page.

The cool thing is that you can extend this metadata to the limits of your imagination. This is exactly what I did in order to automate displaying custom hero images at the top of each article. Let's find out how.

Unsplash images

For Unsplash images, such as the current article, I've added these extra parameters:

---
# defaults
image: https://source.unsplash.com/6yjAC0-OwkA/?fit=max&w=1350
image_thumb: https://source.unsplash.com/6yjAC0-OwkA/?fit=max&w=200&q=75
image_author: Esteban Lopez
image_author_url: https://unsplash.com/@exxteban
image_unsplash: true
image_overlay_text:
---

The above will render the image along with the attribution right below it: Photo by Esteban Lopez on Unsplash. Both the site and the author are linked.

Custom, self-hosted images

My article on the Laravel Console features a custom image that is self-hosted and saved in the /assets/img/ project directory. This is how the metadata looks:

# defaults
image: /assets/img/2020-02-10-laravel-console-fun.png
image_thumb: /assets/img/2020-02-10-laravel-console-fun.png
image_author: 
image_author_url: 
image_unsplash: 
image_overlay_text:

You can omit the empty keys, of course. I choose to keep them around to remind myself they exist.

Modifying the post Blade template

Simply adding the additional metadata won't magically cause the image to be rendered. The first thing to make that happen is to include a Blade partial at the top of the source/_layouts/post.blade.php file.

@include('_partials.post-hero-image')

Then in source/_partials/post-hero-image.blade.php I have the following:

@if($image = $page->image)
    <section class="w-full flex flex-col items-center justify-center relative">
        @if($imageOverlayText = $page->image_overlay_text)
            <div
                class="absolute font-black p-12 text-6xl rounded-full"
                style="
                    color: #ff0a5c;
                    background-color: #ffeb3b;
                    filter: invert(1);
                    mix-blend-mode: exclusion;
                    transform: rotate(-5deg);
                    box-shadow: 15px 15px #ff0a5c;
                    text-shadow: 5px 5px 1px #05e2ff;
                "
            >
                {{ $imageOverlayText }}
            </div>
        @endif

        <img src="{{ $image }}" alt="{{ $page->imageAttribution() ?: $page->title }}">

        @if($imageAttribution = $page->imageAttribution(true))
            <small class="block text-center text-xs">
                {!! $imageAttribution !!}
            </small>
        @endif
    </section>
@endif

This part @if($imageOverlayText = $page->image_overlay_text) is recent, and I'll circle back to it in a shortly.

Starting at the top, the entire block is wrapped in a check for the existence of an image source @if($image = $page->image). For Unsplash images it's a absolute URL, while for local images it's a relative path.

Next, the image is displayed <img src="{{ $image }}" alt="{{ $page->imageAttribution() ?: $page->title }}">. The alt text will be either the Unsplash author attribution, or the title of the page if the former is missing.

Finally, if there's an attribution (in other words an Unsplash image), I'll show the Photo by X on Unsplash snippet below the image:

@if($imageAttribution = $page->imageAttribution(true))
    <small class="block text-center text-xs">
        {!! $imageAttribution !!}
    </small>
@endif

The final piece of the puzzle is the custom $page->imageAttribution() method, which I will explain next.

The image attribution method

In Jigsaw, you can define your own global helper methods in /config.php, inside the main array. Here's what imageAttribution() looks like:

return [
    // ...

    'imageAttribution' => function ($page, $html = false) {
        $str = '';

        $image_author = $page->image_author;
        $image_author_url = $page->image_author_url;

        if ($image_author) {
            $str .=  "Photo by ";

            if ($html) {
                if ($image_author_url) {
                    $str .= '<a href="' . $image_author_url . '" title="' . $image_author . '">' . $image_author . '</a>';
                } else {
                    $str .=  "$image_author ($image_author_url)";
                }
            } else {
                $str .= "$image_author";
            }
        }

        if ($page->image_unsplash) {
            if ($html) {
                $str .= ' on <a href="https://unsplash.com" title="Unsplash">Unsplash</a>';
            } else {
                $str .= ' on Unsplash (https://unsplash.com)';
            }
        }

        return $str;
    },
];

I hope the above is self-documenting, but in a nutshell it's sole purpose is to render the Photo by X on Unsplash snippet, with the choice of plain text or HTML (pass the true argument). I use the HTML version for displaying below the image, while the plain text goes in the image alt text.

Custom image overlay text

My 2020 Tech Radar article features some funky text overlayed on top of an Unsplash image. This text is controlled by this post meta:

---
# ...
image_overlay_text: 2020 Tech Radar
---

Going back to source/_partials/post-hero-image.blade.php, the following block controls whether this text is displayed:

@if($imageOverlayText = $page->image_overlay_text)
    <div
        class="absolute font-black p-12 text-6xl rounded-full"
        style="
            color: #ff0a5c;
            background-color: #ffeb3b;
            filter: invert(1);
            mix-blend-mode: exclusion;
            transform: rotate(-5deg);
            box-shadow: 15px 15px #ff0a5c;
            text-shadow: 5px 5px 1px #05e2ff;
        "
    >
        {{ $imageOverlayText }}
    </div>
@endif

There is an obvious issue with this approach: it is quite inflexible. I tweaked the text styling to work well with that particular image, but if I try to apply it to other images, it will likely look out of place.

I'm a fan of the Rule of three refactoring principle, so if I reach the point where I'm doing this 2-3 more times (each would have to be individually tweaked), one solution I could reach for is to add another meta parameter that points to a CSS class. I'd then move the styling to one of my SCSS files and simply apply the corresponding class to the snippet above.

Conclusion

I ❤️ how flexible Jigsaw is, and the extreme degree to which it can be customized. This is a relatively simple example of what can be done within this platform, but the world is your oyster, as they say.

Liked this article? Share it on your favorite platform.