@anydigital/eleventy-bricks 0.27.0 → 0.28.0-alpha

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,668 +2,309 @@
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:
11
+ Then choose one of the following options:
14
12
 
15
- ### Option 1: As a Plugin
13
+ ### Option A. Starting 11ty from scratch?
16
14
 
17
- Import and use the entire plugin. You can configure which helpers to enable using the options parameter:
15
+ Consider symlinking entire `eleventy.config.js`:
18
16
 
19
- **ES Modules:**
20
-
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
- ```
138
-
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.
140
-
141
- ## Configuration Options
142
-
143
- When using the plugin (Option 1), you can configure which helpers to enable:
144
-
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) |
152
-
153
- **Available filter names for the `filters` array:**
154
-
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`)
163
-
164
- **Example:**
165
-
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
- });
174
- ```
175
-
176
- ### Additional Exports
177
-
178
- The plugin also exports the following utility functions for advanced usage:
179
-
180
- - `transformAutoRaw(content)`: The transform function used by `mdAutoRawTags` preprocessor. Can be used programmatically to wrap Nunjucks syntax with raw tags.
181
- - `transformNl2br(content)`: The transform function used by `mdAutoNl2br` preprocessor. Can be used programmatically to convert `\n` sequences to `<br>` 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 transform function used by `autoLinkFavicons` that transforms a single link to include a favicon.
186
- - `replaceLinksInHtml(content, transformer)`: Helper function that replaces all anchor links in HTML content with transformed 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.
194
-
195
- <!--section:11ty-->
196
-
197
- ## Tricks from [Eleventy Bricks](https://github.com/anydigital/eleventy-bricks) {#eleventy-bricks}
198
-
199
- ### Filters
200
-
201
- | Input | Nunjucks | Liquid <hr> |
202
- | ---------: | ------------------------------------------------------------------------------ | ---------------------------------------------------- |
203
- | {.divider} | Logical |
204
- | `ANY \| ` | `default(VALUE)` <br>= `d(...)` | `default: VALUE` |
205
- | `ANY \|` | [`if(TEST, OP, VALUE)`](#if) <sub>currently only `if(TEST)`</sub> | [`if: TEST, OP, VALUE`](#if) |
206
- | {.divider} | On objects |
207
- | `OBJ \|` | [`merge(OBJ2)`](#merge) | [`merge: OBJ2`](#merge) |
208
- | {.divider} | On object attributes |
209
- | `OBJ \|` | `selectattr(BOOL_ATTR)` | `where: ATTR, VALUE` |
210
- | `OBJ \|` | `rejectattr(BOOL_ATTR)` | N/A |
211
- | `OBJ \|` | [`attr_includes(ARRAY_ATTR, VALUE)`](#attr_includes) <sub>was `where_in`</sub> | [`attr_includes: ARRAY_ATTR, VALUE`](#attr_includes) |
212
- | `OBJ \|` | [`attr_set(ATTR, VALUE)`](#attr_set) <sub>was `attr`</sub> | [`attr_set: ATTR, VALUE`](#attr_set) |
213
- | `OBJ \|` | [`attr_concat(ARRAY_ATTR, ARRAY2)`](#attr_concat) | [`attr_concat: ARRAY_ATTR, ARRAY2`](#attr_concat) |
214
- | {.divider} | Textual |
215
- | `HTML \|` | `striptags` | `strip_html` |
216
- | `HTML \|` | [`remove_tag(TAG)`](#remove_tag) | [`remove_tag: TAG`](#remove_tag) |
217
- | `HTML \|` | [`section(NAME)`](#section) | [`section: NAME`](#section) |
218
- | `STR \|` | `remove: STR2` | `remove: STR2` |
219
- | {.divider} | Other |
220
- | `URL \|` | [`fetch`](#fetch) | [`fetch`](#fetch) |
221
-
222
- Ref:
223
-
224
- - https://mozilla.github.io/nunjucks/templating.html#builtin-filters
225
- - https://shopify.github.io/liquid/
226
-
227
- #### `attr_set`
228
-
229
- 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.
230
-
231
- **Why use this?**
232
-
233
- 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.
63
+ </details>
234
64
 
235
- **Usage:**
65
+ ## Command Line Tools
236
66
 
237
- 1. Enable the `attr_set` filter in your Eleventy config:
67
+ <!--section:npm-h3-->
238
68
 
239
- ```javascript
240
- import { attrSetFilter } from "@anydigital/eleventy-bricks";
69
+ ### 🥷 Reusable 11ty npm scripts <small>via npm workspace</small> <br><sub>from https://github.com/anydigital/eleventy-bricks</sub>
241
70
 
