rails_onboarding 0.6.1

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 (148) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1351 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/javascripts/rails_onboarding/admin/chart_controller.js +25 -0
  6. data/app/assets/javascripts/rails_onboarding/admin/filter_controller.js +49 -0
  7. data/app/assets/javascripts/rails_onboarding/admin/flash_controller.js +21 -0
  8. data/app/assets/javascripts/rails_onboarding/admin/flow_editor_controller.js +86 -0
  9. data/app/assets/javascripts/rails_onboarding/application.js +231 -0
  10. data/app/assets/javascripts/rails_onboarding/milestone_celebration_controller.js +177 -0
  11. data/app/assets/javascripts/rails_onboarding/milestone_dashboard_controller.js +132 -0
  12. data/app/assets/javascripts/rails_onboarding/milestone_detail_controller.js +34 -0
  13. data/app/assets/javascripts/rails_onboarding/navigation_controller.js +277 -0
  14. data/app/assets/javascripts/rails_onboarding/onboarding_controller.js +326 -0
  15. data/app/assets/javascripts/rails_onboarding/progress_controller.js +203 -0
  16. data/app/assets/javascripts/rails_onboarding/progressive_disclosure_controller.js +218 -0
  17. data/app/assets/javascripts/rails_onboarding/tooltip_controller.js +1237 -0
  18. data/app/assets/javascripts/rails_onboarding/tooltip_scheduler_controller.js +511 -0
  19. data/app/assets/javascripts/rails_onboarding/tour_controller.js +763 -0
  20. data/app/assets/stylesheets/rails_onboarding/accessibility.css +536 -0
  21. data/app/assets/stylesheets/rails_onboarding/admin.css +816 -0
  22. data/app/assets/stylesheets/rails_onboarding/application.css +1145 -0
  23. data/app/assets/stylesheets/rails_onboarding/flash_messages.css +237 -0
  24. data/app/assets/stylesheets/rails_onboarding/milestones.css +545 -0
  25. data/app/assets/stylesheets/rails_onboarding/mobile.css +856 -0
  26. data/app/assets/stylesheets/rails_onboarding/progressive_disclosure.css +299 -0
  27. data/app/assets/stylesheets/rails_onboarding/tooltips.css +468 -0
  28. data/app/assets/stylesheets/rails_onboarding/tour.css +495 -0
  29. data/app/assets/stylesheets/rails_onboarding/utilities.css +393 -0
  30. data/app/controllers/concerns/rails_onboarding/admin_authorization.rb +98 -0
  31. data/app/controllers/concerns/rails_onboarding/rate_limitable.rb +104 -0
  32. data/app/controllers/rails_onboarding/ab_tests_controller.rb +257 -0
  33. data/app/controllers/rails_onboarding/admin/ab_tests_controller.rb +121 -0
  34. data/app/controllers/rails_onboarding/admin/base_controller.rb +68 -0
  35. data/app/controllers/rails_onboarding/admin/dashboard_controller.rb +189 -0
  36. data/app/controllers/rails_onboarding/admin/flows_controller.rb +175 -0
  37. data/app/controllers/rails_onboarding/admin/users_controller.rb +366 -0
  38. data/app/controllers/rails_onboarding/application_controller.rb +9 -0
  39. data/app/controllers/rails_onboarding/milestones_controller.rb +126 -0
  40. data/app/controllers/rails_onboarding/onboarding_controller.rb +409 -0
  41. data/app/controllers/rails_onboarding/progressive_features_controller.rb +134 -0
  42. data/app/controllers/rails_onboarding/templates_controller.rb +162 -0
  43. data/app/controllers/rails_onboarding/test_sessions_controller.rb +16 -0
  44. data/app/controllers/rails_onboarding/tooltips_controller.rb +92 -0
  45. data/app/helpers/rails_onboarding/application_helper.rb +4 -0
  46. data/app/helpers/rails_onboarding/i18n_helper.rb +137 -0
  47. data/app/helpers/rails_onboarding/personalization_helper.rb +184 -0
  48. data/app/helpers/rails_onboarding/templates_helper.rb +253 -0
  49. data/app/jobs/rails_onboarding/application_job.rb +4 -0
  50. data/app/mailers/rails_onboarding/application_mailer.rb +6 -0
  51. data/app/mailers/rails_onboarding/onboarding_mailer.rb +72 -0
  52. data/app/models/concerns/rails_onboarding/ab_testable.rb +149 -0
  53. data/app/models/concerns/rails_onboarding/onboardable.rb +649 -0
  54. data/app/models/concerns/rails_onboarding/personalizable.rb +211 -0
  55. data/app/models/concerns/rails_onboarding/progressive_disclosure.rb +276 -0
  56. data/app/models/rails_onboarding/analytics.rb +255 -0
  57. data/app/models/rails_onboarding/analytics_event.rb +168 -0
  58. data/app/models/rails_onboarding/application_record.rb +5 -0
  59. data/app/models/rails_onboarding/flow.rb +41 -0
  60. data/app/services/rails_onboarding/milestone_service.rb +105 -0
  61. data/app/views/layouts/rails_onboarding/admin.html.erb +106 -0
  62. data/app/views/layouts/rails_onboarding/application.html.erb +65 -0
  63. data/app/views/rails_onboarding/ab_tests/index.html.erb +209 -0
  64. data/app/views/rails_onboarding/ab_tests/results.html.erb +253 -0
  65. data/app/views/rails_onboarding/admin/ab_tests/_ab_test_card.html.erb +24 -0
  66. data/app/views/rails_onboarding/admin/ab_tests/index.html.erb +57 -0
  67. data/app/views/rails_onboarding/admin/ab_tests/show.html.erb +41 -0
  68. data/app/views/rails_onboarding/admin/dashboard/index.html.erb +224 -0
  69. data/app/views/rails_onboarding/admin/flows/_form.html.erb +147 -0
  70. data/app/views/rails_onboarding/admin/flows/edit.html.erb +7 -0
  71. data/app/views/rails_onboarding/admin/flows/index.html.erb +125 -0
  72. data/app/views/rails_onboarding/admin/flows/new.html.erb +7 -0
  73. data/app/views/rails_onboarding/admin/flows/preview.html.erb +40 -0
  74. data/app/views/rails_onboarding/admin/flows/show.html.erb +114 -0
  75. data/app/views/rails_onboarding/admin/users/index.html.erb +180 -0
  76. data/app/views/rails_onboarding/admin/users/show.html.erb +187 -0
  77. data/app/views/rails_onboarding/milestones/index.html.erb +64 -0
  78. data/app/views/rails_onboarding/milestones/show.html.erb +57 -0
  79. data/app/views/rails_onboarding/onboarding/_custom_footer.html.erb +2 -0
  80. data/app/views/rails_onboarding/onboarding/_progress_indicator.html.erb +56 -0
  81. data/app/views/rails_onboarding/onboarding/_step_navigation.html.erb +44 -0
  82. data/app/views/rails_onboarding/onboarding/step.html.erb +38 -0
  83. data/app/views/rails_onboarding/onboarding/welcome.html.erb +37 -0
  84. data/app/views/rails_onboarding/onboarding_mailer/completion_email.html.erb +109 -0
  85. data/app/views/rails_onboarding/onboarding_mailer/completion_email.text.erb +29 -0
  86. data/app/views/rails_onboarding/onboarding_mailer/reminder_email.html.erb +99 -0
  87. data/app/views/rails_onboarding/onboarding_mailer/reminder_email.text.erb +20 -0
  88. data/app/views/rails_onboarding/onboarding_mailer/step_completed_email.html.erb +105 -0
  89. data/app/views/rails_onboarding/onboarding_mailer/step_completed_email.text.erb +19 -0
  90. data/app/views/rails_onboarding/onboarding_mailer/welcome_email.html.erb +74 -0
  91. data/app/views/rails_onboarding/onboarding_mailer/welcome_email.text.erb +17 -0
  92. data/app/views/rails_onboarding/shared/_flash.html.erb +25 -0
  93. data/app/views/rails_onboarding/shared/_milestone_badge.html.erb +22 -0
  94. data/app/views/rails_onboarding/shared/_milestone_celebration.html.erb +44 -0
  95. data/app/views/rails_onboarding/shared/_onboarding_banner.html.erb +48 -0
  96. data/app/views/rails_onboarding/templates/index.html.erb +255 -0
  97. data/config/importmap.rb +33 -0
  98. data/config/locales/en.yml +62 -0
  99. data/config/locales/es.yml +62 -0
  100. data/config/locales/fr.yml +62 -0
  101. data/config/routes.rb +79 -0
  102. data/lib/generators/rails_onboarding/install_generator.rb +124 -0
  103. data/lib/generators/rails_onboarding/templates/README +120 -0
  104. data/lib/generators/rails_onboarding/templates/add_analytics_to_rails_onboarding.rb +33 -0
  105. data/lib/generators/rails_onboarding/templates/add_milestone_tracking_to_users.rb +38 -0
  106. data/lib/generators/rails_onboarding/templates/add_onboarding_indexes.rb +93 -0
  107. data/lib/generators/rails_onboarding/templates/add_onboarding_to_users.rb +51 -0
  108. data/lib/generators/rails_onboarding/templates/add_robustness_fields_to_users.rb.tt +20 -0
  109. data/lib/generators/rails_onboarding/templates/create_rails_onboarding_flows.rb +18 -0
  110. data/lib/generators/rails_onboarding/templates/onboarding.css +18 -0
  111. data/lib/generators/rails_onboarding/templates/rails_onboarding.rb +75 -0
  112. data/lib/generators/rails_onboarding/update_generator.rb +73 -0
  113. data/lib/rails_onboarding/api_mode.rb +367 -0
  114. data/lib/rails_onboarding/backfill.rb +130 -0
  115. data/lib/rails_onboarding/background_jobs.rb +412 -0
  116. data/lib/rails_onboarding/caching.rb +180 -0
  117. data/lib/rails_onboarding/cdn_support.rb +202 -0
  118. data/lib/rails_onboarding/configuration/ab_testing.rb +24 -0
  119. data/lib/rails_onboarding/configuration/analytics.rb +29 -0
  120. data/lib/rails_onboarding/configuration/integrations.rb +31 -0
  121. data/lib/rails_onboarding/configuration/milestones.rb +92 -0
  122. data/lib/rails_onboarding/configuration/personalization.rb +25 -0
  123. data/lib/rails_onboarding/configuration/progressive_disclosure.rb +41 -0
  124. data/lib/rails_onboarding/configuration/rate_limiting.rb +18 -0
  125. data/lib/rails_onboarding/configuration/steps.rb +121 -0
  126. data/lib/rails_onboarding/configuration/templates.rb +85 -0
  127. data/lib/rails_onboarding/configuration/tooltips.rb +32 -0
  128. data/lib/rails_onboarding/configuration.rb +133 -0
  129. data/lib/rails_onboarding/configuration_errors.rb +19 -0
  130. data/lib/rails_onboarding/configuration_validator.rb +477 -0
  131. data/lib/rails_onboarding/controller_helpers.rb +153 -0
  132. data/lib/rails_onboarding/current.rb +13 -0
  133. data/lib/rails_onboarding/deprecation.rb +64 -0
  134. data/lib/rails_onboarding/devise_integration.rb +122 -0
  135. data/lib/rails_onboarding/engine.rb +245 -0
  136. data/lib/rails_onboarding/error_recovery.rb +173 -0
  137. data/lib/rails_onboarding/lazy_loading.rb +155 -0
  138. data/lib/rails_onboarding/multi_tenant.rb +306 -0
  139. data/lib/rails_onboarding/requirements_validator.rb +315 -0
  140. data/lib/rails_onboarding/responsive_helper.rb +218 -0
  141. data/lib/rails_onboarding/session_manager.rb +187 -0
  142. data/lib/rails_onboarding/skip_logic.rb +159 -0
  143. data/lib/rails_onboarding/turbo_compatibility.rb +229 -0
  144. data/lib/rails_onboarding/version.rb +3 -0
  145. data/lib/rails_onboarding.rb +44 -0
  146. data/lib/tasks/rails_onboarding_analytics.rake +136 -0
  147. data/lib/tasks/rails_onboarding_tasks.rake +162 -0
  148. metadata +303 -0
