@anydigital/eleventy-bricks 0.27.1 → 0.28.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,429 +2,374 @@
2
2
 
3
3
  A collection of helpful utilities and filters for Eleventy (11ty).
4
4
 
5
- ## Installation
5
+ ## Install
6
6
 
7
- ```bash
7
+ ```sh
8
8
  npm install @anydigital/eleventy-bricks
9
9
  ```
10
10
 
11
- ## Usage
12
-
13
- You can use this library in two ways:
14
-
15
- ### Option 1: As a Plugin
11
+ Then choose one of the following options:
16
12
 
17
- Import and use the entire plugin. You can configure which helpers to enable using the options parameter:
13
+ ### Option A. Starting 11ty from scratch?
18
14
 
19
- **ES Modules:**
15
+ Consider symlinking entire `eleventy.config.js`:
20
16
 
21
- ```javascript
22
- import eleventyBricks from "@anydigital/eleventy-bricks";
17
+ ```sh
18
+ ln -s ./node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
19
+ ```
23
20
 
24
- export default function (eleventyConfig) {
25
- eleventyConfig.addPlugin(eleventyBricks, {
26
- mdAutoRawTags: true,
27
- mdAutoNl2br: true,
28
- autoLinkFavicons: true,
29
- siteData: true,
30
- filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "section", "fetch"],
31
- });
21
+ [Learn more below](#symlink-config) and see https://github.com/anydigital/sveleven as a living example.
32
22
 
33
- // Your other configuration...
34
- }
35
- ```
23
+ ### Option B. Adding to existing 11ty site?
36
24
 
37
- **CommonJS:**
25
+ Use as a plugin in `eleventy.config.js` (recommended):
38
26
 
39
- ```javascript
40
- const eleventyBricks = require("@anydigital/eleventy-bricks");
27
+ ```js
28
+ import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
41
29
 
42
- module.exports = function (eleventyConfig) {
43
- eleventyConfig.addPlugin(eleventyBricks, {
30
+ export default function (eleventyConfig) {
31
+ eleventyConfig.addPlugin(eleventyBricksPlugin, {
44
32
  mdAutoRawTags: true,
45
33
  mdAutoNl2br: true,
46
34
  autoLinkFavicons: true,
47
35
  siteData: true,
48
- filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "section", "fetch"],
36
+ filters: ["attr_set", "attr_concat", ...],
49
37
  });
50
-
51
- // Your other configuration...
52
- };
38
+ }
53
39
  ```
54
40
 
55
- > **Note:** The CommonJS wrapper uses dynamic imports internally and returns async functions. Eleventy's `addPlugin()` method handles this automatically.
41
+ <details><summary>
56
42
 
57
- ### Option 2: Import Individual Helpers (Recommended)
43
+ ### Option C. Individual imports
58
44
 
59
- Import only the specific helpers you need without using the plugin:
45
+ </summary>
60
46
 
61
- **ES Modules:**
47
+ For advanced usage, import individual components only in `eleventy.config.js`:
62
48
 
63
- ```javascript
64
- import {
65
- mdAutoRawTags,
66
- mdAutoNl2br,
67
- autoLinkFavicons,
68
- attrSetFilter,
69
- attrIncludesFilter,
70
- mergeFilter,
71
- removeTagFilter,
72
- ifFilter,
73
- attrConcatFilter,
74
- sectionFilter,
75
- fetchFilter,
76
- siteData,
77
- } from "@anydigital/eleventy-bricks";
49
+ ```js
50
+ import { siteData, mdAutoRawTags, mdAutoNl2br, autoLinkFavicons, attrSetFilter, attrConcatFilter, ... } from "@anydigital/eleventy-bricks";
78
51
 
79
52
  export default function (eleventyConfig) {
53
+ siteData(eleventyConfig);
80
54
  mdAutoRawTags(eleventyConfig);
81
55
  mdAutoNl2br(eleventyConfig);
82
56
  autoLinkFavicons(eleventyConfig);
83
57
  attrSetFilter(eleventyConfig);
84
- attrIncludesFilter(eleventyConfig);
85
- mergeFilter(eleventyConfig);
86
- removeTagFilter(eleventyConfig);
87
- ifFilter(eleventyConfig);
88
58
  attrConcatFilter(eleventyConfig);
89
- sectionFilter(eleventyConfig);
90
- // fetchFilter is only available if @11ty/eleventy-fetch is installed
91
- if (fetchFilter) {
92
- fetchFilter(eleventyConfig);
93
- }
94
- siteData(eleventyConfig);
95
-
96
- // Your other configuration...
59
+ ...
97
60
  }
98
61
  ```
99
62
 
100
- **CommonJS:**
101
-
102
- ```javascript
103
- const {
104
- mdAutoRawTags,
105
- mdAutoNl2br,
106
- autoLinkFavicons,
107
- attrSetFilter,
108
- attrIncludesFilter,
109
- mergeFilter,
110
- removeTagFilter,
111
- ifFilter,
112
- attrConcatFilter,
113
- sectionFilter,
114
- fetchFilter,
115
- siteData,
116
- } = require("@anydigital/eleventy-bricks");
117
-
118
- module.exports = async function (eleventyConfig) {
119
- await mdAutoRawTags(eleventyConfig);
120
- await mdAutoNl2br(eleventyConfig);
121
- await autoLinkFavicons(eleventyConfig);
122
- await attrSetFilter(eleventyConfig);
123
- await attrIncludesFilter(eleventyConfig);
124
- await mergeFilter(eleventyConfig);
125
- await removeTagFilter(eleventyConfig);
126
- await ifFilter(eleventyConfig);
127
- await attrConcatFilter(eleventyConfig);
128
- await sectionFilter(eleventyConfig);
129
- // fetchFilter is only available if @11ty/eleventy-fetch is installed
130
- if (fetchFilter) {
131
- await fetchFilter(eleventyConfig);
132
- }
133
- await siteData(eleventyConfig);
134
-
135
- // Your other configuration...
136
- };
137
- ```
63
+ </details>
64
+
65
+ ## Command Line Tools
138
66
 
139
- > **Note:** When using CommonJS with individual helpers, the config function must be `async` and each helper must be `await`ed, as the CommonJS wrapper uses dynamic imports internally.
67
+ <!--section:npm-h3-->
140
68
 
141
- ## Configuration Options
69
+ ### 🥷 Reusable 11ty npm scripts <small>via npm workspace</small> <br><sub>from https://github.com/anydigital/eleventy-bricks</sub>
142
70
 
143
- When using the plugin (Option 1), you can configure which helpers to enable:
71
+ This package provides a pre-configured `do` folder setup that helps organize your development workflow using npm workspaces. The `do` folder contains scripts for building and running your Eleventy project.
144
72
 
145
- | Option | Type | Default | Description |
146
- | ------------------ | --------------- | ------- | ---------------------------------------------------------------- |
147
- | `mdAutoRawTags` | boolean | `false` | Enable the mdAutoRawTags preprocessor for Markdown files |
148
- | `mdAutoNl2br` | boolean | `false` | Enable the mdAutoNl2br preprocessor to convert \n to `<br>` tags |
149
- | `autoLinkFavicons` | boolean | `false` | Enable the autoLinkFavicons transform to add favicons to links |
150
- | `siteData` | boolean | `false` | Enable site.year and site.prod global data |
151
- | `filters` | array of string | `[]` | Array of filter names to enable (see Available Filters section) |
73
+ **Installation:**
152
74
 
153
- **Available filter names for the `filters` array:**
75
+ 1. Install https://github.com/anydigital/eleventy-bricks to reuse pre-defined 11ty scripts from there:
154
76
 
155
- - `'attr_set'` - Override object attributes
156
- - `'attr_includes'` - Filter collections by attribute values
157
- - `'merge'` - Merge arrays or objects
158
- - `'remove_tag'` - Remove HTML elements from content
159
- - `'if'` - Inline conditional/ternary operator
160
- - `'attr_concat'` - Concatenate values to an attribute array
161
- - `'section'` - Extract named sections from content marked with HTML comments
162
- - `'fetch'` - Fetch remote URLs or local files (requires `@11ty/eleventy-fetch`)
77
+ ```sh
78
+ npm install @anydigital/eleventy-bricks
79
+ ```
163
80
 
164
- **Example:**
81
+ 2. Create a helper folder `do` to symlink the `do/package.json` within:
165
82
 
166
- ```javascript
167
- eleventyConfig.addPlugin(eleventyBricks, {
168
- mdAutoRawTags: true,
169
- mdAutoNl2br: true,
170
- autoLinkFavicons: true,
171
- siteData: true,
172
- filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "section", "fetch"],
173
- });
83
+ ```sh
84
+ mkdir do
85
+ cd ./do
86
+ ln -s ../node_modules/@anydigital/eleventy-bricks/src/do/package.json
87
+ ```
88
+
89
+ 3. Finally register `do` folder as npm workspace in your root `package.json`:
90
+
91
+ ```json {data-caption=./package.json}
92
+ {
93
+ ...
94
+ "workspaces": ["do"],
95
+ "scripts": {
96
+ "start": "npm -w do run start",
97
+ "stage": "npm -w do run stage",
98
+ "build": "npm -w do run build"
99
+ },
100
+ ...
101
+ }
174
102
  ```
