@20dimensions/liquid-site-builder
v0.1.11
Published
Convention-based multi-language static site builder with Liquid templates and Vite integration
Readme
@20dimensions/liquid-site-builder
Convention-based multi-language static site builder with Liquid templates and Vite integration.
TODO
- Documentation: Use the playground as the actual documentation for this project.
- Validation (Unused Translations): Warn when
en.jsoncontains translation keys that are never used in any.liquidtemplates (e.g. by scanning for{{ 'key' | t }}or{{ i18n.key }}). - Validation (Unused Components): Warn when a file in
src/components/is never included via{% render '...' %}anywhere in the codebase. - Validation (Empty Frontmatter): Warn when pages have formatting errors in their frontmatter or supply unknown liquid parameters.
Features
- File-based routing — drop a
.liquidfile insrc/pages/, it becomes a page - Multi-language — auto-detects languages from
src/locales/*.jsonand merges them with page-specific translations (src/pages/.../en.json) - YAML frontmatter — layouts, sitemap priority, noindex, per-page metadata
- Layout system — multiple layouts selectable via frontmatter
- Auto sitemap (Git-based) — generates
sitemap.xmlwithhreflangalternates, usinggit logto calculate hyper-accuratelastmoddates across templates and localized JSON files. - 404 fallback — automatically serves
/{lang}/404/on missing routes during development - Validation — warns on missing translations, broken includes, missing layouts
- Vite plugin — auto rollup inputs, file watcher with HMR, root redirect, and dev server routing
- Per-page JS —
<script type="module">in templates for progressive enhancement - Environment variables —
.envfiles with Vite-compatible precedence - SVG Spritemaps — Built-in support for
vite-plugin-svg-spritemap
Installation
npm install @20dimensions/liquid-site-builderPeer Dependencies
| Package | Version |
|---|---|
| vite | >=5.0.0 (optional, for Vite plugin) |
Official Plugins
- @20dimensions/vite-plugin-liquid-image: A drop-in framework-agnostic LiquidJS
{% optimized_image %}tag + Vite pipeline for responsive WebP/AVIF generation. Automatically prevents Layout Shift and generates retina HTML components.
Quick Start
1. Project Structure
my-site/
src/
pages/
index.liquid ← /en/, /de/
pricing/
index.liquid ← /en/pricing/, /de/pricing/
en.json ← Page-specific translations
de.json
components/
hero.liquid ← partials via {% render 'hero' %}
layouts/
default.liquid ← HTML shell, receives {{ content }}
locales/
en.json ← Global translations
de.json
css/
images/
vite.config.js2. Configuration
vite.config.js:
import { defineConfig } from 'vite';
import { liquidSitePlugin } from '@20dimensions/liquid-site-builder/vite';
export default defineConfig({
plugins: [
liquidSitePlugin({
domain: 'https://example.com',
defaultLanguage: 'en',
})
],
});3. Layout Template
src/layouts/default.liquid:
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="UTF-8">
<title>{{ i18n.meta.title }}</title>
<link rel="canonical" href="{{ canonicalUrl }}">
{% for altLang in allLangs %}
<link rel="alternate" hreflang="{{ altLang }}" href="{{ alternateUrls[altLang] }}">
{% endfor %}
{% block head %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>4. Page with Frontmatter
src/pages/pricing/index.liquid:
---
layout: default
priority: 0.8
---
<section>
<h1>{{ i18n.title }}</h1>
<p>{{ i18n.description }}</p>
{% render 'pricing-table' %}
</section>src/pages/pricing/en.json:
{
"title": "Pricing Plans",
"description": "Choose the best plan for you."
}By colocating your translations right next to the .liquid file, i18n will automatically be deep-merged with the global locales/en.json specifically and exclusively for the pricing page!
You can also natively inject completely custom code, logic, or metadata specifically up into the layout's <head> block by simply defining it anywhere within your page file:
{% block head %}
<meta name="author" content="Jane Doe">
<script src="https://cdn.jsdelivr.net/example.js"></script>
{% endblock %}5. Automated JSON-LD (SEO Schema)
The site builder ships with a native json_ld Liquid filter specifically designed to safely escape and serialize variables for <script type="application/ld+json"> tags to prevent XSS:
{% block head %}
<script type="application/ld+json">
{{ page.my_schema | json_ld }}
</script>
{% endblock %}Template Context
Every page template and layout receives these variables:
| Variable | Type | Description |
|---|---|---|
| i18n | object | Full translation object for the current language |
| lang | string | Current language code (e.g. 'en') |
| allLangs | string[] | All available language codes |
| globals | object | Global variables from vite.config.js |
| canonicalUrl | string | Full canonical URL for this page |
| alternateUrls | object | Map of {lang: url} for hreflang |
| alternatePaths | object | Map of {lang: path} for relative site navigation |
| slug | string | The route slug for the current page (e.g. 'about') |
| page | object | Frontmatter data for the current page |
Frontmatter Options
| Field | Type | Default | Description |
|---|---|---|---|
| layout | string | 'default' | Layout template to use |
| priority | number | 0.5 | Sitemap priority (0.0–1.0) |
| noindex | boolean | false | Exclude from sitemap |
| title | string | — | Translation key or string override for <title> |
| description | string | — | Translation key or string override for meta description |
Configuration Reference
| Option | Type | Default | Description |
|---|---|---|---|
| domain | string | required | Production domain |
| defaultLanguage | string | required | Default language for root redirect |
| pagesDir | string | 'src/pages' | Pages directory |
| componentsDir | string | 'src/components' | Partials directory |
| layoutsDir | string | 'src/layouts' | Layouts directory |
| localesDir | string | 'src/locales' | Locale JSON directory |
| outputDir | string | 'dist' | Build output directory |
| globals | object | — | Global variables for templates |
Globals
Pass any values to your templates via the globals field in your Vite config:
export default defineConfig({
plugins: [
liquidSitePlugin({
domain: 'https://example.com',
defaultLanguage: 'en',
globals: {
apiUrl: 'https://api.example.com',
analyticsId: 'UA-12345',
},
})
],
});Access in templates via {{ globals.apiUrl }}.
SVG Spritemaps
The builder includes robust, enterprise-ready support for SVG spritemapping via vite-plugin-svg-spritemap.
1. Installation
This is an optional feature. To enable it, simply install the peer dependency:
npm install vite-plugin-svg-spritemap -D2. Usage
Once installed, simply create a src/icons/ directory in your project root and drop your raw .svg files there. The builder will automatically:
- Scan for any
.svgfiles insrc/icons/. - Generate an optimized SVG spritemap with
sprite-prefixed IDs. - Automatically serve and bundle the spritemap as a static asset.
In your .liquid templates:
You can now use the native {% icon %} tag to instantly render icons. The tag supports passing raw strings or dynamic variables, and it correctly forwards any attributes to the wrapper <svg> element.
<!-- Basic usage -->
{% icon 'arrow-right' %}
<!-- Passing attributes -->
{% icon 'check', class: 'text-green-500 w-4 h-4', fill: 'currentColor' %}
<!-- Dynamic resolution (notice lack of quotes) -->
{% assign dynamic_name = "globe" %}
{% icon dynamic_name %}3. Accessibility & Styling
Accessibility Built-in: If you don't provide a title or aria-hidden attribute, the tag automatically injects aria-hidden="true" to hide decorative icons from screen readers.
Styling: Because the spritemap plugin optimizes SVGs and strips root attributes, ensure you define your desired structural styles (like stroke/fill) in your CSS targeting the svg element, or pass them directly as attributes to the {% icon %} tag.
Development
npm install
npm run dev # Watch mode (TypeScript compiler)
npm run build # Build library
npm run test # Run tests
npm run lint # Lint & fix
npm run format # PrettierPublishing
npm run lint && npm run test
git add . && git commit -m "chore: prepare for new release"
npm version patch # or minor/major
npm run release