@@ -0,0 +1,536 @@
1
+ /* ======================
2
+ Accessibility Enhancements
3
+ ======================
4
+
5
+ Scoping: every rule here is confined to markup the engine owns, via
6
+ `:where(.onboarding-container)` (engine pages) or `:where(.onboarding-banner)`
7
+ (chrome rendered on host-app pages). `:where()` carries zero specificity, so
8
+ scoping a rule does not change how it competes in the cascade with the gem's
9
+ other stylesheets - mobile.css's media-query overrides still win the same ties
10
+ they won before.
11
+
12
+ Bare element selectors (`a`, `table`, `fieldset`, `[role="dialog"]`, ...) must
13
+ never appear unscoped in this file. Host apps bundle this stylesheet into
14
+ their global CSS, so an unscoped `a { text-decoration: underline }` restyles
15
+ every link in the host application.
16
+
17
+ Utility classes meant to be used outside the container (screen-reader text,
18
+ skip links) are namespaced with `onboarding-` instead of scoped, since they
19
+ are opt-in by class name and cannot collide unintentionally. */
20
+
21
+ /* Skip links for keyboard navigation.
22
+ Deliberately unscoped: a skip link has to be the first focusable element in
23
+ the document, which puts it above .onboarding-container in the DOM. The
24
+ `onboarding-` prefix is what keeps it from colliding with host styles. */
25
+ .onboarding-skip-link {
26
+ position: absolute;
27
+ top: -2.5rem;
28
+ left: var(--onboarding-space-md);
29
+ background: var(--onboarding-primary);
30
+ color: white;
31
+ padding: var(--onboarding-space-sm) var(--onboarding-space-md);
32
+ border-radius: 0.375rem;
33
+ text-decoration: none;
34
+ font-weight: 500;
35
+ z-index: 10000;
36
+ transition: top 0.3s ease;
37
+ }
38
+
39
+ .onboarding-skip-link:focus {
40
+ top: var(--onboarding-space-md);
41
+ }
42
+
43
+ /* Enhanced focus indicators */
44
+ .onboarding-container *:focus-visible {
45
+ outline: 2px solid var(--onboarding-primary);
46
+ outline-offset: 2px;
47
+ border-radius: 0.25rem;
48
+ }
49
+
50
+ /* High contrast focus for interactive elements */
51
+ :where(.onboarding-container) button:focus-visible,
52
+ :where(.onboarding-container) input:focus-visible,
53
+ :where(.onboarding-container) select:focus-visible,
54
+ :where(.onboarding-container) textarea:focus-visible,
55
+ :where(.onboarding-container) a:focus-visible,
56
+ :where(.onboarding-container) [tabindex]:focus-visible {
57
+ outline: 3px solid var(--onboarding-primary);
58
+ outline-offset: 2px;
59
+ }
60
+
61
+ /* Ensure minimum touch target size */
62
+ :where(.onboarding-container) button,
63
+ :where(.onboarding-container) input[type="button"],
64
+ :where(.onboarding-container) input[type="submit"],
65
+ :where(.onboarding-container) input[type="reset"],
66
+ :where(.onboarding-container) .primary-action,
67
+ :where(.onboarding-container) .secondary-action,
68
+ :where(.onboarding-container) .skip-action,
69
+ :where(.onboarding-container) .help-action,
70
+ :where(.onboarding-container) .feature-button,
71
+ :where(.onboarding-container) .tooltip-action {
72
+ min-height: 2.75rem; /* 44px minimum touch target */
73
+ min-width: 2.75rem;
74
+ }
75
+
76
+ /* The banner renders on host-app pages, outside .onboarding-container, so its
77
+ controls need the touch-target floor applied on their own scope rather than
78
+ inheriting it from the rule above. */
79
+ :where(.onboarding-banner) .onboarding-banner-continue,
80
+ :where(.onboarding-banner) .onboarding-banner-skip {
81
+ min-height: 2.75rem;
82
+ }
83
+
84
+ /* Enhanced button states for better accessibility */
85
+ :where(.onboarding-container) button:disabled,
86
+ :where(.onboarding-container) input:disabled,
87
+ :where(.onboarding-container) select:disabled,
88
+ :where(.onboarding-container) textarea:disabled {
89
+ opacity: 0.6;
90
+ cursor: not-allowed;
91
+ background-color: var(--onboarding-surface);
92
+ color: var(--onboarding-text-muted);
93
+ }
94
+
95
+ /* Better color contrast for text */
96
+ .onboarding-text-high-contrast {
97
+ color: var(--onboarding-text);
98
+ font-weight: 500;
99
+ }
100
+
101
+ .onboarding-text-medium-contrast {
102
+ color: var(--onboarding-text-muted);
103
+ font-weight: 400;
104
+ }
105
+
106
+ /* ARIA live regions for dynamic content */
107
+ .onboarding-live-region {
108
+ position: absolute;
109
+ left: -10000px;
110
+ width: 1px;
111
+ height: 1px;
112
+ overflow: hidden;
113
+ }
114
+
115
+ /* Screen reader only content that can be focused.
116
+ Unscoped for the same reason as .onboarding-skip-link - these are opt-in
117
+ utilities that may be used outside the container. The base
118
+ `.onboarding-sr-only` lives in utilities.css. */
119
+ .onboarding-sr-only-focusable:focus,
120
+ .onboarding-sr-only-focusable:active {
121
+ position: static !important;
122
+ width: auto !important;
123
+ height: auto !important;
124
+ margin: 0 !important;
125
+ overflow: visible !important;
126
+ clip: auto !important;
127
+ white-space: normal !important;
128
+ }
129
+
130
+ /* Enhanced error messaging */
131
+ :where(.onboarding-container) .onboarding-error-message {
132
+ color: var(--onboarding-danger);
133
+ font-size: var(--onboarding-text-sm);
134
+ margin-top: var(--onboarding-space-xs);
135
+ display: flex;
136
+ align-items: flex-start;
137
+ gap: var(--onboarding-space-xs);
138
+ }
139
+
140
+ :where(.onboarding-container) .onboarding-error-icon {
141
+ flex-shrink: 0;
142
+ margin-top: 0.125rem;
143
+ font-size: var(--onboarding-text-base);
144
+ }
145
+
146
+ /* Required field indicators */
147
+ :where(.onboarding-container) .required-indicator {
148
+ color: var(--onboarding-danger);
149
+ margin-left: var(--onboarding-space-xs);
150
+ font-weight: 600;
151
+ }
152
+
153
+ /* Field descriptions and help text */
154
+ :where(.onboarding-container) .field-description {
155
+ font-size: var(--onboarding-text-sm);
156
+ color: var(--onboarding-text-muted);
157
+ margin-top: var(--onboarding-space-xs);
158
+ line-height: 1.4;
159
+ }
160
+
161
+ /* Progress indicators with better semantics */
162
+ :where(.onboarding-container, .onboarding-banner) .onboarding-progress-container[role="progressbar"] {
163
+ background: var(--onboarding-surface);
164
+ border: 1px solid var(--onboarding-border);
165
+ border-radius: 0.5rem;
166
+ overflow: hidden;
167
+ }
168
+
169
+ :where(.onboarding-container, .onboarding-banner) .onboarding-progress-container[role="progressbar"]::before {
170
+ content: attr(aria-label);
171
+ position: absolute;
172
+ left: -10000px;
173
+ }
174
+
175
+ /* Enhanced step indicators */
176
+ :where(.onboarding-container) .step-marker[aria-current="step"] {
177
+ position: relative;
178
+ }
179
+
180
+ :where(.onboarding-container) .step-marker[aria-current="step"]::after {
181
+ content: '';
182
+ position: absolute;
183
+ top: -0.25rem;
184
+ left: -0.25rem;
185
+ right: -0.25rem;
186
+ bottom: -0.25rem;
187
+ border: 2px solid var(--onboarding-primary);
188
+ border-radius: 50%;
189
+ animation: onboarding-pulse-ring 2s infinite;
190
+ }
191
+
192
+ /* Keyframe names are a single global namespace shared with the host app, so
193
+ this one is prefixed like the custom properties are. */
194
+ @keyframes onboarding-pulse-ring {
195
+ 0% {
196
+ transform: scale(1);
197
+ opacity: 1;
198
+ }
199
+ 100% {
200
+ transform: scale(1.2);
201
+ opacity: 0;
202
+ }
203
+ }
204
+
205
+ /* Enhanced tooltip accessibility */
206
+ :where(.onboarding-container) .onboarding-tooltip[role="tooltip"] {
207
+ /* Ensure tooltips are announced by screen readers */
208
+ pointer-events: auto;
209
+ }
210
+
211
+ :where(.onboarding-container) .tooltip-trigger[aria-describedby] {
212
+ position: relative;
213
+ }
214
+
215
+ /* Enhanced form styling for accessibility */
216
+ :where(.onboarding-container) fieldset {
217
+ border: 2px solid var(--onboarding-border);
218
+ border-radius: 0.5rem;
219
+ padding: var(--onboarding-space-lg);
220
+ margin: 0 0 var(--onboarding-space-lg);
221
+ }
222
+
223
+ :where(.onboarding-container) legend {
224
+ font-weight: 600;
225
+ color: var(--onboarding-text);
226
+ padding: 0 var(--onboarding-space-sm);
227
+ }
228
+
229
+ /* Radio and checkbox groups */
230
+ :where(.onboarding-container) .onboarding-radio-group,
231
+ :where(.onboarding-container) .onboarding-checkbox-group {
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: var(--onboarding-space-sm);
235
+ }
236
+
237
+ :where(.onboarding-container) .onboarding-radio-option,
238
+ :where(.onboarding-container) .onboarding-checkbox-option {
239
+ display: flex;
240
+ align-items: flex-start;
241
+ gap: var(--onboarding-space-sm);
242
+ cursor: pointer;
243
+ padding: var(--onboarding-space-sm);
244
+ border-radius: 0.375rem;
245
+ transition: var(--onboarding-transition);
246
+ }
247
+
248
+ :where(.onboarding-container) .onboarding-radio-option:hover,
249
+ :where(.onboarding-container) .onboarding-checkbox-option:hover {
250
+ background: var(--onboarding-surface);
251
+ }
252
+
253
+ :where(.onboarding-container) .onboarding-radio-option:focus-within,
254
+ :where(.onboarding-container) .onboarding-checkbox-option:focus-within {
255
+ background: var(--onboarding-surface);
256
+ outline: 2px solid var(--onboarding-primary);
257
+ outline-offset: 2px;
258
+ }
259
+
260
+ /* Enhanced table accessibility */
261
+ :where(.onboarding-container) table {
262
+ border-collapse: collapse;
263
+ width: 100%;
264
+ }
265
+
266
+ :where(.onboarding-container) th {
267
+ text-align: left;
268
+ font-weight: 600;
269
+ background: var(--onboarding-surface);
270
+ padding: var(--onboarding-space-md);
271
+ border: 1px solid var(--onboarding-border);
272
+ }
273
+
274
+ :where(.onboarding-container) td {
275
+ padding: var(--onboarding-space-md);
276
+ border: 1px solid var(--onboarding-border);
277
+ }
278
+
279
+ /* Caption styling */
280
+ :where(.onboarding-container) caption {
281
+ font-weight: 600;
282
+ margin-bottom: var(--onboarding-space-md);
283
+ text-align: left;
284
+ }
285
+
286
+ /* Enhanced link styling */
287
+ :where(.onboarding-container) a:not(.primary-action):not(.secondary-action):not(.skip-action) {
288
+ color: var(--onboarding-primary);
289
+ text-decoration: underline;
290
+ text-underline-offset: 0.125em;
291
+ transition: var(--onboarding-transition);
292
+ }
293
+
294
+ :where(.onboarding-container) a:not(.primary-action):not(.secondary-action):not(.skip-action):hover {
295
+ color: var(--onboarding-primary-hover);
296
+ text-decoration-thickness: 2px;
297
+ }
298
+
299
+ :where(.onboarding-container) a:not(.primary-action):not(.secondary-action):not(.skip-action):focus-visible {
300
+ outline: 2px solid var(--onboarding-primary);
301
+ outline-offset: 0.125em;
302
+ border-radius: 0.125rem;
303
+ }
304
+
305
+ /* High contrast mode enhancements.
306
+ The :root block only reassigns --onboarding-* custom properties, which
307
+ nothing outside this gem reads, so it stays unscoped. */
308
+ @media (prefers-contrast: high) {
309
+ :root {
310
+ --onboarding-border: #000000;
311
+ --onboarding-text-muted: #000000;
312
+ }
313
+
314
+ .onboarding-container {
315
+ background: #ffffff;
316
+ color: #000000;
317
+ }
318
+
319
+ :where(.onboarding-container) .primary-action,
320
+ :where(.onboarding-container) .secondary-action {
321
+ border-width: 2px;
322
+ font-weight: 700;
323
+ }
324
+
325
+ :where(.onboarding-container, .onboarding-banner) .onboarding-progress-fill {
326
+ background: #000000;
327
+ }
328
+
329
+ :where(.onboarding-container) .step-marker {
330
+ border-width: 3px;
331
+ }
332
+
333
+ :where(.onboarding-container) .onboarding-tooltip {
334
+ border: 2px solid #000000;
335
+ }
336
+ }
337
+
338
+ /* Reduced motion preferences.
339
+ Scoped to the engine's own markup: honouring prefers-reduced-motion across
340
+ the whole page is the host application's call, not a mounted engine's. */
341
+ @media (prefers-reduced-motion: reduce) {
342
+ :where(.onboarding-container, .onboarding-banner),
343
+ :where(.onboarding-container, .onboarding-banner) *,
344
+ :where(.onboarding-container, .onboarding-banner) *::before,
345
+ :where(.onboarding-container, .onboarding-banner) *::after {
346
+ animation-duration: 0.01ms !important;
347
+ animation-iteration-count: 1 !important;
348
+ transition-duration: 0.01ms !important;
349
+ scroll-behavior: auto !important;
350
+ }
351
+
352
+ :where(.onboarding-container) .step-marker[aria-current="step"]::after {
353
+ animation: none;
354
+ }
355
+
356
+ .onboarding-container[data-step-changed] .step-content {
357
+ animation: none;
358
+ }
359
+ }
360
+
361
+ /* Enhanced color contrast for dark mode - --onboarding-* properties only. */
362
+ @media (prefers-color-scheme: dark) and (prefers-contrast: high) {
363
+ :root {
364
+ --onboarding-background: #000000;
365
+ --onboarding-text: #ffffff;
366
+ --onboarding-border: #ffffff;
367
+ --onboarding-surface: #1a1a1a;
368
+ }
369
+ }
370
+
371
+ /* Print accessibility */
372
+ @media print {
373
+ .onboarding-skip-link,
374
+ .onboarding-live-region,
375
+ .onboarding-sr-only {
376
+ display: none;
377
+ }
378
+
379
+ :where(.onboarding-container) a {
380
+ color: #000000 !important;
381
+ text-decoration: underline !important;
382
+ }
383
+
384
+ :where(.onboarding-container) .primary-action,
385
+ :where(.onboarding-container) .secondary-action {
386
+ border: 2px solid #000000 !important;
387
+ background: transparent !important;
388
+ color: #000000 !important;
389
+ }
390
+ }
391
+
392
+ /* Screen reader improvements for interactive elements */
393
+ :where(.onboarding-container) .interactive-element {
394
+ position: relative;
395
+ }
396
+
397
+ :where(.onboarding-container) .interactive-element::before {
398
+ content: attr(aria-label);
399
+ position: absolute;
400
+ left: -10000px;
401
+ top: auto;
402
+ width: 1px;
403
+ height: 1px;
404
+ overflow: hidden;
405
+ }
406
+
407
+ /* Enhanced status messages */
408
+ :where(.onboarding-container) .onboarding-status-message {
409
+ padding: var(--onboarding-space-md);
410
+ border-radius: 0.5rem;
411
+ margin: var(--onboarding-space-md) 0;
412
+ border: 1px solid;
413
+ }
414
+
415
+ :where(.onboarding-container) .onboarding-status-message.onboarding-status-success {
416
+ background: #f0fdf4;
417
+ border-color: var(--onboarding-success);
418
+ color: #166534;
419
+ }
420
+
421
+ :where(.onboarding-container) .onboarding-status-message.onboarding-status-error {
422
+ background: #fef2f2;
423
+ border-color: var(--onboarding-danger);
424
+ color: #dc2626;
425
+ }
426
+
427
+ :where(.onboarding-container) .onboarding-status-message.onboarding-status-warning {
428
+ background: #fffbeb;
429
+ border-color: var(--onboarding-warning);
430
+ color: #92400e;
431
+ }
432
+
433
+ :where(.onboarding-container) .onboarding-status-message.onboarding-status-info {
434
+ background: #eff6ff;
435
+ border-color: var(--onboarding-primary);
436
+ color: #1e40af;
437
+ }
438
+
439
+ /* Keyboard navigation indicators */
440
+ .keyboard-user .onboarding-container :focus {
441
+ outline: 3px solid var(--onboarding-primary);
442
+ outline-offset: 2px;
443
+ }
444
+
445
+ .mouse-user .onboarding-container :focus {
446
+ outline: none;
447
+ }
448
+
449
+ /* Enhanced input validation states */
450
+ :where(.onboarding-container) input:invalid,
451
+ :where(.onboarding-container) select:invalid,
452
+ :where(.onboarding-container) textarea:invalid {
453
+ border-color: var(--onboarding-danger);
454
+ box-shadow: inset 0 1px 2px rgba(239, 68, 68, 0.1);
455
+ }
456
+
457
+ :where(.onboarding-container) input:valid:not(:placeholder-shown),
458
+ :where(.onboarding-container) select:valid,
459
+ :where(.onboarding-container) textarea:valid:not(:placeholder-shown) {
460
+ border-color: var(--onboarding-success);
461
+ }
462
+
463
+ /* Loading state accessibility */
464
+ :where(.onboarding-container) .loading-announcement {
465
+ position: absolute;
466
+ left: -10000px;
467
+ width: 1px;
468
+ height: 1px;
469
+ overflow: hidden;
470
+ }
471
+
472
+ :where(.onboarding-container) [aria-busy="true"] {
473
+ position: relative;
474
+ }
475
+
476
+ :where(.onboarding-container) [aria-busy="true"]::after {
477
+ content: '';
478
+ position: absolute;
479
+ top: 0;
480
+ left: 0;
481
+ right: 0;
482
+ bottom: 0;
483
+ background: rgba(255, 255, 255, 0.8);
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: center;
487
+ z-index: 1;
488
+ }
489
+
490
+ /* Enhanced dialog/modal accessibility */
491
+ :where(.onboarding-container) [role="dialog"] {
492
+ position: fixed;
493
+ top: 50%;
494
+ left: 50%;
495
+ transform: translate(-50%, -50%);
496
+ z-index: 10000;
497
+ background: var(--onboarding-background);
498
+ border: 1px solid var(--onboarding-border);
499
+ border-radius: 0.75rem;
500
+ box-shadow: var(--onboarding-shadow-lg);
501
+ padding: var(--onboarding-space-xl);
502
+ max-width: 90vw;
503
+ max-height: 90vh;
504
+ overflow: auto;
505
+ }
506
+
507
+ :where(.onboarding-container) .dialog-backdrop {
508
+ position: fixed;
509
+ top: 0;
510
+ left: 0;
511
+ width: 100%;
512
+ height: 100%;
513
+ background: rgba(0, 0, 0, 0.5);
514
+ z-index: 9999;
515
+ }
516
+
517
+ /* Focus trap for modals */
518
+ :where(.onboarding-container) .focus-trap {
519
+ outline: none;
520
+ }
521
+
522
+ /* Enhanced list accessibility */
523
+ :where(.onboarding-container) ul[role="list"],
524
+ :where(.onboarding-container) ol[role="list"] {
525
+ list-style: none;
526
+ padding: 0;
527
+ }
528
+
529
+ :where(.onboarding-container) li[role="listitem"] {
530
+ padding: var(--onboarding-space-sm) 0;
531
+ border-bottom: 1px solid var(--onboarding-border);
532
+ }
533
+
534
+ :where(.onboarding-container) li[role="listitem"]:last-child {
535
+ border-bottom: none;
536
+ }