npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.json contains translation keys that are never used in any .liquid templates (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 .liquid file in src/pages/, it becomes a page
  • Multi-language — auto-detects languages from src/locales/*.json and 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.xml with hreflang alternates, using git log to calculate hyper-accurate lastmod dates 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.env files with Vite-compatible precedence
  • SVG Spritemaps — Built-in support for vite-plugin-svg-spritemap

Installation

npm install @20dimensions/liquid-site-builder

Peer 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.js

2. 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 -D

2. Usage

Once installed, simply create a src/icons/ directory in your project root and drop your raw .svg files there. The builder will automatically:

  1. Scan for any .svg files in src/icons/.
  2. Generate an optimized SVG spritemap with sprite- prefixed IDs.
  3. 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    # Prettier

Publishing

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