rails_onboarding 0.8.6 → 0.8.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1eb640c48091d5cc3028c5563bbdd22d3b5a2b2a32e3ce6079f20afb99b14cec
4
- data.tar.gz: b85af1963ce35f84da739613dc6168179ea0e548bc90d11a91e7a01be2c5c484
3
+ metadata.gz: 408fd1e294065de10f66f097028bca6ea0ef929c2da585fb5fe20034a14e0202
4
+ data.tar.gz: 2e015226988b1bda4bf5594d43c00341f4a9fb2c21c020b0607446be87dfe671
5
5
  SHA512:
6
- metadata.gz: 1f76a6f1b8d8ec877f5efe98026c2f75039d6a6102bc3eda76cbdbabb36d9926b9b2fe3f88044e0a515fcb76053eb087d7929b7fa62799acdd5ab2abafacb476
7
- data.tar.gz: b58edc5a568ad4691982422fe8a21c557d074fed14c91990fbda23ef2e8cdd9584eee8b54241ac5996f97578eeffdbdcd6250c5812d8880e2753ed644893fab8
6
+ metadata.gz: 6f924adbe61c18fc3b8070653655b04d74bd01a97562ed5eaa274d43276c07a331a11ef233ceab5484f545b20779485cbe55417921516bc9c2cd0557da694884
7
+ data.tar.gz: 678c83c71d204f027f92d65555af8cff1b3aecd53992cda79e5d234f5deffaf3a465e4b786295bc5ca2319b97a74946750e3a98083a45b8f1ad5ea4a37f3d4c1
@@ -39,6 +39,7 @@ export default class extends Controller {
39
39
 
40
40
  this.parseSteps()
41
41
  this.setupKeyboardHandlers()
42
+ this.setupViewportHandlers()
42
43
 
43
44
  // Auto-start if configured and not completed
44
45
  if (this.autoStartValue && !this.isTourCompleted()) {
@@ -188,6 +189,8 @@ export default class extends Controller {
188
189
  // Find target element
189
190
  const targetElement = step.selector ? document.querySelector(step.selector) : null
190
191
 
192
+ this.currentTargetElement = targetElement
193
+
191
194
  if (targetElement) {
192
195
  this.scrollToElement(targetElement, step)
193
196
  this.createHighlight(targetElement, step)
@@ -277,8 +280,12 @@ export default class extends Controller {
277
280
 
278
281
  document.body.appendChild(this.overlay)
279
282
 
280
- // Prevent body scroll
281
- document.body.style.overflow = 'hidden'
283
+ // Deliberately no `document.body.style.overflow = 'hidden'` here. The overlay is
284
+ // position:fixed and already covers the viewport, and locking the body made
285
+ // scrollToElement()'s window.scrollTo a no-op - so a target below the fold was
286
+ // spotlit off-screen, since the spotlight and popup are placed from
287
+ // getBoundingClientRect(). Scrolling stays live and repositionCurrentStep()
288
+ // keeps the highlight glued to its element instead.
282
289
 
283
290
  // Trigger fade in
284
291
  requestAnimationFrame(() => {
@@ -290,18 +297,14 @@ export default class extends Controller {
290
297
  * Remove modal overlay
291
298
  */
292
299
  removeOverlay() {
293
- if (!this.overlay) return
300
+ const overlay = this.overlay
301
+ if (!overlay) return
294
302
 
295
- this.overlay.style.opacity = '0'
303
+ this.overlay = null
304
+ overlay.style.opacity = '0'
296
305
 
297
306
  setTimeout(() => {
298
- if (this.overlay && this.overlay.parentNode) {
299
- this.overlay.parentNode.removeChild(this.overlay)
300
- }
301
- this.overlay = null
302
-
303
- // Restore body scroll
304
- document.body.style.overflow = ''
307
+ if (overlay.parentNode) overlay.parentNode.removeChild(overlay)
305
308
  }, 300)
306
309
  }
307
310
 
@@ -372,7 +375,11 @@ export default class extends Controller {
372
375
 
373
376
  document.body.appendChild(this.spotlight)
374
377
 
375
- // Make highlighted element interactive
378
+ // Make highlighted element interactive, remembering what we overwrote so
379
+ // removeHighlight() can put it back without searching the document.
380
+ this.highlightedElement = element
381
+ this.previousElementPosition = element.style.position
382
+ this.previousElementZIndex = element.style.zIndex
376
383
  element.style.position = 'relative'
377
384
  element.style.zIndex = '10000'
378
385
  }
@@ -388,10 +395,14 @@ export default class extends Controller {
388
395
  this.spotlight = null
389
396
  }
390
397
 
391
- // Reset z-index of previously highlighted elements
392
- document.querySelectorAll('[style*="z-index: 10000"]').forEach(el => {
393
- el.style.zIndex = ''
394
- })
398
+ // Restore only the element we actually touched. This used to sweep the whole
399
+ // document for [style*="z-index: 10000"], which clears the inline z-index of
400
+ // unrelated host-app elements that happen to carry that value.
401
+ if (this.highlightedElement) {
402
+ this.highlightedElement.style.position = this.previousElementPosition || ''
403
+ this.highlightedElement.style.zIndex = this.previousElementZIndex || ''
404
+ this.highlightedElement = null
405
+ }
395
406
  }
396
407
 
397
408
  /**
@@ -425,20 +436,20 @@ export default class extends Controller {
425
436
 
426
437
  <div class="tour-popup-actions">
427
438
  ${step.showSkip ? `
428
- <button type="button" class="tour-btn tour-btn-skip" data-action="click->tour#skip">
439
+ <button type="button" class="tour-btn tour-btn-skip">
429
440
  ${step.skipLabel}
430
441
  </button>
431
442
  ` : '<div></div>'}
432
443
 
433
444
  <div class="tour-popup-nav">
434
445
  ${step.showPrev && !isFirstStep ? `
435
- <button type="button" class="tour-btn tour-btn-prev" data-action="click->tour#previous">
446
+ <button type="button" class="tour-btn tour-btn-prev">
436
447
  ← ${step.prevLabel}
437
448
  </button>
438
449
  ` : ''}
439
450
 
440
451
  ${step.showNext ? `
441
- <button type="button" class="tour-btn tour-btn-next" data-action="click->tour#next">
452
+ <button type="button" class="tour-btn tour-btn-next">
442
453
  ${isLastStep ? step.completeLabel : step.nextLabel} →
443
454
  </button>
444
455
  ` : ''}
@@ -452,32 +463,49 @@ export default class extends Controller {
452
463
 
453
464
  document.body.appendChild(this.popup)
454
465
 
466
+ // The popup lives on document.body, so that host overflow and stacking
467
+ // contexts cannot clip it - which also puts it outside every controller
468
+ // scope, where Stimulus will not bind a data-action. These buttons are
469
+ // wired directly for that reason; a data-action on them would be silently
470
+ // inert, which is what left the tour unable to advance at all.
471
+ this.bindPopupActions()
472
+
455
473
  // Position popup relative to target
456
474
  this.positionPopup(step, targetElement)
457
475
 
458
476
  // Animate in
459
477
  requestAnimationFrame(() => {
460
- this.popup.style.opacity = '1'
461
- this.popup.style.transform = 'scale(1)'
478
+ if (this.popup) this.popup.classList.add('onboarding-show')
462
479
  })
463
480
  }
464
481
 
482
+ /**
483
+ * Wire the popup's own controls. See the note in createPopup().
484
+ */
485
+ bindPopupActions() {
486
+ const actions = {
487
+ '.tour-btn-next': () => this.next(),
488
+ '.tour-btn-prev': () => this.previous(),
489
+ '.tour-btn-skip': () => this.skip()
490
+ }
491
+
492
+ for (const [selector, handler] of Object.entries(actions)) {
493
+ this.popup.querySelector(selector)?.addEventListener('click', (event) => {
494
+ event.preventDefault()
495
+ handler()
496
+ })
497
+ }
498
+ }
499
+
465
500
  /**
466
501
  * Style the popup element
467
502
  */
468
503
  stylePopup(step) {
469
- this.popup.style.cssText = `
470
- position: fixed;
471
- z-index: 10001;
472
- background: white;
473
- border-radius: 12px;
474
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
475
- max-width: ${step.width}px;
476
- width: calc(100% - 2rem);
477
- opacity: 0;
478
- transform: scale(0.95);
479
- transition: all 0.3s ease;
480
- `
504
+ // Only the per-step width is set inline. Everything else - including the
505
+ // background - belongs to `.tour-popup` in tour.css, so that a host app
506
+ // redefining the --onboarding-* tokens (and the dark-mode block that flips
507
+ // them) actually reaches the popup. An inline background would win over both.
508
+ this.popup.style.maxWidth = `${step.width}px`
481
509
  }
482
510
 
483
511
  /**
@@ -569,17 +597,19 @@ export default class extends Controller {
569
597
  * Remove popup
570
598
  */
571
599
  removePopup() {
572
- if (this.popup) {
573
- this.popup.style.opacity = '0'
574
- this.popup.style.transform = 'scale(0.95)'
600
+ const popup = this.popup
601
+ if (!popup) return
575
602
 
576
- setTimeout(() => {
577
- if (this.popup && this.popup.parentNode) {
578
- this.popup.parentNode.removeChild(this.popup)
579
- }
580
- this.popup = null
581
- }, 300)
582
- }
603
+ // Detach from the instance up front and let the timeout close over the local.
604
+ // createPopup() calls removePopup() and then assigns a new this.popup straight
605
+ // away, so a timeout reading the instance property would remove the popup that
606
+ // replaced this one - every Next and Previous would blank the tour after 300ms.
607
+ this.popup = null
608
+ popup.classList.remove('onboarding-show')
609
+
610
+ setTimeout(() => {
611
+ if (popup.parentNode) popup.parentNode.removeChild(popup)
612
+ }, 300)
583
613
  }
584
614
 
585
615
  /**
@@ -623,6 +653,38 @@ export default class extends Controller {
623
653
  }
624
654
  }
625
655
 
656
+ /**
657
+ * Re-place the highlight and popup for the current step. The spotlight and popup
658
+ * are position:fixed and derived from getBoundingClientRect(), so they have to be
659
+ * recomputed whenever the viewport moves under them.
660
+ */
661
+ repositionCurrentStep() {
662
+ if (!this.isActive) return
663
+
664
+ const step = this.tourSteps[this.currentStepIndex]
665
+ if (!step) return
666
+
667
+ const element = this.currentTargetElement
668
+ if (element && element.isConnected) {
669
+ this.createHighlight(element, step)
670
+ }
671
+ this.positionPopup(step, element)
672
+ }
673
+
674
+ /**
675
+ * Setup viewport handlers that keep the current step aligned
676
+ */
677
+ setupViewportHandlers() {
678
+ this.viewportHandler = () => {
679
+ if (!this.isActive) return
680
+ if (this.viewportFrame) cancelAnimationFrame(this.viewportFrame)
681
+ this.viewportFrame = requestAnimationFrame(() => this.repositionCurrentStep())
682
+ }
683
+
684
+ window.addEventListener('scroll', this.viewportHandler, { passive: true })
685
+ window.addEventListener('resize', this.viewportHandler)
686
+ }
687
+
626
688
  /**
627
689
  * Setup keyboard event handlers
628
690
  */
@@ -759,5 +821,12 @@ export default class extends Controller {
759
821
  if (this.keyboardHandler) {
760
822
  document.removeEventListener('keydown', this.keyboardHandler)
761
823
  }
824
+
825
+ if (this.viewportHandler) {
826
+ window.removeEventListener('scroll', this.viewportHandler)
827
+ window.removeEventListener('resize', this.viewportHandler)
828
+ }
829
+
830
+ if (this.viewportFrame) cancelAnimationFrame(this.viewportFrame)
762
831
  }
763
832
  }
@@ -19,12 +19,46 @@
19
19
  font-size: 2.5rem;
20
20
  font-weight: 700;
21
21
  color: #2c3e50;
22
- margin: 0;
22
+ margin: 0 1rem;
23
+ }
24
+
25
+ .milestone-header-actions {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 1.5rem;
23
29
  }
24
30
 
25
31
  .milestone-stats {
26
32
  display: flex;
27
- gap: 2rem;
33
+ gap: 1rem;
34
+ }
35
+
36
+ .milestone-home-link {
37
+ display: inline-flex;
38
+ align-items: center;
39
+ gap: 0.5rem;
40
+ padding: 0.75rem 1.25rem;
41
+ border: 2px solid #e5e7eb;
42
+ border-radius: 12px;
43
+ background: white;
44
+ color: #2c3e50;
45
+ font-size: 0.9375rem;
46
+ font-weight: 600;
47
+ text-decoration: none;
48
+ white-space: nowrap;
49
+ transition: border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
50
+ }
51
+
52
+ .milestone-home-link:hover,
53
+ .milestone-home-link:focus-visible {
54
+ border-color: #667eea;
55
+ color: #667eea;
56
+ transform: translateY(-1px);
57
+ }
58
+
59
+ .milestone-home-icon {
60
+ font-size: 1.125rem;
61
+ line-height: 1;
28
62
  }
29
63
 
30
64
  .total-points,
@@ -490,6 +524,11 @@
490
524
  text-align: center;
491
525
  }
492
526
 
527
+ .milestone-header-actions {
528
+ flex-direction: column;
529
+ gap: 1rem;
530
+ }
531
+
493
532
  .milestone-stats {
494
533
  justify-content: center;
495
534
  }
@@ -39,43 +39,43 @@
39
39
 
40
40
  /* Border highlight style */
41
41
  .tour-spotlight-border {
42
- border: 3px solid #3b82f6;
43
- box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
42
+ border: 3px solid var(--onboarding-primary);
43
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
44
44
  animation: onboarding-borderPulse 2s ease-in-out infinite;
45
45
  }
46
46
 
47
47
  @keyframes onboarding-borderPulse {
48
48
  0%, 100% {
49
- border-color: #3b82f6;
50
- box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
49
+ border-color: var(--onboarding-primary);
50
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
51
51
  }
52
52
  50% {
53
- border-color: #2563eb;
54
- box-shadow: 0 0 0 6px rgba(37, 99, 235, 0.3);
53
+ border-color: var(--onboarding-primary-hover);
54
+ box-shadow: 0 0 0 6px color-mix(in srgb, var(--onboarding-primary-hover) 30%, transparent);
55
55
  }
56
56
  }
57
57
 
58
58
  /* Glow highlight style */
59
59
  .tour-spotlight-glow {
60
60
  box-shadow:
61
- 0 0 0 4px rgba(59, 130, 246, 0.3),
62
- 0 0 20px 8px rgba(59, 130, 246, 0.4),
63
- inset 0 0 20px rgba(59, 130, 246, 0.2);
61
+ 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 30%, transparent),
62
+ 0 0 20px 8px color-mix(in srgb, var(--onboarding-primary) 40%, transparent),
63
+ inset 0 0 20px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
64
64
  animation: onboarding-glowPulse 2s ease-in-out infinite;
65
65
  }
66
66
 
67
67
  @keyframes onboarding-glowPulse {
68
68
  0%, 100% {
69
69
  box-shadow:
70
- 0 0 0 4px rgba(59, 130, 246, 0.3),
71
- 0 0 20px 8px rgba(59, 130, 246, 0.4),
72
- inset 0 0 20px rgba(59, 130, 246, 0.2);
70
+ 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 30%, transparent),
71
+ 0 0 20px 8px color-mix(in srgb, var(--onboarding-primary) 40%, transparent),
72
+ inset 0 0 20px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
73
73
  }
74
74
  50% {
75
75
  box-shadow:
76
- 0 0 0 6px rgba(59, 130, 246, 0.4),
77
- 0 0 30px 12px rgba(59, 130, 246, 0.5),
78
- inset 0 0 30px rgba(59, 130, 246, 0.3);
76
+ 0 0 0 6px color-mix(in srgb, var(--onboarding-primary) 40%, transparent),
77
+ 0 0 30px 12px color-mix(in srgb, var(--onboarding-primary) 50%, transparent),
78
+ inset 0 0 30px color-mix(in srgb, var(--onboarding-primary) 30%, transparent);
79
79
  }
80
80
  }
81
81
 
@@ -83,7 +83,9 @@
83
83
  .tour-popup {
84
84
  position: fixed;
85
85
  z-index: 10001;
86
- background: white;
86
+ background: var(--onboarding-surface-elevated);
87
+ color: var(--onboarding-text);
88
+ border: 1px solid var(--onboarding-border);
87
89
  border-radius: 12px;
88
90
  box-shadow:
89
91
  0 20px 25px -5px rgba(0, 0, 0, 0.1),
@@ -110,7 +112,7 @@
110
112
  margin: 0 0 1rem;
111
113
  font-size: 1.25rem;
112
114
  font-weight: 600;
113
- color: #111827;
115
+ color: var(--onboarding-text);
114
116
  line-height: 1.4;
115
117
  }
116
118
 
@@ -119,7 +121,7 @@
119
121
  margin: 0 0 1.5rem;
120
122
  font-size: 0.9375rem;
121
123
  line-height: 1.6;
122
- color: #4b5563;
124
+ color: var(--onboarding-text-muted);
123
125
  }
124
126
 
125
127
  .tour-popup-body p {
@@ -142,12 +144,12 @@
142
144
 
143
145
  .tour-popup-body strong {
144
146
  font-weight: 600;
145
- color: #111827;
147
+ color: var(--onboarding-text);
146
148
  }
147
149
 
148
150
  .tour-popup-body code {
149
151
  padding: 0.125rem 0.375rem;
150
- background: #f3f4f6;
152
+ background: var(--onboarding-surface);
151
153
  border-radius: 0.25rem;
152
154
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
153
155
  font-size: 0.875em;
@@ -157,14 +159,14 @@
157
159
  .tour-popup-progress {
158
160
  margin: 1.5rem 0 1rem;
159
161
  padding-top: 1rem;
160
- border-top: 1px solid #e5e7eb;
162
+ border-top: 1px solid var(--onboarding-border);
161
163
  }
162
164
 
163
165
  /* Progress Bar */
164
166
  .tour-progress-bar {
165
167
  width: 100%;
166
168
  height: 6px;
167
- background: #e5e7eb;
169
+ background: var(--onboarding-border);
168
170
  border-radius: 3px;
169
171
  overflow: hidden;
170
172
  margin-bottom: 0.5rem;
@@ -172,7 +174,7 @@
172
174
 
173
175
  .tour-progress-fill {
174
176
  height: 100%;
175
- background: linear-gradient(90deg, #3b82f6, #2563eb);
177
+ background: linear-gradient(90deg, var(--onboarding-primary-fill), var(--onboarding-primary-fill-hover));
176
178
  border-radius: 3px;
177
179
  transition: width 0.3s ease;
178
180
  }
@@ -180,7 +182,7 @@
180
182
  /* Progress Text */
181
183
  .tour-progress-text {
182
184
  font-size: 0.8125rem;
183
- color: #6b7280;
185
+ color: var(--onboarding-text-muted);
184
186
  text-align: center;
185
187
  font-weight: 500;
186
188
  }
@@ -217,7 +219,7 @@
217
219
 
218
220
  .tour-btn:focus {
219
221
  outline: none;
220
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
222
+ box-shadow: var(--onboarding-focus-ring);
221
223
  }
222
224
 
223
225
  .tour-btn:active {
@@ -226,47 +228,47 @@
226
228
 
227
229
  /* Next Button - Primary */
228
230
  .tour-btn-next {
229
- background: #3b82f6;
230
- color: white;
231
+ background: var(--onboarding-primary-fill);
232
+ color: var(--onboarding-on-primary);
231
233
  padding: 0.625rem 1.25rem;
232
234
  }
233
235
 
234
236
  .tour-btn-next:hover {
235
- background: #2563eb;
237
+ background: var(--onboarding-primary-fill-hover);
236
238
  }
237
239
 
238
240
  .tour-btn-next:active {
239
- background: #1d4ed8;
241
+ background: var(--onboarding-primary-fill-hover);
240
242
  }
241
243
 
242
244
  /* Previous Button - Secondary */
243
245
  .tour-btn-prev {
244
- background: #f3f4f6;
245
- color: #374151;
246
+ background: var(--onboarding-surface);
247
+ color: var(--onboarding-text);
246
248
  }
247
249
 
248
250
  .tour-btn-prev:hover {
249
- background: #e5e7eb;
251
+ background: var(--onboarding-border);
250
252
  }
251
253
 
252
254
  .tour-btn-prev:active {
253
- background: #d1d5db;
255
+ background: var(--onboarding-border);
254
256
  }
255
257
 
256
258
  /* Skip Button - Ghost */
257
259
  .tour-btn-skip {
258
260
  background: transparent;
259
- color: #6b7280;
261
+ color: var(--onboarding-text-muted);
260
262
  padding: 0.5rem 0.75rem;
261
263
  }
262
264
 
263
265
  .tour-btn-skip:hover {
264
- color: #374151;
265
- background: #f9fafb;
266
+ color: var(--onboarding-text);
267
+ background: var(--onboarding-surface);
266
268
  }
267
269
 
268
270
  .tour-btn-skip:active {
269
- background: #f3f4f6;
271
+ background: var(--onboarding-border);
270
272
  }
271
273
 
272
274
  /* Mobile Responsive */
@@ -330,11 +332,11 @@
330
332
  }
331
333
 
332
334
  .tour-btn-next {
333
- background: #1d4ed8;
335
+ background: var(--onboarding-primary-fill-hover);
334
336
  }
335
337
 
336
338
  .tour-btn-prev {
337
- border: 2px solid #374151;
339
+ border: 2px solid var(--onboarding-border);
338
340
  }
339
341
  }
340
342
 
@@ -362,63 +364,6 @@
362
364
  }
363
365
  }
364
366
 
365
- /* Dark Mode Support */
366
- @media (prefers-color-scheme: dark) {
367
- .tour-popup {
368
- background: #1f2937;
369
- box-shadow:
370
- 0 20px 25px -5px rgba(0, 0, 0, 0.3),
371
- 0 10px 10px -5px rgba(0, 0, 0, 0.2);
372
- }
373
-
374
- .tour-popup-title {
375
- color: #f9fafb;
376
- }
377
-
378
- .tour-popup-body {
379
- color: #d1d5db;
380
- }
381
-
382
- .tour-popup-body strong {
383
- color: #f3f4f6;
384
- }
385
-
386
- .tour-popup-body code {
387
- background: #374151;
388
- color: #e5e7eb;
389
- }
390
-
391
- .tour-popup-progress {
392
- border-top-color: #374151;
393
- }
394
-
395
- .tour-progress-bar {
396
- background: #374151;
397
- }
398
-
399
- .tour-progress-text {
400
- color: #9ca3af;
401
- }
402
-
403
- .tour-btn-prev {
404
- background: #374151;
405
- color: #e5e7eb;
406
- }
407
-
408
- .tour-btn-prev:hover {
409
- background: #4b5563;
410
- }
411
-
412
- .tour-btn-skip {
413
- color: #9ca3af;
414
- }
415
-
416
- .tour-btn-skip:hover {
417
- color: #d1d5db;
418
- background: #374151;
419
- }
420
- }
421
-
422
367
  /* Print Styles - Hide tour elements when printing */
423
368
  @media print {
424
369
  .tour-overlay,
@@ -434,13 +379,13 @@
434
379
  }
435
380
 
436
381
  .tour-btn:focus-visible {
437
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
382
+ box-shadow: var(--onboarding-focus-ring);
438
383
  }
439
384
 
440
385
  /* Utility classes for custom tour content */
441
386
  .tour-highlight {
442
- background: #dbeafe;
443
- color: #1e40af;
387
+ background: color-mix(in srgb, var(--onboarding-primary-fill) 18%, transparent);
388
+ color: var(--onboarding-text);
444
389
  padding: 0.125rem 0.375rem;
445
390
  border-radius: 0.25rem;
446
391
  font-weight: 500;
@@ -6,15 +6,27 @@
6
6
  data-milestone-dashboard-milestones-path-value="<%= milestones_path %>">
7
7
  <div class="milestone-header">
8
8
  <h1 class="milestone-title">🏆 Your Achievements</h1>
9
- <div class="milestone-stats">
10
- <div class="total-points">
11
- <span class="points-value" data-milestone-dashboard-target="pointsValue"><%= @total_points %></span>
12
- <span class="points-label">Total Points</span>
13
- </div>
14
- <div class="achievement-count">
15
- <span class="count-value" data-milestone-dashboard-target="countValue"><%= @achieved_milestones.length %></span>
16
- <span class="count-label">Milestones</span>
9
+ <div class="milestone-header-actions">
10
+ <div class="milestone-stats">
11
+ <div class="total-points">
12
+ <span class="points-value" data-milestone-dashboard-target="pointsValue"><%= @total_points %></span>
13
+ <span class="points-label">Total Points</span>
14
+ </div>
15
+ <div class="achievement-count">
16
+ <span class="count-value" data-milestone-dashboard-target="countValue"><%= @achieved_milestones.length %></span>
17
+ <span class="count-label">Milestones</span>
18
+ </div>
17
19
  </div>
20
+
21
+ <%# Leaves the engine for the host app's root. Guarded because a host app
22
+ is not required to define a root route, and a missing one here would
23
+ break the whole page - same reasoning as the admin layout's Home link. %>
24
+ <% if main_app.respond_to?(:root_path) %>
25
+ <%= link_to main_app.root_path, class: "milestone-home-link" do %>
26
+ <span class="milestone-home-icon" aria-hidden="true">🏠</span>
27
+ Home
28
+ <% end %>
29
+ <% end %>
18
30
  </div>
19
31
  </div>
20
32
 
@@ -1,3 +1,3 @@
1
1
  module RailsOnboarding
2
- VERSION = "0.8.6"
2
+ VERSION = "0.8.8"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_onboarding
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.8.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Lewis
@@ -282,7 +282,7 @@ licenses:
282
282
  metadata:
283
283
  allowed_push_host: https://rubygems.org
284
284
  homepage_uri: https://github.com/bunnahabhain/rails_onboarding
285
- source_code_uri: https://github.com/bunnahabhain/rails_onboarding/tree/v0.8.6
285
+ source_code_uri: https://github.com/bunnahabhain/rails_onboarding/tree/v0.8.8
286
286
  changelog_uri: https://github.com/bunnahabhain/rails_onboarding/blob/master/docs/CHANGELOG.md
287
287
  rdoc_options: []
288
288
  require_paths:
@@ -298,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
298
  - !ruby/object:Gem::Version
299
299
  version: '0'
300
300
  requirements: []
301
- rubygems_version: 4.0.18
301
+ rubygems_version: 4.0.19
302
302
  specification_version: 4
303
303
  summary: Flexible onboarding engine for Rails applications
304
304
  test_files: []