Documentation

Everything you need to know to build with @plushveil/pages

Installation

npm Package

npm install @plushveil/pages

VS Code Extension

Search for "Pages" by Plushveil in the VS Code Extensions marketplace, or install via command line:

code --install-extension plushveil.pages

File Format

Pages works with .page, .htms, and .htmlfiles.

⚠️ Required:Files must include a <link rel="canonical" href="..." />tag in the <head>section. This defines the output URL. Without it, the file will not be built.

Basic Structure

<!DOCTYPE html>
<html lang="${lang}">
  <head>
    <meta charset="UTF-8">
    <title>${title}</title>
    <link rel="canonical" href="/" />
  </head>
  <body>
    <h1>${greeting}</h1>
  </body>
</html>

<script target="html">
  export const lang = 'en'
  export const title = 'My Page'
  export const greeting = 'Hello, World!'
</script>

Using JavaScript Logic

<ul>
  ${items.map(item => `
    <li>${item.name}</li>
  `).join('')}
</ul>

<script target="ul">
  export const items = [
    { name: 'Item 1' },
    { name: 'Item 2' },
    { name: 'Item 3' }
  ]
</script>

Generating Multiple Pages from Arrays

Use arrays in canonical URLs to generate multiple pages from a single source file:

<html>
  <head>
    <link rel="canonical" href="/articles/${articleId}" />
  </head>
  <body>
    <h1>${article.title}</h1>
    <p>${article.content}</p>
  </body>
</html>

<script target="html">
  // Array in canonical URL generates multiple pages
  export const articleId = ['getting-started', 'advanced', 'deployment']
</script>

<script target="body">
  // Access current page data using page:page import
  import page from 'page:page'

  const articles = {
    'getting-started': { title: 'Getting Started', content: '...' },
    'advanced': { title: 'Advanced Guide', content: '...' },
    'deployment': { title: 'Deployment', content: '...' }
  }

  // Extract current articleId from page URL
  const currentId = page.url.pathname.split('/').pop()
  export const article = articles[currentId]
</script>

This generates three pages: /articles/getting-started,/articles/advanced, and/articles/deployment

Reusable Components

There are two ways to reuse code across pages:

Method 1: Simple Imports

Import HTML snippets directly using template literals:

Create a snippet: snippets/header.html

<header>
  <h1>My Site</h1>
  <nav>...</nav>
</header>

Import in page:

<body>
  ${import('./snippets/header.html')}
  <main>Content</main>
</body>

Passing Attributes to Imports

You can pass data to imported snippets using import attributes:

Create snippet with attributes: snippets/card.html

<div class="card">
  <h2>${title}</h2>
  <p>${description}</p>
</div>

<script target="div">
  import args from 'page:args'
  export const title = args.title || 'Default Title'
  export const description = args.description || 'No description'
</script>

Import with attributes in page:

<body>
  ${import('./snippets/card.html', { with: { title: 'Hello', description: 'World' } })}
  ${import('./snippets/card.html', { with: { title: 'Another Card' } })}
</body>

Use page:argsimport to access attributes passed via the withoption.

Method 2: Auto-Loading Components

Create reusable components with separate HTML, CSS, and JavaScript that automatically load:

📦 Enable:Add <enable-components></enable-components>in <head>to enable component auto-loading.

1. Create folder structure:

components/
└── my-button/
    ├── my-button.html
    ├── my-button.css
    └── my-button.ts

2. Component HTML (components/my-button/my-button.html):

<my-button class="btn">
  <button>${__attributes.label || 'Click'}</button>
</my-button>

<script target="my-button">
  import page from 'page:page'
  export const __attributes = page.params.__attributes
</script>

3. Use in pages:

<html>
  <head>
    <link rel="canonical" href="/" />
    <enable-components></enable-components>
  </head>
  <body>
    <my-button label="Submit"></my-button>
  </body>
</html>

How it works:

  • Component names must have a hyphen (e.g., my-button)
  • HTML is injected where the component is used
  • CSS/JS files auto-link in <enable-components>
  • Attributes passed via page.params.__attributes
  • Classes from wrapper merge with the component element

CLI Commands

Serve (Development)

npx pages serve <folder> [--config pages.config.mjs]

Starts a development server with hot reload.

Build (Production)

npx pages build <folder> [--config pages.config.mjs]

Builds static files to the ./buildfolder.

Configuration

Create a pages.config.mjsfile:

/**
 * @file Configuration for pages
 */

export const baseURI = new URL('https://example.com')

export const build = {
  ignore: [
    /\/(node_modules|.git)\//,
    /\/(components|snippets)\//,
  ],
}

export const html = {
  minify: true,
  resolve: true
}

export const css = {
  minify: true,
  integrity: true,
  tailwind: 'tailwind.config.mjs',
}

export const js = {
  minify: true,
  integrity: true,
  target: '.browserslistrc'
}

GitHub Action Usage

Use the official GitHub Action to build and deploy your site:

name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pages: write
      id-token: write

    steps:
      - uses: actions/checkout@v4

      - id: build
        name: Build pages
        uses: plushveil/pages@latest
        with:
          folder: pages
          config: pages.config.mjs

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ${{steps.build.outputs.folder}}

      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v4

💡 Using the Output Folder

The build action outputs a foldervariable containing the path to the built files. Use ${{steps.build.outputs.folder}}to reference it in subsequent steps.

This is useful for custom deployment workflows, copying files to specific locations, or running post-build scripts.

TypeScript Support

Use TypeScript in your script blocks:

<script target="html">
  interface Item {
    name: string
    price: number
  }

  export const items: Item[] = [
    { name: 'Product 1', price: 29.99 },
    { name: 'Product 2', price: 39.99 },
  ]

  export const total: number = items.reduce(
    (sum, item) => sum + item.price,
    0
  )
</script>

Programmatic API

Use @plushveil/pages in your Node.js scripts:

Serve Pages

import { serve } from '@plushveil/pages'

const server = await serve('pages/', 'pages.config.mjs')
// Server is now running

Build Pages

import { build } from '@plushveil/pages'

await build('pages/', 'pages.config.mjs', 'build/')
// Pages built to build/ folder

Render Individual Page

import { render } from '@plushveil/pages'

const html = await render('pages/index.page', 'pages.config.mjs')
console.log(html)