@todovue/tv-ui 0.1.0

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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +207 -0
  3. package/dist/.htaccess +8 -0
  4. package/dist/demos/tv-alert/CHANGELOG.md +127 -0
  5. package/dist/demos/tv-alert/README.md +334 -0
  6. package/dist/demos/tv-article/CHANGELOG.md +221 -0
  7. package/dist/demos/tv-article/README.md +258 -0
  8. package/dist/demos/tv-breadcrumbs/CHANGELOG.md +135 -0
  9. package/dist/demos/tv-breadcrumbs/README.md +364 -0
  10. package/dist/demos/tv-button/CHANGELOG.md +158 -0
  11. package/dist/demos/tv-button/README.md +255 -0
  12. package/dist/demos/tv-card/CHANGELOG.md +158 -0
  13. package/dist/demos/tv-card/README.md +332 -0
  14. package/dist/demos/tv-demo/CHANGELOG.md +352 -0
  15. package/dist/demos/tv-demo/README.md +229 -0
  16. package/dist/demos/tv-footer/CHANGELOG.md +67 -0
  17. package/dist/demos/tv-footer/README.md +760 -0
  18. package/dist/demos/tv-hero/CHANGELOG.md +137 -0
  19. package/dist/demos/tv-hero/README.md +410 -0
  20. package/dist/demos/tv-label/CHANGELOG.md +138 -0
  21. package/dist/demos/tv-label/README.md +357 -0
  22. package/dist/demos/tv-menu/CHANGELOG.md +145 -0
  23. package/dist/demos/tv-menu/README.md +389 -0
  24. package/dist/demos/tv-modal/CHANGELOG.md +127 -0
  25. package/dist/demos/tv-modal/README.md +466 -0
  26. package/dist/demos/tv-pagination/CHANGELOG.md +125 -0
  27. package/dist/demos/tv-pagination/README.md +275 -0
  28. package/dist/demos/tv-progress-bar/CHANGELOG.md +84 -0
  29. package/dist/demos/tv-progress-bar/README.md +894 -0
  30. package/dist/demos/tv-relative-time/CHANGELOG.md +122 -0
  31. package/dist/demos/tv-relative-time/README.md +405 -0
  32. package/dist/demos/tv-scroll-top/CHANGELOG.md +69 -0
  33. package/dist/demos/tv-scroll-top/README.md +445 -0
  34. package/dist/demos/tv-search/CHANGELOG.md +155 -0
  35. package/dist/demos/tv-search/README.md +407 -0
  36. package/dist/demos/tv-settings/CHANGELOG.md +94 -0
  37. package/dist/demos/tv-settings/README.md +314 -0
  38. package/dist/demos/tv-sidebar/CHANGELOG.md +229 -0
  39. package/dist/demos/tv-sidebar/README.md +592 -0
  40. package/dist/demos/tv-theme-button/CHANGELOG.md +136 -0
  41. package/dist/demos/tv-theme-button/README.md +392 -0
  42. package/dist/demos/tv-toc/CHANGELOG.md +80 -0
  43. package/dist/demos/tv-toc/README.md +288 -0
  44. package/dist/entry.d.ts +48 -0
  45. package/dist/favicon.ico +0 -0
  46. package/dist/tv-ui.cjs.js +1 -0
  47. package/dist/tv-ui.css +1 -0
  48. package/dist/tv-ui.d.ts +6 -0
  49. package/dist/tv-ui.es.js +92 -0
  50. package/nuxt.js +58 -0
  51. package/package.json +92 -0
