@anydigital/eleventy-bricks 1.0.0-alpha.17 → 1.0.0-alpha.19

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
@@ -17,27 +17,35 @@ You can use this library in two ways:
17
17
  Import and use the entire plugin. You can configure which helpers to enable using the options parameter:
18
18
 
19
19
  **ES Modules:**
20
+
20
21
  ```javascript
21
22
  import eleventyBricks from "@anydigital/eleventy-bricks";
22
23
 
23
- export default function(eleventyConfig) {
24
+ export default function (eleventyConfig) {
24
25
  eleventyConfig.addPlugin(eleventyBricks, {
25
- mdAutoRawTags: true // Enable mdAutoRawTags preprocessor (default: false)
26
+ mdAutoRawTags: true,
27
+ mdAutoNl2br: true,
28
+ siteData: true,
29
+ filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
26
30
  });
27
-
31
+
28
32
  // Your other configuration...
29
33
  }
30
34
  ```
31
35
 
32
36
  **CommonJS:**
37
+
33
38
  ```javascript
34
39
  const eleventyBricks = require("@anydigital/eleventy-bricks");
35
40
 
36
- module.exports = function(eleventyConfig) {
41
+ module.exports = function (eleventyConfig) {
37
42
  eleventyConfig.addPlugin(eleventyBricks, {
38
- mdAutoRawTags: true // Enable mdAutoRawTags preprocessor (default: false)
43
+ mdAutoRawTags: true,
44
+ mdAutoNl2br: true,
45
+ siteData: true,
46
+ filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
39
47
  });
40
-
48
+
41
49
  // Your other configuration...
42
50
  };
43
51
  ```
