Skip to main content

Project Structure

A Weblisk project has a clean, flat structure:

code
my-app/
├── index.html             ← Home page (full standalone HTML)
├── about.html             ← About page
├── 404.html               ← Not found page
├── docs/
│   └── index.html         ← Docs landing
├── js/
│   └── islands/           ← Interactive islands
│       └── shell.js       ← Shell bootstrapper
├── css/
│   └── styles.css         ← App styles
├── images/                ← Image assets (SVGs, favicons)
├── sw.js                  ← Service Worker
└── .env                   ← Environment config

Pages

Every page is a full standalone HTML document<!DOCTYPE html>, <head>, <body>, everything. What you write is what ships. No composition, no layout cascade, no build-time transformation.

Each file in the project root maps directly to a URL path:

| File | URL |

|------|-----|

| index.html | /index.html |

| about.html | /about.html |

| blog.html | /blog.html |

| docs/index.html | /docs/index.html |

Your web server can map cleaner URLs (e.g. /about/about.html) if desired — that's a server concern, not a framework concern.

Page Template

Every page includes its own import map and shell script:

html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Page</title>
  <meta name="description" content="Page description.">
  <link rel="stylesheet" href="/css/styles.css">
  <script type="importmap">
  { "imports": { "weblisk": "/lib/weblisk/weblisk.js", "weblisk/": "/lib/weblisk/" } }
  </script>
</head>
<body>
  <!-- your content -->
  <script type="module" src="/js/islands/shell.js"></script>
</body>
</html>

Islands

Islands are JavaScript files that enhance static HTML using data-island and data-hydrate attributes. They live in js/islands/ by convention.

An island script exports a default function that receives the DOM element:

js
// js/islands/counter.js
import { signal, effect } from 'weblisk';

export default function counter(el) {
  const [count, setCount] = signal(0);
  const btn = el.querySelector('button');
  btn.addEventListener('click', () => setCount(count() + 1));
  effect(() => { btn.textContent = 'Count: ' + count(); });
}

Use it in your HTML:

html
<div data-island="/js/islands/counter.js" data-hydrate="idle">
  <button>Count: 0</button>
</div>

The data-hydrate attribute controls when the island loads: load, idle, visible, media, event, or never.

.env

Environment configuration (loaded automatically by the CLI):

bash
WL_ORIGIN=http://localhost:5500
WL_PORT=5500
WL_DIST=dist

Blueprint-Driven Layout

When using the starter template, the project structure is declaration-first — YAML files describe the site, and code is generated:

code
my-site/
├── global.yaml            ← Project identity, navigation, SEO
├── code.yaml              ← Code conventions, formatting rules
├── theme.yaml             ← Design tokens (colors, typography, spacing)
├── pages/                 ← Page declarations (YAML)
│   ├── index.yaml
│   ├── features.yaml
│   └── docs.yaml
├── components/            ← Component declarations (YAML)
│   ├── hero.yaml
│   └── pricing-card.yaml
├── weblisk.json           ← Project config
├── .env                   ← Config
└── dist/                  ← Generated output (build artifact)

Run weblisk generate to produce the full site from these declarations. See Project Standards for details.