rails_onboarding 0.8.7 → 0.8.9

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: 735f78958744dc1965b60dbbe1fd195bcf252c179b7ef5b582462410ec1af3ea
4
- data.tar.gz: 302e224c0b4c63800c7f7eec5f49a63063dd4cbf8bd47b5d739a897d021f5d07
3
+ metadata.gz: 5de350daba589470bba3cfa3afe9daa037307873d47a888f81899b2e542172fa
4
+ data.tar.gz: 6871219f6639948ec9e66747c2e89be3b58d2260d6ef8bc2750a84900db14b1d
5
5
  SHA512:
6
- metadata.gz: dddebe786b830ba2ae4fe7cfaa0897e2eeec2b0a35b697ca9ffdf8a62df56702c4d4b86580396e40c19a9c15fa49b3fdf0ff94e5afde9c94008755a62de47123
7
- data.tar.gz: d8c1a9c239d9567f9474563d196163c3108d65cdd260a4278e41065469db5136b9faa8d72cf328a969bcea472c81856bb31ab9d9514774f63c89c28fc34a9b6b
6
+ metadata.gz: 2b6e4ccc57e00c7d41e4b21604be312330888ac2cc96e6b8ef0bc39206c529416ddc35f5d4bdc998632a6488fca0ce2a1c53e900c50115a17b08c17c2f9193f3
7
+ data.tar.gz: a4714eaed7cca529878cc9cc99ed06e0e48fa117a1cfa7f327eed5e9de97abf98cf5c35cc4ef7b51e2c8449cca5e78bf64953f9d2a8b2114487b60a9a0dc27d5
@@ -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,9 @@ 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
+ this.applyScrim(step, targetElement)
194
+
191
195
  if (targetElement) {
192
196
  this.scrollToElement(targetElement, step)
193
197
  this.createHighlight(targetElement, step)
@@ -269,7 +273,7 @@ export default class extends Controller {
269
273
  left: 0;
270
274
  width: 100%;
271
275
  height: 100%;
272
- background: rgba(0, 0, 0, ${this.overlayOpacityValue});
276
+ background: transparent;
273
277
  z-index: 9998;
274
278
  opacity: 0;
275
279
  transition: opacity 0.3s ease;
@@ -277,8 +281,12 @@ export default class extends Controller {
277
281
 
278
282
  document.body.appendChild(this.overlay)
279
283
 
280
- // Prevent body scroll
281
- document.body.style.overflow = 'hidden'
284
+ // Deliberately no `document.body.style.overflow = 'hidden'` here. The overlay is
285
+ // position:fixed and already covers the viewport, and locking the body made
286
+ // scrollToElement()'s window.scrollTo a no-op - so a target below the fold was
287
+ // spotlit off-screen, since the spotlight and popup are placed from
288
+ // getBoundingClientRect(). Scrolling stays live and repositionCurrentStep()
289
+ // keeps the highlight glued to its element instead.
282
290
 
283
291
  // Trigger fade in
284
292
  requestAnimationFrame(() => {
@@ -290,18 +298,15 @@ export default class extends Controller {
290
298
  * Remove modal overlay
291
299
  */
292
300
  removeOverlay() {
293
- if (!this.overlay) return
301
+ const overlay = this.overlay
302
+ if (!overlay) return
294
303
 
295
- this.overlay.style.opacity = '0'
304
+ document.documentElement.style.removeProperty('--onboarding-tour-scrim')
305
+ this.overlay = null
306
+ overlay.style.opacity = '0'
296
307
 
297
308
  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 = ''
309
+ if (overlay.parentNode) overlay.parentNode.removeChild(overlay)
305
310
  }, 300)
306
311
  }
307
312
 
@@ -336,7 +341,6 @@ export default class extends Controller {
336
341
  left: ${rect.left - padding}px;
337
342
  width: ${rect.width + (padding * 2)}px;
338
343
  height: ${rect.height + (padding * 2)}px;
339
- box-shadow: 0 0 0 9999px rgba(0, 0, 0, ${this.overlayOpacityValue});
340
344
  border-radius: 8px;
341
345
  `
342
346
  break
@@ -372,11 +376,39 @@ export default class extends Controller {
372
376
 
373
377
  document.body.appendChild(this.spotlight)
374
378
 
375
- // Make highlighted element interactive
379
+ // Make highlighted element interactive, remembering what we overwrote so
380
+ // removeHighlight() can put it back without searching the document.
381
+ this.highlightedElement = element
382
+ this.previousElementPosition = element.style.position
383
+ this.previousElementZIndex = element.style.zIndex
376
384
  element.style.position = 'relative'
377
385
  element.style.zIndex = '10000'
378
386
  }
379
387
 
388
+ /**
389
+ * Decide which layer paints the scrim for this step, and at what strength.
390
+ *
391
+ * A 'spotlight' highlight is already a full-viewport scrim - its box-shadow has a
392
+ * 9999px spread and covers everything but the cutout - so painting the overlay on
393
+ * top of it darkens every pixel twice. At the default 0.7 that composites to ~0.92,
394
+ * which buries the surrounding page and leaves someone unfamiliar with the layout
395
+ * with no idea what the highlight is being singled out *from*. Every other style
396
+ * (border, glow, none) draws no scrim of its own, and nor does a step with no target
397
+ * element, so those still need the overlay.
398
+ */
399
+ applyScrim(step, targetElement) {
400
+ document.documentElement.style.setProperty(
401
+ '--onboarding-tour-scrim', this.overlayOpacityValue
402
+ )
403
+
404
+ if (!this.overlay) return
405
+
406
+ const spotlit = Boolean(targetElement) && step.highlightStyle === 'spotlight'
407
+ this.overlay.style.background = spotlit
408
+ ? 'transparent'
409
+ : `rgba(0, 0, 0, ${this.overlayOpacityValue})`
410
+ }
411
+
380
412
  /**
381
413
  * Remove highlight/spotlight
382
414
  */
@@ -388,10 +420,14 @@ export default class extends Controller {
388
420
  this.spotlight = null
389
421
  }
390
422
 
391
- // Reset z-index of previously highlighted elements
392
- document.querySelectorAll('[style*="z-index: 10000"]').forEach(el => {
393
- el.style.zIndex = ''
394
- })
423
+ // Restore only the element we actually touched. This used to sweep the whole
424
+ // document for [style*="z-index: 10000"], which clears the inline z-index of
425
+ // unrelated host-app elements that happen to carry that value.
426
+ if (this.highlightedElement) {
427
+ this.highlightedElement.style.position = this.previousElementPosition || ''
428
+ this.highlightedElement.style.zIndex = this.previousElementZIndex || ''
429
+ this.highlightedElement = null
430
+ }
395
431
  }
396
432
 
397
433
  /**
@@ -425,20 +461,20 @@ export default class extends Controller {
425
461
 
426
462
  <div class="tour-popup-actions">
427
463
  ${step.showSkip ? `
428
- <button type="button" class="tour-btn tour-btn-skip" data-action="click->tour#skip">
464
+ <button type="button" class="tour-btn tour-btn-skip">
429
465
  ${step.skipLabel}
430
466
  </button>
431
467
  ` : '<div></div>'}
432
468
 
433
469
  <div class="tour-popup-nav">
434
470
  ${step.showPrev && !isFirstStep ? `
435
- <button type="button" class="tour-btn tour-btn-prev" data-action="click->tour#previous">
471
+ <button type="button" class="tour-btn tour-btn-prev">
436
472
  ← ${step.prevLabel}
437
473
  </button>
438
474
  ` : ''}
439
475
 
440
476
  ${step.showNext ? `
441
- <button type="button" class="tour-btn tour-btn-next" data-action="click->tour#next">
477
+ <button type="button" class="tour-btn tour-btn-next">
442
478
  ${isLastStep ? step.completeLabel : step.nextLabel} →
443
479
  </button>
444
480
  ` : ''}
@@ -452,32 +488,49 @@ export default class extends Controller {
452
488
 
453
489
  document.body.appendChild(this.popup)
454
490
 
491
+ // The popup lives on document.body, so that host overflow and stacking
492
+ // contexts cannot clip it - which also puts it outside every controller
493
+ // scope, where Stimulus will not bind a data-action. These buttons are
494
+ // wired directly for that reason; a data-action on them would be silently
495
+ // inert, which is what left the tour unable to advance at all.
496
+ this.bindPopupActions()
497
+
455
498
  // Position popup relative to target
456
499
  this.positionPopup(step, targetElement)
457
500
 
458
501
  // Animate in
459
502
  requestAnimationFrame(() => {
460
- this.popup.style.opacity = '1'
461
- this.popup.style.transform = 'scale(1)'
503
+ if (this.popup) this.popup.classList.add('onboarding-show')
462
504
  })
463
505
  }
464
506
 
507
+ /**
508
+ * Wire the popup's own controls. See the note in createPopup().
509
+ */
510
+ bindPopupActions() {
511
+ const actions = {
512
+ '.tour-btn-next': () => this.next(),
513
+ '.tour-btn-prev': () => this.previous(),
514
+ '.tour-btn-skip': () => this.skip()
515
+ }
516
+
517
+ for (const [selector, handler] of Object.entries(actions)) {
518
+ this.popup.querySelector(selector)?.addEventListener('click', (event) => {
519
+ event.preventDefault()
520
+ handler()
521
+ })
522
+ }
523
+ }
524
+
465
525
  /**
466
526
  * Style the popup element
467
527
  */
468
528
  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
- `
529
+ // Only the per-step width is set inline. Everything else - including the
530
+ // background - belongs to `.tour-popup` in tour.css, so that a host app
531
+ // redefining the --onboarding-* tokens (and the dark-mode block that flips
532
+ // them) actually reaches the popup. An inline background would win over both.
533
+ this.popup.style.maxWidth = `${step.width}px`
481
534
  }
482
535
 
483
536
  /**
@@ -569,17 +622,19 @@ export default class extends Controller {
569
622
  * Remove popup
570
623
  */
571
624
  removePopup() {
572
- if (this.popup) {
573
- this.popup.style.opacity = '0'
574
- this.popup.style.transform = 'scale(0.95)'
625
+ const popup = this.popup
626
+ if (!popup) return
575
627
 
576
- setTimeout(() => {
577
- if (this.popup && this.popup.parentNode) {
578
- this.popup.parentNode.removeChild(this.popup)
579
- }
580
- this.popup = null
581
- }, 300)
582
- }
628
+ // Detach from the instance up front and let the timeout close over the local.
629
+ // createPopup() calls removePopup() and then assigns a new this.popup straight
630
+ // away, so a timeout reading the instance property would remove the popup that
631
+ // replaced this one - every Next and Previous would blank the tour after 300ms.
632
+ this.popup = null
633
+ popup.classList.remove('onboarding-show')
634
+
635
+ setTimeout(() => {
636
+ if (popup.parentNode) popup.parentNode.removeChild(popup)
637
+ }, 300)
583
638
  }
584
639
 
585
640
  /**
@@ -623,6 +678,38 @@ export default class extends Controller {
623
678
  }
624
679
  }
625
680
 
681
+ /**
682
+ * Re-place the highlight and popup for the current step. The spotlight and popup
683
+ * are position:fixed and derived from getBoundingClientRect(), so they have to be
684
+ * recomputed whenever the viewport moves under them.
685
+ */
686
+ repositionCurrentStep() {
687
+ if (!this.isActive) return
688
+
689
+ const step = this.tourSteps[this.currentStepIndex]
690
+ if (!step) return
691
+
692
+ const element = this.currentTargetElement
693
+ if (element && element.isConnected) {
694
+ this.createHighlight(element, step)
695
+ }
696
+ this.positionPopup(step, element)
697
+ }
698
+
699
+ /**
700
+ * Setup viewport handlers that keep the current step aligned
701
+ */
702
+ setupViewportHandlers() {
703
+ this.viewportHandler = () => {
704
+ if (!this.isActive) return
705
+ if (this.viewportFrame) cancelAnimationFrame(this.viewportFrame)
706
+ this.viewportFrame = requestAnimationFrame(() => this.repositionCurrentStep())
707
+ }
708
+
709
+ window.addEventListener('scroll', this.viewportHandler, { passive: true })
710
+ window.addEventListener('resize', this.viewportHandler)
711
+ }
712
+
626
713
  /**
627
714
  * Setup keyboard event handlers
628
715
  */
@@ -759,5 +846,12 @@ export default class extends Controller {
759
846
  if (this.keyboardHandler) {
760
847
  document.removeEventListener('keydown', this.keyboardHandler)
761
848
  }
849
+
850
+ if (this.viewportHandler) {
851
+ window.removeEventListener('scroll', this.viewportHandler)
852
+ window.removeEventListener('resize', this.viewportHandler)
853
+ }
854
+
855
+ if (this.viewportFrame) cancelAnimationFrame(this.viewportFrame)
762
856
  }
763
857
  }
@@ -22,60 +22,56 @@
22
22
  border-radius: 8px;
23
23
  }
24
24
 
25
- /* Spotlight style - Creates cutout effect */
25
+ /* Spotlight style - Creates cutout effect.
26
+ The 9999px spread makes this box-shadow a full-viewport scrim in its own right, so
27
+ tour_controller stops painting .tour-overlay whenever a spotlight is drawn - the two
28
+ together composited to ~0.92 and buried the surrounding page.
29
+ The alpha comes from --onboarding-tour-scrim, which the controller sets on :root from
30
+ its overlayOpacity value. It is deliberately not animated: an animation on this
31
+ property overrides the inline style, which is what made overlayOpacity a dead knob. */
26
32
  .tour-spotlight-spotlight {
27
- box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7);
28
- animation: onboarding-spotlightPulse 2s ease-in-out infinite;
29
- }
30
-
31
- @keyframes onboarding-spotlightPulse {
32
- 0%, 100% {
33
- box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7);
34
- }
35
- 50% {
36
- box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.75);
37
- }
33
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, var(--onboarding-tour-scrim, 0.7));
38
34
  }