@@ -49,37 +57,61 @@ module.exports = function(eleventyConfig) {
49
57
  Import only the specific helpers you need without using the plugin:
50
58
 
51
59
  **ES Modules:**
52
- ```javascript
53
- import { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, mergeFilter, removeTagFilter, siteData } from "@anydigital/eleventy-bricks";
54
60
 
55
- export default function(eleventyConfig) {
56
- bricks(eleventyConfig);
61
+ ```javascript
62
+ import {
63
+ mdAutoRawTags,
64
+ mdAutoNl2br,
65
+ setAttrFilter,
66
+ whereInFilter,
67
+ mergeFilter,
68
+ removeTagFilter,
69
+ ifFilter,
70
+ attrConcatFilter,
71
+ siteData,
72
+ } from "@anydigital/eleventy-bricks";
73
+
74
+ export default function (eleventyConfig) {
57
75
  mdAutoRawTags(eleventyConfig);
58
76
  mdAutoNl2br(eleventyConfig);
59
77
  setAttrFilter(eleventyConfig);
60
- byAttrFilter(eleventyConfig);
78
+ whereInFilter(eleventyConfig);
61
79
  mergeFilter(eleventyConfig);
62
80
  removeTagFilter(eleventyConfig);
81
+ ifFilter(eleventyConfig);
82
+ attrConcatFilter(eleventyConfig);
63
83
  siteData(eleventyConfig);
64
-
84
+
65
85
  // Your other configuration...
66
86
  }
67
87
  ```
68
88
 
69
89
  **CommonJS:**
70
- ```javascript
71
- const { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, mergeFilter, removeTagFilter, siteData } = require("@anydigital/eleventy-bricks");
72
90
 
73
- module.exports = async function(eleventyConfig) {
74
- await bricks(eleventyConfig);
91
+ ```javascript
92
+ const {
93
+ mdAutoRawTags,
94
+ mdAutoNl2br,
95
+ setAttrFilter,
96
+ whereInFilter,
97
+ mergeFilter,
98
+ removeTagFilter,
99
+ ifFilter,
100
+ attrConcatFilter,
101
+ siteData,
102
+ } = require("@anydigital/eleventy-bricks");
103
+
104
+ module.exports = async function (eleventyConfig) {
75
105
  await mdAutoRawTags(eleventyConfig);
76
106
  await mdAutoNl2br(eleventyConfig);
77
107
  await setAttrFilter(eleventyConfig);
78
- await byAttrFilter(eleventyConfig);
108
+ await whereInFilter(eleventyConfig);
79
109
  await mergeFilter(eleventyConfig);
80
110
  await removeTagFilter(eleventyConfig);
111
+ await ifFilter(eleventyConfig);
112
+ await attrConcatFilter(eleventyConfig);
81
113
  await siteData(eleventyConfig);
82
-
114
+
83
115
  // Your other configuration...
84
116
  };
85
117
  ```
@@ -90,134 +122,35 @@ module.exports = async function(eleventyConfig) {
90
122
 
91
123
  When using the plugin (Option 1), you can configure which helpers to enable:
92
124
 
93
- | Option | Type | Default | Description |
94
- |--------|------|---------|-------------|
95
- | `bricks` | boolean | `false` | Enable the bricks system for dependency management |
96
- | `mdAutoRawTags` | boolean | `false` | Enable the mdAutoRawTags preprocessor for Markdown files |
97
- | `mdAutoNl2br` | boolean | `false` | Enable the mdAutoNl2br preprocessor to convert \n to `<br>` tags |
98
- | `setAttrFilter` | boolean | `false` | Enable the setAttr filter for overriding object attributes |
99
- | `byAttrFilter` | boolean | `false` | Enable the byAttr filter for filtering collections by attribute values |
100
- | `mergeFilter` | boolean | `false` | Enable the merge filter for merging arrays or objects |
101
- | `removeTagFilter` | boolean | `false` | Enable the removeTag filter for removing HTML elements from content |
102
- | `siteData` | boolean | `false` | Enable site.year and site.isProd global data |
125
+ | Option | Type | Default | Description |
126
+ | --------------- | --------------- | ------- | ---------------------------------------------------------------- |
127
+ | `mdAutoRawTags` | boolean | `false` | Enable the mdAutoRawTags preprocessor for Markdown files |
128
+ | `mdAutoNl2br` | boolean | `false` | Enable the mdAutoNl2br preprocessor to convert \n to `<br>` tags |
129
+ | `siteData` | boolean | `false` | Enable site.year and site.prod global data |
130
+ | `filters` | array of string | `[]` | Array of filter names to enable (see Available Filters section) |
131
+
132
+ **Available filter names for the `filters` array:**
133
+
134
+ - `'attr'` - Override object attributes
135
+ - `'where_in'` - Filter collections by attribute values
136
+ - `'merge'` - Merge arrays or objects
137
+ - `'remove_tag'` - Remove HTML elements from content
138
+ - `'if'` - Inline conditional/ternary operator
139
+ - `'attr_concat'` - Concatenate values to an attribute array
103
140
 
104
141
  **Example:**
142
+
105
143
  ```javascript
106
144
  eleventyConfig.addPlugin(eleventyBricks, {
107
- bricks: true,
108
145
  mdAutoRawTags: true,
109
- byAttrFilter: true,
110
- siteData: true
146
+ mdAutoNl2br: true,
147
+ siteData: true,
148
+ filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
111
149
  });
112
150
  ```
113
151
 
114
152
  ## Available 11ty Helpers
115
153
 
116
- ### bricks
117
-
118
- A dependency management system for Eleventy that automatically collects and injects CSS and JavaScript dependencies (both external and inline) per page. This allows brick components to declare their dependencies, and the system will inject them in the correct location in your HTML.
119
-
120
- **Why use this?**
121
-
122
- When building reusable components (bricks) in Eleventy, you often need to include CSS and JavaScript dependencies. Instead of manually adding these to every page, `bricks` automatically:
123
- - Collects dependencies from all bricks used on a page
124
- - Categorizes them (external CSS, external JS, inline styles, inline scripts)
125
- - Injects them in the correct location in your HTML output
126
-
127
- **How it works:**
128
-
129
- 1. Use the `bricksDependencies` shortcode in your base template to mark where dependencies should be injected
130
- 2. Use the `brick` shortcode to register and render brick components that declare their dependencies
131
- 3. The system automatically collects all dependencies and injects them when the page is built
132
-
133
- **Usage:**
134
-
135
- 1. Enable `bricks` in your Eleventy config:
136
-
137
- ```javascript
138
- import { bricks } from "@anydigital/eleventy-bricks";
139
-
140
- export default function(eleventyConfig) {
141
- bricks(eleventyConfig);
142
- // Or use as plugin:
143
- // eleventyConfig.addPlugin(eleventyBricks, { bricks: true });
144
- }
145
- ```
146
-
147
- 2. Add the `bricksDependencies` shortcode in your base template (typically in the `<head>` section):
148
-
149
- ```njk
150
- <head>
151
- <meta charset="UTF-8">
152
- <title>My Site</title>
153
- {% bricksDependencies [
154
- ... (global dependencies can be set here) ...
155
- ] %}
156
- <!-- Other head content -->
157
- </head>
158
- ```
159
-
160
- 3. Create brick components that declare their dependencies:
161
-
162
- ```javascript
163
- // myBrick.js
164
- export default {
165
- dependencies: [
166
- 'https://cdn.example.com/library.css',
167
- 'https://cdn.example.com/library.js'
168
- ],
169
- style: `
170
- .my-component { color: blue; }
171
- `,
172
- script: `
173
- console.log('Component initialized');
174
- `,
175
- render: function() {
176
- return '<div class="my-component">Hello World</div>';
177
- }
178
- };
179
- ```
180
-
181
- 4. Use the `brick` shortcode in your templates:
182
-
183
- ```njk
184
- {% set myBrick = require('./myBrick.js') %}
185
- {% brick myBrick %}
186
- ```
187
-
188
- **Brick Component Structure:**
189
-
190
- A brick component is a JavaScript object with the following optional properties:
191
-
192
- - `dependencies`: Array of URLs to external CSS or JavaScript files (e.g., `['https://cdn.example.com/style.css', 'https://cdn.example.com/script.js']`)
193
- - `style`: String containing inline CSS
194
- - `script`: String containing inline JavaScript
195
- - `render`: Function that returns the HTML markup for the component
196
-
197
- **Output:**
198
-
199
- The system will automatically inject all dependencies in the order they were registered:
200
-
201
- ```html
202
- <head>
203
- <meta charset="UTF-8">
204
- <title>My Site</title>
205
- <link rel="stylesheet" href="https://cdn.example.com/library.css">
206
- <style>.my-component { color: blue; }</style>
207
- <script src="https://cdn.example.com/library.js"></script>
208
- <script>console.log('Component initialized');</script>
209
- <!-- Other head content -->
210
- </head>
211
- ```
212
-
213
- **Features:**
214
-
215
- - Automatic dependency collection per page
216
- - Categorizes dependencies (CSS vs JS, external vs inline)
217
- - Deduplicates dependencies (using Sets internally)
218
- - Works with both external URLs and inline code
219
- - Clears registry before each build to prevent stale data
220
-
221
154
  ### mdAutoRawTags
222
155
 
223
156
  Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
@@ -233,7 +166,7 @@ When writing documentation or tutorials about templating in Markdown files, you
233
166
  ```javascript
234
167
  import { mdAutoRawTags } from "@anydigital/eleventy-bricks";
235
168
 
236
- export default function(eleventyConfig) {
169
+ export default function (eleventyConfig) {
237
170
  mdAutoRawTags(eleventyConfig);
238
171
  // Or use as plugin:
239
172
  // eleventyConfig.addPlugin(eleventyBricks, { mdAutoRawTags: true });
@@ -243,6 +176,7 @@ export default function(eleventyConfig) {
243
176
  **Example:**
244
177
 
245
178
  Before `mdAutoRawTags`, writing this in Markdown:
179
+
246
180
  ```markdown
247
181
  Use {{ variable }} to output variables.
248
182
  ```
@@ -264,7 +198,7 @@ Markdown tables don't support multi-line content in cells. By using `\n` in your
264
198
  ```javascript
265
199
  import { mdAutoNl2br } from "@anydigital/eleventy-bricks";
266
200
 
267
- export default function(eleventyConfig) {
201
+ export default function (eleventyConfig) {
268
202
  mdAutoNl2br(eleventyConfig);
269
203
  // Or use as plugin:
270
204
  // eleventyConfig.addPlugin(eleventyBricks, { mdAutoNl2br: true });
@@ -274,39 +208,41 @@ export default function(eleventyConfig) {
274
208
  **Example:**
275
209
 
276
210
  In your Markdown file:
211
+
277
212
  ```markdown
278
- | Column 1 | Column 2 |
279
- |----------|----------|
213
+ | Column 1 | Column 2 |
214
+ | ---------------------- | --------------------------------- |
280
215
  | Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |
281
216
  ```
282
217
 
283
218
  Will render as:
219
+
284
220
  ```html
285
- <td>Line 1<br>Line 2<br>Line 3</td>
286
- <td>Another cell<br>With multiple lines</td>
221
+ <td>Line 1<br />Line 2<br />Line 3</td>
222
+ <td>Another cell<br />With multiple lines</td>
287
223
  ```
288
224
 
289
225
  **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.
290
226
 
291
- ### setAttr
227
+ ### attr
292
228
 
293
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.
294
230
 
295
231
  **Why use this?**
296
232
 
297
- When working with Eleventy data, you sometimes need to modify an object's properties for a specific use case. The `setAttr` filter provides a clean way to create a modified copy of an object without affecting the original.
233
+ When working with Eleventy data, you sometimes need to modify an object's properties for a specific use case. The `attr` filter provides a clean way to create a modified copy of an object without affecting the original.
298
234
 
299
235
  **Usage:**
300
236
 
301
- 1. Enable `setAttr` in your Eleventy config:
237
+ 1. Enable the `attr` filter in your Eleventy config:
302
238
 
303
239
  ```javascript
304
240
  import { setAttrFilter } from "@anydigital/eleventy-bricks";
305
241
 
306
- export default function(eleventyConfig) {
242
+ export default function (eleventyConfig) {
307
243
  setAttrFilter(eleventyConfig);
308
244
  // Or use as plugin:
309
- // eleventyConfig.addPlugin(eleventyBricks, { setAttrFilter: true });
245
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr'] });
310
246
  }
311
247
  ```
312
248
 
@@ -314,7 +250,7 @@ export default function(eleventyConfig) {
314
250
 
315
251
  ```njk
316
252
  {# Create a modified version of a page object #}
317
- {% set modifiedPage = page | setAttr('title', 'New Title') %}
253
+ {% set modifiedPage = page | attr('title', 'New Title') %}
318
254
 
319
255
  <h1>{{ modifiedPage.title }}</h1>
320
256
  <p>Original title: {{ page.title }}</p>
@@ -341,49 +277,50 @@ A new object with the specified attribute set to the given value. The original o
341
277
 
342
278
  ```njk
343
279
  {# Override a single attribute #}
344
- {% set updatedPost = post | setAttr('featured', true) %}
280
+ {% set updatedPost = post | attr('featured', true) %}
345
281
 
346
- {# Chain multiple setAttr filters #}
347
- {% set modifiedPost = post
348
- | setAttr('category', 'blog')
349
- | setAttr('priority', 1)
282
+ {# Chain multiple attr filters #}
283
+ {% set modifiedPost = post
284
+ | attr('category', 'blog')
285
+ | attr('priority', 1)
350
286
  %}
351
287
 
352
288
  {# Use in loops #}
353
289
  {% for item in collection %}
354
- {% set enhancedItem = item | setAttr('processed', true) %}
290
+ {% set enhancedItem = item | attr('processed', true) %}
355
291
  {# ... use enhancedItem ... #}
356
292
  {% endfor %}
357
293
  ```
358
294
 
359
- ### byAttr
295
+ ### where_in
360
296
 
361
- A filter that filters collection items by attribute value. It checks if an item's attribute matches a target value. If the attribute is an array, it checks if the array includes the target value.
297
+ A filter that filters collection items by attribute value. It checks if an item's attribute matches a target value. If the attribute is an array, it checks if the array includes the target value. Supports nested attribute names using dot notation.
362
298
 
363
299
  **Why use this?**
364
300
 
365
- When working with Eleventy collections, you often need to filter items based on front matter data. The `byAttr` filter provides a flexible way to filter by any attribute, with special handling for array attributes (like tags).
301
+ When working with Eleventy collections, you often need to filter items based on front matter data. The `where_in` filter provides a flexible way to filter by any attribute, with special handling for array attributes (like tags) and support for nested properties using dot notation.
366
302
 
367
303
  **Usage:**
368
304
 
369
- 1. Enable `byAttr` in your Eleventy config:
305
+ 1. Enable the `where_in` filter in your Eleventy config:
370
306
 
371
307
  ```javascript
372
- import { byAttrFilter } from "@anydigital/eleventy-bricks";
308
+ import { whereInFilter } from "@anydigital/eleventy-bricks";
373
309
 
374
- export default function(eleventyConfig) {
375
- byAttrFilter(eleventyConfig);
310
+ export default function (eleventyConfig) {
311
+ whereInFilter(eleventyConfig);
376
312
  // Or use as plugin:
377
- // eleventyConfig.addPlugin(eleventyBricks, { byAttrFilter: true });
313
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['where_in'] });
378
314
  }
379
315
  ```
380
316
 
381
317
  2. Use the filter in your templates:
382
318
 
383
319
  **Filter by exact attribute match:**
320
+
384
321
  ```njk
385
322
  {# Get all posts with category 'blog' #}
386
- {% set blogPosts = collections.all | byAttr('category', 'blog') %}
323
+ {% set blogPosts = collections.all | where_in('data.category', 'blog') %}
387
324
 
388
325
  {% for post in blogPosts %}
389
326
  <h2>{{ post.data.title }}</h2>
@@ -391,9 +328,10 @@ export default function(eleventyConfig) {
391
328
  ```
392
329
 
393
330
  **Filter by array attribute (tags):**
331
+
394
332
  ```njk
395
333
  {# Get all posts that include 'javascript' tag #}
396
- {% set jsPosts = collections.all | byAttr('tags', 'javascript') %}
334
+ {% set jsPosts = collections.all | where_in('data.tags', 'javascript') %}
397
335
 
398
336
  {% for post in jsPosts %}
399
337
  <h2>{{ post.data.title }}</h2>
@@ -403,13 +341,13 @@ export default function(eleventyConfig) {
403
341
  **Parameters:**
404
342
 
405
343
  - `collection`: The collection to filter (array of items)
406
- - `attrName`: The attribute name to check (string)
344
+ - `attrName`: The attribute name to check (string, supports dot notation for nested properties)
407
345
  - `targetValue`: The value to match against (any type)
408
346
 
409
347
  **Features:**
410
348
 
411
349
  - Works with any attribute in front matter
412
- - Handles both `item.data.attrName` and `item.attrName` patterns
350
+ - Supports dot notation for nested properties (e.g., `'data.tags'`, `'data.author.name'`)
413
351
  - Special handling for array attributes (uses `includes()` check)
414
352
  - Returns empty array if collection is invalid
415
353
  - Filters out items without the specified attribute
@@ -417,6 +355,7 @@ export default function(eleventyConfig) {
417
355
  **Examples:**
418
356
 
419
357
  Front matter:
358
+
420
359
  ```yaml
421
360
  ---
422
361
  title: My Post
@@ -427,18 +366,19 @@ priority: 1
427
366
  ```
428
367
 
429
368
  Template usage:
369
+
430
370
  ```njk
431
- {# Filter by category #}
432
- {% set blogPosts = collections.all | byAttr('category', 'blog') %}
371
+ {# Filter by category (using dot notation for nested properties) #}
372
+ {% set blogPosts = collections.all | where_in('data.category', 'blog') %}
433
373
 
434
374
  {# Filter by tag (array) #}
435
- {% set jsTutorials = collections.all | byAttr('tags', 'javascript') %}
375
+ {% set jsTutorials = collections.all | where_in('data.tags', 'javascript') %}
436
376
 
437
377
  {# Filter by numeric value #}
438
- {% set highPriority = collections.all | byAttr('priority', 1) %}
378
+ {% set highPriority = collections.all | where_in('data.priority', 1) %}
439
379
 
440
380
  {# Chain filters #}
441
- {% set recentBlogPosts = collections.all | byAttr('category', 'blog') | reverse | limit(5) %}
381
+ {% set recentBlogPosts = collections.all | where_in('data.category', 'blog') | reverse | limit(5) %}
442
382
  ```
443
383
 
444
384
  ### merge
@@ -451,21 +391,22 @@ When working with data in templates, you often need to combine multiple arrays o
451
391
 
452
392
  **Usage:**
453
393
 
454
- 1. Enable `merge` in your Eleventy config:
394
+ 1. Enable the `merge` filter in your Eleventy config:
455
395
 
456
396
  ```javascript
457
397
  import { mergeFilter } from "@anydigital/eleventy-bricks";
458
398
 
459
- export default function(eleventyConfig) {
399
+ export default function (eleventyConfig) {
460
400
  mergeFilter(eleventyConfig);
461
401
  // Or use as plugin:
462
- // eleventyConfig.addPlugin(eleventyBricks, { mergeFilter: true });
402
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['merge'] });
463
403
  }
464
404
  ```
465
405
 
466
406
  2. Use the filter in your templates:
467
407
 
468
408
  **Merge arrays:**
409
+
469
410
  ```njk
470
411
  {# Combine two arrays #}
471
412
  {% set allItems = featured | merge(regular) %}
@@ -479,6 +420,7 @@ export default function(eleventyConfig) {
479
420
  ```
480
421
 
481
422
  **Merge objects:**
423
+
482
424
  ```njk
483
425
  {# Merge configuration objects #}
484
426
  {% set defaultConfig = { theme: 'light', lang: 'en' } %}
@@ -506,15 +448,15 @@ export default function(eleventyConfig) {
506
448
 
507
449
  ```njk
508
450
  {# Combine featured and regular posts #}
509
- {% set featuredPosts = collections.all | byAttr('featured', true) %}
510
- {% set regularPosts = collections.all | byAttr('featured', false) %}
451
+ {% set featuredPosts = collections.all | where_in('data.featured', true) %}
452
+ {% set regularPosts = collections.all | where_in('data.featured', false) %}
511
453
  {% set allPosts = featuredPosts | merge(regularPosts) %}
512
454
 
513
455
  {# Merge page metadata with defaults #}
514
- {% set defaultMeta = {
456
+ {% set defaultMeta = {
515
457
  author: 'Site Admin',
516
458
  category: 'general',
517
- comments: false
459
+ comments: false
518
460
  } %}
519
461
  {% set pageMeta = defaultMeta | merge(page.data) %}
520
462
 
@@ -527,25 +469,25 @@ export default function(eleventyConfig) {
527
469
  {% set config = defaults | merge(siteConfig, pageConfig, userPrefs) %}
528
470
  ```
529
471
 
530
- ### removeTag
472
+ ### remove_tag
531
473
 
532
474
  A filter that removes a specified HTML element from provided HTML content. It removes the tag along with its content, including self-closing tags.
533
475
 
534
476
  **Why use this?**
535
477
 
536
- 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 `removeTag` filter provides a simple way to remove unwanted tags like `<script>`, `<style>`, or any other HTML elements from your content.
478
+ 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.
537
479
 
538
480
  **Usage:**
539
481
 
540
- 1. Enable `removeTag` in your Eleventy config:
482
+ 1. Enable the `remove_tag` filter in your Eleventy config:
541
483
 
542
484
  ```javascript
543
485
  import { removeTagFilter } from "@anydigital/eleventy-bricks";
544
486
 
545
- export default function(eleventyConfig) {
487
+ export default function (eleventyConfig) {
546
488
  removeTagFilter(eleventyConfig);
547
489
  // Or use as plugin:
548
- // eleventyConfig.addPlugin(eleventyBricks, { removeTagFilter: true });
490
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['remove_tag'] });
549
491
  }
550
492
  ```
551
493
 
@@ -553,7 +495,7 @@ export default function(eleventyConfig) {
553
495
 
554
496
  ```njk
555
497
  {# Remove all script tags from content #}
556
- {% set cleanContent = htmlContent | removeTag('script') %}
498
+ {% set cleanContent = htmlContent | remove_tag('script') %}
557
499
 
558
500
  {{ cleanContent | safe }}
559
501
  ```
@@ -576,30 +518,187 @@ export default function(eleventyConfig) {
576
518
  ```njk
577
519
  {# Remove scripts from user-generated content #}
578
520
  {% set userContent = '<p>Hello</p><script>alert("XSS")</script><p>World</p>' %}
579
- {% set safeContent = userContent | removeTag('script') %}
521
+ {% set safeContent = userContent | remove_tag('script') %}
580
522
  {# Result: '<p>Hello</p><p>World</p>' #}
581
523
 
582
524
  {# Strip specific formatting tags #}
583
525
  {% set formatted = '<div><strong>Bold</strong> and <em>italic</em> text</div>' %}
584
- {% set noStrong = formatted | removeTag('strong') %}
526
+ {% set noStrong = formatted | remove_tag('strong') %}
585
527
  {# Result: '<div>Bold and <em>italic</em> text</div>' #}
586
528
 
587
- {# Chain multiple removeTag filters for multiple tags #}
529
+ {# Chain multiple remove_tag filters for multiple tags #}
588
530
  {% set richContent = page.content %}
589
- {% set stripped = richContent
590
- | removeTag('script')
591
- | removeTag('style')
592
- | removeTag('iframe')
531
+ {% set stripped = richContent
532
+ | remove_tag('script')
533
+ | remove_tag('style')
534
+ | remove_tag('iframe')
593
535
  %}
594
536
 
595
537
  {# Remove images for text-only preview #}
596
- {% set textOnly = htmlContent | removeTag('img') %}
538
+ {% set textOnly = htmlContent | remove_tag('img') %}
597
539
  ```
598
540
 
599
541
  **Security Note:**
600
542
 
601
543
  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.
602
544
 
545
+ ### if
546
+
547
+ 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.
548
+
549
+ **Why use this?**
550
+
551
+ 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.
552
+
553
+ **Usage:**
554
+
555
+ 1. Enable the `if` filter in your Eleventy config:
556
+
557
+ ```javascript
558
+ import { ifFilter } from "@anydigital/eleventy-bricks";
559
+
560
+ export default function (eleventyConfig) {
561
+ ifFilter(eleventyConfig);
562
+ // Or use as plugin:
563
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['if'] });
564
+ }
565
+ ```
566
+
567
+ 2. Use the filter in your templates:
568
+
569
+ ```njk
570
+ {# Basic usage #}
571
+ <div class="{{ 'active' | if: isActive, 'inactive' }}">Status</div>
572
+
573
+ {# Without falsy value (defaults to empty string) #}
574
+ <span class="{{ 'highlight' | if: shouldHighlight }}">Text</span>
575
+
576
+ {# With variable values #}
577
+ {% set status = 'Published' | if: post.published, 'Draft' %}
578
+ ```
579
+
580
+ **Parameters:**
581
+
582
+ - `trueValue`: The value to return if condition is truthy
583
+ - `condition`: The condition to evaluate
584
+ - `falseValue`: The value to return if condition is falsy (optional, defaults to empty string)
585
+
586
+ **Features:**
587
+
588
+ - Returns `trueValue` if condition is truthy, otherwise returns `falseValue`
589
+ - Treats empty objects `{}` as falsy
590
+ - Default `falseValue` is an empty string if not provided
591
+ - Works with any data type for values
592
+
593
+ **Examples:**
594
+
595
+ ```njk
596
+ {# Toggle CSS classes #}
597
+ <button class="{{ 'btn-primary' | if: isPrimary, 'btn-secondary' }}">
598
+ Click me
599
+ </button>
600
+
601
+ {# Display different text #}
602
+ <p>{{ 'Online' | if: user.isOnline, 'Offline' }}</p>
603
+
604
+ {# Use with boolean values #}
605
+ {% set isEnabled = true %}
606
+ <div>{{ 'Enabled' | if: isEnabled, 'Disabled' }}</div>
607
+
608
+ {# Conditional attribute values #}
609
+ <input type="checkbox" {{ 'checked' | if: isChecked }}>
610
+
611
+ {# With numeric values #}
612
+ <span class="{{ 'has-items' | if: items.length }}">
613
+ {{ items.length }} items
614
+ </span>
615
+
616
+ {# Chain with other filters #}
617
+ {% set cssClass = 'featured' | if: post.featured | upper %}
618
+ ```
619
+
620
+ ### attr_concat
621
+
622
+ 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.
623
+
624
+ **Why use this?**
625
+
626
+ 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.
627
+
628
+ **Usage:**
629
+
630
+ 1. Enable the `attr_concat` filter in your Eleventy config:
631
+
632
+ ```javascript
633
+ import { attrConcatFilter } from "@anydigital/eleventy-bricks";
634
+
635
+ export default function (eleventyConfig) {
636
+ attrConcatFilter(eleventyConfig);
637
+ // Or use as plugin:
638
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_concat'] });
639
+ }
640
+ ```
641
+
642
+ 2. Use the filter in your templates:
643
+
644
+ ```njk
645
+ {# Add tags to a post object #}
646
+ {% set enhancedPost = post | attr_concat('tags', ['featured', 'popular']) %}
647
+
648
+ {# Add a single value #}
649
+ {% set updatedPost = post | attr_concat('tags', 'important') %}
650
+
651
+ {# Add values from a JSON string #}
652
+ {% set modifiedPost = post | attr_concat('tags', '["new", "trending"]') %}
653
+ ```
654
+
655
+ **Parameters:**
656
+
657
+ - `obj`: The object to modify
658
+ - `attr`: The attribute name (must be an array or will be treated as one)
659
+ - `values`: Values to concatenate (can be an array, JSON string array, or single value)
660
+
661
+ **Returns:**
662
+
663
+ A new object with the specified attribute containing the combined unique array. The original object is not modified.
664
+
665
+ **Features:**
666
+
667
+ - Non-mutating: Creates a new object, leaving the original unchanged
668
+ - Automatically removes duplicates using Set
669
+ - Handles multiple input types: arrays, JSON string arrays, or single values
670
+ - Creates the attribute as an empty array if it doesn't exist
671
+ - Logs an error if the existing attribute is not an array
672
+
673
+ **Examples:**
674
+
675
+ ```njk
676
+ {# Add multiple tags #}
677
+ {% set post = { title: 'My Post', tags: ['javascript'] } %}
678
+ {% set enhancedPost = post | attr_concat('tags', ['tutorial', 'beginner']) %}
679
+ {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner'] } #}
680
+
681
+ {# Add single value #}
682
+ {% set updatedPost = post | attr_concat('tags', 'featured') %}
683
+ {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner', 'featured'] } #}
684
+
685
+ {# No duplicates #}
686
+ {% set deduped = post | attr_concat('tags', ['javascript', 'advanced']) %}
687
+ {# Result: Only 'advanced' is added, 'javascript' already exists #}
688
+
689
+ {# Chain multiple attr_concat filters #}
690
+ {% set finalPost = post
691
+ | attr_concat('tags', 'popular')
692
+ | attr_concat('categories', ['tech', 'programming'])
693
+ %}
694
+
695
+ {# Use in loops to enhance collection items #}
696
+ {% for item in collections.posts %}
697
+ {% set enhancedItem = item | attr_concat('data.tags', 'blog') %}
698
+ {# ... use enhancedItem ... #}
699
+ {% endfor %}
700
+ ```
701
+
603
702
  ### siteData
604
703
 
605
704
  Adds global site data to your Eleventy project, providing commonly needed values that can be accessed in all templates.
@@ -615,7 +714,7 @@ Many websites need access to the current year (for copyright notices) and enviro
615
714
  ```javascript
616
715
  import { siteData } from "@anydigital/eleventy-bricks";
617
716
 
618
- export default function(eleventyConfig) {
717
+ export default function (eleventyConfig) {
619
718
  siteData(eleventyConfig);
620
719
  // Or use as plugin:
621
720
  // eleventyConfig.addPlugin(eleventyBricks, { siteData: true });
@@ -625,6 +724,7 @@ export default function(eleventyConfig) {
625
724
  2. Use the global data in your templates:
626
725
 
627
726
  **Current Year:**
727
+
628
728
  ```njk
629
729
  <footer>
630
730
  <p>&copy; {{ site.year }} Your Company Name. All rights reserved.</p>
@@ -632,8 +732,9 @@ export default function(eleventyConfig) {
632
732
  ```
633
733
 
634
734
  **Environment Check:**
735
+
635
736
  ```njk
636
- {% if site.isProd %}
737
+ {% if site.prod %}
637
738
  <!-- Production-only features -->
638
739
  <script async src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"></script>
639
740
  {% else %}
@@ -645,7 +746,7 @@ export default function(eleventyConfig) {
645
746
  **Available Data:**
646
747
 
647
748
  - `site.year`: The current year as a number (e.g., `2026`)
648
- - `site.isProd`: Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`)
749
+ - `site.prod`: Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`)
649
750
 
650
751
  **Features:**
651
752
 
@@ -661,12 +762,12 @@ export default function(eleventyConfig) {
661
762
  <p>Copyright &copy; {{ site.year }} My Site</p>
662
763
 
663
764
  {# Conditional loading of analytics #}
664
- {% if site.isProd %}
765
+ {% if site.prod %}
665
766
  <script src="/analytics.js"></script>
666
767
  {% endif %}
667
768
 
668
769
  {# Different behavior in dev vs prod #}
669
- {% if site.isProd %}
770
+ {% if site.prod %}
670
771
  <link rel="stylesheet" href="/css/styles.min.css">
671
772
  {% else %}
672
773
  <link rel="stylesheet" href="/css/styles.css">
@@ -676,10 +777,14 @@ export default function(eleventyConfig) {
676
777
 
677
778
  ### Additional Exports
678
779
 
679
- The plugin also exports the following for advanced usage:
780
+ The plugin also exports the following utility functions for advanced usage:
680
781
 
681
782
  - `transformAutoRaw(content)`: The transform function used by `mdAutoRawTags` preprocessor. Can be used programmatically to wrap Nunjucks syntax with raw tags.
682
783
  - `transformNl2br(content)`: The transform function used by `mdAutoNl2br` preprocessor. Can be used programmatically to convert `\n` sequences to `<br>` tags.
784
+ - `merge(first, ...rest)`: The core merge function used by the `merge` filter. Can be used programmatically to merge arrays or objects.
785
+ - `removeTag(html, tagName)`: The core function used by the `remove_tag` filter. Can be used programmatically to remove HTML tags from content.
786
+ - `iff(trueValue, condition, falseValue)`: The core conditional function used by the `if` filter. Can be used programmatically as a ternary operator.
787
+ - `attrConcat(obj, attr, values)`: The core function used by the `attr_concat` filter. Can be used programmatically to concatenate values to an attribute array.
683
788
 
684
789
  ## Starter Configuration Files
685
790
 
@@ -690,19 +795,22 @@ The package includes pre-configured starter files in `node_modules/@anydigital/e
690
795
  #### eleventy.config.js
691
796
 
692
797
  A fully-configured Eleventy config file with:
798
+
693
799
  - All eleventy-bricks plugins enabled
694
800
  - Eleventy Navigation plugin
695
- - Markdown-it with anchors
801
+ - Markdown-it with anchors and attributes
696
802
  - YAML data support
697
803
  - CLI input directory support
698
804
  - Symlink support for development
699
805
 
700
806
  **Required dependencies:**
807
+
701
808
  ```bash
702
- npm install @11ty/eleventy-navigation markdown-it markdown-it-anchor js-yaml minimist
809
+ npm install @11ty/eleventy-navigation markdown-it markdown-it-anchor markdown-it-attrs js-yaml minimist
703
810
  ```
704
811
 
705
812
  **Symlink to your project:**
813
+
706
814
  ```bash
707
815
  ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js eleventy.config.js
708
816
  ```
@@ -712,6 +820,7 @@ ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js eleventy.c
712
820
  A ready-to-use Sveltia CMS admin interface for content management.
713
821
 
714
822
  **Symlink to your project:**
823
+
715
824
  ```bash
716
825
  mkdir -p admin
717
826
  ln -s ../node_modules/@anydigital/eleventy-bricks/src/admin/index.html admin/index.html
@@ -852,7 +961,7 @@ npx download-files --output public
852
961
  ## Requirements
853
962
 
854
963
  - Node.js >= 18.0.0
855
- - Eleventy >= 2.0.0 (supports both 2.x and 3.x)
964
+ - Eleventy >= 3.0.0
856
965
 
857
966
  ## License
858
967