175
103
 
176
- ### Additional Exports
104
+ **Done!** 🎉 Now you can run:
177
105
 
178
- The plugin also exports the following utility functions for advanced usage:
106
+ - `npm start` to start 11ty dev server with live reload and Tailwind watch mode
107
+ - `npm run stage` to build and serve production-like site locally
108
+ - `npm run build` to finally build the site for production
109
+ - all available scripts: https://github.com/anydigital/eleventy-bricks/blob/main/src/do/package.json
179
110
 
180
- - `transformAutoRaw(content)`: The processor function used by `mdAutoRawTags` preprocessor. Can be used programmatically to wrap Nunjucks syntax with raw tags.
181
- - `transformNl2br(content)`: The processor function used by `mdAutoNl2br` preprocessor. Can be used programmatically to convert `\\n` sequences to `\u003cbr\u003e` tags.
182
- - `isPlainUrlText(linkText, domain)`: Helper function that checks if link text looks like a plain URL or domain.
183
- - `cleanLinkText(linkText, domain)`: Helper function that cleans link text by removing protocol, domain, and leading slash.
184
- - `buildFaviconLink(attrs, domain, text)`: Helper function that builds HTML for a link with favicon.
185
- - `transformLink(match, attrs, url, linkText)`: The processor function used by `autoLinkFavicons` that processes a single link to include a favicon.
186
- - `replaceLinksInHtml(content, processor)`: Helper function that replaces all anchor links in HTML content with processed versions.
187
- - `attrIncludes(collection, attrName, targetValue)`: The core logic for filtering collection items by checking if an attribute array includes a target value. Can be used programmatically to filter collections.
188
- - `merge(first, ...rest)`: The core merge function used by the `merge` filter. Can be used programmatically to merge arrays or objects.
189
- - `removeTag(html, tagName)`: The core function used by the `remove_tag` filter. Can be used programmatically to remove HTML tags from content.
190
- - `iff(trueValue, condition, falseValue)`: The core conditional function used by the `if` filter. Can be used programmatically as a ternary operator.
191
- - `attrConcat(obj, attr, values)`: The core function used by the `attr_concat` filter. Can be used programmatically to concatenate values to an attribute array.
192
- - `attrSet(obj, key, value)`: The core function used by the `attr_set` filter. Can be used programmatically to override object attributes.
193
- - `section(content, sectionName)`: The core function used by the `section` filter. Can be used programmatically to extract named sections from content.
111
+ **Living example:** https://github.com/anydigital/sveleven
194
112
 
195
- ## Features
113
+ **Benefits:**
196
114
 
197
- <!--section:filters-h3-->
115
+ - **Clean separation**: Keep build scripts separate from project configuration
116
+ - **Reusable workflows**: Update scripts by upgrading the package
117
+ - **Workspace isolation**: Scripts run in their own workspace context
118
+ - **Easy maintenance**: No need to manually maintain build scripts
198
119
 
199
- ### Universal 11ty filters <small>for `.njk` & `.liquid`</small> <sub>by https://github.com/anydigital/eleventy-bricks</sub>
120
+ <!--section-->
200
121
 
