ultimate-jekyll-manager 0.0.117 → 0.0.119
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/CLAUDE.md +616 -138
- package/README.md +108 -0
- package/TODO.md +1 -1
- package/dist/assets/js/core/auth.js +8 -11
- package/dist/assets/js/core/cookieconsent.js +24 -17
- package/dist/assets/js/core/exit-popup.js +15 -12
- package/dist/assets/js/core/social-sharing.js +8 -4
- package/dist/assets/js/libs/auth/pages.js +14 -13
- package/dist/assets/js/libs/dev.js +192 -129
- package/dist/assets/js/libs/prerendered-icons.js +27 -0
- package/dist/assets/js/pages/account/index.js +4 -3
- package/dist/assets/js/pages/account/sections/api-keys.js +2 -6
- package/dist/assets/js/pages/account/sections/connections.js +101 -59
- package/dist/assets/js/pages/account/sections/delete.js +83 -84
- package/dist/assets/js/pages/account/sections/referrals.js +29 -29
- package/dist/assets/js/pages/account/sections/security.js +51 -71
- package/dist/assets/js/pages/admin/notifications/new/index.js +17 -10
- package/dist/assets/js/pages/blog/index.js +7 -5
- package/dist/assets/js/pages/contact/index.js +6 -33
- package/dist/assets/js/pages/download/index.js +3 -2
- package/dist/assets/js/pages/payment/checkout/index.js +6 -6
- package/dist/assets/js/pages/payment/checkout/modules/processors-main.js +2 -2
- package/dist/assets/js/pages/payment/checkout/modules/session.js +4 -4
- package/dist/assets/js/pages/pricing/index.js +5 -2
- package/dist/assets/themes/classy/css/components/_cards.scss +2 -2
- package/dist/defaults/_.env +6 -0
- package/dist/defaults/_.gitignore +7 -1
- package/dist/defaults/dist/_includes/core/body.html +18 -3
- package/dist/defaults/dist/_includes/core/foot.html +1 -0
- package/dist/defaults/dist/_includes/themes/classy/frontend/sections/nav.html +51 -36
- package/dist/defaults/dist/_layouts/blueprint/admin/notifications/new.html +13 -2
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/about.html +84 -42
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +67 -35
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/index.html +72 -58
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/post.html +46 -29
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +36 -16
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +111 -73
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +111 -56
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +127 -81
- package/dist/defaults/dist/pages/pricing.md +7 -0
- package/dist/defaults/dist/pages/test/libraries/lazy-loading.html +1 -1
- package/dist/gulp/tasks/defaults.js +210 -1
- package/firebase-debug.log +504 -0
- package/package.json +5 -5
package/CLAUDE.md
CHANGED
|
@@ -1,41 +1,214 @@
|
|
|
1
|
-
#
|
|
2
|
-
This is Ultimate Jekyll, it is a template project that other "consuming" projects can use to build a Jekyll site. It comes installed as an NPM module and is helpful for setting up, configuring, and maintaining a Jekyll site with best practices in mind.
|
|
1
|
+
# Ultimate Jekyll Manager
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
* You cannot use `npm start` or `npm run build` in this project.
|
|
3
|
+
## Project Overview
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
Ultimate Jekll has the following structure:
|
|
9
|
-
src/gulp/tasks = Gulp tasks that are used to build the Jekyll site.
|
|
10
|
-
src/defaults/src = Default source files that can be used as a starting point for a Jekyll site and ARE meant to be edited by the user. They are copied to the consuming project's "src" directory.
|
|
11
|
-
src/defaults/dist = Default distribution files that can be used as a starting point for a Jekyll site and NOT meant to be edited by the user. They are copied to the consuming project's "dist" directory.
|
|
5
|
+
Ultimate Jekyll Manager is a template framework that consuming projects install as an NPM module to build Jekyll sites quickly and efficiently. It provides best-practice configurations, default components, themes, and build tools.
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
**Important:** This is NOT a standalone project. You cannot run `npm start` or `npm run build` directly in this repository.
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
### Directory Organization
|
|
12
|
+
- `src/gulp/tasks` - Gulp tasks for building Jekyll sites
|
|
13
|
+
- `src/defaults/src` - Default source files (editable by users, copied to consuming project's `src/`)
|
|
14
|
+
- `src/defaults/dist` - Default distribution files (not editable by users, copied to consuming project's `dist/`)
|
|
15
|
+
- `src/assets/css` - Stylesheets (global, pages, themes)
|
|
16
|
+
- `src/assets/js` - JavaScript modules (core, pages, libraries)
|
|
17
|
+
- `src/assets/themes` - Theme SCSS and JS files
|
|
18
|
+
|
|
19
|
+
### Consuming Project Structure
|
|
20
|
+
- `src/` - Compiled to `dist/` via npm
|
|
21
|
+
- `dist/` - Compiled to `_site/` via Jekyll
|
|
16
22
|
|
|
17
23
|
## Local Development
|
|
18
|
-
When working with a consuming project, the local development server URL can be found in the `.temp/_config_browsersync.yml` file in the consuming project's root directory. You should read this file to determine the correct local URL for browsing and testing the site.
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
The local development server URL is stored in `.temp/_config_browsersync.yml` in the consuming project's root directory. Read this file to determine the correct URL for browsing and testing.
|
|
26
|
+
|
|
27
|
+
## Asset Organization
|
|
28
|
+
|
|
29
|
+
### Ultimate Jekyll Manager Files (THIS project)
|
|
30
|
+
|
|
31
|
+
**CSS:**
|
|
32
|
+
- `src/assets/css/ultimate-jekyll-manager.scss` - Main UJ stylesheet (provides core styles)
|
|
33
|
+
- `src/assets/css/global/` - Global UJ styles
|
|
34
|
+
- `src/assets/css/pages/` - Page-specific styles provided by UJ
|
|
35
|
+
- Format: `src/assets/css/pages/[page-name]/index.scss`
|
|
36
|
+
- Example: `src/assets/css/pages/download/index.scss`
|
|
37
|
+
|
|
38
|
+
**JavaScript:**
|
|
39
|
+
- `src/assets/js/ultimate-jekyll-manager.js` - Main UJ JavaScript entry point (provides core functionality)
|
|
40
|
+
- `src/assets/js/core/` - Core UJ modules
|
|
41
|
+
- `src/assets/js/pages/` - Page-specific JavaScript provided by UJ
|
|
42
|
+
- Format: `src/assets/js/pages/[page-name]/index.js`
|
|
43
|
+
- Example: `src/assets/js/pages/download/index.js`
|
|
44
|
+
- `src/assets/js/libs/` - UJ library modules (prerendered-icons, form-manager, authorized-fetch, etc.)
|
|
45
|
+
|
|
46
|
+
**Default Pages & Layouts:**
|
|
47
|
+
|
|
48
|
+
UJ provides default page templates and layouts in `src/defaults/dist/` that are copied to consuming projects. These are NOT meant to be edited by users.
|
|
49
|
+
|
|
50
|
+
- Format: `src/defaults/dist/_layouts/themes/[theme-id]/frontend/pages/[page-name].html`
|
|
51
|
+
- Examples:
|
|
52
|
+
- `src/defaults/dist/_layouts/themes/classy/frontend/pages/download.html`
|
|
53
|
+
- `src/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html`
|
|
54
|
+
- `src/defaults/dist/_layouts/themes/classy/frontend/pages/payment/checkout.html`
|
|
55
|
+
- `src/defaults/dist/_layouts/themes/classy/frontend/pages/payment/confirmation.html`
|
|
56
|
+
- `src/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html`
|
|
57
|
+
- Core layouts:
|
|
58
|
+
- `src/defaults/dist/_layouts/core/root.html` - Root HTML wrapper
|
|
59
|
+
- `src/defaults/dist/_layouts/themes/[theme-id]/frontend/core/base.html` - Theme base layout
|
|
60
|
+
|
|
61
|
+
**Complete UJ Page Example:**
|
|
62
|
+
- **HTML:** `src/defaults/dist/_layouts/themes/classy/frontend/pages/download.html`
|
|
63
|
+
- **CSS:** `src/assets/css/pages/download/index.scss`
|
|
64
|
+
- **JS:** `src/assets/js/pages/download/index.js`
|
|
65
|
+
|
|
66
|
+
These files serve as blueprints and reference implementations. When building custom pages in consuming projects, reference these for patterns and best practices.
|
|
67
|
+
|
|
68
|
+
**IMPORTANT:** Consuming projects CAN create files with the same paths in their own `src/` directory to override UJ defaults, but this should ONLY be done when absolutely necessary. Prefer using `src/pages/` and `src/_layouts/` for custom pages instead of overriding UJ default files.
|
|
69
|
+
|
|
70
|
+
### Section Configuration Files (JSON)
|
|
71
|
+
|
|
72
|
+
UJ provides JSON configuration files for common sections like navigation and footer. These JSON files are consumed by corresponding HTML templates during the build process.
|
|
73
|
+
|
|
74
|
+
**Configuration Files:**
|
|
75
|
+
- `src/defaults/src/_includes/frontend/sections/nav.json` - Navigation configuration
|
|
76
|
+
- `src/defaults/src/_includes/frontend/sections/footer.json` - Footer configuration
|
|
77
|
+
|
|
78
|
+
**How It Works:**
|
|
79
|
+
1. JSON files contain structured data (links, labels, settings)
|
|
80
|
+
2. HTML templates in `src/defaults/dist/_includes/themes/[theme-id]/frontend/sections/` read and render this data
|
|
81
|
+
3. The build process converts `.json` → data loaded by `.html` templates
|
|
82
|
+
|
|
83
|
+
**Customizing Navigation/Footer:**
|
|
84
|
+
|
|
85
|
+
Consuming projects should create their own JSON files in `src/_includes/frontend/sections/`:
|
|
86
|
+
- `src/_includes/frontend/sections/nav.json`
|
|
87
|
+
- `src/_includes/frontend/sections/footer.json`
|
|
88
|
+
|
|
89
|
+
**Example: Footer Configuration**
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
logo: {
|
|
94
|
+
href: '/',
|
|
95
|
+
class: 'filter-adaptive',
|
|
96
|
+
text: '{{ site.brand.name }}',
|
|
97
|
+
description: '{{ site.meta.description }}',
|
|
98
|
+
},
|
|
99
|
+
links: [
|
|
100
|
+
{
|
|
101
|
+
label: 'Company',
|
|
102
|
+
href: null,
|
|
103
|
+
links: [
|
|
104
|
+
{
|
|
105
|
+
label: 'About Us',
|
|
106
|
+
href: '/about',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
label: 'Pricing',
|
|
110
|
+
href: '/pricing',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
socials: {
|
|
116
|
+
enabled: true,
|
|
117
|
+
},
|
|
118
|
+
copyright: {
|
|
119
|
+
enabled: true,
|
|
120
|
+
text: null,
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Note:** These are JSON5 files (support comments, trailing commas, unquoted keys). The corresponding HTML templates automatically process these files during the build.
|
|
126
|
+
|
|
127
|
+
### Customizing Default Pages via Frontmatter
|
|
128
|
+
|
|
129
|
+
**BEST PRACTICE:** UJ default pages are designed to be customized through frontmatter WITHOUT writing any HTML. Consuming projects can create a simple page that includes ONLY frontmatter to configure the default page's behavior.
|
|
130
|
+
|
|
131
|
+
**How It Works:**
|
|
132
|
+
1. UJ default pages use `page.resolved` to access merged frontmatter (site → layout → page)
|
|
133
|
+
2. **IMPORTANT:** Before customizing, READ the UJ default page in `src/defaults/dist/_layouts/` to understand available frontmatter options and how they're used
|
|
134
|
+
3. Consuming projects create a page in `src/pages/` with custom frontmatter
|
|
135
|
+
4. The page uses a UJ layout (e.g., `blueprint/pricing`)
|
|
136
|
+
5. Frontmatter overrides default values without any HTML
|
|
137
|
+
|
|
138
|
+
**Example: Customizing the Pricing Page**
|
|
139
|
+
|
|
140
|
+
**Step 1:** Read the UJ default pricing page to see available frontmatter options:
|
|
141
|
+
- File: `src/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html`
|
|
142
|
+
- Look for frontmatter at the top and how `page.resolved.pricing` is used in the HTML
|
|
143
|
+
|
|
144
|
+
**Step 2:** In consuming project, create `src/pages/pricing.html`:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
---
|
|
148
|
+
### ALL PAGES ###
|
|
149
|
+
layout: blueprint/pricing
|
|
150
|
+
permalink: /pricing
|
|
151
|
+
|
|
152
|
+
### PAGE CONFIG ###
|
|
153
|
+
pricing:
|
|
154
|
+
price_per_unit:
|
|
155
|
+
enabled: true
|
|
156
|
+
feature_id: "credits"
|
|
157
|
+
label: "credit"
|
|
158
|
+
plans:
|
|
159
|
+
- id: "basic"
|
|
160
|
+
name: "Basic"
|
|
161
|
+
tagline: "best for getting started"
|
|
162
|
+
url: "/download"
|
|
163
|
+
pricing:
|
|
164
|
+
monthly: 0
|
|
165
|
+
annually: 0
|
|
166
|
+
features:
|
|
167
|
+
- id: "credits"
|
|
168
|
+
name: "Credits"
|
|
169
|
+
value: 1
|
|
170
|
+
icon: "sparkles"
|
|
171
|
+
...
|
|
172
|
+
---
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
That's it! No HTML needed. The UJ pricing layout reads `page.resolved.pricing` and renders the plans accordingly.
|
|
176
|
+
|
|
177
|
+
**When to Use Frontmatter Customization:**
|
|
178
|
+
- ✅ Customizing UJ default pages (pricing, contact, download, etc.)
|
|
179
|
+
- ✅ Changing configuration without touching HTML
|
|
180
|
+
- ✅ Maintaining upgradability when UJ updates
|
|
25
181
|
|
|
26
|
-
|
|
27
|
-
|
|
182
|
+
**When to Create Custom Pages:**
|
|
183
|
+
- ❌ Building entirely new page types
|
|
184
|
+
- ❌ Needing custom HTML structure
|
|
185
|
+
- ❌ Pages with unique layouts not provided by UJ
|
|
28
186
|
|
|
187
|
+
### Consuming Project Files
|
|
29
188
|
|
|
30
|
-
|
|
31
|
-
src/assets/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
189
|
+
**CSS:**
|
|
190
|
+
- `src/assets/css/main.scss` - Site-wide custom styles (runs on every page, edits by consuming project)
|
|
191
|
+
- `src/assets/css/pages/` - Page-specific custom styles
|
|
192
|
+
- Format: `src/assets/css/pages/[page-name]/index.scss`
|
|
193
|
+
|
|
194
|
+
**JavaScript:**
|
|
195
|
+
- `src/assets/js/main.js` - Site-wide custom JavaScript (runs on every page, edits by consuming project)
|
|
196
|
+
- `src/assets/js/pages/` - Page-specific custom JavaScript
|
|
197
|
+
- Format: `src/assets/js/pages/[page-name]/index.js`
|
|
198
|
+
|
|
199
|
+
**Pages & Layouts:**
|
|
200
|
+
- `src/pages/` - Individual page HTML/Markdown files
|
|
201
|
+
- `src/_layouts/` - Custom layouts for the consuming project
|
|
202
|
+
|
|
203
|
+
**Asset Loading:** Page-specific CSS/JS files are automatically included based on the page's canonical path. Override with `asset_path` frontmatter.
|
|
204
|
+
|
|
205
|
+
### Page Module Structure
|
|
206
|
+
|
|
207
|
+
All page modules must follow this standardized pattern:
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
37
210
|
/**
|
|
38
|
-
*
|
|
211
|
+
* [Page Name] Page JavaScript
|
|
39
212
|
*/
|
|
40
213
|
|
|
41
214
|
// Libraries
|
|
@@ -50,7 +223,7 @@ export default (Manager) => {
|
|
|
50
223
|
// Initialize when DOM is ready
|
|
51
224
|
await webManager.dom().ready();
|
|
52
225
|
|
|
53
|
-
//
|
|
226
|
+
// Page initialization logic
|
|
54
227
|
helper1();
|
|
55
228
|
|
|
56
229
|
// Resolve after initialization
|
|
@@ -60,155 +233,460 @@ export default (Manager) => {
|
|
|
60
233
|
|
|
61
234
|
// Helper functions
|
|
62
235
|
function helper1() {
|
|
63
|
-
//
|
|
236
|
+
// Helper implementation
|
|
64
237
|
}
|
|
65
238
|
```
|
|
66
239
|
|
|
67
|
-
|
|
68
|
-
|
|
240
|
+
**Key Points:**
|
|
241
|
+
- Helpers are defined outside the main export function
|
|
242
|
+
- Use `webManager` shortcuts for common operations
|
|
243
|
+
- Always wait for DOM ready before manipulating elements
|
|
244
|
+
|
|
245
|
+
## Layouts and Pages
|
|
246
|
+
|
|
247
|
+
### Page Types
|
|
248
|
+
- **One-off pages** (e.g., `/categories`, `/sitemap`) - Create as pages without custom layouts; use existing layouts
|
|
249
|
+
- **Repeating page types** (e.g., blog posts, category pages) - Create a dedicated layout (e.g., `_layouts/category.html`)
|
|
250
|
+
|
|
251
|
+
### Layout Requirements
|
|
252
|
+
All layouts and pages must eventually require a theme entry point:
|
|
253
|
+
```yaml
|
|
254
|
+
layout: themes/[ site.theme.id ]/frontend/core/base
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Note:** The `[ site.theme.id ]` syntax is correct and allows dynamic theme selection.
|
|
258
|
+
|
|
259
|
+
### Asset Path Configuration
|
|
260
|
+
|
|
261
|
+
For pages sharing the same assets, use the `asset_path` frontmatter variable:
|
|
262
|
+
|
|
263
|
+
```yaml
|
|
264
|
+
---
|
|
265
|
+
# Instead of deriving path from page.canonical.path
|
|
266
|
+
asset_path: categories/category
|
|
267
|
+
---
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Example:**
|
|
271
|
+
- One-off page: `pages/categories.html` → `src/assets/css/pages/categories/index.scss`
|
|
272
|
+
- Repeating layout: `_layouts/category.html` → `src/assets/css/pages/categories/category.scss` (set `asset_path: categories/category` in layout frontmatter)
|
|
273
|
+
|
|
274
|
+
## UJ Powertools (Jekyll Plugin)
|
|
275
|
+
|
|
276
|
+
Ultimate Jekyll uses the `jekyll-uj-powertools` gem for custom Liquid functionality.
|
|
277
|
+
|
|
278
|
+
**Documentation:** `/Users/ian/Developer/Repositories/ITW-Creative-works/jekyll-uj-powertools/README.md`
|
|
279
|
+
|
|
280
|
+
### Available Features
|
|
281
|
+
- **Filters:** `uj_strip_ads`, `uj_json_escape`, `uj_title_case`, `uj_content_format`
|
|
282
|
+
- **Tags:** `iftruthy`, `iffalsy`, `uj_icon`, `uj_logo`, `uj_image`, `uj_member`, `uj_post`, `uj_readtime`, `uj_social`, `uj_translation_url`, `uj_fake_comments`, `uj_language`
|
|
283
|
+
- **Global Variables:** `site.uj.cache_breaker`
|
|
284
|
+
- **Page Variables:** `page.random_id`, `page.extension`, `page.layout_data`, `page.resolved`
|
|
285
|
+
|
|
286
|
+
**Always check the README before assuming functionality.**
|
|
287
|
+
|
|
288
|
+
### Key Liquid Functions
|
|
289
|
+
|
|
290
|
+
#### `uj_content_format`
|
|
291
|
+
Formats content by first liquifying it, then markdownifying it (if markdown file).
|
|
292
|
+
|
|
293
|
+
#### `iftruthy` / `iffalsy`
|
|
294
|
+
Custom tags that check JavaScript truthiness (not null, undefined, or empty string).
|
|
295
|
+
|
|
296
|
+
```liquid
|
|
297
|
+
{% iftruthy variable %}
|
|
298
|
+
<!-- Content -->
|
|
299
|
+
{% endiftruthy %}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Limitations:**
|
|
303
|
+
- Does NOT support logical operators
|
|
304
|
+
- Does NOT support `else` statements
|
|
305
|
+
- CAN contain nested sub-statements
|
|
306
|
+
|
|
307
|
+
#### `page.resolved`
|
|
308
|
+
A deeply merged object containing all site, layout, and page variables. Precedence: page > layout > site. Enables a system of defaults with progressive overrides.
|
|
309
|
+
|
|
310
|
+
#### `uj_icon`
|
|
311
|
+
Inserts Font Awesome icons:
|
|
312
|
+
|
|
313
|
+
```liquid
|
|
314
|
+
{% uj_icon icon-name, "fa-md" %}
|
|
315
|
+
{% uj_icon "rocket", "fa-3xl" %}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Parameters:**
|
|
319
|
+
1. Icon name (string or variable, without "fa-" prefix)
|
|
320
|
+
2. CSS classes (optional, defaults to "fa-3xl")
|
|
321
|
+
|
|
322
|
+
#### `asset_path` Override
|
|
323
|
+
Override default page-specific CSS/JS path derivation:
|
|
324
|
+
|
|
325
|
+
```yaml
|
|
326
|
+
---
|
|
327
|
+
asset_path: blog/post
|
|
328
|
+
---
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Uses `/assets/css/pages/{{ asset_path }}.bundle.css` instead of deriving from `page.canonical.path`. Useful when multiple pages share assets (e.g., all blog posts).
|
|
332
|
+
|
|
333
|
+
## Icon System
|
|
334
|
+
|
|
335
|
+
Ultimate Jekyll uses Font Awesome icons but does NOT include the Font Awesome JavaScript or CSS library. All icons must be rendered server-side using Jekyll's `{% uj_icon %}` tag.
|
|
336
|
+
|
|
337
|
+
### When to Use `{% uj_icon %}` vs Prerendered Icons
|
|
338
|
+
|
|
339
|
+
**IMPORTANT:** Use the correct method based on WHERE the icon will be used:
|
|
340
|
+
|
|
341
|
+
#### Use `{% uj_icon %}` in HTML/Liquid Templates
|
|
342
|
+
|
|
343
|
+
When icons are part of the static HTML template, use `{% uj_icon %}` directly:
|
|
344
|
+
|
|
345
|
+
```liquid
|
|
346
|
+
<!-- Alerts -->
|
|
347
|
+
<div class="alert alert-success">
|
|
348
|
+
{% uj_icon "circle-check", "fa-sm" %} Success message
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<!-- Buttons -->
|
|
352
|
+
<button class="btn btn-primary">
|
|
353
|
+
{% uj_icon "paper-plane", "fa-md me-2" %}
|
|
354
|
+
Send
|
|
355
|
+
</button>
|
|
356
|
+
|
|
357
|
+
<!-- Labels -->
|
|
358
|
+
<label>
|
|
359
|
+
{% uj_icon "envelope", "fa-sm me-1 text-info" %}
|
|
360
|
+
Email
|
|
361
|
+
</label>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Use this when:**
|
|
365
|
+
- The icon is in a Jekyll template (.html file)
|
|
366
|
+
- The icon is static and known at build time
|
|
367
|
+
- The icon is part of the page structure
|
|
368
|
+
|
|
369
|
+
#### Use Prerendered Icons in JavaScript
|
|
370
|
+
|
|
371
|
+
When icons need to be dynamically inserted via JavaScript, pre-render them in frontmatter and access them via the library:
|
|
372
|
+
|
|
373
|
+
**1. Add icons to page frontmatter:**
|
|
374
|
+
```yaml
|
|
375
|
+
---
|
|
376
|
+
prerender_icons:
|
|
377
|
+
- name: "mobile"
|
|
378
|
+
class: "fa-sm me-1"
|
|
379
|
+
- name: "envelope"
|
|
380
|
+
class: "fa-sm me-1"
|
|
381
|
+
- name: "bell"
|
|
382
|
+
class: "fa-sm me-1"
|
|
383
|
+
---
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**2. Import the library in JavaScript:**
|
|
387
|
+
```javascript
|
|
388
|
+
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**3. Use in your code:**
|
|
392
|
+
```javascript
|
|
393
|
+
const iconHTML = getPrerenderedIcon('mobile');
|
|
394
|
+
$badge.innerHTML = `${iconHTML}Push Notification`;
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Use this when:**
|
|
398
|
+
- Icons are dynamically inserted via JavaScript
|
|
399
|
+
- Icons are part of dynamically generated content
|
|
400
|
+
- Icons are added to elements created with `document.createElement()`
|
|
401
|
+
|
|
402
|
+
### What NOT to Do
|
|
403
|
+
|
|
404
|
+
**NEVER use manual icon HTML in JavaScript:**
|
|
405
|
+
```javascript
|
|
406
|
+
// ❌ WRONG - Bootstrap Icons (we don't use Bootstrap Icons)
|
|
407
|
+
$el.innerHTML = '<i class="bi bi-check-circle"></i> Text';
|
|
408
|
+
|
|
409
|
+
// ❌ WRONG - Manual Font Awesome (we don't have FA JS/CSS)
|
|
410
|
+
$el.innerHTML = '<i class="fa-solid fa-check"></i> Text';
|
|
411
|
+
|
|
412
|
+
// ✅ CORRECT - Use prerendered icons
|
|
413
|
+
const iconHTML = getPrerenderedIcon('circle-check');
|
|
414
|
+
$el.innerHTML = `${iconHTML} Text`;
|
|
415
|
+
```
|
|
69
416
|
|
|
70
|
-
|
|
417
|
+
### Benefits
|
|
418
|
+
- Icons are rendered server-side with proper Font Awesome classes
|
|
419
|
+
- No client-side icon generation overhead
|
|
420
|
+
- Consistent icon styling across the application
|
|
421
|
+
- No Font Awesome JavaScript/CSS library needed
|
|
71
422
|
|
|
72
|
-
##
|
|
73
|
-
Ultimate Jekyll uses the `jekyll-uj-powertools` gem which provides many custom Liquid filters, tags, and variables. You should review the full documentation to understand all available tools: `/Users/ian/Developer/Repositories/ITW-Creative-works/jekyll-uj-powertools/README.md`
|
|
423
|
+
## CSS Guidelines
|
|
74
424
|
|
|
75
|
-
|
|
76
|
-
* **Filters**: `uj_strip_ads`, `uj_json_escape`, `uj_title_case`, `uj_content_format`
|
|
77
|
-
* **Tags**: `iftruthy`, `iffalsy`, `uj_icon`, `uj_logo`, `uj_image`, `uj_member`, `uj_post`, `uj_readtime`, `uj_social`, `uj_translation_url`, `uj_fake_comments`, `uj_language`
|
|
78
|
-
* **Global Variables**: `site.uj.cache_breaker`
|
|
79
|
-
* **Page Variables**: `page.random_id`, `page.extension`, `page.layout_data`, `page.resolved`
|
|
425
|
+
### Theme-Adaptive Classes
|
|
80
426
|
|
|
81
|
-
|
|
427
|
+
**DO NOT USE:** `bg-light`, `bg-dark`, `text-light`, `text-dark`
|
|
82
428
|
|
|
83
|
-
|
|
84
|
-
* uj_content_format = A custom liquid filter that formats the content like so: first, it LIQUIFIES the content, then, if it's a markdown file, it runs MARKDOWNIFY.
|
|
85
|
-
* iftruthy = a custom liquid tag that checks if a variable is truthy in JavaScript terms, meaning it checks if the variable is not null, undefined, or an empty string. Use it like this: `{% iftruthy variable %}`. But it DOES NOT SUPPORT any logical operators and does not support `else` statements. But you CAN put sub-statements inside it.
|
|
86
|
-
* iffalsy = same but opposite of iftruthy.
|
|
87
|
-
* page.resolved = a custom page object property of all site, layout, and page variables deeply merged together, with page variables taking precedence over layout, and layout variables taking precedence over site variables. This allows a system of site defaults, layout overrides, and page-specific overrides.
|
|
429
|
+
Ultimate Jekyll supports both light and dark modes. Use adaptive classes instead:
|
|
88
430
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
431
|
+
**Backgrounds:**
|
|
432
|
+
- `bg-body` - Primary background
|
|
433
|
+
- `bg-body-secondary` - Secondary background
|
|
434
|
+
- `bg-body-tertiary` - Tertiary background
|
|
92
435
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* **One-off pages** (like `/categories`, `/sitemap`, etc.) should be created directly as pages (e.g., `pages/categories.html`) WITHOUT their own custom layout. These pages should use an existing layout.
|
|
96
|
-
* **Repeating page types** (like individual category pages, individual blog posts, etc.) should have a dedicated layout that all instances use (e.g., `_layouts/category.html` used by all category pages).
|
|
97
|
-
* **All layouts and pages** must eventually require one of the theme entry points, such as `layout: themes/[ site.theme.id ]/frontend/core/base`. This ensures consistent theming across the site.
|
|
98
|
-
* YES, the "[[ site.theme.id ]]" syntax is correct here; it allows dynamic selection of the theme based on site configuration.
|
|
99
|
-
* This approach keeps the codebase maintainable by reusing layouts for similar pages while avoiding unnecessary layout files for unique pages.
|
|
100
|
-
* So you should put assets like CSS and JS for a one-off page like `categories.html` in `src/assets/css/pages/categories/index.scss` and `src/assets/js/pages/categories/index.js` respectively, and for repeating page types like an individual category that uses a LAYOUT, put them in `src/assets/css/pages/categories/category.scss` and `src/assets/js/pages/categories/category.js` (and you will of course set the `asset_path: categories/category` frontmatter variable on the layout so that the correct assets are loaded).
|
|
436
|
+
**Text:**
|
|
437
|
+
- `text-body` - Body text color
|
|
101
438
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
439
|
+
**Buttons:**
|
|
440
|
+
- `btn-adaptive` - Adaptive button
|
|
441
|
+
- `btn-outline-adaptive` - Adaptive outline button
|
|
105
442
|
|
|
106
|
-
|
|
107
|
-
Ultimate Jekyll implements a sophisticated system to prevent premature button clicks during page initialization. This prevents race conditions and ensures JavaScript is fully loaded before users can trigger actions.
|
|
443
|
+
These classes automatically adapt to the current theme mode.
|
|
108
444
|
|
|
109
|
-
|
|
110
|
-
1. **Initial State**: The HTML element starts with `data-page-loading="true"` and `aria-busy="true"` attributes (set in `src/defaults/dist/_layouts/core/root.html`)
|
|
111
|
-
2. **Protection During Load**: While `data-page-loading` is present, certain elements are automatically disabled via CSS and JavaScript
|
|
112
|
-
3. **Ready State**: When JavaScript initialization completes, these attributes are removed by `src/assets/js/core/complete.js`
|
|
445
|
+
## Page Loading Protection System
|
|
113
446
|
|
|
114
|
-
|
|
115
|
-
When `data-page-loading` is active, the following elements are automatically protected from clicks:
|
|
447
|
+
Ultimate Jekyll prevents race conditions by disabling buttons during JavaScript initialization.
|
|
116
448
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
449
|
+
### How It Works
|
|
450
|
+
1. HTML element starts with `data-page-loading="true"` and `aria-busy="true"` (`src/defaults/dist/_layouts/core/root.html`)
|
|
451
|
+
2. Protected elements are automatically disabled during this state
|
|
452
|
+
3. Attributes are removed when JavaScript completes (`src/assets/js/core/complete.js`)
|
|
121
453
|
|
|
122
|
-
###
|
|
123
|
-
|
|
454
|
+
### Protected Elements
|
|
455
|
+
- All form buttons (`<button>`, `<input type="submit">`, `<input type="button">`, `<input type="reset">`)
|
|
456
|
+
- Elements with `.btn` class (Bootstrap buttons)
|
|
457
|
+
- Elements with `.btn-action` class (custom action triggers)
|
|
458
|
+
|
|
459
|
+
### The `.btn-action` Class
|
|
460
|
+
|
|
461
|
+
Selectively protect non-standard elements that trigger important actions:
|
|
124
462
|
|
|
125
463
|
```html
|
|
126
|
-
<!--
|
|
464
|
+
<!-- Protected during page load -->
|
|
127
465
|
<a href="/api/delete" class="custom-link btn-action">Delete Item</a>
|
|
128
466
|
<div class="card-action btn-action" onclick="processData()">Process</div>
|
|
129
|
-
<span class="text-link btn-action" data-action="save">Save Changes</span>
|
|
130
467
|
|
|
131
|
-
<!--
|
|
468
|
+
<!-- NOT protected (regular navigation/UI) -->
|
|
132
469
|
<a href="/about" class="btn btn-primary">About Us</a>
|
|
133
470
|
<button data-bs-toggle="modal">Show Info</button>
|
|
134
|
-
<button data-bs-toggle="collapse">Toggle Details</button>
|
|
135
471
|
```
|
|
136
472
|
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
473
|
+
**Use `.btn-action` for:**
|
|
474
|
+
- API calls
|
|
475
|
+
- Form submissions
|
|
476
|
+
- Data modifications
|
|
477
|
+
- Payment processing
|
|
478
|
+
- Destructive actions
|
|
479
|
+
|
|
480
|
+
**Don't use for:**
|
|
481
|
+
- Navigation links
|
|
482
|
+
- UI toggles (modals, accordions, tabs)
|
|
483
|
+
- Harmless interactions
|
|
140
484
|
|
|
141
|
-
### Implementation
|
|
142
|
-
- **CSS
|
|
143
|
-
- **Click Prevention
|
|
144
|
-
- **State Removal
|
|
485
|
+
### Implementation
|
|
486
|
+
- **CSS:** `src/assets/css/core/utilities.scss` - Disabled styling
|
|
487
|
+
- **Click Prevention:** `src/defaults/dist/_includes/core/body.html` - Inline script
|
|
488
|
+
- **State Removal:** `src/assets/js/core/complete.js` - Removes loading state
|
|
145
489
|
|
|
146
|
-
|
|
490
|
+
## Lazy Loading System
|
|
147
491
|
|
|
148
|
-
|
|
149
|
-
Ultimate Jekyll uses a custom lazy loading system powered by web-manager. Elements can be lazy-loaded using the `data-lazy` attribute with the following format:
|
|
492
|
+
Ultimate Jekyll uses a custom lazy loading system powered by web-manager.
|
|
150
493
|
|
|
494
|
+
### Syntax
|
|
151
495
|
```html
|
|
152
496
|
data-lazy="@type value"
|
|
153
497
|
```
|
|
154
498
|
|
|
155
|
-
### Supported Types
|
|
156
|
-
* `@src` - Lazy load the `src` attribute (for images, iframes, videos, source elements)
|
|
157
|
-
```html
|
|
158
|
-
<img data-lazy="@src /assets/images/hero.jpg" alt="Hero">
|
|
159
|
-
<iframe data-lazy="@src https://example.com/embed"></iframe>
|
|
160
|
-
```
|
|
499
|
+
### Supported Types
|
|
161
500
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
501
|
+
#### `@src` - Lazy load src attribute
|
|
502
|
+
```html
|
|
503
|
+
<img data-lazy="@src /assets/images/hero.jpg" alt="Hero">
|
|
504
|
+
<iframe data-lazy="@src https://example.com/embed"></iframe>
|
|
505
|
+
```
|
|
166
506
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
507
|
+
#### `@srcset` - Lazy load srcset attribute
|
|
508
|
+
```html
|
|
509
|
+
<img data-lazy="@srcset /img/small.jpg 480w, /img/large.jpg 1024w">
|
|
510
|
+
```
|
|
171
511
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
512
|
+
#### `@bg` - Lazy load background images
|
|
513
|
+
```html
|
|
514
|
+
<div data-lazy="@bg /assets/images/background.jpg"></div>
|
|
515
|
+
```
|
|
176
516
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
517
|
+
#### `@class` - Lazy add CSS classes
|
|
518
|
+
```html
|
|
519
|
+
<div data-lazy="@class animation-fade-in">Content</div>
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### `@html` - Lazy inject HTML content
|
|
523
|
+
```html
|
|
524
|
+
<div data-lazy="@html <p>Lazy loaded content</p>"></div>
|
|
525
|
+
```
|
|
181
526
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
527
|
+
#### `@script` - Lazy load external scripts
|
|
528
|
+
```html
|
|
529
|
+
<div data-lazy='@script {"src": "https://example.com/widget.js", "attributes": {"async": true}}'></div>
|
|
530
|
+
```
|
|
186
531
|
|
|
187
|
-
### Features
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
532
|
+
### Features
|
|
533
|
+
- Automatic cache busting via `buildTime`
|
|
534
|
+
- IntersectionObserver for performance (50px threshold)
|
|
535
|
+
- Loading state CSS classes: `lazy-loading`, `lazy-loaded`, `lazy-error`
|
|
536
|
+
- Intelligent handling of video/audio sources
|
|
537
|
+
- Automatic DOM re-scanning for dynamic elements
|
|
193
538
|
|
|
194
|
-
|
|
539
|
+
**Implementation:** `src/assets/js/core/lazy-loading.js`
|
|
195
540
|
|
|
196
|
-
## Libraries
|
|
197
|
-
I have some library preferences that I want you to follow:
|
|
541
|
+
## JavaScript Libraries
|
|
198
542
|
|
|
199
543
|
### WebManager
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
544
|
+
|
|
545
|
+
Custom library for site management functionality.
|
|
546
|
+
|
|
547
|
+
**Documentation:** `/Users/ian/Developer/Repositories/ITW-Creative-Works/web-manager/README.md`
|
|
548
|
+
|
|
549
|
+
**Available Utilities:**
|
|
550
|
+
- `webManager.auth()` - Authentication management
|
|
551
|
+
- `webManager.utilities()` - Utility functions
|
|
552
|
+
- `webManager.sentry()` - Error tracking
|
|
553
|
+
- `webManager.dom()` - DOM manipulation
|
|
554
|
+
|
|
555
|
+
**Important:** Always check the source code or README before assuming a function exists. Do not guess at API methods.
|
|
556
|
+
|
|
557
|
+
### Ultimate Jekyll Libraries
|
|
558
|
+
|
|
559
|
+
Ultimate Jekyll provides helper libraries in `src/assets/js/libs/` that can be imported as needed.
|
|
560
|
+
|
|
561
|
+
#### Prerendered Icons Library
|
|
562
|
+
|
|
563
|
+
Provides access to icons defined in page frontmatter and rendered server-side.
|
|
564
|
+
|
|
565
|
+
**Import:**
|
|
566
|
+
```javascript
|
|
567
|
+
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
**Usage:**
|
|
571
|
+
```javascript
|
|
572
|
+
const iconHTML = getPrerenderedIcon('apple');
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**Reference:** `src/assets/js/libs/prerendered-icons.js`
|
|
576
|
+
|
|
577
|
+
#### Authorized Fetch Library
|
|
578
|
+
|
|
579
|
+
Simplifies authenticated API requests by automatically adding Firebase authentication tokens via Authorization Bearer header.
|
|
580
|
+
|
|
581
|
+
**Import:**
|
|
582
|
+
```javascript
|
|
583
|
+
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Usage:**
|
|
587
|
+
```javascript
|
|
588
|
+
const response = await authorizedFetch(url, options);
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Key Benefits:**
|
|
592
|
+
- No need to manually call `webManager.auth().getIdToken()`
|
|
593
|
+
- Automatic token injection as Authorization Bearer header
|
|
594
|
+
- Centralized authentication handling
|
|
595
|
+
|
|
596
|
+
**Reference:** `src/assets/js/libs/authorized-fetch.js`
|
|
597
|
+
|
|
598
|
+
#### FormManager Library
|
|
599
|
+
|
|
600
|
+
Form handling library with built-in state management and validation.
|
|
601
|
+
|
|
602
|
+
**Import:**
|
|
603
|
+
```javascript
|
|
604
|
+
import { FormManager } from '__main_assets__/js/libs/form-manager.js';
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**Usage:**
|
|
608
|
+
```javascript
|
|
609
|
+
const formManager = new FormManager('#my-form', options);
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Reference:** `src/assets/js/libs/form-manager.js`
|
|
613
|
+
**Example:** `src/assets/js/pages/contact/index.js`
|
|
614
|
+
|
|
615
|
+
## Analytics & Tracking
|
|
616
|
+
|
|
617
|
+
Ultimate Jekyll uses three tracking platforms: Google Analytics (gtag), Facebook Pixel (fbq), and TikTok Pixel (ttq).
|
|
618
|
+
|
|
619
|
+
### Tracking Guidelines
|
|
620
|
+
|
|
621
|
+
**IMPORTANT Rules:**
|
|
622
|
+
- Track important user events with `gtag()`, `fbq()`, and `ttq()` functions
|
|
623
|
+
- NEVER add conditional checks for tracking functions (e.g., `if (typeof gtag !== 'undefined')`)
|
|
624
|
+
- Always assume tracking functions exist - they're globally available or stubbed
|
|
625
|
+
- Reference standard events documentation before implementing custom tracking
|
|
626
|
+
|
|
627
|
+
**Standard Events Documentation:**
|
|
628
|
+
- **Google Analytics GA4:** https://developers.google.com/analytics/devguides/collection/ga4/reference/events
|
|
629
|
+
- **Facebook Pixel:** https://www.facebook.com/business/help/402791146561655?id=1205376682832142
|
|
630
|
+
- **TikTok Pixel:** https://ads.tiktok.com/help/article/standard-events-parameters?redirected=2
|
|
631
|
+
|
|
632
|
+
### Platform-Specific Requirements
|
|
633
|
+
|
|
634
|
+
#### TikTok Pixel Requirements
|
|
635
|
+
TikTok has strict validation requirements:
|
|
636
|
+
|
|
637
|
+
**Required Parameters:**
|
|
638
|
+
- `content_id` - MUST be included in all events
|
|
639
|
+
|
|
640
|
+
**Valid Content Types:**
|
|
641
|
+
- `"product"`
|
|
642
|
+
- `"product_group"`
|
|
643
|
+
- `"destination"`
|
|
644
|
+
- `"hotel"`
|
|
645
|
+
- `"flight"`
|
|
646
|
+
- `"vehicle"`
|
|
647
|
+
|
|
648
|
+
Any other content type will generate a validation error.
|
|
649
|
+
|
|
650
|
+
**Example:**
|
|
651
|
+
```javascript
|
|
652
|
+
// ✅ CORRECT
|
|
653
|
+
ttq.track('ViewContent', {
|
|
654
|
+
content_id: 'product-123',
|
|
655
|
+
content_type: 'product'
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
// ❌ WRONG - Missing content_id
|
|
659
|
+
ttq.track('ViewContent', {
|
|
660
|
+
content_type: 'product'
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
// ❌ WRONG - Invalid content_type
|
|
664
|
+
ttq.track('ViewContent', {
|
|
665
|
+
content_id: 'product-123',
|
|
666
|
+
content_type: 'custom' // Not in approved list
|
|
667
|
+
});
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Tracking Implementation
|
|
671
|
+
|
|
672
|
+
**IMPORTANT:** Always track events to ALL THREE platforms in this order:
|
|
673
|
+
1. Google Analytics (gtag)
|
|
674
|
+
2. Facebook Pixel (fbq)
|
|
675
|
+
3. TikTok Pixel (ttq)
|
|
676
|
+
|
|
677
|
+
Track events directly without existence checks. All three tracking calls should be made together for every event.
|
|
678
|
+
|
|
679
|
+
**Development Mode:**
|
|
680
|
+
In development mode, all tracking calls are intercepted and logged to the console for debugging. See `src/assets/js/libs/dev.js` for implementation.
|
|
681
|
+
|
|
682
|
+
## Audit Workflow
|
|
683
|
+
|
|
684
|
+
When fixing issues identified by the audit task (`src/gulp/tasks/audit.js`):
|
|
685
|
+
|
|
686
|
+
1. Review the audit file location provided
|
|
687
|
+
2. Create a TODO list for each audit category
|
|
688
|
+
3. Read the ENTIRE audit file and plan fixes for each category
|
|
689
|
+
4. Tackle issues incrementally - DO NOT attempt to fix everything at once
|
|
690
|
+
5. Work through one category at a time
|
|
691
|
+
|
|
692
|
+
**Remember:** Audit files are large. Systematic, incremental fixes prevent errors and ensure thoroughness.
|