39
35
 
40
36
  /* Border highlight style */
41
37
  .tour-spotlight-border {
42
- border: 3px solid #3b82f6;
43
- box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
38
+ border: 3px solid var(--onboarding-primary);
39
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
44
40
  animation: onboarding-borderPulse 2s ease-in-out infinite;
45
41
  }
46
42
 
47
43
  @keyframes onboarding-borderPulse {
48
44
  0%, 100% {
49
- border-color: #3b82f6;
50
- box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
45
+ border-color: var(--onboarding-primary);
46
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
51
47
  }
52
48
  50% {
53
- border-color: #2563eb;
54
- box-shadow: 0 0 0 6px rgba(37, 99, 235, 0.3);
49
+ border-color: var(--onboarding-primary-hover);
50
+ box-shadow: 0 0 0 6px color-mix(in srgb, var(--onboarding-primary-hover) 30%, transparent);
55
51
  }
56
52
  }
57
53
 
58
54
  /* Glow highlight style */
59
55
  .tour-spotlight-glow {
60
56
  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);
57
+ 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 30%, transparent),
58
+ 0 0 20px 8px color-mix(in srgb, var(--onboarding-primary) 40%, transparent),
59
+ inset 0 0 20px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
64
60
  animation: onboarding-glowPulse 2s ease-in-out infinite;