201
- | Input | Filter | Arguments |
202
- | ---------: | --------------------------------- | -------------------------------------------------- |
203
- | {.divider} | Logical filters: |
204
- | `ANY \|` | [`if`](#if) | `TEST, OP, VALUE` <sub>currently only `TEST`</sub> |
205
- | {.divider} | Filters for objects: |
206
- | `OBJ \|` | [`merge`](#merge) | `OBJ2` |
207
- | `OBJ \|` | [`attr_set`](#attr_set) | `ATTR, VALUE` |
208
- | `OBJ \|` | [`attr_concat`](#attr_concat) | `ATTR, ARRAY2` |
209
- | `OBJ \|` | [`attr_includes`](#attr_includes) | `ATTR, VALUE` |
210
- | {.divider} | Other filters: |
211
- | `URL \|` | [`fetch`](#fetch) | |
212
- | `HTML \|` | [`section`](#section) | `NAME` |
213
- | `HTML \|` | [`remove_tag`](#remove_tag) | `TAG` |
122
+ ## Configuration Tools
214
123
 
215
- #### `attr_set`
124
+ <!--section:config-h3-->
216
125
 
217
- A filter that creates a new object with an overridden attribute value. This is useful for modifying data objects in templates without mutating the original.
126
+ ### 🥷 Symlinked `eleventy.config.js` <br><sub>from https://github.com/anydigital/eleventy-bricks</sub> <a id="symlink-config"></a>
218
127
 
219
- **Why use this?**
128
+ The package includes a fully-configured Eleventy config file `eleventy.config.js` that you can symlink to your project to get:
220
129
 
221
- When working with Eleventy data, you sometimes need to modify an object's properties for a specific use case. The `attr_set` filter provides a clean way to create a modified copy of an object without affecting the original.
130
+ - All eleventy-bricks plugins enabled
131
+ - Eleventy Navigation plugin
132
+ - Table of Contents plugin (conditionally loaded if installed)
133
+ - Markdown-it with anchors and attributes
134
+ - YAML data support
135
+ - CLI input directory support
136
+ - Symlink support for development
137
+ - _and more_
222
138
 
223
- **Usage:**
139
+ **Benefits of symlinking:**
224
140
 
225
- 1. Enable the `attr_set` filter in your Eleventy config:
141
+ - **Always up-to-date**: Configuration automatically updates when you upgrade the package
142
+ - **Less maintenance**: No need to manually sync configuration changes
143
+ - **Quick setup**: Get started immediately with best-practice configurations
144
+ - **Easy customization**: Override specific settings by creating your own config that imports from the symlinked version
226
145
 
227
- ```javascript
228
- import { attrSetFilter } from "@anydigital/eleventy-bricks";
146
+ **Installation as simple as:**
229
147
 
230
- export default function (eleventyConfig) {
231
- attrSetFilter(eleventyConfig);
232
- // Or use as plugin:
233
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_set'] });
234
- }
148
+ ```sh
149
+ npm install @anydigital/eleventy-bricks
150
+ ln -s ./node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
235
151
  ```
236
152
 
237
- 2. Use the filter in your templates:
153
+ <!--section:cms-h3-->
154
+
155
+ <details><summary>
238
156
 
239
- ```njk
240
- {# Create a modified version of a page object #}
241
- {% set modifiedPage = page | attr_set('title', 'New Title') %}
157
+ ### Symlinked CMS `index.html`
242
158
 
243
- <h1>{{ modifiedPage.title }}</h1>
244
- <p>Original title: {{ page.title }}</p>
159
+ </summary>
160
+
161
+ A ready-to-use Sveltia CMS admin interface for content management.
162
+
163
+ **Installation:**
164
+
165
+ ```sh
166
+ mkdir -p ./src/admin
167
+ cd ./src/admin
168
+ ln -s ../../node_modules/@anydigital/eleventy-bricks/src/admin/index.html
245
169
  ```
246
170
 
247
- **Parameters:**
171
+ </details>
248
172
 
249
- - `obj`: The object to modify
250
- - `key`: The attribute name to set (string)
251
- - `value`: The value to set for the attribute (any type)
173
+ ## Data Tools & Processors
252
174
 
253
- **Returns:**
175
+ <!--section:data&processors-h3-->
254
176
 
255
- A new object with the specified attribute set to the given value. The original object is not modified.
177
+ ### Global `siteData` helper
256
178
 
257
- **Features:**
179
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) — or copy-paste from
180
+ [`src/siteData.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/siteData.js)
258
181
 
259
- - Non-mutating: Creates a new object, leaving the original unchanged
260
- - Works with any object type
261
- - Supports any attribute name and value type
262
- - Can be chained with other filters
182
+ Adds global `site` data to your Eleventy project, providing commonly needed values that can be accessed in all templates:
263
183
 
264
- **Examples:**
184
+ | Variable | Value |
185
+ | ----------------- | ------------------------------------------------------------------------------------------------------------ |
186
+ | `{{ site.year }}` | The current year as a number (e.g., `2026`) |
187
+ | `{{ site.prod }}` | Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`) |
265
188
 
266
- ```njk
267
- {# Override a single attribute #}
268
- {% set updatedPost = post | attr_set('featured', true) %}
189
+ ### 🥷 `autoLinkFavicons` transformer
269
190
 
270
- {# Chain multiple attr_set filters #}
271
- {% set modifiedPost = post
272
- | attr_set('category', 'blog')
273
- | attr_set('priority', 1)
274
- %}
191
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
192
+ [`src/processors/autoLinkFavicons.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/processors/autoLinkFavicons.js)
275
193
 
276
- {# Use in loops #}
277
- {% for item in collection %}
278
- {% set enhancedItem = item | attr_set('processed', true) %}
279
- {# ... use enhancedItem ... #}
280
- {% endfor %}
281
- ```
194
+ Automatically adds favicon images from Google's favicon service to links that display plain URLs or domain names. This processor processes all HTML output files and adds inline favicon images next to link text that appears to be a plain URL.
282
195
 
283
- #### `attr_includes`
196
+ **Why use this?** When you have links in your content that display raw URLs or domain names (like `https://example.com/page`), adding favicons provides a visual indicator of the external site. This processor automatically detects these plain-text URL links and enhances them with favicon images, making them more visually appealing and easier to recognize.
284
197
 
285
- A filter that filters collection items by checking if an attribute array includes a target value. Supports nested attribute names using dot notation.
198
+ **How it works:**
286
199
 
287
- **Why use this?**
200
+ 1. Scans all HTML output files for `<a>` tags
201
+ 2. Checks if the link text appears to be a plain URL or domain
202
+ 3. Extracts the domain from the URL
203
+ 4. Removes the domain from the link text (keeping only the path)
204
+ 5. Adds a favicon image from Google's favicon service inline with the remaining text
288
205
 
289
- When working with Eleventy collections, you often need to filter items based on tags or other array attributes in front matter. The `attr_includes` filter provides a flexible way to filter by any array attribute, with support for nested properties using dot notation.
206
+ **Example:**
290
207
 
291
- **Usage:**
208
+ Before processing:
292
209
 
293
- 1. Enable the `attr_includes` filter in your Eleventy config:
210
+ ```html
211
+ <a href="https://github.com/anydigital/eleventy-bricks">https://github.com/anydigital/eleventy-bricks</a>
212
+ ```
294
213
 
295
- ```javascript
296
- import { attrIncludesFilter } from "@anydigital/eleventy-bricks";
214
+ After processing:
297
215
 
298
- export default function (eleventyConfig) {
299
- attrIncludesFilter(eleventyConfig);
300
- // Or use as plugin:
301
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_includes'] });
302
- }
216
+ ```html
217
+ <a href="https://github.com/anydigital/eleventy-bricks" class="whitespace-nowrap" target="_blank">
218
+ <i><img src="https://www.google.com/s2/favicons?domain=github.com&sz=32" /></i>
219
+ <span>/anydigital/eleventy-bricks</span>
220
+ </a>
303
221
  ```
304
222
 
305
- 2. Use the filter in your templates:
223
+ **Rules:**
306
224
 
307
- **Filter by array attribute (tags):**
225
+ - Only applies to links where the text looks like a plain URL (contains the domain or starts with `http://`/`https://`)
226
+ - Removes the protocol and domain from the display text
227
+ - Removes the trailing slash from the display text
228
+ - Only applies if at least 3 characters remain after removing the domain (to avoid showing favicons for bare domain links)
229
+ - Uses Google's favicon service at `https://www.google.com/s2/favicons?domain=DOMAIN&sz=32`
230
+ - Adds `target="_blank"` to the processed links (only if not already present)
231
+ - Adds `whitespace-nowrap` class to the link
232
+ - Wraps the link text in a `<span>` element
233
+ - The favicon is wrapped in an `<i>` tag for easy styling
308
234
 
309
- ```njk
310
- {# Get all posts that include 'javascript' tag #}
311
- {% set jsPosts = collections.all | attr_includes('data.tags', 'javascript') %}
235
+ <details><summary>
312
236
 
313
- {% for post in jsPosts %}
314
- <h2>{{ post.data.title }}</h2>
315
- {% endfor %}
316
- ```
237
+ ### mdAutoRawTags preprocessor
317
238
 
318
- **Parameters:**
239
+ </summary>
319
240
 
320
- - `collection`: The collection to filter (array of items)
321
- - `attrName`: The attribute name to check (string, supports dot notation for nested properties)
322
- - `targetValue`: The value to check for in the array (any type)
241
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
242
+ [`src/processors/markdown.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/processors/markdown.js)
323
243
 
324
- **Features:**
244
+ Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
325
245
 
326
- - Works with any array attribute in front matter
327
- - Supports dot notation for nested properties (e.g., `'data.tags'`, `'data.author.roles'`)
328
- - Returns empty array if collection is invalid
329
- - Filters out items where the specified attribute is not an array or doesn't exist
246
+ **Why use this?** When writing documentation or tutorials about templating in Markdown files, you often want to show Nunjucks/Liquid syntax as literal text. This preprocessor automatically escapes these special characters so they display as-is instead of being processed by the template engine.
330
247
 
331
- **Examples:**
248
+ **Example:**
332
249
 
333
- Front matter:
250
+ Before `mdAutoRawTags`, writing this in Markdown:
334
251
 
335
- ```yaml
336
- ---
337
- title: My Post
338
- category: blog
339
- tags: [javascript, tutorial, beginner]
340
- priority: 1
341
- ---
252
+ ```markdown
253
+ ### Using {{ variable }} to output variables
342
254
  ```
343
255
 
344
- Template usage:
256
+ Would try to process `{{ variable }}` as a template variable. With `mdAutoRawTags`, it displays exactly as written.
345
257
 
346
- ```njk
347
- {# Filter by tag (array) using dot notation for nested properties #}
348
- {% set jsTutorials = collections.all | attr_includes('data.tags', 'javascript') %}
258
+ </details>
349
259
 
350
- {# Filter by numeric value in array #}
351
- {% set highPriority = collections.all | attr_includes('data.priorities', 1) %}
260
+ <details><summary>
352
261
 
353
- {# Chain filters #}
354
- {% set recentTutorials = collections.all | attr_includes('data.tags', 'tutorial') | reverse | limit(5) %}
355
- ```
262
+ ### mdAutoNl2br converter
356
263
 
357
- #### `merge`
264
+ </summary>
358
265
 
359
- A filter that merges arrays or objects together, similar to Twig's merge filter. For arrays, it concatenates them. For objects, it performs a shallow merge where later values override earlier ones.
266
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
267
+ [`src/processors/markdown.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/processors/markdown.js)
360
268
 
361
- **Why use this?**
269
+ Automatically converts `\n` sequences to `<br>` tags in Markdown content. This is particularly useful for adding line breaks inside Markdown tables where standard newlines don't work.
362
270
 
363
- When working with data in templates, you often need to combine multiple arrays or objects. The `merge` filter provides a clean way to merge data structures without writing custom JavaScript, making it easy to combine collections, merge configuration objects, or aggregate data from multiple sources.
271
+ **Why use this?** Markdown tables don't support multi-line content in cells. By using `\n` in your content, this preprocessor will convert it to `<br>` tags, allowing you to display line breaks within table cells and other content.
364
272
 
365
- **Usage:**
273
+ **Example:**
366
274
 
367
- 1. Enable the `merge` filter in your Eleventy config:
275
+ In your Markdown file:
368
276
 
369
- ```javascript
370
- import { mergeFilter } from "@anydigital/eleventy-bricks";
277
+ ```markdown
278
+ | Column 1 | Column 2 |
279
+ | ---------------------- | --------------------------------- |
280
+ | Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |
281
+ ```
371
282
 
372
- export default function (eleventyConfig) {
373
- mergeFilter(eleventyConfig);
374
- // Or use as plugin:
375
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['merge'] });
376
- }
283
+ Will render as:
284
+
285
+ ```html
286
+ <td>Line 1<br />Line 2<br />Line 3</td>
287
+ <td>Another cell<br />With multiple lines</td>
377
288
  ```
378
289
 
379
- 2. Use the filter in your templates:
290
+ **Note:** This processes literal `\n` sequences (backslash followed by 'n'), not actual newline characters. Type `\n` in your source files where you want line breaks.
291
+
292
+ </details>
380
293
 
381
- **Merge arrays:**
294
+ <!--section:filters-h2-->
382
295
 
383
- ```njk
384
- {# Combine two arrays #}
385
- {% set allItems = featured | merge(regular) %}
296
+ ## 🥷 Universal 11ty Filters <small>for `.njk` & `.liquid`</small> <sub>from https://github.com/anydigital/eleventy-bricks</sub> <a id="filters"></a>
386
297
 
387
- {# Merge multiple arrays #}
388
- {% set combined = array1 | merge(array2, array3, array4) %}
298
+ <details><summary>
389
299
 
390
- {% for item in allItems %}
391
- <p>{{ item }}</p>
392
- {% endfor %}
393
- ```
300
+ ### `if`
394
301
 
395
- **Merge objects:**
302
+ </summary>
396
303
 
397
- ```njk
398
- {# Merge configuration objects #}
399
- {% set defaultConfig = { theme: 'light', lang: 'en' } %}
400
- {% set userConfig = { theme: 'dark' } %}
401
- {% set finalConfig = defaultConfig | merge(userConfig) %}
304
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) — or copy-paste from
305
+ [`src/filters/if.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/if.js)
402
306
 
403
- {# Result: { theme: 'dark', lang: 'en' } #}
307
+ An inline conditional/ternary operator filter that returns one value if a condition is truthy, and another if it's falsy. Similar to Nunjucks' inline if syntax, it is especially useful in `.liquid` templates.
308
+
309
+ **Features:**
310
+
311
+ - Returns `trueValue` if condition is truthy, otherwise returns `falseValue`
312
+ - Treats empty objects `{}` as falsy
313
+ - Default `falseValue` is an empty string if not provided
314
+ - Works with any data type for values
315
+
316
+ **Examples:** <!-- @TODO: better examples -->
317
+
318
+ ```jinja2
319
+ {# Basic usage (defaults to empty string) #}
320
+ <div class="{{ 'active' | if: isActive | default: 'inactive' }}">Status</div>
321
+
322
+ {# Toggle CSS classes #}
323
+ <button class="{{ 'btn-primary' | if: isPrimary | default: 'btn-secondary' }}">
324
+ Click me
325
+ </button>
326
+
327
+ {# Display different text #}
328
+ <p>{{ 'Online' | if: user.isOnline, 'Offline' }}</p>
329
+
330
+ {# Use with boolean values #}
331
+ {% set isEnabled = true %}
332
+ <div>{{ 'Enabled' | if: isEnabled, 'Disabled' }}</div>
333
+
334
+ {# Conditional attribute values #}
335
+ <input type="checkbox" {{ 'checked' | if: isChecked }}>
336
+
337
+ {# With numeric values #}
338
+ <span class="{{ 'has-items' | if: items.length }}">
339
+ {{ items.length }} items
340
+ </span>
341
+
342
+ {# Chain with other filters #}
343
+ {% set cssClass = 'featured' | if: post.featured | upper %}
404
344
  ```
405
345
 
406
- **Parameters:**
346
+ </details>
347
+
348
+ <details><summary>
407
349
 
408
- - `first`: The first array or object (the base to merge into)
409
- - `...rest`: One or more arrays or objects to merge in
350
+ ### `merge`
410
351
 
411
- **Features:**
352
+ </summary>
412
353
 
413
- - Works with both arrays and objects
414
- - Supports merging multiple items at once
415
- - Non-mutating: Creates new arrays/objects, leaving originals unchanged
416
- - For objects: Later values override earlier ones (shallow merge)
417
- - For arrays: Concatenates all arrays together
418
- - Handles null/undefined gracefully
354
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
355
+ [`src/filters/merge.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/merge.js)
419
356
 
420
- **Examples:**
357
+ A filter that merges arrays or objects together, similar to Twig's merge filter. For arrays, it concatenates them. For objects, it performs a shallow merge where later values override earlier ones.
358
+
359
+ **Why use this?** When working with data in templates, you often need to combine multiple arrays or objects. The `merge` filter provides a clean way to merge data structures without writing custom JavaScript, making it easy to combine collections, merge configuration objects, or aggregate data from multiple sources.
360
+
361
+ **Examples:** <!-- @TODO: better examples -->
362
+
363
+ ```jinja2
364
+ {# Merge configuration objects #}
365
+ {% set defaultConfig = { theme: 'light', lang: 'en' } %}
366
+ {% set userConfig = { theme: 'dark' } %}
367
+ {% set finalConfig = defaultConfig | merge(userConfig) %}
421
368
 
422
- ```njk
423
- {# Combine featured and regular posts #}
424
- {% set featuredPosts = collections.all | attr_includes('data.featured', true) %}
425
- {% set regularPosts = collections.all | attr_includes('data.featured', false) %}
426
- {% set allPosts = featuredPosts | merge(regularPosts) %}
369
+ {# Result: { theme: 'dark', lang: 'en' } #}
370
+ ```
427
371
 
372
+ ```jinja2
428
373
  {# Merge page metadata with defaults #}
429
374
  {% set defaultMeta = {
430
375
  author: 'Site Admin',
@@ -432,426 +377,152 @@ export default function (eleventyConfig) {
432
377
  comments: false
433
378
  } %}
434
379
  {% set pageMeta = defaultMeta | merge(page.data) %}
435
-
436
- {# Combine arrays of tags #}
437
- {% set commonTags = ['javascript', 'html', 'css'] %}
438
- {% set specialTags = page.data.tags or [] %}
439
- {% set allTags = commonTags | merge(specialTags) %}
440
-
441
- {# Merge multiple configuration sources #}
442
- {% set config = defaults | merge(siteConfig, pageConfig, userPrefs) %}
443
380
  ```
444
381
 
445
- #### `remove_tag`
382
+ </details>
446
383
 
447
- A filter that removes a specified HTML element from provided HTML content. It removes the tag along with its content, including self-closing tags.
384
+ <details><summary>
448
385
 
449
- **Why use this?**
386
+ ### `attr_set`
450
387
 
451
- When working with content from external sources or user-generated content, you may need to strip certain HTML tags for security or presentation purposes. The `remove_tag` filter provides a simple way to remove unwanted tags like `<script>`, `<style>`, or any other HTML elements from your content.
388
+ </summary>
452
389
 
453
- **Usage:**
390
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) — or copy-paste from
391
+ [`src/filters/attr_set.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/attr_set.js)
454
392
 
455
- 1. Enable the `remove_tag` filter in your Eleventy config:
393
+ A filter that creates a new object with an overridden attribute value. This is useful for modifying data objects in templates without mutating the original. Or even constructing an object from scratch.
456
394
 
457
- ```javascript
458
- import { removeTagFilter } from "@anydigital/eleventy-bricks";
395
+ #### Example: How to pass object(s) as argument(s) to a filter in `.liquid`?
459
396
 
460
- export default function (eleventyConfig) {
461
- removeTagFilter(eleventyConfig);
462
- // Or use as plugin:
463
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['remove_tag'] });
464
- }
397
+ ```liquid {data-caption="trick for '| renderContent' filter"}
398
+ {% assign _ctx = null | attr_set: 'collections', collections %}
399
+ {{ ... | renderContent: 'liquid,md', _ctx }}
465
400
  ```
466
401
 
467
- 2. Use the filter in your templates:
402
+ </details>
468
403
 
469
- ```njk
470
- {# Remove all script tags from content #}
471
- {% set cleanContent = htmlContent | remove_tag('script') %}
404
+ <details><summary>
472
405
 
473
- {{ cleanContent | safe }}
474
- ```
406
+ ### `attr_concat`
475
407
 
476
- **Parameters:**
408
+ </summary>
477
409
 
478
- - `html`: The HTML content to process (string)
479
- - `tagName`: The tag name to remove (string)
410
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
411
+ [`src/filters/attr_concat.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/attr_concat.js)
412
+
413
+ A filter that concatenates values to an attribute array, returning a new object with the combined array. Useful for adding items to arrays like tags, classes, or other list-based attributes.
414
+
415
+ **Why use this?** When working with objects that have array attributes (like tags), you often need to add additional values without mutating the original object. The `attr_concat` filter provides a clean way to combine existing array values with new ones, automatically handling duplicates.
480
416
 
481
417
  **Features:**
482
418
 
483
- - Removes both opening and closing tags along with their content
484
- - Handles self-closing tags (e.g., `<br />`, `<img />`)
485
- - Handles tags with attributes
486
- - Case-insensitive matching
487
- - Non-destructive: Returns new string, doesn't modify original
419
+ - Non-mutating: Creates a new object, leaving the original unchanged
420
+ - Automatically removes duplicates using Set
421
+ - Handles multiple input types: arrays, JSON string arrays (killer feature for `.liquid`), or single values
422
+ - Creates the attribute as an empty array if it doesn't exist
423
+ - Logs an error if the existing attribute is not an array
424
+ - `TBC:` Supports nested attributes (e.g., `data.tags`)
488
425
 
489
- **Examples:**
426
+ #### Example: Add tags to a post object in `.njk`:
490
427
 
491
- ```njk
492
- {# Remove scripts from user-generated content #}
493
- {% set userContent = '<p>Hello</p><script>alert("XSS")</script><p>World</p>' %}
494
- {% set safeContent = userContent | remove_tag('script') %}
495
- {# Result: '<p>Hello</p><p>World</p>' #}
496
-
497
- {# Strip specific formatting tags #}
498
- {% set formatted = '<div><strong>Bold</strong> and <em>italic</em> text</div>' %}
499
- {% set noStrong = formatted | remove_tag('strong') %}
500
- {# Result: '<div>Bold and <em>italic</em> text</div>' #}
501
-
502
- {# Chain multiple remove_tag filters for multiple tags #}
503
- {% set richContent = page.content %}
504
- {% set stripped = richContent
505
- | remove_tag('script')
506
- | remove_tag('style')
507
- | remove_tag('iframe')
508
- %}
509
-
510
- {# Remove images for text-only preview #}
511
- {% set textOnly = htmlContent | remove_tag('img') %}
428
+ ```jinja2
429
+ {% set enhancedPost = post | attr_concat('tags', ['featured', 'popular']) %}
512
430
  ```
513
431
 
514
- **Security Note:**
432
+ #### `PRO` Example: Add scripts and styles to the `site` object in `.liquid`:
515
433
 
516
- While this filter can help sanitize HTML content, it should not be relied upon as the sole security measure. For critical security requirements, use a dedicated HTML sanitization library on the server side before content reaches your templates.
434
+ ```liquid
435
+ {% capture _ %}[
436
+ "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-tomorrow.min.css",
437
+ "https://cdn.jsdelivr.net/npm/prismjs@1/plugins/treeview/prism-treeview.min.css",
438
+ "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@7/css/all.min.css",
439
+ "/styles.css"
440
+ ]{% endcapture %}
441
+ {% assign site = site | attr_concat: 'styles', _ %}
517
442
 
518
- #### `section`
443
+ {% capture _ %}[
444
+ "https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js",
445
+ "https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js",
446
+ "https://cdn.jsdelivr.net/npm/prismjs@1/plugins/treeview/prism-treeview.min.js"
447
+ ]{% endcapture %}
448
+ {% assign site = site | attr_concat: 'scripts', _ %}
449
+ ```
519
450
 
520
- A filter that extracts a named section from content marked with HTML comments. This is useful for splitting a single content file (like a Markdown post) into multiple parts that can be displayed and styled independently in your templates.
451
+ </details>
521
452
 
522
- **Why use this?**
453
+ <details><summary>
523
454
 
524
- When working with Markdown content in Eleventy, you're usually limited to a single `content` variable. The `section` filter allows you to define multiple named sections within your content using simple HTML comments, giving you granular control over where different parts of your content appear in your layout.
455
+ ### `attr_includes`
525
456
 
526
- **Usage:**
457
+ </summary>
527
458
 
528
- 1. Enable the `section` filter in your Eleventy config:
459
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
460
+ [`src/filters/attr_includes.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/attr_includes.js)
529
461
 
530
- ```javascript
531
- import { sectionFilter } from "@anydigital/eleventy-bricks";
462
+ A filter that filters a list of items by checking if an attribute array includes a target value. Supports nested attribute names using dot notation.
532
463
 
533
- export default function (eleventyConfig) {
534
- sectionFilter(eleventyConfig);
535
- // Or use as plugin:
536
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['section'] });
537
- }
464
+ **Why use this?** When working with Eleventy collections, you often need to filter items based on tags or other array attributes in front matter. The `attr_includes` filter provides a flexible way to filter by any array attribute, with support for nested properties using dot notation.
465
+
466
+ #### Example: Get all posts that include `#javascript` tag
467
+
468
+ ```jinja2 {data-caption="in .njk:"}
469
+ {% set js_posts = collections.all | attr_includes('data.tags', '#javascript') %}
470
+
471
+ {% for post in js_posts %}
472
+ <h2>{{ post.data.title }}</h2>
473
+ {% endfor %}
538
474
  ```
539
475
 
540
- 2. Mark sections in your content file (e.g., `post.md`):
476
+ </details>
541
477
 
542
- ```markdown
543
- # My Post
478
+ <details><summary>
544
479
 
545
- <¡--section:intro-->
480
+ ### `fetch`
546
481
 
547
- This is the introduction that appears at the top of the page.
482
+ </summary>
548
483
 
549
- <¡--section:main-->
484
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) — or copy-paste from
485
+ [`src/filters/fetch.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/fetch.js)
550
486
 
551
- This is the main body of the post with all the details.
487
+ A filter that fetches content from remote URLs or local files. For remote URLs, it uses `@11ty/eleventy-fetch` to download and cache files. For local paths, it reads files relative to the input directory.
552
488
 
553
- <¡--section:summary,sidebar-->
489
+ **Why use this?** When building static sites, you often need to include content from external sources or reuse content from local files. The `fetch` filter provides a unified way to retrieve content from both remote URLs and local files, with automatic caching for remote resources to improve build performance.
554
490
 
555
- This content appears in both the summary and the sidebar!
556
- ```
491
+ **Requirements:** This filter requires the `@11ty/eleventy-fetch` package to be installed:
557
492
 
558
- 3. Use the filter in your templates:
493
+ ```bash
494
+ npm install @11ty/eleventy-fetch
495
+ ```
559
496
 
560
- ```njk
561
- {# Get the intro section #}
562
- <div class="page-intro">
563
- {{ content | section('intro') | safe }}
564
- </div>
497
+ > `NOTE:` If `@11ty/eleventy-fetch` is not installed, this filter will not be available. The plugin automatically detects whether the package is installed and only enables the filter if it's present.
565
498
 
566
- {# Get the main section #}
567
- <article>
568
- {{ content | section('main') | safe }}
569
- </article>
499
+ **Features:**
570
500
 
571
- {# Get the sidebar section #}
572
- <aside>
573
- {{ content | section('sidebar') | safe }}
574
- </aside>
575
- ```
576
-
577
- **Parameters:**
578
-
579
- - `content`: The string content to process (usually `content` variable)
580
- - `sectionName`: The name(s) of the section to extract (string)
581
-
582
- **Features:**
583
-
584
- - **Multiple names**: A single section can have multiple names separated by commas: `<¡--section:name1,name2-->`
585
- - **Case-insensitive**: Section names are matched without regard to case
586
- - **Multiple occurrences**: If a section name appears multiple times, the filter concatenates all matching sections
587
- - **Non-destructive**: Returns extracted content without modifying the original input
588
- - **EOF support**: Sections continue until the next `<¡--section*-->` marker or the end of the file
589
-
590
- **Examples:**
591
-
592
- ```njk
593
- {# Extract multiple sections with same name #}
594
- {# Example content has two &lt;!--section:note--> blocks #}
595
- <div class="notes-box">
596
- {{ content | section('note') | safe }}
597
- </div>
598
-
599
- {# Use case-insensitive names #}
600
- {{ content | section('INTRO') | safe }}
601
-
602
- {# Handle missing sections gracefully (returns empty string) #}
603
- {% set footer = content | section('non-existent-section') %}
604
- {% if footer %}
605
- <footer>{{ footer | safe }}</footer>
606
- {% endif %}
607
- ```
608
-
609
- **Syntax Rules:**
610
-
611
- - Sections start with: `<¡--section:NAME-->` or `<¡--section:NAME1,NAME2-->`
612
- - Sections end at the next `<¡--section*-->` marker or end of file
613
- - Whitespace around names and inside comments is automatically trimmed
614
-
615
- #### `if`
616
-
617
- An inline conditional/ternary operator filter that returns one value if a condition is truthy, and another if it's falsy. Similar to Nunjucks' inline if syntax.
618
-
619
- **Why use this?**
620
-
621
- When you need simple conditional values in templates without verbose if/else blocks, the `if` filter provides a clean inline solution. It's especially useful for class names, attributes, or displaying alternate text based on conditions.
622
-
623
- **Usage:**
624
-
625
- 1. Enable the `if` filter in your Eleventy config:
626
-
627
- ```javascript
628
- import { ifFilter } from "@anydigital/eleventy-bricks";
629
-
630
- export default function (eleventyConfig) {
631
- ifFilter(eleventyConfig);
632
- // Or use as plugin:
633
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['if'] });
634
- }
635
- ```
636
-
637
- 2. Use the filter in your templates:
638
-
639
- ```njk
640
- {# Basic usage #}
641
- <div class="{{ 'active' | if: isActive, 'inactive' }}">Status</div>
642
-
643
- {# Without falsy value (defaults to empty string) #}
644
- <span class="{{ 'highlight' | if: shouldHighlight }}">Text</span>
645
-
646
- {# With variable values #}
647
- {% set status = 'Published' | if: post.published, 'Draft' %}
648
- ```
649
-
650
- **Parameters:**
651
-
652
- - `trueValue`: The value to return if condition is truthy
653
- - `condition`: The condition to evaluate
654
- - `falseValue`: The value to return if condition is falsy (optional, defaults to empty string)
655
-
656
- **Features:**
657
-
658
- - Returns `trueValue` if condition is truthy, otherwise returns `falseValue`
659
- - Treats empty objects `{}` as falsy
660
- - Default `falseValue` is an empty string if not provided
661
- - Works with any data type for values
662
-
663
- **Examples:**
664
-
665
- ```njk
666
- {# Toggle CSS classes #}
667
- <button class="{{ 'btn-primary' | if: isPrimary, 'btn-secondary' }}">
668
- Click me
669
- </button>
670
-
671
- {# Display different text #}
672
- <p>{{ 'Online' | if: user.isOnline, 'Offline' }}</p>
673
-
674
- {# Use with boolean values #}
675
- {% set isEnabled = true %}
676
- <div>{{ 'Enabled' | if: isEnabled, 'Disabled' }}</div>
677
-
678
- {# Conditional attribute values #}
679
- <input type="checkbox" {{ 'checked' | if: isChecked }}>
680
-
681
- {# With numeric values #}
682
- <span class="{{ 'has-items' | if: items.length }}">
683
- {{ items.length }} items
684
- </span>
685
-
686
- {# Chain with other filters #}
687
- {% set cssClass = 'featured' | if: post.featured | upper %}
688
- ```
689
-
690
- #### `attr_concat`
691
-
692
- A filter that concatenates values to an attribute array, returning a new object with the combined array. Useful for adding items to arrays like tags, classes, or other list-based attributes.
693
-
694
- **Why use this?**
695
-
696
- When working with objects that have array attributes (like tags), you often need to add additional values without mutating the original object. The `attr_concat` filter provides a clean way to combine existing array values with new ones, automatically handling duplicates.
697
-
698
- **Usage:**
699
-
700
- 1. Enable the `attr_concat` filter in your Eleventy config:
701
-
702
- ```javascript
703
- import { attrConcatFilter } from "@anydigital/eleventy-bricks";
704
-
705
- export default function (eleventyConfig) {
706
- attrConcatFilter(eleventyConfig);
707
- // Or use as plugin:
708
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_concat'] });
709
- }
710
- ```
711
-
712
- 2. Use the filter in your templates:
713
-
714
- ```njk
715
- {# Add tags to a post object #}
716
- {% set enhancedPost = post | attr_concat('tags', ['featured', 'popular']) %}
717
-
718
- {# Add a single value #}
719
- {% set updatedPost = post | attr_concat('tags', 'important') %}
720
-
721
- {# Add values from a JSON string #}
722
- {% set modifiedPost = post | attr_concat('tags', '["new", "trending"]') %}
723
- ```
724
-
725
- **Parameters:**
726
-
727
- - `obj`: The object to modify
728
- - `attr`: The attribute name (must be an array or will be treated as one)
729
- - `values`: Values to concatenate (can be an array, JSON string array, or single value)
730
-
731
- **Returns:**
732
-
733
- A new object with the specified attribute containing the combined unique array. The original object is not modified.
734
-
735
- **Features:**
736
-
737
- - Non-mutating: Creates a new object, leaving the original unchanged
738
- - Automatically removes duplicates using Set
739
- - Handles multiple input types: arrays, JSON string arrays, or single values
740
- - Creates the attribute as an empty array if it doesn't exist
741
- - Logs an error if the existing attribute is not an array
742
-
743
- **Examples:**
744
-
745
- ```njk
746
- {# Add multiple tags #}
747
- {% set post = { title: 'My Post', tags: ['javascript'] } %}
748
- {% set enhancedPost = post | attr_concat('tags', ['tutorial', 'beginner']) %}
749
- {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner'] } #}
750
-
751
- {# Add single value #}
752
- {% set updatedPost = post | attr_concat('tags', 'featured') %}
753
- {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner', 'featured'] } #}
754
-
755
- {# No duplicates #}
756
- {% set deduped = post | attr_concat('tags', ['javascript', 'advanced']) %}
757
- {# Result: Only 'advanced' is added, 'javascript' already exists #}
758
-
759
- {# Chain multiple attr_concat filters #}
760
- {% set finalPost = post
761
- | attr_concat('tags', 'popular')
762
- | attr_concat('categories', ['tech', 'programming'])
763
- %}
764
-
765
- {# Use in loops to enhance collection items #}
766
- {% for item in collections.posts %}
767
- {% set enhancedItem = item | attr_concat('data.tags', 'blog') %}
768
- {# ... use enhancedItem ... #}
769
- {% endfor %}
770
- ```
771
-
772
- #### `fetch`
773
-
774
- A filter that fetches content from remote URLs or local files. For remote URLs, it uses `@11ty/eleventy-fetch` to download and cache files. For local paths, it reads files relative to the input directory.
775
-
776
- **Why use this?**
777
-
778
- When building static sites, you often need to include content from external sources or reuse content from local files. The `fetch` filter provides a unified way to retrieve content from both remote URLs and local files, with automatic caching for remote resources to improve build performance.
779
-
780
- **Requirements:**
781
-
782
- This filter requires the `@11ty/eleventy-fetch` package to be installed:
783
-
784
- ```bash
785
- npm install @11ty/eleventy-fetch
786
- ```
787
-
788
- > **Note:** If `@11ty/eleventy-fetch` is not installed, this filter will not be available. The plugin automatically detects whether the package is installed and only enables the filter if it's present.
789
-
790
- **Usage:**
791
-
792
- 1. Install the required dependency:
793
-
794
- ```bash
795
- npm install @11ty/eleventy-fetch
796
- ```
797
-
798
- 2. Enable the `fetch` filter in your Eleventy config:
799
-
800
- ```javascript
801
- import { fetchFilter } from "@anydigital/eleventy-bricks";
802
-
803
- export default function (eleventyConfig) {
804
- fetchFilter(eleventyConfig);
805
- // Or use as plugin:
806
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['fetch'] });
807
- }
808
- ```
809
-
810
- 3. Use the filter in your templates:
811
-
812
- **Fetch remote URLs:**
813
-
814
- ```njk
815
- {# Fetch content from a remote URL #}
816
- {% set externalContent = "https://example.com/data.json" | fetch %}
817
- {{ externalContent }}
818
-
819
- {# Fetch and parse JSON #}
820
- {% set apiData = "https://api.example.com/posts" | fetch %}
821
- {% set posts = apiData | fromJson %}
822
- ```
823
-
824
- **Fetch local files:**
825
-
826
- ```njk
827
- {# Fetch content from a local file (relative to input directory) #}
828
- {% set localData = "_data/content.txt" | fetch %}
829
- {{ localData }}
830
-
831
- {# Include content from another file #}
832
- {% set snippet = "_includes/snippets/example.md" | fetch %}
833
- {{ snippet | markdown | safe }}
834
- ```
835
-
836
- **Parameters:**
501
+ - Supports a URL (starting with `http://` or `https://`) or a local file path (relative to the input directory):
502
+ - **Remote URLs**: Downloads and caches content using `@11ty/eleventy-fetch`
503
+ - Caches files for 1 day by default
504
+ - Stores cached files in `[input-dir]/_downloads/` directory
505
+ - Automatically revalidates after cache expires
506
+ - **Local files**: Reads files relative to the Eleventy input directory
507
+ - No caching needed for local files
508
+ - Supports any file type that can be read as text
509
+ - **Error handling**: Throws descriptive errors if fetching fails
510
+ - **Conditional loading**: Only available when `@11ty/eleventy-fetch` is installed
837
511
 
838
- - `url`: A URL (starting with `http://` or `https://`) or a local file path (relative to the input directory)
512
+ **Use Cases:**
839
513
 
840
- **Features:**
514
+ - Fetch content from external APIs during build time
515
+ - Include README files from GitHub repositories
516
+ - Reuse content from local files across multiple pages
517
+ - Download and inline external CSS or JavaScript
518
+ - Fetch data from headless CMS or external data sources
519
+ - Include shared content snippets without using Eleventy's include syntax
841
520
 
842
- - **Remote URLs**: Downloads and caches content using `@11ty/eleventy-fetch`
843
- - Caches files for 1 day by default
844
- - Stores cached files in `[input-dir]/_downloads/` directory
845
- - Automatically revalidates after cache expires
846
- - **Local files**: Reads files relative to the Eleventy input directory
847
- - No caching needed for local files
848
- - Supports any file type that can be read as text
849
- - **Error handling**: Throws descriptive errors if fetching fails
850
- - **Conditional loading**: Only available when `@11ty/eleventy-fetch` is installed
521
+ > `NOTE:` The filter returns raw text content. Use Eleventy's built-in filters like `| safe`, `| markdown`, or `| fromJson` to process the content as needed.
851
522
 
852
523
  **Examples:**
853
524
 
854
- ```njk
525
+ ```jinja2
855
526
  {# Fetch and display remote content #}
856
527
  {% set readme = "https://raw.githubusercontent.com/user/repo/main/README.md" | fetch %}
857
528
  <div class="readme">
@@ -879,358 +550,152 @@ export default function (eleventyConfig) {
879
550
  {{ sharedContent | safe }}
880
551
  ```
881
552
 
882
- **Cache Directory:**
883
-
884
- Remote files are cached in the `_downloads` folder within your input directory:
885
-
886
- ```
887
- your-project/
888
- ├── src/ (or your input directory)
889
- │ ├── _downloads/ (cached remote files)
890
- │ ├── index.njk
891
- │ └── ...
892
- ```
893
-
894
- **Use Cases:**
895
-
896
- - Fetch content from external APIs during build time
897
- - Include README files from GitHub repositories
898
- - Reuse content from local files across multiple pages
899
- - Download and inline external CSS or JavaScript
900
- - Fetch data from headless CMS or external data sources
901
- - Include shared content snippets without using Eleventy's include syntax
902
-
903
- **Note:** The filter returns raw text content. Use Eleventy's built-in filters like `| safe`, `| markdown`, or `| fromJson` to process the content as needed.
904
-
905
- <!--section:data&processors-h3-->
906
-
907
- ### Global `site` data helpers
908
-
909
- Adds global `site` data to your Eleventy project, providing commonly needed values that can be accessed in all templates:
910
-
911
- | Variable | Value |
912
- | ----------------- | ------------------------------------------------------------------------------------------------------------ |
913
- | `{{ site.year }}` | The current year as a number (e.g., `2026`) |
914
- | `{{ site.prod }}` | Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`) |
915
-
916
- <details>
917
- <summary>Quick setup</summary>
918
-
919
- ```sh
920
- npm install @anydigital/eleventy-bricks
921
- ```
553
+ </details>
922
554
 
923
- Then choose one of the following options:
555
+ <details><summary>
924
556
 
925
- 1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
926
- import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
557
+ ### `section`
927
558
 
928
- export default function (eleventyConfig) {
929
- eleventyConfig.addPlugin(eleventyBricksPlugin, { siteData: true });
930
- }
931
- ```
559
+ </summary>
932
560
 
933
- 2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
934
- import { siteData } from "@anydigital/eleventy-bricks";
561
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
562
+ [`src/filters/section.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/section.js)
935
563
 
936
- export default function (eleventyConfig) {
937
- siteData(eleventyConfig);
938
- }
939
- ```
564
+ A filter that extracts a named section from content marked with HTML comments. This is useful for splitting a single content file (like a Markdown post) into multiple parts that can be displayed and styled independently in your templates.
940
565
 
941
- 3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
942
- ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
943
- ```
566
+ **Usage:**
944
567
 
945
- {.list-[upper-roman]}
568
+ 1. Mark sections in your content file (e.g., `post.md`):
946
569
 
947
- </details>
570
+ ⚠️ `NOTE:` The `¡` symbol is used instead of `!` only to give examples below. Use `!` in your actual content files.
948
571
 
949
- ### `mdAutoRawTags` preprocessor
572
+ ```markdown
573
+ # My Post
950
574
 
951
- Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
575
+ <¡--section:intro-->
952
576
 
953
- **Why use this?**
577
+ This is the introduction that appears at the top of the page.
954
578
 
955
- When writing documentation or tutorials about templating in Markdown files, you often want to show Nunjucks/Liquid syntax as literal text. This preprocessor automatically escapes these special characters so they display as-is instead of being processed by the template engine.
579
+ <¡--section:main-->
956
580
 
957
- **Example:**
581
+ This is the main body of the post with all the details.
958
582
 
959
- Before `mdAutoRawTags`, writing this in Markdown:
583
+ <¡--section:summary,sidebar-->
960
584
 
961
- ```markdown
962
- ### Using {{ variable }} to output variables
585
+ This content appears in both the summary and the sidebar!
963
586
  ```
964
587
 
965
- Would try to process `{{ variable }}` as a template variable. With `mdAutoRawTags`, it displays exactly as written.
966
-
967
- <details>
968
- <summary>Quick setup</summary>
969
-
970
- ```sh
971
- npm install @anydigital/eleventy-bricks
972
- ```
588
+ 2. Use the filter in your templates: <!-- @TODO: better examples -->
973
589
 
974
- Then choose one of the following options:
590
+ ```jinja2
591
+ {# Get the intro section #}
592
+ <div class="page-intro">
593
+ {{ content | section('intro') | safe }}
594
+ </div>
975
595
 
976
- 1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
977
- import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
596
+ {# Get the main section #}
597
+ <article>
598
+ {{ content | section('main') | safe }}
599
+ </article>
978
600
 
979
- export default function (eleventyConfig) {
980
- eleventyConfig.addPlugin(eleventyBricksPlugin, { mdAutoRawTags: true });
981
- }
982
- ```
601
+ {# Get the sidebar section #}
602
+ <aside>
603
+ {{ content | section('sidebar') | safe }}
604
+ </aside>
605
+ ```
983
606
 
984
- 2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
985
- import { mdAutoRawTags } from "@anydigital/eleventy-bricks";
607
+ **Features:**
986
608
 
987
- export default function (eleventyConfig) {
988
- mdAutoRawTags(eleventyConfig);
989
- }
990
- ```
609
+ - **Multiple names**: A single section can have multiple names separated by commas: `<¡--section:name1,name2-->`
610
+ - **Case-insensitive**: Section names are matched without regard to case
611
+ - **Multiple occurrences**: If a section name appears multiple times, the filter concatenates all matching sections
612
+ - **Non-destructive**: Returns extracted content without modifying the original input
613
+ - **EOF support**: Sections continue until the next `<¡--section*-->` marker or the end of the file
991
614
 
992
- 3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
993
- ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
994
- ```
615
+ **Syntax Rules:**
995
616
 
996
- {.list-[upper-roman]}
617
+ - Sections start with: `<¡--section:NAME-->` or `<¡--section:NAME1,NAME2-->`
618
+ - Sections end at the next `<¡--section*-->` marker or end of file
619
+ - Whitespace around names and inside comments is automatically trimmed
997
620
 
998
621
  </details>
999
622
 
1000
- ### `mdAutoNl2br` converter
623
+ <details><summary>
1001
624
 
1002
- Automatically converts `\n` sequences to `<br>` tags in Markdown content. This is particularly useful for adding line breaks inside Markdown tables where standard newlines don't work.
625
+ ### `remove_tag`
1003
626
 
1004
- **Why use this?**
627
+ </summary>
1005
628
 
1006
- Markdown tables don't support multi-line content in cells. By using `\n` in your content, this preprocessor will convert it to `<br>` tags, allowing you to display line breaks within table cells and other content.
629
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
630
+ [`src/filters/remove_tag.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/remove_tag.js)
1007
631
 
1008
- **Example:**
632
+ A filter that removes a specified HTML element from provided HTML content. It removes the tag along with its content, including self-closing tags.
1009
633
 
1010
- In your Markdown file:
634
+ **Why use this?** When working with content from external sources or user-generated content, you may need to strip certain HTML tags for security or presentation purposes. The `remove_tag` filter provides a simple way to remove unwanted tags like `<script>`, `<style>`, or any other HTML elements from your content.
1011
635
 
1012
- ```markdown
1013
- | Column 1 | Column 2 |
1014
- | ---------------------- | --------------------------------- |
1015
- | Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |
1016
- ```
636
+ **Features:**
1017
637
 
1018
- Will render as:
638
+ - Removes both opening and closing tags along with their content
639
+ - Handles self-closing tags (e.g., `<br />`, `<img />`)
640
+ - Handles tags with attributes
641
+ - Case-insensitive matching
642
+ - Non-destructive: Returns new string, doesn't modify original
1019
643
 
1020
- ```html
1021
- <td>Line 1<br />Line 2<br />Line 3</td>
1022
- <td>Another cell<br />With multiple lines</td>
1023
- ```
644
+ **Security note:** While this filter can help sanitize HTML content, it should not be relied upon as the sole security measure. For critical security requirements, use a dedicated HTML sanitization library on the server side before content reaches your templates.
1024
645
 
1025
- **Note:** This processes literal `\n` sequences (backslash followed by 'n'), not actual newline characters. Type `\n` in your source files where you want line breaks.
646
+ #### Example: Remove all script tags from content <!-- @TODO: better examples -->
1026
647
 
1027
- <details>
1028
- <summary>Quick setup</summary>
648
+ ```jinja2
649
+ {% set cleanContent = htmlContent | remove_tag('script') %}
1029
650
 
1030
- ```sh
1031
- npm install @anydigital/eleventy-bricks
651
+ {{ cleanContent | safe }}
1032
652
  ```
1033
653
 
1034
- Then choose one of the following options:
1035
-
1036
- 1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
1037
- import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
1038
-
1039
- export default function (eleventyConfig) {
1040
- eleventyConfig.addPlugin(eleventyBricksPlugin, { mdAutoNl2br: true });
1041
- }
1042
- ```
1043
-
1044
- 2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
1045
- import { mdAutoNl2br } from "@anydigital/eleventy-bricks";
1046
-
1047
- export default function (eleventyConfig) {
1048
- mdAutoNl2br(eleventyConfig);
1049
- }
1050
- ```
1051
-
1052
- 3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
1053
- ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
1054
- ```
654
+ </details>
1055
655
 
1056
- {.list-[upper-roman]}
656
+ <details><summary>
1057
657
 
1058
- </details>
658
+ ### `strip_tag`
1059
659
 
1060
- ### `autoLinkFavicons` processor
660
+ </summary>
1061
661
 
1062
- Automatically adds favicon images from Google's favicon service to links that display plain URLs or domain names. This processor processes all HTML output files and adds inline favicon images next to link text that appears to be a plain URL.
662
+ 🧩 [Install via Plugin](https://github.com/anydigital/eleventy-bricks#install) or copy-paste from
663
+ [`src/filters/strip_tag.js`](https://github.com/anydigital/eleventy-bricks/blob/main/src/filters/strip_tag.js)
1063
664
 
1064
- **Why use this?**
665
+ A filter that strips a specified HTML element from content while keeping its inner content intact. Only the opening and closing tags are removed; everything inside the tag is preserved in place.
1065
666
 
1066
- When you have links in your content that display raw URLs or domain names (like `https://example.com/page`), adding favicons provides a visual indicator of the external site. This processor automatically detects these plain-text URL links and enhances them with favicon images, making them more visually appealing and easier to recognize.
667
+ **Why use this?** When rendering HTML from a CMS or external source you sometimes need to unwrap a specific element (e.g. remove a wrapping `<div>` or `<section>`) without losing the content it contains. Unlike `remove_tag`, which discards the entire element and its content, `strip_tag` surgically removes only the tags themselves.
1067
668
 
1068
- **How it works:**
669
+ **Features:**
1069
670
 
1070
- 1. Scans all HTML output files for `<a>` tags
1071
- 2. Checks if the link text appears to be a plain URL or domain
1072
- 3. Extracts the domain from the URL
1073
- 4. Removes the domain from the link text (keeping only the path)
1074
- 5. Adds a favicon image from Google's favicon service inline with the remaining text
671
+ - Removes only the opening and closing tags — inner content is preserved
672
+ - Handles tags with any attributes
673
+ - Strips all occurrences of the tag, including nested ones
674
+ - Case-insensitive matching
675
+ - Non-destructive: Returns a new string, leaves the original unchanged
1075
676
 
1076
- **Example:**
677
+ #### Example: Unwrap a wrapping `<div>` from content
1077
678
 
1078
- Before processing:
679
+ ```jinja2
680
+ {% set unwrapped = htmlContent | strip_tag('div') %}
1079
681
 
1080
- ```html
1081
- <a href="https://github.com/anydigital/eleventy-bricks">https://github.com/anydigital/eleventy-bricks</a>
682
+ {{ unwrapped | safe }}
1082
683
  ```
1083
684
 
1084
- After processing:
685
+ Input:
1085
686
 
1086
687
  ```html
1087
- <a href="https://github.com/anydigital/eleventy-bricks" class="whitespace-nowrap" target="_blank">
1088
- <i><img src="https://www.google.com/s2/favicons?domain=github.com&sz=32" /></i>
1089
- <span>/anydigital/eleventy-bricks</span>
1090
- </a>
688
+ <div class="wrapper">
689
+ <p>Hello</p>
690
+ <p>World</p>
691
+ </div>
1091
692
  ```
1092
693
 
1093
- **Rules:**
1094
-
1095
- - Only applies to links where the text looks like a plain URL (contains the domain or starts with `http://`/`https://`)
1096
- - Removes the protocol and domain from the display text
1097
- - Removes the trailing slash from the display text
1098
- - Only applies if at least 3 characters remain after removing the domain (to avoid showing favicons for bare domain links)
1099
- - Uses Google's favicon service at `https://www.google.com/s2/favicons?domain=DOMAIN&sz=32`
1100
- - Adds `target="_blank"` to the processed links (only if not already present)
1101
- - Adds `whitespace-nowrap` class to the link
1102
- - Wraps the link text in a `<span>` element
1103
- - The favicon is wrapped in an `<i>` tag for easy styling
1104
-
1105
- <details>
1106
- <summary>Quick setup</summary>
694
+ Output:
1107
695
 
1108
- ```sh
1109
- npm install @anydigital/eleventy-bricks
696
+ ```html
697
+ <p>Hello</p>
698
+ <p>World</p>
1110
699
  ```
1111
700
 
1112
- Then choose one of the following options:
1113
-
1114
- 1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
1115
- import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
1116
-
1117
- export default function (eleventyConfig) {
1118
- eleventyConfig.addPlugin(eleventyBricksPlugin, { autoLinkFavicons: true });
1119
- }
1120
- ```
1121
-
1122
- 2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
1123
- import { autoLinkFavicons } from "@anydigital/eleventy-bricks";
1124
-
1125
- export default function (eleventyConfig) {
1126
- autoLinkFavicons(eleventyConfig);
1127
- }
1128
- ```
1129
-
1130
- 3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
1131
- ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
1132
- ```
1133
-
1134
- {.list-[upper-roman]}
1135
-
1136
701
  </details>
1137
-
1138
- <!--section:config-h3-->
1139
-
1140
- ### Symlinked `eleventy.config.js` <sub>from https://github.com/anydigital/eleventy-bricks</sub>
1141
-
1142
- The package includes a fully-configured Eleventy config file `eleventy.config.js` that you can symlink to your project to get:
1143
-
1144
- - All eleventy-bricks plugins enabled
1145
- - Eleventy Navigation plugin
1146
- - Table of Contents plugin (conditionally loaded if installed)
1147
- - Markdown-it with anchors and attributes
1148
- - YAML data support
1149
- - CLI input directory support
1150
- - Symlink support for development
1151
- - _and more_
1152
-
1153
- **Benefits of symlinking:**
1154
-
1155
- - **Always up-to-date**: Configuration automatically updates when you upgrade the package
1156
- - **Less maintenance**: No need to manually sync configuration changes
1157
- - **Quick setup**: Get started immediately with best-practice configurations
1158
- - **Easy customization**: Override specific settings by creating your own config that imports from the symlinked version
1159
-
1160
- **Quick setup:**
1161
-
1162
- ```sh
1163
- npm install @anydigital/eleventy-bricks
1164
- ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
1165
- ```
1166
-
1167
- Done! 🎉
1168
-
1169
- <!--section:cms-h4-->
1170
-
1171
- ### Symlinked CMS
1172
-
1173
- A ready-to-use Sveltia CMS admin interface for content management.
1174
-
1175
- **Symlink to your project:**
1176
-
1177
- ```bash
1178
- mkdir -p admin
1179
- ln -s ../node_modules/@anydigital/eleventy-bricks/src/admin/index.html admin/index.html
1180
- ```
1181
-
1182
- <!--section:npm-h3-->
1183
-
1184
- ### Reusable 11ty npm scripts <small>via npm workspace</small> <sub>from https://github.com/anydigital/eleventy-bricks</sub>
1185
-
1186
- This package provides a pre-configured `do` folder setup that helps organize your development workflow using npm workspaces. The `do` folder contains scripts for building and running your Eleventy project.
1187
-
1188
- **Quick setup:**
1189
-
1190
- 1. Create a simple folder, which will hold reusable npm scripts:
1191
-
1192
- ```sh
1193
- mkdir do
1194
- ```
1195
-
1196
- 2. Install https://github.com/anydigital/eleventy-bricks to reuse default 11ty scripts from there:
1197
-
1198
- ```sh
1199
- npm install @anydigital/eleventy-bricks
1200
- ```
1201
-
1202
- 3. Symlink the `do/package.json` containing scripts into your project's `do` folder:
1203
-
1204
- ```sh
1205
- cd do
1206
- ln -s node_modules/@anydigital/eleventy-bricks/src/do/package.json
1207
- ```
1208
-
1209
- 4. Finally register `do` folder as npm workspace in your `package.json`, and enjoy default 11ty scripts as simple as:
1210
-
1211
- ```json {data-caption="YOUR project's package.json"}
1212
- {
1213
- "workspaces": ["do"],
1214
- "scripts": {
1215
- "start": "npm -w do run start",
1216
- "stage": "npm -w do run stage",
1217
- "build": "npm -w do run build"
1218
- }
1219
- }
1220
- ```
1221
-
1222
- **Done!** 🎉 Now you can run:
1223
-
1224
- - `npm start` to start 11ty dev server with live reload and Tailwind watch mode
1225
- - `npm run stage` to build and serve production-like site locally
1226
- - `npm run build` to finally build the site for production
1227
- - all available scripts: https://github.com/anydigital/eleventy-bricks/blob/main/src/do/package.json
1228
-
1229
- **Example setup:** https://github.com/anydigital/sveleven
1230
-
1231
- **Benefits:**
1232
-
1233
- - **Clean separation**: Keep build scripts separate from project configuration
1234
- - **Reusable workflows**: Update scripts by upgrading the package
1235
- - **Workspace isolation**: Scripts run in their own workspace context
1236
- - **Easy maintenance**: No need to manually maintain build scripts