@hortonstudio/main 1.9.9 → 1.9.11

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## **Overview**
4
4
 
5
- The accessibility system provides 7 modular functions to enhance website accessibility and functionality. Each function operates independently and can be customized through data attributes.
5
+ The accessibility system provides 8 modular functions to enhance website accessibility and functionality. Each function operates independently and can be customized through data attributes.
6
6
 
7
7
  **Note:** This module auto-initializes and loads all functions on page load.
8
8
 
@@ -61,6 +61,13 @@ Universal dropdown system for FAQ, summary/read-more, and general toggle compone
61
61
 
62
62
  ---
63
63
 
64
+ ### **8. Pagination**
65
+ Complete pagination system for paginated lists with controls, counters, dot navigation, and infinite looping. Supports responsive layouts and accessibility features.
66
+
67
+ **Use case:** Multi-item paginated lists (product grids, blog lists, search results) and carousels (with `show-1` config).
68
+
69
+ ---
70
+
64
71
  ## **Documentation**
65
72
 
66
73
  Each function has detailed documentation in its respective folder:
@@ -72,6 +79,7 @@ Each function has detailed documentation in its respective folder:
72
79
  - `functions/text-synchronization/README.md`
73
80
  - `functions/toc/README.md`
74
81
  - `functions/dropdown/README.md`
82
+ - `functions/pagination/README.md`
75
83
 
76
84
  ---
77
85
 
@@ -12,7 +12,8 @@ export async function init() {
12
12
  "click-forwarding": () => import("./functions/click-forwarding/click-forwarding.js"),
13
13
  "text-synchronization": () => import("./functions/text-synchronization/text-synchronization.js"),
14
14
  "toc": () => import("./functions/toc/toc.js"),
15
- "dropdown": () => import("./functions/dropdown/dropdown.js")
15
+ "dropdown": () => import("./functions/dropdown/dropdown.js"),
16
+ "pagination": () => import("./functions/pagination/pagination.js")
16
17
  };
17
18
 