65
61
  }
66
62
 
67
63
  @keyframes onboarding-glowPulse {
68
64
  0%, 100% {
69
65
  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);
66
+ 0 0 0 4px color-mix(in srgb, var(--onboarding-primary) 30%, transparent),
67
+ 0 0 20px 8px color-mix(in srgb, var(--onboarding-primary) 40%, transparent),
68
+ inset 0 0 20px color-mix(in srgb, var(--onboarding-primary) 20%, transparent);
73
69
  }
74
70
  50% {
75
71
  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);
72
+ 0 0 0 6px color-mix(in srgb, var(--onboarding-primary) 40%, transparent),
73
+ 0 0 30px 12px color-mix(in srgb, var(--onboarding-primary) 50%, transparent),
74
+ inset 0 0 30px color-mix(in srgb, var(--onboarding-primary) 30%, transparent);
79
75
  }
80
76
  }
81
77
 
@@ -83,7 +79,9 @@
83
79
  .tour-popup {
84
80
  position: fixed;
85
81
  z-index: 10001;
86
- background: white;
82
+ background: var(--onboarding-surface-elevated);
83
+ color: var(--onboarding-text);
84
+ border: 1px solid var(--onboarding-border);
87
85
  border-radius: 12px;
88
86
  box-shadow:
89
87
  0 20px 25px -5px rgba(0, 0, 0, 0.1),
@@ -110,7 +108,7 @@
110
108
  margin: 0 0 1rem;
111
109
  font-size: 1.25rem;
112
110
  font-weight: 600;
113
- color: #111827;
111
+ color: var(--onboarding-text);
114
112
  line-height: 1.4;
115
113
  }