@@ -0,0 +1,894 @@
1
+ <p align="center"><img width="150" src="https://res.cloudinary.com/dcdfhi8qz/image/upload/v1763663056/uqqtkgp1lg3xdplutpga.png" alt="TODOvue logo">
2
+ </p>
3
+
4
+ # TODOvue Progress Bar (TvProgressBar)
5
+ A lightweight, customizable Vue 3 reading progress bar component that tracks scroll position through content. Features smooth animations, flexible target selection, configurable offsets, and SSR support. Works seamlessly in Single Page Apps and Server-Side Rendered (SSR) environments like Nuxt 3.
6
+
7
+ [![npm](https://img.shields.io/npm/v/@todovue/tv-progress-bar.svg)](https://www.npmjs.com/package/@todovue/tv-progress-bar)
8
+ [![npm downloads](https://img.shields.io/npm/dm/@todovue/tv-progress-bar.svg)](https://www.npmjs.com/package/@todovue/tv-progress-bar)
9
+ [![npm total downloads](https://img.shields.io/npm/dt/@todovue/tv-progress-bar.svg)](https://www.npmjs.com/package/@todovue/tv-progress-bar)
10
+ ![License](https://img.shields.io/github/license/TODOvue/tv-progress-bar)
11
+ ![Release Date](https://img.shields.io/github/release-date/TODOvue/tv-progress-bar)
12
+ ![Bundle Size](https://img.shields.io/bundlephobia/minzip/@todovue/tv-progress-bar)
13
+ ![Node Version](https://img.shields.io/node/v/@todovue/tv-progress-bar)
14
+ ![Last Commit](https://img.shields.io/github/last-commit/TODOvue/tv-progress-bar)
15
+ ![Stars](https://img.shields.io/github/stars/TODOvue/tv-progress-bar?style=social)
16
+
17
+ > Demo: https://ui.todovue.blog/progressbar
18
+
19
+ ## Table of Contents
20
+ - [Features](#features)
21
+ - [Installation](#installation)
22
+ - [Quick Start (SPA)](#quick-start-spa)
23
+ - [Nuxt 4 / SSR Usage](#nuxt-4--ssr-usage)
24
+ - [Component Registration Options](#component-registration-options)
25
+ - [Props](#props)
26
+ - [Composable API](#composable-api)
27
+ - [Usage Examples](#usage-examples)
28
+ - [Accessibility](#accessibility)
29
+ - [SSR Notes](#ssr-notes)
30
+ - [Development](#development)
31
+ - [Contributing](#contributing)
32
+ - [License](#license)
33
+
34
+ ## Features
35
+ - Real-time reading progress tracking based on scroll position
36
+ - Flexible target selection (CSS selector, element reference, or DOM element)
37
+ - Configurable height and color
38
+ - Support for Gradients: Pass multiple colors for a modern look
39
+ - Glow Effect: Optional neon glow that follows the progress bar
40
+ - Customizable Transitions: Configure duration and easing functions
41
+ - **Vertical Orientation**: Support for side progress bars (left/right)
42
+ - **Reading Checkpoints**: Display indicators at specific progress points (e.g., 25%, 50%, 75%)
43
+ - **Progress Labels**: Show percentage inside the bar or as a floating bubble
44
+ - **Flexible Positioning**: Fix the bar at the top, bottom, left, right, or use sticky behavior
45
+ - Top and bottom offset support for fixed headers/footers
46
+ - Smooth linear transitions with reduced motion support
47
+ - SSR-safe (works with Nuxt 3 and other SSR frameworks)
48
+ - Composable API (`useProgressBar`) for custom implementations
49
+ - ResizeObserver support for responsive content
50
+ - RequestAnimationFrame optimization for smooth performance
51
+ - Keyboard accessible with ARIA labels
52
+ - Lightweight and tree-shakeable
53
+ - TypeScript support
54
+
55
+ ## Installation
56
+ Using npm:
57
+ ```bash
58
+ npm install @todovue/tv-progress-bar
59
+ ```
60
+ Using yarn:
61
+ ```bash
62
+ yarn add @todovue/tv-progress-bar
63
+ ```
64
+ Using pnpm:
65
+ ```bash
66
+ pnpm add @todovue/tv-progress-bar
67
+ ```
68
+
69
+ ### Importing Styles
70
+ **Important:** You must explicitly import the stylesheet in your application.
71
+
72
+ #### For Vue/Vite SPA:
73
+ ```ts
74
+ // main.ts
75
+ import { createApp } from 'vue'
76
+ import App from './App.vue'
77
+
78
+ import '@todovue/tv-progress-bar/style.css'
79
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
80
+
81
+ const app = createApp(App)
82
+ app.component('TvProgressBar', TvProgressBar)
83
+ app.mount('#app')
84
+ ```
85
+
86
+ #### For Nuxt 3/4:
87
+ ```ts
88
+ // nuxt.config.ts
89
+ export default defineNuxtConfig({
90
+ modules: [
91
+ '@todovue/tv-progress-bar/nuxt'
92
+ ]
93
+ })
94
+ ```
95
+
96
+ Then register the component in a plugin as shown in the [Nuxt 3 / SSR Usage](#nuxt-3--ssr-usage) section.
97
+
98
+ ## Quick Start (SPA)
99
+ Global registration (main.js / main.ts):
100
+ ```js
101
+ import { createApp } from 'vue'
102
+ import App from './App.vue'
103
+ import '@todovue/tv-progress-bar/style.css'
104
+ import TvProgressBar from '@todovue/tv-progress-bar'
105
+
106
+ createApp(App)
107
+ .use(TvProgressBar) // enables <TvProgressBar /> globally
108
+ .mount('#app')
109
+ ```
110
+ Local import inside a component:
111
+ ```vue
112
+ <script setup>
113
+ import { ref } from 'vue'
114
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
115
+ import '@todovue/tv-progress-bar/style.css'
116
+
117
+ const articleContainer = ref(null)
118
+ </script>
119
+
120
+ <template>
121
+ <div>
122
+ <!-- Progress bar tracks the article container -->
123
+ <TvProgressBar :target="articleContainer" />
124
+
125
+ <!-- Your article content -->
126
+ <article ref="articleContainer">
127
+ <h1>My Article</h1>
128
+ <p>Lorem ipsum dolor sit amet...</p>
129
+ <!-- Long content here -->
130
+ </article>
131
+ </div>
132
+ </template>
133
+ ```
134
+ **Note:** Don't forget to import the CSS in your main entry file as shown above.
135
+
136
+ ## Nuxt 4 / SSR Usage
137
+ First, add the module to your `nuxt.config.ts`:
138
+ ```ts
139
+ // nuxt.config.ts
140
+ export default defineNuxtConfig({
141
+ modules: ['@todovue/tv-progress-bar/nuxt']
142
+ })
143
+ ```
144
+
145
+ Alternatively, you can manually add the CSS:
146
+ ```ts
147
+ // nuxt.config.ts
148
+ export default defineNuxtConfig({
149
+ css: ['@todovue/tv-progress-bar/style.css'],
150
+ })
151
+ ```
152
+
153
+ Then create a plugin file: `plugins/tv-progress-bar.client.ts`:
154
+ ```ts
155
+ import { defineNuxtPlugin } from '#app'
156
+ import TvProgressBar from '@todovue/tv-progress-bar'
157
+
158
+ export default defineNuxtPlugin(nuxtApp => {
159
+ nuxtApp.vueApp.use(TvProgressBar)
160
+ })
161
+ ```
162
+ Use anywhere in your Nuxt app:
163
+ ```vue
164
+ <script setup>
165
+ import { ref } from 'vue'
166
+
167
+ const mainContent = ref(null)
168
+ </script>
169
+
170
+ <template>
171
+ <div>
172
+ <TvProgressBar :target="mainContent" :offset-top="60" />
173
+
174
+ <main ref="mainContent">
175
+ <NuxtPage />
176
+ </main>
177
+ </div>
178
+ </template>
179
+ ```
180
+ Optional direct import (no plugin):
181
+ ```vue
182
+ <script setup>
183
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
184
+ </script>
185
+ ```
186
+
187
+ ## Component Registration Options
188
+ | Approach | When to use |
189
+ |------------------------------------------------------------------------------|------------------------------------------------|
190
+ | Global via `app.use(TvProgressBar)` | Many usages across app / design system install |
191
+ | Local named import `{ TvProgressBar }` | Isolated / code-split contexts |
192
+ | Direct default import `import TvProgressBar from '@todovue/tv-progress-bar'` | Single usage or manual registration |
193
+
194
+ ## Props
195
+ | Prop | Type | Default | Description |
196
+ |---------------|------------------|--------------------------------------------------|-----------------------------------------------------------------|
197
+ | target | String \| Object | '.container-blog' | CSS selector or element reference to track scroll progress. |
198
+ | offsetTop | Number | 0 | Top offset in pixels (useful for fixed headers). |
199
+ | offsetBottom | Number | 0 | Bottom offset in pixels (useful for fixed footers). |
200
+ | height | String | '4px' | Height of horizontal progress bar (CSS value). |
201
+ | width | String | '4px' | Width of vertical progress bar (CSS value). |
202
+ | zIndex | Number | 1200 | Z-index for the progress bar positioning. |
203
+ | disabled | Boolean | false | Whether the progress bar is enabled and visible. |
204
+ | color | String | '' | Custom background color for the progress bar (CSS color value). |
205
+ | gradient | Array | [] | Array of colors for a linear gradient background. |
206
+ | glow | Boolean | false | Whether to enable the glow effect. |
207
+ | glowColor | String | '' | Custom color for the glow effect. |
208
+ | duration | String | '120ms' | Transition duration (e.g., '300ms', '0.5s'). |
209
+ | easing | String | 'linear' | Transition easing function (e.g., 'ease-in-out'). |
210
+ | orientation | String | 'horizontal' | Bar orientation: 'horizontal' or 'vertical'. |
211
+ | position | String | 'top' (horiz) / 'left' (vert) | Positioning: 'top', 'bottom', 'left', 'right', or 'sticky'. |
212
+ | showLabel | Boolean | false | Whether to show the percentage label. |
213
+ | labelPosition | String | 'inside' | Label position: 'inside' or 'floating'. |
214
+ | checkpoints | Array | [] | Array of numbers (0-100) to show indicators on the bar. |
215
+
216
+ ### Prop Details
217
+
218
+ #### `target`
219
+ The element to track for reading progress. Can be:
220
+ - A CSS selector string (e.g., `'.article'`, `'#content'`)
221
+ - A template ref (e.g., `ref(null)`)
222
+ - An HTMLElement reference
223
+
224
+ Example:
225
+ ```vue
226
+ <script setup>
227
+ import { ref } from 'vue'
228
+
229
+ const articleRef = ref(null)
230
+ </script>
231
+
232
+ <template>
233
+ <!-- Using template ref -->
234
+ <TvProgressBar :target="articleRef" />
235
+ <article ref="articleRef">...</article>
236
+
237
+ <!-- Using CSS selector -->
238
+ <TvProgressBar target="#my-article" />
239
+ <article id="my-article">...</article>
240
+ </template>
241
+ ```
242
+
243
+ #### `offsetTop`
244
+ Accounts for fixed headers or navigation bars at the top of the page. The progress calculation will consider this offset.
245
+
246
+ Example:
247
+ ```vue
248
+ <!-- 60px offset for fixed header -->
249
+ <TvProgressBar :target="articleRef" :offset-top="60" />
250
+ ```
251
+
252
+ #### `offsetBottom`
253
+ Accounts for fixed footers or bottom bars. The progress calculation will consider this offset.
254
+
255
+ Example:
256
+ ```vue
257
+ <!-- 80px offset for fixed footer -->
258
+ <TvProgressBar :target="articleRef" :offset-bottom="80" />
259
+ ```
260
+
261
+ #### `height`
262
+ Controls the thickness of the progress bar. Accepts any CSS height value.
263
+
264
+ Example:
265
+ ```vue
266
+ <TvProgressBar :target="articleRef" height="8px" />
267
+ <TvProgressBar :target="articleRef" height="0.5rem" />
268
+ ```
269
+
270
+ #### `zIndex`
271
+ Controls the stacking order of the progress bar. Default is 1200 to ensure it appears above most content.
272
+
273
+ Example:
274
+ ```vue
275
+ <TvProgressBar :target="articleRef" :z-index="9999" />
276
+ ```
277
+
278
+ #### `disabled`
279
+ Allows you to conditionally enable/disable the progress bar. When disabled, the bar won't be rendered.
280
+
281
+ Example:
282
+ ```vue
283
+ <script setup>
284
+ import { ref } from 'vue'
285
+
286
+ const showProgress = ref(true)
287
+ </script>
288
+
289
+ <template>
290
+ <TvProgressBar :target="articleRef" :disabled="showProgress" />
291
+ </template>
292
+ ```
293
+
294
+ #### `color`
295
+ Custom color for the progress bar. Accepts any CSS color value. If not provided, uses the default theme color.
296
+
297
+ Example:
298
+ ```vue
299
+ <TvProgressBar :target="articleRef" color="#42b983" />
300
+ <TvProgressBar :target="articleRef" color="rgb(66, 185, 131)" />
301
+ <TvProgressBar :target="articleRef" color="var(--primary-color)" />
302
+ ```
303
+
304
+ #### `gradient`
305
+ Array of colors to create a linear gradient background. When provided, it overrides the `color` prop.
306
+
307
+ Example:
308
+ ```vue
309
+ <TvProgressBar :target="articleRef" :gradient="['#f093fb', '#f5576c']" />
310
+ <TvProgressBar :target="articleRef" :gradient="['#84fab0', '#8fd3f4']" />
311
+ ```
312
+
313
+ #### `glow`
314
+ Enables a shadow effect that follows the progress bar, giving it a depth or "neon" look.
315
+
316
+ Example:
317
+ ```vue
318
+ <TvProgressBar :target="articleRef" glow />
319
+ <TvProgressBar :target="articleRef" color="#00f2fe" glow />
320
+ ```
321
+
322
+ #### `glowColor`
323
+ Customizes the color of the glow effect. If not provided, it defaults to the `color` prop or the last color in the `gradient`.
324
+
325
+ Example:
326
+ ```vue
327
+ <TvProgressBar :target="articleRef" glow glow-color="#ff00ff" />
328
+ ```
329
+
330
+ #### `duration`
331
+ Sets the duration of the progress bar transition.
332
+
333
+ Example:
334
+ ```vue
335
+ <TvProgressBar :target="articleRef" duration="300ms" />
336
+ <TvProgressBar :target="articleRef" duration="0.5s" />
337
+ ```
338
+
339
+ #### `easing`
340
+ Sets the easing function for the progress bar transition.
341
+
342
+ Example:
343
+ ```vue
344
+ <TvProgressBar :target="articleRef" easing="ease-in-out" />
345
+ <TvProgressBar :target="articleRef" easing="cubic-bezier(0.4, 0, 0.2, 1)" />
346
+ ```
347
+
348
+ ## Composable API
349
+ TvProgressBar includes a composable `useProgressBar` that you can use to build custom progress tracking functionality.
350
+
351
+ ### `useProgressBar(targetEl, options)`
352
+ ```js
353
+ import { useProgressBar } from '@todovue/tv-progress-bar'
354
+
355
+ const targetEl = ref(null)
356
+ const { progress, progressPercent, recalculate } = useProgressBar(targetEl, {
357
+ offsetTop: computed(() => 60),
358
+ offsetBottom: computed(() => 0)
359
+ })
360
+ ```
361
+
362
+ **Parameters:**
363
+ - `targetEl` (Ref<HTMLElement>): Reactive reference to the element to track
364
+ - `options` (Object): Configuration options
365
+ - `offsetTop` (Number|Ref|ComputedRef): Top offset in pixels (default: 0)
366
+ - `offsetBottom` (Number|Ref|ComputedRef): Bottom offset in pixels (default: 0)
367
+
368
+ **Returns:**
369
+ - `progress` (Ref<Number>): Reactive number between 0 and 1 representing scroll progress
370
+ - `progressPercent` (Ref<Number>): Reactive number between 0 and 100 (rounded)
371
+ - `recalculate` (Function): Function to manually trigger a progress recalculation
372
+
373
+ **Example:**
374
+ ```vue
375
+ <script setup>
376
+ import { ref, computed } from 'vue'
377
+ import { useProgressBar } from '@todovue/tv-progress-bar'
378
+
379
+ const articleRef = ref(null)
380
+ const { progress, progressPercent, recalculate } = useProgressBar(articleRef, {
381
+ offsetTop: computed(() => 60),
382
+ offsetBottom: computed(() => 0)
383
+ })
384
+ </script>
385
+
386
+ <template>
387
+ <div>
388
+ <!-- Custom progress display -->
389
+ <div class="custom-progress">
390
+ Reading progress: {{ progressPercent }}%
391
+ </div>
392
+
393
+ <!-- Custom progress bar -->
394
+ <div class="custom-bar">
395
+ <div
396
+ class="custom-fill"
397
+ :style="{ width: `${progress * 100}%` }"
398
+ />
399
+ </div>
400
+
401
+ <article ref="articleRef">
402
+ <!-- Your content -->
403
+ </article>
404
+
405
+ <button @click="recalculate">Recalculate Progress</button>
406
+ </div>
407
+ </template>
408
+
409
+ <style scoped>
410
+ .custom-progress {
411
+ position: fixed;
412
+ top: 10px;
413
+ right: 10px;
414
+ background: rgba(0, 0, 0, 0.7);
415
+ color: white;
416
+ padding: 8px 12px;
417
+ border-radius: 4px;
418
+ font-size: 14px;
419
+ }
420
+
421
+ .custom-bar {
422
+ position: fixed;
423
+ top: 0;
424
+ left: 0;
425
+ right: 0;
426
+ height: 4px;
427
+ background: rgba(0, 0, 0, 0.1);
428
+ }
429
+
430
+ .custom-fill {
431
+ height: 100%;
432
+ background: #42b983;
433
+ transition: width 120ms linear;
434
+ }
435
+ </style>
436
+ ```
437
+
438
+ ## Usage Examples
439
+
440
+ ### Default (CSS Selector)
441
+ ```vue
442
+ <script setup>
443
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
444
+ import '@todovue/tv-progress-bar/style.css'
445
+ </script>
446
+
447
+ <template>
448
+ <div>
449
+ <TvProgressBar target=".article-content" />
450
+
451
+ <article class="article-content">
452
+ <h1>My Article</h1>
453
+ <p>Lorem ipsum dolor sit amet...</p>
454
+ <!-- Your long content -->
455
+ </article>
456
+ </div>
457
+ </template>
458
+ ```
459
+
460
+ ### Using Template Ref
461
+ ```vue
462
+ <script setup>
463
+ import { ref } from 'vue'
464
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
465
+ import '@todovue/tv-progress-bar/style.css'
466
+
467
+ const articleContainer = ref(null)
468
+ </script>
469
+
470
+ <template>
471
+ <div>
472
+ <TvProgressBar :target="articleContainer" />
473
+
474
+ <article ref="articleContainer">
475
+ <h1>My Article</h1>
476
+ <p>Lorem ipsum dolor sit amet...</p>
477
+ <!-- Your long content -->
478
+ </article>
479
+ </div>
480
+ </template>
481
+ ```
482
+
483
+ ### Custom Color
484
+ ```vue
485
+ <script setup>
486
+ import { ref } from 'vue'
487
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
488
+ import '@todovue/tv-progress-bar/style.css'
489
+
490
+ const articleContainer = ref(null)
491
+ </script>
492
+
493
+ <template>
494
+ <div>
495
+ <TvProgressBar
496
+ :target="articleContainer"
497
+ color="#42b983"
498
+ />
499
+
500
+ <article ref="articleContainer">
501
+ <h1>My Article</h1>
502
+ <p>Lorem ipsum dolor sit amet...</p>
503
+ </article>
504
+ </div>
505
+ </template>
506
+ ```
507
+
508
+ ### Custom Height
509
+ ```vue
510
+ <script setup>
511
+ import { ref } from 'vue'
512
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
513
+ import '@todovue/tv-progress-bar/style.css'
514
+
515
+ const articleContainer = ref(null)
516
+ </script>
517
+
518
+ <template>
519
+ <div>
520
+ <TvProgressBar
521
+ :target="articleContainer"
522
+ height="8px"
523
+ />
524
+
525
+ <article ref="articleContainer">
526
+ <h1>My Article</h1>
527
+ <p>Lorem ipsum dolor sit amet...</p>
528
+ </article>
529
+ </div>
530
+ </template>
531
+ ```
532
+
533
+ ### Gradient Support
534
+ ```vue
535
+ <script setup>
536
+ import { ref } from 'vue'
537
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
538
+ import '@todovue/tv-progress-bar/style.css'
539
+
540
+ const articleContainer = ref(null)
541
+ </script>
542
+
543
+ <template>
544
+ <div>
545
+ <TvProgressBar
546
+ :target="articleContainer"
547
+ :gradient="['#f093fb', '#f5576c']"
548
+ height="6px"
549
+ />
550
+
551
+ <article ref="articleContainer">
552
+ <h1>My Article with Gradient</h1>
553
+ <p>Scroll to see the gradient progress bar...</p>
554
+ </article>
555
+ </div>
556
+ </template>
557
+ ```
558
+
559
+ ### Glow Effect
560
+ ```vue
561
+ <script setup>
562
+ import { ref } from 'vue'
563
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
564
+ import '@todovue/tv-progress-bar/style.css'
565
+
566
+ const articleContainer = ref(null)
567
+ </script>
568
+
569
+ <template>
570
+ <div>
571
+ <TvProgressBar
572
+ :target="articleContainer"
573
+ color="#00f2fe"
574
+ glow
575
+ height="4px"
576
+ />
577
+
578
+ <article ref="articleContainer">
579
+ <h1>Neon Glow Progress</h1>
580
+ <p>Notice the subtle glow under the bar...</p>
581
+ </article>
582
+ </div>
583
+ </template>
584
+ ```
585
+
586
+ ### Custom Transitions
587
+ ```vue
588
+ <script setup>
589
+ import { ref } from 'vue'
590
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
591
+ import '@todovue/tv-progress-bar/style.css'
592
+
593
+ const articleContainer = ref(null)
594
+ </script>
595
+
596
+ <template>
597
+ <div>
598
+ <TvProgressBar
599
+ :target="articleContainer"
600
+ duration="800ms"
601
+ easing="cubic-bezier(0.68, -0.55, 0.265, 1.55)"
602
+ color="#3f51b5"
603
+ />
604
+
605
+ <article ref="articleContainer">
606
+ <h1>Bouncy Progress Bar</h1>
607
+ <p>Scroll fast to see the custom easing effect...</p>
608
+ </article>
609
+ </div>
610
+ </template>
611
+
612
+ ### Vertical Orientation
613
+ ```vue
614
+ <template>
615
+ <TvProgressBar
616
+ target=".content"
617
+ orientation="vertical"
618
+ position="left"
619
+ width="6px"
620
+ color="#4f46e5"
621
+ />
622
+ </template>
623
+ ```
624
+
625
+ ### Reading Checkpoints
626
+ ```vue
627
+ <template>
628
+ <TvProgressBar
629
+ target=".content"
630
+ :checkpoints="[25, 50, 75]"
631
+ color="#f59e0b"
632
+ />
633
+ </template>
634
+ ```
635
+
636
+ ### Floating Percentage Label
637
+ ```vue
638
+ <template>
639
+ <TvProgressBar
640
+ target=".content"
641
+ show-label
642
+ label-position="floating"
643
+ color="#10b981"
644
+ glow
645
+ />
646
+ </template>
647
+ ```
648
+
649
+ ### Sticky Position (Inside Container)
650
+ ```vue
651
+ <template>
652
+ <div class="relative-container">
653
+ <TvProgressBar
654
+ target=".content"
655
+ position="sticky"
656
+ color="#ec4899"
657
+ />
658
+ <div class="content">...</div>
659
+ </div>
660
+ </template>
661
+ ```
662
+ ```
663
+
664
+ ### With Fixed Header (Offset Top)
665
+ ```vue
666
+ <script setup>
667
+ import { ref } from 'vue'
668
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
669
+ import '@todovue/tv-progress-bar/style.css'
670
+
671
+ const articleContainer = ref(null)
672
+ </script>
673
+
674
+ <template>
675
+ <div>
676
+ <header style="position: fixed; top: 0; height: 60px;">
677
+ <!-- Fixed header content -->
678
+ </header>
679
+
680
+ <TvProgressBar
681
+ :target="articleContainer"
682
+ :offset-top="60"
683
+ />
684
+
685
+ <article ref="articleContainer" style="margin-top: 60px;">
686
+ <h1>My Article</h1>
687
+ <p>Lorem ipsum dolor sit amet...</p>
688
+ </article>
689
+ </div>
690
+ </template>
691
+ ```
692
+
693
+ ### With Fixed Header and Footer
694
+ ```vue
695
+ <script setup>
696
+ import { ref } from 'vue'
697
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
698
+ import '@todovue/tv-progress-bar/style.css'
699
+
700
+ const articleContainer = ref(null)
701
+ </script>
702
+
703
+ <template>
704
+ <div>
705
+ <header style="position: fixed; top: 0; height: 60px;">
706
+ <!-- Fixed header -->
707
+ </header>
708
+
709
+ <TvProgressBar
710
+ :target="articleContainer"
711
+ :offset-top="60"
712
+ :offset-bottom="80"
713
+ />
714
+
715
+ <article ref="articleContainer" style="margin: 60px 0 80px;">
716
+ <h1>My Article</h1>
717
+ <p>Lorem ipsum dolor sit amet...</p>
718
+ </article>
719
+
720
+ <footer style="position: fixed; bottom: 0; height: 80px;">
721
+ <!-- Fixed footer -->
722
+ </footer>
723
+ </div>
724
+ </template>
725
+ ```
726
+
727
+ ### Conditional Rendering
728
+ ```vue
729
+ <script setup>
730
+ import { ref } from 'vue'
731
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
732
+ import '@todovue/tv-progress-bar/style.css'
733
+
734
+ const articleContainer = ref(null)
735
+ const showProgress = ref(true)
736
+ </script>
737
+
738
+ <template>
739
+ <div>
740
+ <button @click="showProgress = !showProgress">
741
+ Toggle Progress Bar
742
+ </button>
743
+
744
+ <TvProgressBar
745
+ :target="articleContainer"
746
+ :disabled="showProgress"
747
+ />
748
+
749
+ <article ref="articleContainer">
750
+ <h1>My Article</h1>
751
+ <p>Lorem ipsum dolor sit amet...</p>
752
+ </article>
753
+ </div>
754
+ </template>
755
+ ```
756
+
757
+ ### All Props Combined
758
+ ```vue
759
+ <script setup>
760
+ import { ref } from 'vue'
761
+ import { TvProgressBar } from '@todovue/tv-progress-bar'
762
+ import '@todovue/tv-progress-bar/style.css'
763
+
764
+ const articleContainer = ref(null)
765
+ </script>
766
+
767
+ <template>
768
+ <div>
769
+ <TvProgressBar
770
+ :target="articleContainer"
771
+ :offset-top="60"
772
+ :offset-bottom="40"
773
+ height="6px"
774
+ color="#ff6b6b"
775
+ :z-index="9999"
776
+ disabled
777
+ />
778
+
779
+ <article ref="articleContainer">
780
+ <h1>My Article</h1>
781
+ <p>Lorem ipsum dolor sit amet...</p>
782
+ </article>
783
+ </div>
784
+ </template>
785
+ ```
786
+
787
+ ### Custom Implementation with Composable
788
+ ```vue
789
+ <script setup>
790
+ import { ref, computed } from 'vue'
791
+ import { useProgressBar } from '@todovue/tv-progress-bar'
792
+
793
+ const contentRef = ref(null)
794
+ const { progress, progressPercent, recalculate } = useProgressBar(contentRef, {
795
+ offsetTop: computed(() => 0),
796
+ offsetBottom: computed(() => 0)
797
+ })
798
+ </script>
799
+
800
+ <template>
801
+ <div>
802
+ <!-- Circular progress indicator -->
803
+ <div class="circular-progress">
804
+ <svg width="60" height="60">
805
+ <circle
806
+ cx="30"
807
+ cy="30"
808
+ r="25"
809
+ fill="none"
810
+ stroke="#e0e0e0"
811
+ stroke-width="5"
812
+ />
813
+ <circle
814
+ cx="30"
815
+ cy="30"
816
+ r="25"
817
+ fill="none"
818
+ stroke="#42b983"
819
+ stroke-width="5"
820
+ :stroke-dasharray="`${progress * 157} 157`"
821
+ transform="rotate(-90 30 30)"
822
+ />
823
+ </svg>
824
+ <span class="percentage">{{ progressPercent }}%</span>
825
+ </div>
826
+
827
+ <article ref="contentRef">
828
+ <h1>Custom Progress Indicator</h1>
829
+ <p>Scroll to see the circular progress...</p>
830
+ <!-- Your content -->
831
+ </article>
832
+ </div>
833
+ </template>
834
+
835
+ <style scoped>
836
+ .circular-progress {
837
+ position: fixed;
838
+ bottom: 20px;
839
+ right: 20px;
840
+ display: flex;
841
+ align-items: center;
842
+ justify-content: center;
843
+ }
844
+
845
+ .percentage {
846
+ position: absolute;
847
+ font-size: 12px;
848
+ font-weight: bold;
849
+ color: #42b983;
850
+ }
851
+ </style>
852
+ ```
853
+
854
+ ## Accessibility
855
+ - **ARIA Attributes**: Progress bar includes proper ARIA attributes:
856
+ - `role="progressbar"`
857
+ - `aria-label="Reading progress"`
858
+ - `aria-valuemin="0"`
859
+ - `aria-valuemax="100"`
860
+ - `aria-valuenow` (dynamically updated percentage)
861
+ - **Pointer Events**: Progress bar uses `pointer-events: none` to not interfere with page interactions
862
+ - **Reduced Motion**: Respects `prefers-reduced-motion` media query to disable transitions for users who prefer reduced motion
863
+ - **Semantic HTML**: Uses semantic div elements with proper ARIA roles
864
+ - **Visual Feedback**: Clear visual indication of reading progress
865
+
866
+ ## SSR Notes
867
+ - **SSR-Safe**: No direct `window`/`document` access during module evaluation
868
+ - **Smart Guards**: Uses `typeof window !== 'undefined'` checks throughout
869
+ - **Lifecycle Hooks**: Scroll listeners and observers are added in `onMounted` hook
870
+ - **Cleanup**: Automatically removes event listeners and observers in `onBeforeUnmount`
871
+ - **Nuxt 3 Compatible**: Works seamlessly with Nuxt 3 out of the box
872
+ - **Hydration Safe**: No hydration mismatches
873
+ - **Performance**: Uses `requestAnimationFrame` for smooth updates
874
+ - **ResizeObserver**: Automatically recalculates on content resize
875
+ - **Passive Listeners**: Scroll event listeners use `passive: true` for better performance
876
+
877
+ ## Development
878
+ ```bash
879
+ git clone https://github.com/TODOvue/tv-progress-bar.git
880
+ cd tv-progress-bar
881
+ npm install
882
+ npm run dev # run demo playground
883
+ npm run build # build library
884
+ ```
885
+ Local demo served from Vite using `index.html` and demo examples in `src/demo`.
886
+
887
+ ## Contributing
888
+ PRs and issues welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
889
+
890
+ ## License
891
+ MIT © TODOvue
892
+
893
+ ### Attributions
894
+ Crafted for the TODOvue component ecosystem