# als-layout

> HTML layout constructor with dynamic meta, styles, and scripts

Latest version **7.2.0** (published 2026-07-02) · MIT license · 0 weekly downloads

## Install

```sh
npm install als-layout
pnpm add als-layout
yarn add als-layout
bun add als-layout
```

## Health

**Score 50/100 (C)** — status: active.

Positive: esm support; no vulnerabilities.

Warnings: low downloads; no types.

## Facts

| | |
|---|---|
| Version | 7.2.0 |
| Published | 2026-07-02 |
| First published | 2022-09-23 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 49.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Alex Sorkin |
| Maintainers | alexsorkin |
| Keywords | html, layout, document, meta, seo, als-document |

## Links

- npm: https://www.npmjs.com/package/als-layout
- npm.io page: https://npm.io/package/als-layout

## Dependencies (1)

- [als-document](https://npm.io/package/als-document.md) ^1.5.0

## Alternatives

- [@tsparticles/shape-image](https://npm.io/package/@tsparticles/shape-image.md) — 303.7K weekly downloads
- [@tsparticles/shape-line](https://npm.io/package/@tsparticles/shape-line.md) — 233.7K weekly downloads
- [stringify-attributes](https://npm.io/package/stringify-attributes.md) — 58.6K weekly downloads
- [mobile-drag-drop](https://npm.io/package/mobile-drag-drop.md) — 46.3K weekly downloads
- [@comunica/actor-rdf-parse-html](https://npm.io/package/@comunica/actor-rdf-parse-html.md) — 29.2K weekly downloads

## Recent versions

- 7.2.0 (latest) — 2026-07-02
- 7.1.1 — 2026-06-11
- 7.1.0 — 2026-06-10
- 7.0.1 — 2025-09-14
- 7.0.0 — 2025-09-14
- 6.1.0 — 2024-12-05
- 6.0.0 — 2024-12-03
- 5.2.0 — 2024-11-26
- 5.1.0 — 2024-10-29
- 5.0.1 — 2024-10-26
- 5.0.0 — 2024-10-26
- 4.3.0 — 2024-10-25
- 4.2.0 — 2024-09-16
- 4.1.0 — 2024-08-26
- 4.0.0 — 2024-08-26
- … 31 more at https://npm.io/package/als-layout/versions

## README

# als-layout

`als-layout` is a small, dependency‑light library for building and manipulating an HTML
document in memory — title, meta tags, Open Graph / Twitter cards, styles, scripts, links,
favicon and viewport — with a fluent, chainable API. It works the same in Node (SSR /
static generation) and in the browser.

It is built on top of [`als-document`](https://www.npmjs.com/package/als-document) and is a
pure **document** builder: it has no global state and no site‑level concerns (collections,
sitemaps, robots). For multi‑page sites built on top of it, see
[`als-site`](https://www.npmjs.com/package/als-site).

---


## Installation

```bash
npm i als-layout
```

### ESM (named export)
```js
import { Layout } from 'als-layout'
```

### Node (CommonJS)
```js
const Layout = require('als-layout')
```

### Browser bundle (global `Layout`, `als-document` inlined)
```html
<script src="layout.js"></script>
<script>const layout = new Layout()</script>
```

---

## Quick start

```js
import { Layout } from 'als-layout'

const layout = new Layout(undefined, 'https://example.com')
   .lang('en')
   .noindex()                                    // adds <meta name="robots" content="noindex">
   .viewport()                                   // width=device-width, initial-scale=1.0
   .title('Home')                                // <title> + og:title
   .description('A cool site')                   // description + og/twitter description
   .keywords(['stats', 'sheets'])
   .favicon('/favicon.png')
   .image('/cover.png')                          // og:image, twitter:image, twitter:card
   .link('/styles.css')                          // <link rel=stylesheet> (deduped)
   .style('body{margin:0}')                      // appends to <style>
   .script({ src: '/app.js' })                   // <script> in <head>
   .script({}, 'console.log("hi")', false)       // inline <script> in <body>
   .url('/')                                      // canonical + og:url, resolved vs host

layout.body.innerHTML = '<h1>Home</h1>'
console.log(layout.rawHtml)                       // full HTML string
```

---

## A Layout is an offline DOM

This is the core idea: a `Layout` extends an `als-document` document, so the instance is a
real, in‑memory **DOM tree** you can query and mutate almost exactly like the browser DOM —
no `jsdom`, no browser, works in plain Node. The chainable `title()/meta()/link()/…` helpers
are just convenience on top of it; for anything else you manipulate nodes directly.

```js
const layout = new Layout(/*html*/`<html><head></head><body><main></main></body></html>`)

// Query like the DOM ($ / $$ are aliases for querySelector / querySelectorAll)
const main = layout.$('main')              // === layout.querySelector('main')
const items = layout.$$('.item')           // === querySelectorAll, returns an array

// Build and insert nodes
main.innerHTML = '<h1>Hello</h1>'
main.insert(2, '<p class="lead">Intro</p>')          // append parsed HTML
main.insertAdjacentHTML('beforeend', '<hr>')

// Mutate elements with the familiar API
const h1 = layout.$('h1')
h1.setAttribute('id', 'top')
h1.classList.add('title')
h1.dataset.role = 'heading'
h1.textContent = 'Welcome'
console.log(h1.outerHTML)                   // <h1 id="top" class="title" data-role="heading">Welcome</h1>

// Traverse
h1.parent          // parent node
h1.nextElementSibling
layout.body.children

// Remove
layout.$('hr').remove()
```

Supported on nodes: `querySelector`/`$`, `querySelectorAll`/`$$`, `getElementById`,
`getAttribute`/`setAttribute`/`removeAttribute`, `classList`, `dataset`, `style`,
`innerHTML`/`outerHTML`/`textContent`, `append`/`insert`/`insertAdjacentHTML`/`remove`,
plus traversal (`parent`, `children`, `prev`/`next`, …). See
[`als-document`](https://www.npmjs.com/package/als-document) for the full node API.

On the client, `toDocument()` flushes this offline DOM into the live `document`.

---

## Cloning

`layout.clone` returns a deep, independent copy — ideal as a template for many pages.
Changes to a clone never affect the original.

```js
const base = new Layout(template, 'https://example.com').link('/styles.css')
const about = base.clone.title('About').url('/about')
```

---

## API

### Constructor
```js
new Layout(html?: string, host?: string)
```
- **html** — optional HTML string to start from.
- **host** — base URL used to resolve relative URLs in `url()`.

### Properties
- `rawHtml` — current document as an HTML string.
- `clone` — a deep, independent copy.
- `urlObj` — the parsed `URL` of the last `url()` call (set after `url()`).
- `head` / `body` / `html` — element accessors (from `als-document`).

### Methods (all chainable, return `this`)
- `lang(lang)` — sets `<html lang>`.
- `title(title)` — sets `<title>` and `og:title`.
- `description(text)` — sets `description`, `og:description`, `twitter:description`.
- `keywords(list)` — sets/merges `meta[name=keywords]`.
- `meta(props)` — sets/updates a `<meta>` tag (keyed by its first attribute).
- `image(src)` — sets `og:image`, `twitter:image`, `twitter:card`.
- `favicon(href)` — sets/updates the favicon link.
- `viewport(content?)` — sets the viewport meta.
- `link(href, attributes?)` — adds a stylesheet `<link>` (skipped if already present).
- `style(css)` — appends CSS to a `<style>` element.
- `script(attrs?, innerHTML?, head = true)` — adds a `<script>` (head or body; `src` deduped).
- `url(url, host = this.URL)` — sets canonical + `og:url`, stores `urlObj`.
- `toDocument()` — in the browser, renders this layout into the live `document` (no‑op in Node); returns `this`.

---

## Changelog

### 7.2.0 - als-document 1.5 (from 1.4.3)
- Updated the `als-document` dependency from `^1.4.3` to `^1.5.0`.
- Improved HTML parsing for attributes, `<meta>`, `<link>`, `<style>`, and `<script>` elements.
- Fixed document title/HTML initialization, cloning, inline styles, and CSS selector handling.
- Fully backward compatible; no `als-layout` code changes are required, and all 50 tests pass.

### 7.1.0
- **Added** `toDocument()` — render a layout into the live DOM on the client.
- **Added** `urlObj` — `url()` now exposes the parsed `URL`.
- **Added** `exports` map — ESM resolves to the named `Layout` export, CommonJS to the class.
- **Fixed** `twitter:description` now uses `name=` (was `property=`), per the Twitter Cards spec.
- **Fixed** `clone` preserves the document URL/host.
- **Changed** `url()` keeps the canonical trailing slash (`URL.href`), and stores `urlObj`.

### 7.0.0 (breaking)
- Merged into a single source file; added ESM build (`index.mjs`) and browser bundle (`layout.js`).
- Removed `status`, `end`, `minified`, `propsToClone`, and JS/CSS uglification.

---

## Notes
- Pure document builder — no global state, no file I/O.
- `index.mjs` and the browser `layout.js` are generated from `src/layout.js` by `node build.js`.
- Use `clone` to derive page variants from a base template.

---
_Source: https://npm.io/package/als-layout · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