116
114
 
@@ -119,7 +117,7 @@
119
117
  margin: 0 0 1.5rem;
120
118
  font-size: 0.9375rem;
121
119
  line-height: 1.6;
122
- color: #4b5563;
120
+ color: var(--onboarding-text-muted);
123
121
  }
124
122
 
125
123
  .tour-popup-body p {
@@ -142,12 +140,12 @@
142
140
 
143
141
  .tour-popup-body strong {
144
142
  font-weight: 600;
145
- color: #111827;
143
+ color: var(--onboarding-text);
146
144
  }
147
145
 
148
146
  .tour-popup-body code {
149
147
  padding: 0.125rem 0.375rem;
150
- background: #f3f4f6;
148
+ background: var(--onboarding-surface);
151
149
  border-radius: 0.25rem;
152
150
  font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
153
151
  font-size: 0.875em;
@@ -157,14 +155,14 @@
157
155
  .tour-popup-progress {
158
156
  margin: 1.5rem 0 1rem;
159
157
  padding-top: 1rem;
160
- border-top: 1px solid #e5e7eb;
158
+ border-top: 1px solid var(--onboarding-border);
161
159
  }
162
160
 
163
161
  /* Progress Bar */
164
162
  .tour-progress-bar {
165
163
  width: 100%;
166
164
  height: 6px;
167
- background: #e5e7eb;
165
+ background: var(--onboarding-border);
168
166
  border-radius: 3px;
169
167
  overflow: hidden;
170
168
  margin-bottom: 0.5rem;
@@ -172,7 +170,7 @@
172
170
 
173
171
  .tour-progress-fill {
174
172
  height: 100%;
175
- background: linear-gradient(90deg, #3b82f6, #2563eb);
173
+ background: linear-gradient(90deg, var(--onboarding-primary-fill), var(--onboarding-primary-fill-hover));
176
174
  border-radius: 3px;
177
175
  transition: width 0.3s ease;
178
176
  }
@@ -180,7 +178,7 @@
180
178
  /* Progress Text */
181
179
  .tour-progress-text {
182
180
  font-size: 0.8125rem;
183
- color: #6b7280;
181
+ color: var(--onboarding-text-muted);
184
182
  text-align: center;
185
183
  font-weight: 500;
186
184
  }
@@ -217,7 +215,7 @@
217
215
 
218
216
  .tour-btn:focus {
219
217
  outline: none;
220
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
218
+ box-shadow: var(--onboarding-focus-ring);
221
219
  }
222
220
 
223
221
  .tour-btn:active {
@@ -226,47 +224,47 @@
226
224
 
227
225
  /* Next Button - Primary */
228
226
  .tour-btn-next {
229
- background: #3b82f6;
230
- color: white;
227
+ background: var(--onboarding-primary-fill);
228
+ color: var(--onboarding-on-primary);
231
229
  padding: 0.625rem 1.25rem;
232
230
  }
233
231
 
234
232
  .tour-btn-next:hover {
235
- background: #2563eb;
233
+ background: var(--onboarding-primary-fill-hover);
236
234
  }
237
235
 
238
236
  .tour-btn-next:active {
239
- background: #1d4ed8;
237
+ background: var(--onboarding-primary-fill-hover);
240
238
  }
241
239
 
242
240
  /* Previous Button - Secondary */
243
241
  .tour-btn-prev {
244
- background: #f3f4f6;
245
- color: #374151;
242
+ background: var(--onboarding-surface);
243
+ color: var(--onboarding-text);
246
244
  }
247
245
 
248
246
  .tour-btn-prev:hover {
249
- background: #e5e7eb;
247
+ background: var(--onboarding-border);
250
248
  }
251
249
 
252
250
  .tour-btn-prev:active {
253
- background: #d1d5db;
251
+ background: var(--onboarding-border);
254
252
  }
255
253
 
256
254
  /* Skip Button - Ghost */
257
255
  .tour-btn-skip {
258
256
  background: transparent;
259
- color: #6b7280;
257
+ color: var(--onboarding-text-muted);
260
258
  padding: 0.5rem 0.75rem;
261
259
  }
262
260
 
263
261
  .tour-btn-skip:hover {
264
- color: #374151;
265
- background: #f9fafb;
262
+ color: var(--onboarding-text);
263
+ background: var(--onboarding-surface);
266
264
  }
267
265
 
268
266
  .tour-btn-skip:active {
269
- background: #f3f4f6;
267
+ background: var(--onboarding-border);
270
268
  }
271
269
 
272
270
  /* Mobile Responsive */
@@ -325,16 +323,20 @@
325
323
  background: rgba(0, 0, 0, 0.85);
326
324
  }
327
325
 
326
+ .tour-spotlight-spotlight {
327
+ --onboarding-tour-scrim: 0.85;
328
+ }
329
+
328
330
  .tour-spotlight-border {
329
331
  border-width: 4px;
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
 
@@ -349,10 +351,6 @@
349
351
  animation: none;
350
352
  }
351
353
 
352
- .tour-spotlight-spotlight {
353
- animation: none;
354
- }
355
-
356
354
  .tour-spotlight-border {
357
355
  animation: none;
358
356
  }
@@ -362,63 +360,6 @@
362
360
  }
363
361
  }
364
362
 
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
363
  /* Print Styles - Hide tour elements when printing */
423
364
  @media print {
424
365
  .tour-overlay,
@@ -434,13 +375,13 @@
434
375
  }
435
376
 
436
377
  .tour-btn:focus-visible {
437
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
378
+ box-shadow: var(--onboarding-focus-ring);
438
379
  }
439
380
 
440
381
  /* Utility classes for custom tour content */
441
382
  .tour-highlight {
442
- background: #dbeafe;
443
- color: #1e40af;
383
+ background: color-mix(in srgb, var(--onboarding-primary-fill) 18%, transparent);
384
+ color: var(--onboarding-text);
444
385
  padding: 0.125rem 0.375rem;
445
386
  border-radius: 0.25rem;
446
387
  font-weight: 500;
@@ -1,3 +1,3 @@
1
1
  module RailsOnboarding
2
- VERSION = "0.8.7"
2
+ VERSION = "0.8.9"
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.7
4
+ version: 0.8.9
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.7
285
+ source_code_uri: https://github.com/bunnahabhain/rails_onboarding/tree/v0.8.9
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: []