Ryan Chandler

Revitalising my site for 2025

3 min read

If you've read some of my posts recently, you might have noticed that this one looks a little different. I've redesigned my site and made some serious upgrades.

The previous iteration of my site was built on top of an old Laravel application from about 4 years ago. It was still running on Laravel 10.x and had a load of old, unreachable code that I wanted to get rid of.

Now I could have gone through the steps to upgrade to Laravel 11.x and removed all of that old code, but I wanted to keep the skeleton up-to-date with the latest changes in Laravel 11.x, so I decided to start from scratch and rebuild the site as a new project.

I'll give you the basics of the new site.

It's running on Laravel 11.x and rendered completely with Blade – no Livewire, Vue or React. I'm using Tailwind for the styling and have purposely kept things minimal. Here's a comparison between the old and new home pages.

The old design.
The old design.
The new design.
The new design.

I found the content being on the right-hand side of a permanent sidebar annoying and the font weights felt wrong, so the new design goes back to a single column layout with everything flowing top-to-bottom.

That's probably enough on general design changes. You can browse the site yourself to see what things look like.

Content Management

All of my posts are written in Markdown. It's mostly CommonMark-flavoured, with a few custom additions.

The previous iteration of my site actually had a Filament panel with a MarkdownEditor where I could manage everything, but I've decided to get rid of it completely. I'm much more comfortable writing Markdown inside of my text editor, so the Filament panel felt pointless.

A screenshot of this blog post's Markdown file in my editor.
A screenshot of this blog post's Markdown file in my editor.

All of the content is stored on-disk in Markdown files and I use my Orbit package to create Eloquent models from the files. This means that I can fetch content from disk the same way that I would from a regular database.

In terms of custom Markdown features, I've actually only got two right now.

Blade blocks

I've added support for Blade to my Markdown files. This means that I can do the following:

@blade
    <!-- Put my custom Blade code here. -->
@endblade

I purposely chose something that resembles a Blade directive since it doesn't interfere with any other syntax in a Markdown and is easy to spot when scanning a file. I'm actually using this to do the little image grid above.

@blade
<x-typography.image-grid>
    <x-typography.figure :src="Vite::asset('...')" />
    <x-typography.figure :src="Vite::asset('...')" />
</x-typography.image-grid>
@endblade

The benefit to this approach is that I can literally embed anything I want here. I can use Vite to get image paths, build a custom Alpine.js component for a blog post, etc.

Aside blocks

Another custom block that I've added is the "aside" block. This literally generates an aside element where I can place additional content that isn't necessarily required reading for a blog post.

The super cool thing is that on larger screens (Tailwind xl and above), I can place them in the left and right gutter, a bit like the one you should see just to the right of this paragraph.

Here's the syntax for that:

>> This block will float to the right of the page, inside of the gutter.

and if I want one on the left, I just reverse the opening sequence of characters.

<< This one should show up on the left side of the screen.
<<
<< Inline elements are supported, such as **bold**, _italic_ and `code snippets`.

When I want these "aside" blocks to have a little bit of context, I can append the opening sequence of characters with an identifier and use CSS to style the block differently.

>>[danger] This one has a little heading and some special colours.

Syntax Highlighting

In the past I've used Shiki, via the spatie/laravel-markdown package and Torchlight, to perform syntax highlighting on code blocks.

If you're not familiar with Shiki, it's a syntax highlighter written in JavaScript that uses TextMate grammars and Visual Studio Code themes to syntax highlight code snippets. It's the same technology (under the hood) that powers Visual Studio Code itself.

This is a powerful tool because any languages that has VSCode support, out of the box or through an extension, can be highlighted by Shiki. The one downside to Shiki and tools that use it, such as the ones mentioned above, is that you need a way of executing that JavaScript from PHP.

laravel-markdown will shell out to a Node script and return the highlighted code and Torchlight does it all for you on a remote server so you need to call it through an API. This comes with some noticeable overhead, especially when you're writing a blog post and viewing changes live in the browser.

To make this more of a seamless experience, I actually wrote my own syntax highlighter in PHP called Phiki. As you can tell from the name, it's "Shiki for PHP".

It uses TextMate grammars and VSCode themes to syntax highlight code, but does it all with PHP instead of relying on a JavaScript package. This makes it much faster to execute and removes any need for caching or memoization.

The generated, highlighted code is pretty much perfect for most languages. Here's an example of some PHP code highlighted with Phiki.

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * Define the application's command schedule.
     */
    protected function schedule(Schedule $schedule): void
    {
        // $schedule->command('inspire')->hourly();
    }

    /**
     * Register the commands for the application.
     */
    protected function commands(): void
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

Phiki adds a data-language attribute to the generated HTML which lets you hook up a "language tag" to your code blocks. That's what you see in the top-right corner of the code block above.

I said that it's perfect for "most languages" and there's a reason for that. Shiki, or more specifically TextMate and vscode-textmate, use the Oniguruma engine to handle RegEx matching. PHP uses the PCRE2 engine and that means there are some compatibility issues with patterns used in TextMate grammars.

Most things can be translated, or "lowered", into PCRE-compatible equivalents. This eliminates quite a few issues and differences between the two engines.

The main problem holding Phiki back right now is the fact that Oniguruma supports "variable-length lookbehinds", but PCRE2 doesn't. The first way to resolve this would be manually patching grammars that use this RegEx feature, but that requires a solid understanding of the grammar itself and intended output. I've done this for quite a few grammars, but it's not the smoothest.

My current idea is building a layer on top of PHP's preg_* functions that implements a very small RegEx engine to handle these incompatible patterns. In 99% of cases, Phiki can use PHP's regular RegEx functions. If it comes across a pattern that throws up a problem then it can defer to the slower, but more powerful, custom RegEx engine.

I'm still reading up on how RegEx engines work, so don't expect to see that fully implemented anytime soon. Do expect some blog posts as I build the engine though, as I think it'll be pretty interesting to read about.

Social images

My old site used a third-party service to generate Open Graph images on-demand. This was overkill and honestly £7 not worth spending, so my new site generates them ahead of time using Puppeteer on my machine.

Here's what they look like:

The Open Graph image for this blog post.
The Open Graph image for this blog post.

I just have a single Artisan command that loops through posts and pages, calls out to Puppeteer and then stores the image so I can commit to GitHub and deploy. It's free and actually quite fast.

Sign off

Thanks for reading if you made it this far. I like reading about how websites are built and what happens behind the scenes, hopefully you enjoyed this too.

Personal sites are never fully complete. I treat mine like a playground / testing ground for ideas and that's why I wanted to wipe the slate clean. Give myself a solid foundation for future ideas.