Minimal Jigsaw site

I like simple under-engineered solutions.

This site was built with Jigsaw a couple of years ago, but I didn't like that I was using both PHP and Node.js (to compile assets with Laravel Mix). I switched to an all Node.js solution (Astro) for about a year, but I missed the simplicity of Jigsaw in comparison.

So I switched back to Jigsaw, but decided I'd take a minimal approach this time with the below goals in mind:

  • Zero NPM dependencies. I want the site to be solely compiled with PHP.
  • Single file components. I want to author the component's CSS in the same file I author the HTML. An approach I really liked in Astro.

Component CSS

The goal is to be able to author CSS and HTML in the same component file

1<style>
2 /* my component CSS */
3</style>
4 
5<div>
6 <!-- my component -->
7</div>

Using Blade's stacks, the <style> element can be pushed to the <head>:

1<!DOCTYPE html>
2<html lang="en">
3<head>
4 @stack('css')
5</head>
6.
7.
8</html>

Using the @pushOnce directive, the <style> element is only pushed to the <head> once per page. It is also only pushed if the component is used on the page.

1@pushOnce('css')
2 <style>
3 /* my component CSS */
4 </style>
5@endPushOnce
6 
7<div>
8 <!-- my component -->
9</div>

For a light site the CSS doesn't need to be served via an external style sheet. So I am happy to keep the components' CSS in embedded <style> elements.

Base CSS

The base CSS is authored in a CSS file. So I could add it to the page via an external style sheet, but seeing how small it is I chose to follow the same approach as the components' CSS and embed it directly to the page using the inline() helper function:

1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <style>
5 {{ inline('/assets/build/css/main.css') }}
6 </style>
7 
8 @stack('css')
9</head>
10.
11.
12</html>

CSS Minification

Considering the way I chose to author the CSS, I decided to create a pair of custom Blade directives that minifies the CSS inside it using a PHP-based minifier (matthiasmullie/minify):

1@minifyCSS
2 <style>
3 {{ inline('/assets/build/css/main.css') }}
4 </style>
5 
6 @stack('css')
7@endMinifyCSS

./blade.php:

1<?php
2/** @var \Illuminate\View\Compilers\BladeCompiler $bladeCompiler */
3 
4$bladeCompiler->directive('minifyCSS', function ($expression) {
5 return '<?php
6 if($page->minifyCSS) {
7 ob_start();
8 }
9 ?>';
10});
11 
12$bladeCompiler->directive('endMinifyCSS', function ($expression) {
13 return '<?php
14 if($page->minifyCSS) {
15 $minifier = new MatthiasMullie\Minify\CSS();
16 $minifier->add(str_replace(["<style>", "</style>"], "", ob_get_clean()));
17 echo "<style>" . $minifier->minify() . "</style>";
18 }
19 ?>';
20});

And in the configuration file of any environment where CSS needs to be minified, set minifyCSS to true:

1return [
2 'production' => true,
3 'minifyCSS' => true,
4];

JavaScript

Right now the site doesn't use any JS, but if I was to add any, I would use Alpine.js. This would enable me to keep this NPM-free and continue with my single file component approach.

Plus I love Alpine!

Syntax highlighting

I blog about web dev so code syntax highlighting is a must. I offloaded the syntax highlighting to Torchlight which has first-party support for Jigsaw.

Putting it together

Example layout

1<!DOCTYPE html>
2<html lang="{{ $page->language ?? 'en' }}">
3 <head>
4 {{-- meta tags --}}
5 
6 @minifyCSS
7 <style>
8 {{ inline('/assets/build/css/main.css') }}
9 </style>
10 @stack('css')
11 @endMinifyCSS
12 </head>
13 <body>
14 <x-header />
15 
16 <main>
17 @yield('body')
18 </main>
19 
20 <x-footer />
21 </body>
22</html>

Example page

1@extends('_layouts.main')
2 
3@pushOnce('css')
4 <style>
5 /* page-specific CSS */
6 </style>
7@endPushOnce
8 
9 
10@section('body')
11 {{-- page content example --}}
12 
13 <section>
14 <h2>Latest posts</h2>
15 
16 @foreach($content_posts->take(5) as $post)
17 <x-posts.card :post="$post" />
18 @endforeach
19 
20 <a href="/blog">View all posts →</a>
21 </section>
22@endsection

Example component

1@pushOnce('css')
2 <style>
3 /* component CSS */
4 </style>
5@endPushOnce
6 
7<div>
8 <!-- component -->
9</div>