18
19
  const loadFunction = async (functionName) => {
@@ -0,0 +1,452 @@
1
+ # **Pagination System Documentation**
2
+
3
+ ## **Overview**
4
+
5
+ Complete pagination system for paginated lists with controls, counters, dot navigation, and infinite looping. Features responsive layouts, ARIA live announcements, and full keyboard accessibility.
6
+
7
+ **Note:** This function auto-initializes on page load as part of the accessibility system.
8
+
9
+ ---
10
+
11
+ ## **Features**
12
+
13
+ - **Infinite Loop Pagination**: Seamless page transitions with cloned pages
14
+ - **Responsive Layouts**: Different items per page for desktop/mobile
15
+ - **Page Controls**: Next/Previous buttons with infinite looping
16
+ - **Page Counter**: Live-updating "X / Y" page indicator
17
+ - **Dot Navigation**: Visual page indicators with click/keyboard support (semantic buttons)
18
+ - **Auto-height Management**: Smooth height transitions between pages
19
+ - **Focus Management**: Uses `inert` attribute for inactive pages
20
+ - **ARIA Live Announcements**: Screen reader announcements for page changes
21
+ - **Mobile Auto-scroll**: Scrolls controls into view on mobile after navigation
22
+
23
+ ---
24
+
25
+ ## **Required Structure**
26
+
27
+ ### **Wrapper** *(required)*
28
+
29
+ **Attribute:** `data-hs-pagination="wrapper"`
30
+
31
+ **What it does:** Main container for entire pagination instance.
32
+
33
+ ---
34
+
35
+ ### **List** *(required)*
36
+
37
+ **Attribute:** `data-hs-pagination="list"`
38
+
39
+ **What it does:** Container for all items to be paginated. System will split these into pages.
40
+
41
+ **Important:** Must be a direct child of a wrapper element (typically a div).
42
+
43
+ ---
44
+
45
+ ### **Next Button** *(required)*
46
+
47
+ **Attribute:** `data-hs-pagination="next"` on wrapper element
48
+
49
+ **What it does:** Navigate to next page. Loops to first page after last page.
50
+
51
+ **Structure:** Uses `data-site-clickable="element"` pattern:
52
+ ```html
53
+ <div data-hs-pagination="next" data-site-clickable="element">
54
+ <button type="button">Next</button>
55
+ </div>
56
+ ```
57
+
58
+ **Accessibility:** Automatically receives `aria-label="Go to next page"` on the button element
59
+
60
+ ---
61
+
62
+ ### **Previous Button** *(required)*
63
+
64
+ **Attribute:** `data-hs-pagination="previous"` on wrapper element
65
+
66
+ **What it does:** Navigate to previous page. Loops to last page from first page.
67
+
68
+ **Structure:** Uses `data-site-clickable="element"` pattern:
69
+ ```html
70
+ <div data-hs-pagination="previous" data-site-clickable="element">
71
+ <button type="button">Previous</button>
72
+ </div>
73
+ ```
74
+
75
+ **Accessibility:** Automatically receives `aria-label="Go to previous page"` on the button element
76
+
77
+ ---
78
+
79
+ ### **Controls Container** *(required for configuration)*
80
+
81
+ **Attribute:** `data-hs-pagination="controls"`
82
+
83
+ **What it does:** Container for pagination controls AND configuration.
84
+
85
+ **Configuration Attributes:**
86
+
87
+ **`data-hs-pagination-show`** - Desktop items per page (default: 6)
88
+ ```html
89
+ data-hs-pagination-show="4"
90
+ ```
91
+
92
+ **`data-hs-pagination-show-mobile`** - Mobile items per page (optional, defaults to desktop value)
93
+ ```html
94
+ data-hs-pagination-show-mobile="2"
95
+ ```
96
+
97
+ **Example:**
98
+ ```html
99
+ <div data-hs-pagination="controls"
100
+ data-hs-pagination-show="4"
101
+ data-hs-pagination-show-mobile="2">
102
+ <!-- Buttons, counter, etc. -->
103
+ </div>
104
+ ```
105
+
106
+ **No Pagination (Infinite Mode):**
107
+
108
+ To disable pagination and show all items, simply don't render the controls element at all. Without controls, the pagination system won't initialize and the list displays naturally.
109
+
110
+ ---
111
+
112
+ ### **Counter** *(optional)*
113
+
114
+ **Attribute:** `data-hs-pagination="counter"`
115
+
116
+ **What it does:** Displays current page and total pages (e.g., "2 / 5").
117
+
118
+ **Accessibility:** Automatically receives:
119
+ - `aria-live="polite"` - Announces changes
120
+ - `aria-label="Current page"`
121
+
122
+ ---
123
+
124
+ ### **Dots Container** *(optional)*
125
+
126
+ **Attribute:** `data-hs-pagination="dots"`
127
+
128
+ **What it does:** Container for pagination dots. Requires template dot as first child.
129
+
130
+ **Template Requirements:**
131
+ - At least one child element (used as template)
132
+ - Use `.is-active` class to designate active template
133
+ - If only one template, same used for active/inactive states
134
+ - **Dots are semantic `<button>` elements with `type="button"`**
135
+
136
+ **Accessibility:** System automatically adds:
137
+ - Individual dots via separate pagination dots system
138
+ - `role="button"`, `tabindex="0"`, `aria-label` on each dot button
139
+ - `aria-current="page"` on active dot
140
+
141
+ ---
142
+
143
+ ## **Usage Example**
144
+
145
+ ### **Basic Pagination**
146
+
147
+ ```html
148
+ <div data-hs-pagination="wrapper">
149
+ <!-- Wrapper for list -->
150
+ <div>
151
+ <div data-hs-pagination="list">
152
+ <div>Item 1</div>
153
+ <div>Item 2</div>
154
+ <div>Item 3</div>
155
+ <div>Item 4</div>
156
+ <div>Item 5</div>
157
+ <div>Item 6</div>
158
+ <div>Item 7</div>
159
+ <div>Item 8</div>
160
+ </div>
161
+ </div>
162
+
163
+ <!-- Controls -->
164
+ <div data-hs-pagination="controls"
165
+ data-hs-pagination-show="4"
166
+ data-hs-pagination-show-mobile="2">
167
+
168
+ <div data-hs-pagination="previous" data-site-clickable="element">
169
+ <button type="button">Previous</button>
170
+ </div>
171
+
172
+ <div data-hs-pagination="counter">1 / 2</div>
173
+
174
+ <div data-hs-pagination="next" data-site-clickable="element">
175
+ <button type="button">Next</button>
176
+ </div>
177
+ </div>
178
+
179
+ <!-- Dots -->
180
+ <div data-hs-pagination="dots">
181
+ <button type="button" class="dot is-active"></button> <!-- Active template -->
182
+ <button type="button" class="dot"></button> <!-- Inactive template -->
183
+ </div>
184
+ </div>
185
+ ```
186
+
187
+ ---
188
+
189
+ ### **No Pagination (Infinite Mode)**
190
+
191
+ To disable pagination and show all items without pagination controls, simply don't render the controls element:
192
+
193
+ ```html
194
+ <div data-hs-pagination="wrapper">
195
+ <div>
196
+ <div data-hs-pagination="list">
197
+ <!-- All items displayed, no pagination -->
198
+ <div>Item 1</div>
199
+ <div>Item 2</div>
200
+ <div>Item 3</div>
201
+ <div>Item 4</div>
202
+ <div>Item 5</div>
203
+ </div>
204
+ </div>
205
+
206
+ <!-- No controls = no pagination initialization -->
207
+ </div>
208
+ ```
209
+
210
+ ---
211
+
212
+ ## **Responsive Breakpoints**
213
+
214
+ Uses CSS custom property `--data-hs-break` to detect layout:
215
+
216
+ ```css
217
+ .pagination-list {
218
+ --data-hs-break: "desktop";
219
+ }
220
+
221
+ @media (max-width: 768px) {
222
+ .pagination-list {
223
+ --data-hs-break: "mobile";
224
+ }
225
+ }
226
+ ```
227
+
228
+ **When breakpoint changes:**
229
+ - Re-calculates items per page
230
+ - Re-generates pagination
231
+ - Maintains current page when possible
232
+ - Updates dots accordingly
233
+
234
+ ---
235
+
236
+ ## **Key Attributes Summary**
237
+
238
+ | Attribute | Purpose | Required | Element Type |
239
+ | ----- | ----- | ----- | ----- |
240
+ | `data-hs-pagination="wrapper"` | Main container | **Required** | Wrapper div |
241
+ | `data-hs-pagination="list"` | Items to paginate | **Required** | List container |
242
+ | `data-hs-pagination="next"` | Next button wrapper | **Required** | Wrapper div with `data-site-clickable="element"` |
243
+ | `data-hs-pagination="previous"` | Previous button wrapper | **Required** | Wrapper div with `data-site-clickable="element"` |
244
+ | `data-hs-pagination="controls"` | Controls + config | Required for config | Wrapper div |
245
+ | `data-hs-pagination-show` | Desktop items per page | Required on controls | Attribute (number) |
246
+ | `data-hs-pagination-show-mobile` | Mobile items per page | Optional on controls | Attribute (number) |
247
+ | `data-hs-pagination="counter"` | Page counter | Optional | Any element |
248
+ | `data-hs-pagination="dots"` | Dots container | Optional | Wrapper div |
249
+
250
+ ---
251
+
252
+ ## **Accessibility Features**
253
+
254
+ ### **ARIA Live Regions**
255
+ - Dynamically created live region for announcements
256
+ - Announces page changes: "Page 2 of 5"
257
+ - Uses `aria-live="assertive"` and `aria-atomic="true"`
258
+ - Visually hidden with sr-only styling
259
+
260
+ ### **Button Accessibility**
261
+ - Next/Previous buttons: `aria-label` with clear descriptions
262
+ - Counter: `aria-live="polite"` for automatic announcements
263
+
264
+ ### **Focus Management**
265
+ - Active page: No `inert` attribute (focusable)
266
+ - Inactive pages: `inert` attribute prevents tab navigation
267
+ - Focus stays on controls during navigation
268
+
269
+ ### **Dot Navigation**
270
+ - Handled by separate pagination dots system
271
+ - Full keyboard support (Enter/Space)
272
+ - ARIA attributes for current page
273
+
274
+ ---
275
+
276
+ ## **How It Works**
277
+
278
+ ### **Initialization:**
279
+ 1. Finds all `[data-hs-pagination="wrapper"]` containers
280
+ 2. Reads configuration from `data-hs-pagination-show` attributes
281
+ 3. Calculates total pages based on items and config
282
+ 4. Splits items into page lists
283
+ 5. Clones pages for infinite loop (adds before/after)
284
+ 6. Positions wrapper at page 1 (middle clone)
285
+ 7. Initializes dots via pagination dots system
286
+
287
+ ### **Navigation:**
288
+ 1. User clicks next/previous or dot
289
+ 2. Updates current page index
290
+ 3. Animates wrapper with CSS transform
291
+ 4. On loop boundary, instantly resets to real page
292
+ 5. Updates counter, announces change, manages focus
293
+ 6. Auto-scrolls on mobile to keep controls visible
294
+
295
+ ### **Responsive:**
296
+ 1. ResizeObserver monitors wrapper size
297
+ 2. Detects breakpoint via CSS custom property
298
+ 3. Re-initializes pagination if breakpoint changes
299
+ 4. Maintains current page position when possible
300
+
301
+ ### **No Pagination (Infinite Mode):**
302
+ 1. Don't render controls element in HTML
303
+ 2. Pagination system won't initialize
304
+ 3. All items display naturally in list
305
+
306
+ ---
307
+
308
+ ## **Pagination Behavior**
309
+
310
+ ### **Single Page:**
311
+ - Controls and dots hidden automatically
312
+ - All items visible
313
+ - No navigation needed
314
+
315
+ ### **Multiple Pages:**
316
+ - Infinite loop (last → first, first → last)
317
+ - Clones pages at boundaries for seamless transitions
318
+ - Instant position reset after loop animation
319
+ - Smooth CSS transitions between pages
320
+
321
+ ### **Mobile Scroll:**
322
+ After page change on mobile:
323
+ - Auto-scrolls to keep controls visible
324
+ - 5rem clearance from bottom of viewport
325
+ - Smooth scroll behavior
326
+
327
+ ---
328
+
329
+ ## **Dot Navigation Integration**
330
+
331
+ The pagination system integrates with the separate pagination dots system:
332
+
333
+ **How Integration Works:**
334
+ 1. Pagination sets attributes on dots container:
335
+ - `data-hs-pagination=""` (empty, triggers dots system)
336
+ - `data-hs-pagination-total` (total pages)
337
+ - `data-hs-pagination-current` (current page)
338
+ 2. Dots system handles rendering and click events
339
+ 3. Pagination listens for `hs-pagination-change` custom event
340
+ 4. Updates are bidirectional via `hsPaginationUpdate()` API
341
+
342
+ **See:** `/autoInit/accessibility/functions/pagination/README.md` for full dots documentation
343
+
344
+ ---
345
+
346
+ ## **Configuration Examples**
347
+
348
+ ### **Desktop: 6 items, Mobile: 3 items**
349
+ ```html
350
+ <div data-hs-pagination="controls"
351
+ data-hs-pagination-show="6"
352
+ data-hs-pagination-show-mobile="3">
353
+ ```
354
+
355
+ ### **Desktop: 4 items, Mobile: 2 items**
356
+ ```html
357
+ <div data-hs-pagination="controls"
358
+ data-hs-pagination-show="4"
359
+ data-hs-pagination-show-mobile="2">
360
+ ```
361
+
362
+ ### **Same on all devices (8 items)**
363
+ ```html
364
+ <div data-hs-pagination="controls"
365
+ data-hs-pagination-show="8">
366
+ ```
367
+
368
+ ### **No pagination (infinite mode)**
369
+ ```html
370
+ <!-- Don't render controls element at all -->
371
+ ```
372
+
373
+ ---
374
+
375
+ ## **Edge Cases**
376
+
377
+ ### **No Items:**
378
+ - Early exit, no initialization
379
+ - Console warning displayed
380
+
381
+ ### **Single Page:**
382
+ - Controls hidden with `display: none` and `aria-hidden="true"`
383
+ - Dots hidden similarly
384
+ - All items displayed on single page
385
+
386
+ ### **No Pagination (Infinite Mode):**
387
+ - Don't render controls element
388
+ - Pagination system won't initialize
389
+ - List displays all items naturally
390
+ - No pagination logic runs
391
+
392
+ ### **Layout Changes:**
393
+ - ResizeObserver detects breakpoint changes
394
+ - Re-initializes pagination with new item count
395
+ - Tries to maintain current page position
396
+
397
+ ---
398
+
399
+ ## **Performance**
400
+
401
+ - Uses CSS custom properties for breakpoint detection
402
+ - ResizeObserver for efficient layout monitoring
403
+ - `inert` attribute prevents unnecessary focus handling
404
+ - Cleanup on destroy for Barba.js compatibility
405
+ - Efficient DOM cloning and manipulation
406
+ - CSS transitions (not JavaScript animations) for page changes
407
+
408
+ ---
409
+
410
+ ## **Common Issues**
411
+
412
+ **Pagination not working:**
413
+ 1. Ensure wrapper, list, next, and previous elements exist
414
+ 2. Check `data-hs-pagination-show` attribute has valid number
415
+ 3. Verify list has children elements
416
+ 4. Ensure button structure uses `data-site-clickable="element"` pattern
417
+ 5. Check console for warnings
418
+
419
+ **Items not splitting into pages:**
420
+ 1. Ensure `data-hs-pagination-show` specifies a number
421
+ 2. Check that total items > items per page
422
+ 3. Verify CSS custom property `--data-hs-break` is set
423
+
424
+ **Dots not appearing:**
425
+ 1. Ensure dots container has template children
426
+ 2. Check that there are multiple pages (>1)
427
+ 3. Verify pagination accessibility system is loaded
428
+ 4. Check that `data-hs-pagination="dots"` attribute is correct
429
+
430
+ **Layout not responsive:**
431
+ 1. Add `--data-hs-break` CSS custom property to list
432
+ 2. Ensure media query updates the property
433
+ 3. Check ResizeObserver is not being blocked
434
+
435
+ **Controls always hidden:**
436
+ 1. Ensure total items > items per page
437
+ 2. Verify controls container exists in DOM
438
+ 3. Check that `data-hs-pagination-show` is set properly
439
+
440
+ ---
441
+
442
+ ## **Notes**
443
+
444
+ - Each wrapper is an independent instance
445
+ - Pages are cloned for infinite looping
446
+ - Dots handled by separate pagination dots system
447
+ - Works with any element type for items
448
+ - Requires GSAP-less approach (uses CSS transitions)
449
+ - Barba.js compatible with proper cleanup
450
+ - Auto-initializes as part of accessibility system
451
+ - Supports any number of items
452
+ - Mobile-first responsive behavior