April 8, 2023
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@endPushOnce6 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@minifyCSS2 <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 '<?php14 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 @endMinifyCSS12 </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 @endforeach19 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@endPushOnce6 7<div>8 <!-- component -->9</div>