242
- export default function (eleventyConfig) {
243
- attrSetFilter(eleventyConfig);
244
- // Or use as plugin:
245
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_set'] });
246
- }
247
- ```
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.
248
72
 
249
- 2. Use the filter in your templates:
73
+ **Installation:**
250
74
 
251
- ```njk
252
- {# Create a modified version of a page object #}
253
- {% set modifiedPage = page | attr_set('title', 'New Title') %}
75
+ 1. Install https://github.com/anydigital/eleventy-bricks to reuse pre-defined 11ty scripts from there:
254
76
 
255
- <h1>{{ modifiedPage.title }}</h1>
256
- <p>Original title: {{ page.title }}</p>
77
+ ```sh
78
+ npm install @anydigital/eleventy-bricks
257
79
  ```
258
80
 
259
- **Parameters:**
260
-
261
- - `obj`: The object to modify
262
- - `key`: The attribute name to set (string)
263
- - `value`: The value to set for the attribute (any type)
264
-
265
- **Returns:**
266
-
267
- A new object with the specified attribute set to the given value. The original object is not modified.
268
-
269
- **Features:**
270
-
271
- - Non-mutating: Creates a new object, leaving the original unchanged
272
- - Works with any object type
273
- - Supports any attribute name and value type
274
- - Can be chained with other filters
275
-
276
- **Examples:**
277
-
278
- ```njk
279
- {# Override a single attribute #}
280
- {% set updatedPost = post | attr_set('featured', true) %}
281
-
282
- {# Chain multiple attr_set filters #}
283
- {% set modifiedPost = post
284
- | attr_set('category', 'blog')
285
- | attr_set('priority', 1)
286
- %}
81
+ 2. Create a helper folder `do` to symlink the `do/package.json` within:
287
82
 
288
- {# Use in loops #}
289
- {% for item in collection %}
290
- {% set enhancedItem = item | attr_set('processed', true) %}
291
- {# ... use enhancedItem ... #}
292
- {% endfor %}
83
+ ```sh
84
+ mkdir do
85
+ cd ./do
86
+ ln -s ../node_modules/@anydigital/eleventy-bricks/src/do/package.json
293
87
  ```
294
88
 
295
- #### `attr_includes`
296
-
297
- A filter that filters collection items by checking if an attribute array includes a target value. Supports nested attribute names using dot notation.
298
-
299
- **Why use this?**
300
-
301
- 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.
302
-
303
- **Usage:**
304
-
305
- 1. Enable the `attr_includes` filter in your Eleventy config:
89
+ 3. Finally register `do` folder as npm workspace in your root `package.json`:
306
90
 
307
- ```javascript
308
- import { attrIncludesFilter } from "@anydigital/eleventy-bricks";
309
-
310
- export default function (eleventyConfig) {
311
- attrIncludesFilter(eleventyConfig);
312
- // Or use as plugin:
313
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_includes'] });
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
+ ...
314
101
  }
315
102
  ```
316
103
 
317
- 2. Use the filter in your templates:
318
-
319
- **Filter by array attribute (tags):**
320
-
321
- ```njk
322
- {# Get all posts that include 'javascript' tag #}
323
- {% set jsPosts = collections.all | attr_includes('data.tags', 'javascript') %}
324
-
325
- {% for post in jsPosts %}
326
- <h2>{{ post.data.title }}</h2>
327
- {% endfor %}
328
- ```
329
-
330
- **Parameters:**
331
-
332
- - `collection`: The collection to filter (array of items)
333
- - `attrName`: The attribute name to check (string, supports dot notation for nested properties)
334
- - `targetValue`: The value to check for in the array (any type)
335
-
336
- **Features:**
337
-
338
- - Works with any array attribute in front matter
339
- - Supports dot notation for nested properties (e.g., `'data.tags'`, `'data.author.roles'`)
340
- - Returns empty array if collection is invalid
341
- - Filters out items where the specified attribute is not an array or doesn't exist
342
-
343
- **Examples:**
104
+ **Done!** 🎉 Now you can run:
344
105
 
345
- Front matter:
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
346
110
 
347
- ```yaml
348
- ---
349
- title: My Post
350
- category: blog
351
- tags: [javascript, tutorial, beginner]
352
- priority: 1
353
- ---
354
- ```
111
+ **Living example:** https://github.com/anydigital/sveleven
355
112
 
356
- Template usage:
113
+ **Benefits:**
357
114
 
358
- ```njk
359
- {# Filter by tag (array) using dot notation for nested properties #}
360
- {% set jsTutorials = collections.all | attr_includes('data.tags', 'javascript') %}
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
361
119
 
362
- {# Filter by numeric value in array #}
363
- {% set highPriority = collections.all | attr_includes('data.priorities', 1) %}
120
+ <!--section-->
364
121
 
365
- {# Chain filters #}
366
- {% set recentTutorials = collections.all | attr_includes('data.tags', 'tutorial') | reverse | limit(5) %}
367
- ```
122
+ ## Configuration Tools
368
123
 
369
- #### `merge`
124
+ <!--section:config-h3-->
370
125
 
371
- 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.
126
+ ### 🥷 Symlinked `eleventy.config.js` <br><sub>from https://github.com/anydigital/eleventy-bricks</sub> <a id="symlink-config"></a>
372
127
 
373
- **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:
374
129
 
375
- 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.
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_
376
138
 
377
- **Usage:**
139
+ **Benefits of symlinking:**
378
140
 
379
- 1. Enable the `merge` 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
380
145
 
381
- ```javascript
382
- import { mergeFilter } from "@anydigital/eleventy-bricks";
146
+ **Installation as simple as:**
383
147
 
384
- export default function (eleventyConfig) {
385
- mergeFilter(eleventyConfig);
386
- // Or use as plugin:
387
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['merge'] });
388
- }
148
+ ```sh
149
+ npm install @anydigital/eleventy-bricks
150
+ ln -s ./node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
389
151
  ```
390
152
 
391
- 2. Use the filter in your templates:
153
+ <!--section:cms-h3-->
392
154
 
393
- **Merge arrays:**
155
+ <details><summary>
394
156
 
395
- ```njk
396
- {# Combine two arrays #}
397
- {% set allItems = featured | merge(regular) %}
157
+ ### Symlinked CMS `index.html`
398
158
 
399
- {# Merge multiple arrays #}
400
- {% set combined = array1 | merge(array2, array3, array4) %}
401
-
402
- {% for item in allItems %}
403
- <p>{{ item }}</p>
404
- {% endfor %}
405
- ```
159
+ </summary>
406
160
 
407
- **Merge objects:**
161
+ A ready-to-use Sveltia CMS admin interface for content management.
408
162
 
409
- ```njk
410
- {# Merge configuration objects #}
411
- {% set defaultConfig = { theme: 'light', lang: 'en' } %}
412
- {% set userConfig = { theme: 'dark' } %}
413
- {% set finalConfig = defaultConfig | merge(userConfig) %}
163
+ **Installation:**
414
164
 
415
- {# Result: { theme: 'dark', lang: 'en' } #}
165
+ ```sh
166
+ mkdir -p ./src/admin
167
+ cd ./src/admin
168
+ ln -s ../../node_modules/@anydigital/eleventy-bricks/src/admin/index.html
416
169
  ```
417
170
 
418
- **Parameters:**
171
+ </details>
419
172
 
420
- - `first`: The first array or object (the base to merge into)
421
- - `...rest`: One or more arrays or objects to merge in
173
+ ## Data Tools & Processors
422
174
 
423
- **Features:**
175
+ <!--section:data&processors-h3-->
424
176
 
425
- - Works with both arrays and objects
426
- - Supports merging multiple items at once
427
- - Non-mutating: Creates new arrays/objects, leaving originals unchanged
428
- - For objects: Later values override earlier ones (shallow merge)
429
- - For arrays: Concatenates all arrays together
430
- - Handles null/undefined gracefully
177
+ ### Global `siteData` helper
431
178
 
432
- **Examples:**
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)
433
181
 
434
- ```njk
435
- {# Combine featured and regular posts #}
436
- {% set featuredPosts = collections.all | attr_includes('data.featured', true) %}
437
- {% set regularPosts = collections.all | attr_includes('data.featured', false) %}
438
- {% set allPosts = featuredPosts | merge(regularPosts) %}
182
+ Adds global `site` data to your Eleventy project, providing commonly needed values that can be accessed in all templates:
439
183
 
440
- {# Merge page metadata with defaults #}
441
- {% set defaultMeta = {
442
- author: 'Site Admin',
443
- category: 'general',
444
- comments: false
445
- } %}
446
- {% set pageMeta = defaultMeta | merge(page.data) %}
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`) |
447
188
 
448
- {# Combine arrays of tags #}
449
- {% set commonTags = ['javascript', 'html', 'css'] %}
450
- {% set specialTags = page.data.tags or [] %}
451
- {% set allTags = commonTags | merge(specialTags) %}
189
+ ### 🥷 `autoLinkFavicons` transformer
452
190
 
453
- {# Merge multiple configuration sources #}
454
- {% set config = defaults | merge(siteConfig, pageConfig, userPrefs) %}
455
- ```
456
-
457
- #### `remove_tag`
458
-
459
- A filter that removes a specified HTML element from provided HTML content. It removes the tag along with its content, including self-closing tags.
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)
460
193
 
461
- **Why use this?**
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.
462
195
 
463
- 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.
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.
464
197
 
465
- **Usage:**
466
-
467
- 1. Enable the `remove_tag` filter in your Eleventy config:
468
-
469
- ```javascript
470
- import { removeTagFilter } from "@anydigital/eleventy-bricks";
198
+ **How it works:**
471
199
 
472
- export default function (eleventyConfig) {
473
- removeTagFilter(eleventyConfig);
474
- // Or use as plugin:
475
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['remove_tag'] });
476
- }
477
- ```
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
478
205
 
479
- 2. Use the filter in your templates:
206
+ **Example:**
480
207
 
481
- ```njk
482
- {# Remove all script tags from content #}
483
- {% set cleanContent = htmlContent | remove_tag('script') %}
208
+ Before processing:
484
209
 
485
- {{ cleanContent | safe }}
210
+ ```html
211
+ <a href="https://github.com/anydigital/eleventy-bricks">https://github.com/anydigital/eleventy-bricks</a>
486
212
  ```
487
213
 
488
- **Parameters:**
489
-
490
- - `html`: The HTML content to process (string)
491
- - `tagName`: The tag name to remove (string)
492
-
493
- **Features:**
494
-
495
- - Removes both opening and closing tags along with their content
496
- - Handles self-closing tags (e.g., `<br />`, `<img />`)
497
- - Handles tags with attributes
498
- - Case-insensitive matching
499
- - Non-destructive: Returns new string, doesn't modify original
500
-
501
- **Examples:**
214
+ After processing:
502
215
 
503
- ```njk
504
- {# Remove scripts from user-generated content #}
505
- {% set userContent = '<p>Hello</p><script>alert("XSS")</script><p>World</p>' %}
506
- {% set safeContent = userContent | remove_tag('script') %}
507
- {# Result: '<p>Hello</p><p>World</p>' #}
508
-
509
- {# Strip specific formatting tags #}
510
- {% set formatted = '<div><strong>Bold</strong> and <em>italic</em> text</div>' %}
511
- {% set noStrong = formatted | remove_tag('strong') %}
512
- {# Result: '<div>Bold and <em>italic</em> text</div>' #}
513
-
514
- {# Chain multiple remove_tag filters for multiple tags #}
515
- {% set richContent = page.content %}
516
- {% set stripped = richContent
517
- | remove_tag('script')
518
- | remove_tag('style')
519
- | remove_tag('iframe')
520
- %}
521
-
522
- {# Remove images for text-only preview #}
523
- {% set textOnly = htmlContent | remove_tag('img') %}
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>
524
221
  ```
525
222
 
526
- **Security Note:**
527
-
528
- 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.
223
+ **Rules:**
529
224
 
530
- #### `section`
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
531
234
 
532
- 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.
235
+ <details><summary>
533
236
 
534
- **Why use this?**
237
+ ### mdAutoRawTags preprocessor
535
238
 
536
- 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.
239
+ </summary>
537
240
 
538
- **Usage:**
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)
539
243
 
540
- 1. Enable the `section` filter in your Eleventy config:
244
+ Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
541
245
 
542
- ```javascript
543
- import { sectionFilter } from "@anydigital/eleventy-bricks";
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.
544
247
 
545
- export default function (eleventyConfig) {
546
- sectionFilter(eleventyConfig);
547
- // Or use as plugin:
548
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['section'] });
549
- }
550
- ```
248
+ **Example:**
551
249
 
552
- 2. Mark sections in your content file (e.g., `post.md`):
250
+ Before `mdAutoRawTags`, writing this in Markdown:
553
251
 
554
252
  ```markdown
555
- # My Post
556
-
557
- &lt;!--section:intro-->
558
-
559
- This is the introduction that appears at the top of the page.
560
-
561
- &lt;!--section:main-->
562
-
563
- This is the main body of the post with all the details.
564
-
565
- &lt;!--section:summary,sidebar-->
566
-
567
- This content appears in both the summary and the sidebar!
253
+ ### Using {{ variable }} to output variables
568
254
  ```
569
255
 
570
- 3. Use the filter in your templates:
571
-
572
- ```njk
573
- {# Get the intro section #}
574
- <div class="page-intro">
575
- {{ content | section('intro') | safe }}
576
- </div>
256
+ Would try to process `{{ variable }}` as a template variable. With `mdAutoRawTags`, it displays exactly as written.
577
257
 
578
- {# Get the main section #}
579
- <article>
580
- {{ content | section('main') | safe }}
581
- </article>
258
+ </details>
582
259
 
583
- {# Get the sidebar section #}
584
- <aside>
585
- {{ content | section('sidebar') | safe }}
586
- </aside>
587
- ```
260
+ <details><summary>
588
261
 
589
- **Parameters:**
262
+ ### mdAutoNl2br converter
590
263
 
591
- - `content`: The string content to process (usually `content` variable)
592
- - `sectionName`: The name(s) of the section to extract (string)
264
+ </summary>
593
265
 
594
- **Features:**
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)
595
268
 
596
- - **Multiple names**: A single section can have multiple names separated by commas: `&lt;!--section:name1,name2-->`
597
- - **Case-insensitive**: Section names are matched without regard to case
598
- - **Multiple occurrences**: If a section name appears multiple times, the filter concatenates all matching sections
599
- - **Non-destructive**: Returns extracted content without modifying the original input
600
- - **EOF support**: Sections continue until the next `&lt;!--section*-->` marker or the end of the file
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.
601
270
 
602
- **Examples:**
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.
603
272
 
604
- ```njk
605
- {# Extract multiple sections with same name #}
606
- {# Example content has two &lt;!--section:note--> blocks #}
607
- <div class="notes-box">
608
- {{ content | section('note') | safe }}
609
- </div>
273
+ **Example:**
610
274
 
611
- {# Use case-insensitive names #}
612
- {{ content | section('INTRO') | safe }}
275
+ In your Markdown file:
613
276
 
614
- {# Handle missing sections gracefully (returns empty string) #}
615
- {% set footer = content | section('non-existent-section') %}
616
- {% if footer %}
617
- <footer>{{ footer | safe }}</footer>
618
- {% endif %}
277
+ ```markdown
278
+ | Column 1 | Column 2 |
279
+ | ---------------------- | --------------------------------- |
280
+ | Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |
619
281
  ```
620
282
 
621
- **Syntax Rules:**
622
-
623
- - Sections start with: `&lt;!--section:NAME-->` or `&lt;!--section:NAME1,NAME2-->`
624
- - Sections end at the next `&lt;!--section*-->` marker or end of file
625
- - Whitespace around names and inside comments is automatically trimmed
626
-
627
- #### `if`
628
-
629
- 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.
630
-
631
- **Why use this?**
632
-
633
- 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.
283
+ Will render as:
634
284
 
635
- **Usage:**
285
+ ```html
286
+ <td>Line 1<br />Line 2<br />Line 3</td>
287
+ <td>Another cell<br />With multiple lines</td>
288
+ ```
636
289
 
637
- 1. Enable the `if` filter in your Eleventy config:
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.
638
291
 
639
- ```javascript
640
- import { ifFilter } from "@anydigital/eleventy-bricks";
292
+ </details>
641
293
 
642
- export default function (eleventyConfig) {
643
- ifFilter(eleventyConfig);
644
- // Or use as plugin:
645
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['if'] });
646
- }
647
- ```
294
+ <!--section:filters-h2-->
648
295
 
649
- 2. Use the filter in your templates:
296
+ ## 🥷 Universal 11ty Filters <small>for `.njk` & `.liquid`</small> <sub>from https://github.com/anydigital/eleventy-bricks</sub> <a id="filters"></a>
650
297
 
651
- ```njk
652
- {# Basic usage #}
653
- <div class="{{ 'active' | if: isActive, 'inactive' }}">Status</div>
298
+ <details><summary>
654
299
 
655
- {# Without falsy value (defaults to empty string) #}
656
- <span class="{{ 'highlight' | if: shouldHighlight }}">Text</span>
300
+ ### `if`
657
301
 
658
- {# With variable values #}
659
- {% set status = 'Published' | if: post.published, 'Draft' %}
660
- ```
302
+ </summary>
661
303
 
662
- **Parameters:**
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)
663
306
 
664
- - `trueValue`: The value to return if condition is truthy
665
- - `condition`: The condition to evaluate
666
- - `falseValue`: The value to return if condition is falsy (optional, defaults to empty string)
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.
667
308
 
668
309
  **Features:**
669
310
 
@@ -672,11 +313,14 @@ export default function (eleventyConfig) {
672
313
  - Default `falseValue` is an empty string if not provided
673
314
  - Works with any data type for values
674
315
 
675
- **Examples:**
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>
676
321
 
677
- ```njk
678
322
  {# Toggle CSS classes #}
679
- <button class="{{ 'btn-primary' | if: isPrimary, 'btn-secondary' }}">
323
+ <button class="{{ 'btn-primary' | if: isPrimary | default: 'btn-secondary' }}">
680
324
  Click me
681
325
  </button>
682
326
 
@@ -699,171 +343,186 @@ export default function (eleventyConfig) {
699
343
  {% set cssClass = 'featured' | if: post.featured | upper %}
700
344
  ```
701
345
 
702
- #### `attr_concat`
346
+ </details>
703
347
 
704
- 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.
348
+ <details><summary>
705
349
 
706
- **Why use this?**
350
+ ### `merge`
707
351
 
708
- 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.
352
+ </summary>
709
353
 
710
- **Usage:**
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)
711
356
 
712
- 1. Enable the `attr_concat` filter in your Eleventy config:
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.
713
358
 
714
- ```javascript
715
- import { attrConcatFilter } from "@anydigital/eleventy-bricks";
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.
716
360
 
717
- export default function (eleventyConfig) {
718
- attrConcatFilter(eleventyConfig);
719
- // Or use as plugin:
720
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_concat'] });
721
- }
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) %}
368
+
369
+ {# Result: { theme: 'dark', lang: 'en' } #}
722
370
  ```
723
371
 
724
- 2. Use the filter in your templates:
372
+ ```jinja2
373
+ {# Merge page metadata with defaults #}
374
+ {% set defaultMeta = {
375
+ author: 'Site Admin',
376
+ category: 'general',
377
+ comments: false
378
+ } %}
379
+ {% set pageMeta = defaultMeta | merge(page.data) %}
380
+ ```
725
381
 
726
- ```njk
727
- {# Add tags to a post object #}
728
- {% set enhancedPost = post | attr_concat('tags', ['featured', 'popular']) %}
382
+ </details>
383
+
384
+ <details><summary>
385
+
386
+ ### `attr_set`
729
387
 
730
- {# Add a single value #}
731
- {% set updatedPost = post | attr_concat('tags', 'important') %}
388
+ </summary>
732
389
 
733
- {# Add values from a JSON string #}
734
- {% set modifiedPost = post | attr_concat('tags', '["new", "trending"]') %}
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)
392
+
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.
394
+
395
+ #### Example: How to pass object(s) as argument(s) to a filter in `.liquid`?
396
+
397
+ ```liquid {data-caption="trick for '| renderContent' filter"}
398
+ {% assign _ctx = null | attr_set: 'collections', collections %}
399
+ {{ ... | renderContent: 'liquid,md', _ctx }}
735
400
  ```
736
401
 
737
- **Parameters:**
402
+ </details>
403
+
404
+ <details><summary>
738
405
 
739
- - `obj`: The object to modify
740
- - `attr`: The attribute name (must be an array or will be treated as one)
741
- - `values`: Values to concatenate (can be an array, JSON string array, or single value)
406
+ ### `attr_concat`
742
407
 
743
- **Returns:**
408
+ </summary>
744
409
 
745
- A new object with the specified attribute containing the combined unique array. The original object is not modified.
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.
746
416
 
747
417
  **Features:**
748
418
 
749
419
  - Non-mutating: Creates a new object, leaving the original unchanged
750
420
  - Automatically removes duplicates using Set
751
- - Handles multiple input types: arrays, JSON string arrays, or single values
421
+ - Handles multiple input types: arrays, JSON string arrays (killer feature for `.liquid`), or single values
752
422
  - Creates the attribute as an empty array if it doesn't exist
753
423
  - Logs an error if the existing attribute is not an array
424
+ - `TBC:` Supports nested attributes (e.g., `data.tags`)
754
425
 
755
- **Examples:**
426
+ #### Example: Add tags to a post object in `.njk`:
756
427
 
757
- ```njk
758
- {# Add multiple tags #}
759
- {% set post = { title: 'My Post', tags: ['javascript'] } %}
760
- {% set enhancedPost = post | attr_concat('tags', ['tutorial', 'beginner']) %}
761
- {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner'] } #}
762
-
763
- {# Add single value #}
764
- {% set updatedPost = post | attr_concat('tags', 'featured') %}
765
- {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner', 'featured'] } #}
766
-
767
- {# No duplicates #}
768
- {% set deduped = post | attr_concat('tags', ['javascript', 'advanced']) %}
769
- {# Result: Only 'advanced' is added, 'javascript' already exists #}
770
-
771
- {# Chain multiple attr_concat filters #}
772
- {% set finalPost = post
773
- | attr_concat('tags', 'popular')
774
- | attr_concat('categories', ['tech', 'programming'])
775
- %}
776
-
777
- {# Use in loops to enhance collection items #}
778
- {% for item in collections.posts %}
779
- {% set enhancedItem = item | attr_concat('data.tags', 'blog') %}
780
- {# ... use enhancedItem ... #}
781
- {% endfor %}
428
+ ```jinja2
429
+ {% set enhancedPost = post | attr_concat('tags', ['featured', 'popular']) %}
782
430
  ```
783
431
 
784
- #### `fetch`
785
-
786
- 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.
432
+ #### `PRO` Example: Add scripts and styles to the `site` object in `.liquid`:
787
433
 
788
- **Why use this?**
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', _ %}
789
442
 
790
- 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.
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
+ ```
791
450
 
792
- **Requirements:**
451
+ </details>
793
452
 
794
- This filter requires the `@11ty/eleventy-fetch` package to be installed:
453
+ <details><summary>
795
454
 
796
- ```bash
797
- npm install @11ty/eleventy-fetch
798
- ```
455
+ ### `attr_includes`
799
456
 
800
- > **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.
457
+ </summary>
801
458
 
802
- **Usage:**
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)
803
461
 
804
- 1. Install the required dependency:
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.
805
463
 
806
- ```bash
807
- npm install @11ty/eleventy-fetch
808
- ```
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.
809
465
 
810
- 2. Enable the `fetch` filter in your Eleventy config:
466
+ #### Example: Get all posts that include `#javascript` tag
811
467
 
812
- ```javascript
813
- import { fetchFilter } from "@anydigital/eleventy-bricks";
468
+ ```jinja2 {data-caption="in .njk:"}
469
+ {% set js_posts = collections.all | attr_includes('data.tags', '#javascript') %}
814
470
 
815
- export default function (eleventyConfig) {
816
- fetchFilter(eleventyConfig);
817
- // Or use as plugin:
818
- // eleventyConfig.addPlugin(eleventyBricks, { filters: ['fetch'] });
819
- }
471
+ {% for post in js_posts %}
472
+ <h2>{{ post.data.title }}</h2>
473
+ {% endfor %}
820
474
  ```
821
475
 
822
- 3. Use the filter in your templates:
476
+ </details>
823
477
 
824
- **Fetch remote URLs:**
478
+ <details><summary>
825
479
 
826
- ```njk
827
- {# Fetch content from a remote URL #}
828
- {% set externalContent = "https://example.com/data.json" | fetch %}
829
- {{ externalContent }}
480
+ ### `fetch`
830
481
 
831
- {# Fetch and parse JSON #}
832
- {% set apiData = "https://api.example.com/posts" | fetch %}
833
- {% set posts = apiData | fromJson %}
834
- ```
482
+ </summary>
483
+
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)
835
486
 
836
- **Fetch local files:**
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.
837
488
 
838
- ```njk
839
- {# Fetch content from a local file (relative to input directory) #}
840
- {% set localData = "_data/content.txt" | fetch %}
841
- {{ localData }}
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.
842
490
 
843
- {# Include content from another file #}
844
- {% set snippet = "_includes/snippets/example.md" | fetch %}
845
- {{ snippet | markdown | safe }}
846
- ```
491
+ **Requirements:** This filter requires the `@11ty/eleventy-fetch` package to be installed:
847
492
 
848
- **Parameters:**
493
+ ```bash
494
+ npm install @11ty/eleventy-fetch
495
+ ```
849
496
 
850
- - `url`: A URL (starting with `http://` or `https://`) or a local file path (relative to the input directory)
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.
851
498
 
852
499
  **Features:**
853
500
 
854
- - **Remote URLs**: Downloads and caches content using `@11ty/eleventy-fetch`
855
- - Caches files for 1 day by default
856
- - Stores cached files in `[input-dir]/_downloads/` directory
857
- - Automatically revalidates after cache expires
858
- - **Local files**: Reads files relative to the Eleventy input directory
859
- - No caching needed for local files
860
- - Supports any file type that can be read as text
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
861
509
  - **Error handling**: Throws descriptive errors if fetching fails
862
510
  - **Conditional loading**: Only available when `@11ty/eleventy-fetch` is installed
863
511
 
512
+ **Use Cases:**
513
+
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
520
+
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.
522
+
864
523
  **Examples:**
865
524
 
866
- ```njk
525
+ ```jinja2
867
526
  {# Fetch and display remote content #}
868
527
  {% set readme = "https://raw.githubusercontent.com/user/repo/main/README.md" | fetch %}
869
528
  <div class="readme">
@@ -891,379 +550,152 @@ export default function (eleventyConfig) {
891
550
  {{ sharedContent | safe }}
892
551
  ```
893
552
 
894
- **Cache Directory:**
895
-
896
- Remote files are cached in the `_downloads` folder within your input directory:
897
-
898
- ```
899
- your-project/
900
- ├── src/ (or your input directory)
901
- │ ├── _downloads/ (cached remote files)
902
- │ ├── index.njk
903
- │ └── ...
904
- ```
905
-
906
- **Use Cases:**
907
-
908
- - Fetch content from external APIs during build time
909
- - Include README files from GitHub repositories
910
- - Reuse content from local files across multiple pages
911
- - Download and inline external CSS or JavaScript
912
- - Fetch data from headless CMS or external data sources
913
- - Include shared content snippets without using Eleventy's include syntax
914
-
915
- **Note:** The filter returns raw text content. Use Eleventy's built-in filters like `| safe`, `| markdown`, or `| fromJson` to process the content as needed.
916
-
917
- ### Transforms
918
-
919
- #### mdAutoRawTags
920
-
921
- Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
922
-
923
- **Why use this?**
924
-
925
- 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.
926
-
927
- **Usage:**
928
-
929
- 1. Enable `mdAutoRawTags` in your Eleventy config:
930
-
931
- ```javascript
932
- import { mdAutoRawTags } from "@anydigital/eleventy-bricks";
933
-
934
- export default function (eleventyConfig) {
935
- mdAutoRawTags(eleventyConfig);
936
- // Or use as plugin:
937
- // eleventyConfig.addPlugin(eleventyBricks, { mdAutoRawTags: true });
938
- }
939
- ```
940
-
941
- **Example:**
942
-
943
- Before `mdAutoRawTags`, writing this in Markdown:
944
-
945
- ```markdown
946
- Use {{ variable }} to output variables.
947
- ```
553
+ </details>
948
554
 
949
- Would try to process `{{ variable }}` as a template variable. With `mdAutoRawTags`, it displays exactly as written.
555
+ <details><summary>
950
556
 
951
- #### mdAutoNl2br
557
+ ### `section`
952
558
 
953
- 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.
559
+ </summary>
954
560
 
955
- **Why use this?**
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)
956
563
 
957
- 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.
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.
958
565
 
959
566
  **Usage:**
960
567
 
961
- 1. Enable `mdAutoNl2br` in your Eleventy config:
962
-
963
- ```javascript
964
- import { mdAutoNl2br } from "@anydigital/eleventy-bricks";
965
-
966
- export default function (eleventyConfig) {
967
- mdAutoNl2br(eleventyConfig);
968
- // Or use as plugin:
969
- // eleventyConfig.addPlugin(eleventyBricks, { mdAutoNl2br: true });
970
- }
971
- ```
972
-
973
- **Example:**
568
+ 1. Mark sections in your content file (e.g., `post.md`):
974
569
 
975
- In your Markdown file:
570
+ ⚠️ `NOTE:` The `¡` symbol is used instead of `!` only to give examples below. Use `!` in your actual content files.
976
571
 
977
572
  ```markdown
978
- | Column 1 | Column 2 |
979
- | ---------------------- | --------------------------------- |
980
- | Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |
981
- ```
982
-
983
- Will render as:
984
-
985
- ```html
986
- <td>Line 1<br />Line 2<br />Line 3</td>
987
- <td>Another cell<br />With multiple lines</td>
988
- ```
989
-
990
- **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.
991
-
992
- #### autoLinkFavicons
993
-
994
- Automatically adds favicon images from Google's favicon service to links that display plain URLs or domain names. This transform processes all HTML output files and adds inline favicon images next to link text that appears to be a plain URL.
995
-
996
- **Why use this?**
997
-
998
- 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 transform automatically detects these plain-text URL links and enhances them with favicon images, making them more visually appealing and easier to recognize.
999
-
1000
- **Usage:**
1001
-
1002
- 1. Enable `autoLinkFavicons` in your Eleventy config:
1003
-
1004
- ```javascript
1005
- import { autoLinkFavicons } from "@anydigital/eleventy-bricks";
1006
-
1007
- export default function (eleventyConfig) {
1008
- autoLinkFavicons(eleventyConfig);
1009
- // Or use as plugin:
1010
- // eleventyConfig.addPlugin(eleventyBricks, { autoLinkFavicons: true });
1011
- }
1012
- ```
1013
-
1014
- **How it works:**
1015
-
1016
- The transform:
1017
-
1018
- 1. Scans all HTML output files for `<a>` tags
1019
- 2. Checks if the link text appears to be a plain URL or domain
1020
- 3. Extracts the domain from the URL
1021
- 4. Removes the domain from the link text (keeping only the path)
1022
- 5. Adds a favicon image from Google's favicon service inline with the remaining text
1023
-
1024
- **Example:**
1025
-
1026
- Before transformation:
1027
-
1028
- ```html
1029
- <a href="https://github.com/anydigital/eleventy-bricks">https://github.com/anydigital/eleventy-bricks</a>
1030
- ```
1031
-
1032
- After transformation:
1033
-
1034
- ```html
1035
- <a href="https://github.com/anydigital/eleventy-bricks" class="whitespace-nowrap" target="_blank">
1036
- <i><img src="https://www.google.com/s2/favicons?domain=github.com&sz=32" /></i>
1037
- <span>/anydigital/eleventy-bricks</span>
1038
- </a>
1039
- ```
1040
-
1041
- **Rules:**
1042
-
1043
- - Only applies to links where the text looks like a plain URL (contains the domain or starts with `http://`/`https://`)
1044
- - Removes the protocol and domain from the display text
1045
- - Removes the trailing slash from the display text
1046
- - Only applies if at least 3 characters remain after removing the domain (to avoid showing favicons for bare domain links)
1047
- - Uses Google's favicon service at `https://www.google.com/s2/favicons?domain=DOMAIN&sz=32`
1048
- - Adds `target="_blank"` to the transformed links (only if not already present)
1049
- - Adds `whitespace-nowrap` class to the link
1050
- - Wraps the link text in a `<span>` element
1051
- - The favicon is wrapped in an `<i>` tag for easy styling
1052
-
1053
- **Styling:**
1054
-
1055
- You can style the favicon icons with CSS:
1056
-
1057
- ```css
1058
- /* Style the favicon wrapper */
1059
- a i {
1060
- display: inline-block;
1061
- margin-right: 0.25em;
1062
- }
1063
-
1064
- /* Style the favicon image */
1065
- a i img {
1066
- width: 16px;
1067
- height: 16px;
1068
- vertical-align: middle;
1069
- }
1070
- ```
1071
-
1072
- **Note:** This transform only processes HTML output files (those ending in `.html`). It does not modify the original content files.
1073
-
1074
- ### Global Data
1075
-
1076
- Adds global site data to your Eleventy project, providing commonly needed values that can be accessed in all templates.
573
+ # My Post
1077
574
 
1078
- **Why use this?**
575
+ <¡--section:intro-->
1079
576
 
1080
- Many websites need access to the current year (for copyright notices) and environment information (to conditionally enable features based on production vs development). This helper provides these as global `site` data without manually setting them up.
577
+ This is the introduction that appears at the top of the page.
1081
578
 
1082
- **Usage:**
579
+ <¡--section:main-->
1083
580
 
1084
- 1. Enable `siteData` in your Eleventy config:
581
+ This is the main body of the post with all the details.
1085
582
 
1086
- ```javascript
1087
- import { siteData } from "@anydigital/eleventy-bricks";
583
+ <¡--section:summary,sidebar-->
1088
584
 
1089
- export default function (eleventyConfig) {
1090
- siteData(eleventyConfig);
1091
- // Or use as plugin:
1092
- // eleventyConfig.addPlugin(eleventyBricks, { siteData: true });
1093
- }
585
+ This content appears in both the summary and the sidebar!
1094
586
  ```
1095
587
 
1096
- 2. Use the global data in your templates:
588
+ 2. Use the filter in your templates: <!-- @TODO: better examples -->
1097
589
 
1098
- **Current Year:**
1099
-
1100
- ```njk
1101
- <footer>
1102
- <p>&copy; {{ site.year }} Your Company Name. All rights reserved.</p>
1103
- </footer>
1104
- ```
590
+ ```jinja2
591
+ {# Get the intro section #}
592
+ <div class="page-intro">
593
+ {{ content | section('intro') | safe }}
594
+ </div>
1105
595
 
1106
- **Environment Check:**
596
+ {# Get the main section #}
597
+ <article>
598
+ {{ content | section('main') | safe }}
599
+ </article>
1107
600
 
1108
- ```njk
1109
- {% if site.prod %}
1110
- <!-- Production-only features -->
1111
- <script async src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"></script>
1112
- {% else %}
1113
- <!-- Development-only features -->
1114
- <div class="dev-toolbar">Development Mode</div>
1115
- {% endif %}
601
+ {# Get the sidebar section #}
602
+ <aside>
603
+ {{ content | section('sidebar') | safe }}
604
+ </aside>
1116
605
  ```
1117
606
 
1118
- **Available Data:**
1119
-
1120
- - `site.year`: The current year as a number (e.g., `2026`)
1121
- - `site.prod`: Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`)
1122
-
1123
607
  **Features:**
1124
608
 
1125
- - Automatically updates the year value
1126
- - Detects production vs development mode based on `ELEVENTY_RUN_MODE` environment variable
1127
- - Available globally in all templates without manual setup
1128
- - No configuration required
1129
-
1130
- **Examples:**
1131
-
1132
- ```njk
1133
- {# Copyright notice #}
1134
- <p>Copyright &copy; {{ site.year }} My Site</p>
1135
-
1136
- {# Conditional loading of analytics #}
1137
- {% if site.prod %}
1138
- <script src="/analytics.js"></script>
1139
- {% endif %}
1140
-
1141
- {# Different behavior in dev vs prod #}
1142
- {% if site.prod %}
1143
- <link rel="stylesheet" href="/css/styles.min.css">
1144
- {% else %}
1145
- <link rel="stylesheet" href="/css/styles.css">
1146
- <script src="/live-reload.js"></script>
1147
- {% endif %}
1148
- ```
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
1149
614
 
1150
- ### Symlinked Configuration Files
615
+ **Syntax Rules:**
1151
616
 
1152
- The package includes pre-configured starter files in `node_modules/@anydigital/eleventy-bricks/src/` that you can symlink to your project for quick setup:
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
1153
620
 
1154
- Benefits of Symlinking:
621
+ </details>
1155
622
 
1156
- - **Always up-to-date**: Configuration automatically updates when you upgrade the package
1157
- - **Less maintenance**: No need to manually sync configuration changes
1158
- - **Quick setup**: Get started immediately with best-practice configurations
1159
- - **Easy customization**: Override specific settings by creating your own config that imports from the symlinked version
623
+ <details><summary>
1160
624
 
1161
- If you prefer to customize the configurations extensively, you can copy the files instead.
625
+ ### `remove_tag`
1162
626
 
1163
- #### eleventy.config.js
627
+ </summary>
1164
628
 
1165
- A fully-configured Eleventy config file with:
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)
1166
631
 
1167
- - All eleventy-bricks plugins enabled
1168
- - Eleventy Navigation plugin
1169
- - Table of Contents plugin (conditionally loaded if installed)
1170
- - Markdown-it with anchors and attributes
1171
- - YAML data support
1172
- - CLI input directory support
1173
- - Symlink support for development
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.
1174
633
 
1175
- **Required dependencies:**
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.
1176
635
 
1177
- ```bash
1178
- npm install @11ty/eleventy-navigation markdown-it markdown-it-anchor markdown-it-attrs js-yaml minimist
1179
- ```
636
+ **Features:**
1180
637
 
1181
- **Optional dependencies:**
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
1182
643
 
1183
- ```bash
1184
- # For the fetch filter
1185
- npm install @11ty/eleventy-fetch
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.
1186
645
 
1187
- # For table of contents generation
1188
- npm install @uncenter/eleventy-plugin-toc
1189
- ```
646
+ #### Example: Remove all script tags from content <!-- @TODO: better examples -->
1190
647
 
1191
- **Symlink to your project:**
648
+ ```jinja2
649
+ {% set cleanContent = htmlContent | remove_tag('script') %}
1192
650
 
1193
- ```bash
1194
- ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js eleventy.config.js
651
+ {{ cleanContent | safe }}
1195
652
  ```
1196
653
 
1197
- #### admin/index.html
1198
-
1199
- A ready-to-use Sveltia CMS admin interface for content management.
1200
-
1201
- **Symlink to your project:**
654
+ </details>
1202
655
 
1203
- ```bash
1204
- mkdir -p admin
1205
- ln -s ../node_modules/@anydigital/eleventy-bricks/src/admin/index.html admin/index.html
1206
- ```
656
+ <details><summary>
1207
657
 
1208
- <!--section:npm,11ty-->
658
+ ### `strip_tag`
1209
659
 
1210
- ### Using the `do` Folder Pattern
660
+ </summary>
1211
661
 
1212
- 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.
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)
1213
664
 
1214
- **Setup:**
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.
1215
666
 
1216
- 1. Create a `do` folder in your project root:
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.
1217
668
 
1218
- ```bash
1219
- mkdir do
1220
- ```
669
+ **Features:**
1221
670
 
1222
- 2. Symlink the package.json from the eleventy-bricks package:
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
1223
676
 
1224
- ```bash
1225
- ln -s node_modules/@anydigital/eleventy-bricks/src/do/package.json do/package.json
1226
- ```
677
+ #### Example: Unwrap a wrapping `<div>` from content
1227
678
 
1228
- 3. Configure your root `package.json` to use npm workspaces:
679
+ ```jinja2
680
+ {% set unwrapped = htmlContent | strip_tag('div') %}
1229
681
 
1230
- ```json
1231
- {
1232
- "name": "my-project",
1233
- "workspaces": ["do"],
1234
- "scripts": {
1235
- "build": "npm -w do run build",
1236
- "start": "npm -w do run start"
1237
- }
1238
- }
682
+ {{ unwrapped | safe }}
1239
683
  ```
1240
684
 
1241
- **Usage:**
1242
-
1243
- Run your Eleventy project:
685
+ Input:
1244
686
 
1245
- ```bash
1246
- npm start
687
+ ```html
688
+ <div class="wrapper">
689
+ <p>Hello</p>
690
+ <p>World</p>
691
+ </div>
1247
692
  ```
1248
693
 
1249
- Build for production:
694
+ Output:
1250
695
 
1251
- ```bash
1252
- npm run build
696
+ ```html
697
+ <p>Hello</p>
698
+ <p>World</p>
1253
699
  ```
1254
700
 
1255
- **Available Scripts in `do` folder:**
1256
-
1257
- - `build` - Build the site with Eleventy and minify CSS with Tailwind
1258
- - `start` - Start Eleventy dev server with live reload and Tailwind watch mode
1259
- - `stage` - Clean build and serve locally for preview
1260
- - `11ty` - Run Eleventy commands directly
1261
- - `11ty:clean` - Remove the `_site` output directory
1262
- - `tw` - Run Tailwind CSS commands
1263
-
1264
- **Benefits:**
1265
-
1266
- - **Clean separation**: Keep build scripts separate from project configuration
1267
- - **Reusable workflows**: Update scripts by upgrading the package
1268
- - **Workspace isolation**: Scripts run in their own workspace context
1269
- - **Easy maintenance**: No need to manually maintain build scripts
701
+ </details>