steppy-dev 0.3.0__py3-none-any.whl

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 (177) hide show
  1. steppy/CHANGELOG.md +862 -0
  2. steppy/__init__.py +9 -0
  3. steppy/__main__.py +13 -0
  4. steppy/_cli_common.py +183 -0
  5. steppy/_docker/Dockerfile.installed +53 -0
  6. steppy/_parallel/__init__.py +7 -0
  7. steppy/_parallel/compose.py +349 -0
  8. steppy/_parallel/event_bus.py +418 -0
  9. steppy/_parallel/failures.py +267 -0
  10. steppy/_parallel/fingerprint.py +141 -0
  11. steppy/_parallel/image.py +380 -0
  12. steppy/_parallel/lastfailed.py +125 -0
  13. steppy/_parallel/lifecycle.py +90 -0
  14. steppy/_parallel/preauth.py +109 -0
  15. steppy/_parallel/progress.py +101 -0
  16. steppy/_parallel/queue.py +385 -0
  17. steppy/_parallel/renderer.py +489 -0
  18. steppy/_parallel/retry.py +316 -0
  19. steppy/_player_bundle/THIRD_PARTY_LICENSES.txt +158 -0
  20. steppy/_player_bundle/__init__.py +100 -0
  21. steppy/_player_bundle/steppy-cursor-legacy.js +2 -0
  22. steppy/_player_bundle/steppy-cursor-legacy.js.map +1 -0
  23. steppy/_player_bundle/steppy-player.js +5419 -0
  24. steppy/_player_bundle/steppy-player.js.map +1 -0
  25. steppy/_player_bundle/steppy-video.js +472 -0
  26. steppy/_player_bundle/steppy-video.js.map +1 -0
  27. steppy/_templating.py +117 -0
  28. steppy/assertions.py +251 -0
  29. steppy/auth.py +493 -0
  30. steppy/build.py +2798 -0
  31. steppy/build_models.py +669 -0
  32. steppy/buildid.py +134 -0
  33. steppy/bundle/__init__.py +47 -0
  34. steppy/bundle/archive.py +239 -0
  35. steppy/bundle/classify.py +329 -0
  36. steppy/bundle/create.py +490 -0
  37. steppy/bundle/hashing.py +28 -0
  38. steppy/bundle/manifest.py +334 -0
  39. steppy/bundle/results.py +546 -0
  40. steppy/bundle_cli.py +455 -0
  41. steppy/captures.py +207 -0
  42. steppy/cli.py +2489 -0
  43. steppy/cloud/__init__.py +58 -0
  44. steppy/cloud/client.py +267 -0
  45. steppy/cloud/upload.py +497 -0
  46. steppy/compat.py +109 -0
  47. steppy/config.py +231 -0
  48. steppy/config_schema.py +87 -0
  49. steppy/contracts/__init__.py +54 -0
  50. steppy/contracts/bundle.py +215 -0
  51. steppy/contracts/report.py +346 -0
  52. steppy/contracts/tours.py +505 -0
  53. steppy/coordinator.py +1352 -0
  54. steppy/coverage/__init__.py +9 -0
  55. steppy/coverage/gaps.py +190 -0
  56. steppy/coverage/manifest.py +193 -0
  57. steppy/coverage/merge.py +363 -0
  58. steppy/coverage/normalize.py +174 -0
  59. steppy/coverage/report.py +178 -0
  60. steppy/coverage/sourcemap.py +219 -0
  61. steppy/coverage/store.py +473 -0
  62. steppy/coverage/triage.py +151 -0
  63. steppy/coverage_cli.py +265 -0
  64. steppy/durations.py +48 -0
  65. steppy/exclusive.py +178 -0
  66. steppy/flaky_cli.py +169 -0
  67. steppy/generator.py +633 -0
  68. steppy/guide.py +1467 -0
  69. steppy/history.py +352 -0
  70. steppy/init_cli.py +330 -0
  71. steppy/locales.py +282 -0
  72. steppy/migrate/__init__.py +35 -0
  73. steppy/migrate/apply.py +194 -0
  74. steppy/migrate/plan.py +199 -0
  75. steppy/migrate/rules.py +465 -0
  76. steppy/migrate/verify.py +163 -0
  77. steppy/migrate_cli.py +152 -0
  78. steppy/models.py +2539 -0
  79. steppy/naming.py +37 -0
  80. steppy/parser.py +475 -0
  81. steppy/pdf.py +193 -0
  82. steppy/presets.py +284 -0
  83. steppy/record_cli.py +796 -0
  84. steppy/report/__init__.py +37 -0
  85. steppy/report/assets.py +197 -0
  86. steppy/report/build.py +261 -0
  87. steppy/report/coverage_source.py +277 -0
  88. steppy/report/model.py +423 -0
  89. steppy/report/render.py +303 -0
  90. steppy/report/source.py +238 -0
  91. steppy/report_cli.py +173 -0
  92. steppy/runmeta.py +114 -0
  93. steppy/selection.py +183 -0
  94. steppy/sitemap.py +205 -0
  95. steppy/skills/steppy-app-tours/SKILL.md +448 -0
  96. steppy/skills/steppy-ci-report/SKILL.md +567 -0
  97. steppy/skills/steppy-scenario-author/SKILL.md +2176 -0
  98. steppy/skills/steppy-template/SKILL.md +676 -0
  99. steppy/templates/_steppy_events.py +291 -0
  100. steppy/templates/_steppy_tours.py +404 -0
  101. steppy/templates/angular/component-interactive.ts.j2 +109 -0
  102. steppy/templates/angular/component-video.ts.j2 +112 -0
  103. steppy/templates/angular/component.ts.j2 +229 -0
  104. steppy/templates/angular/content.ts.j2 +31 -0
  105. steppy/templates/angular/gitignore.j2 +3 -0
  106. steppy/templates/angular/group.component.ts.j2 +39 -0
  107. steppy/templates/angular/onboarding.component.ts.j2 +43 -0
  108. steppy/templates/angular/routes.ts.j2 +34 -0
  109. steppy/templates/ci/github-actions.yml +284 -0
  110. steppy/templates/ci/gitlab-ci.yml +178 -0
  111. steppy/templates/conftest.py.j2 +2405 -0
  112. steppy/templates/guide.book.html.j2 +145 -0
  113. steppy/templates/guide.fragment.html.j2 +257 -0
  114. steppy/templates/guide.html.j2 +301 -0
  115. steppy/templates/guide.md.j2 +18 -0
  116. steppy/templates/init/config.yaml +124 -0
  117. steppy/templates/init/credentials.yaml.example +30 -0
  118. steppy/templates/init/scenarios/example--first-flow.md +53 -0
  119. steppy/templates/report/_head.html.j2 +14 -0
  120. steppy/templates/report/_mark.html.j2 +3 -0
  121. steppy/templates/report/coverage.html.j2 +124 -0
  122. steppy/templates/report/index.html.j2 +203 -0
  123. steppy/templates/report/report.css +401 -0
  124. steppy/templates/report/scenario.html.j2 +137 -0
  125. steppy/templates/static/_headers.j2 +30 -0
  126. steppy/templates/static/_mode_links.html.j2 +22 -0
  127. steppy/templates/static/_seo_head.html.j2 +37 -0
  128. steppy/templates/static/cursor.js.j2 +10 -0
  129. steppy/templates/static/group.html.j2 +78 -0
  130. steppy/templates/static/guides.css +678 -0
  131. steppy/templates/static/index.html.j2 +79 -0
  132. steppy/templates/static/interactive.html.j2 +100 -0
  133. steppy/templates/static/onboarding.html.j2 +64 -0
  134. steppy/templates/static/page.html.j2 +145 -0
  135. steppy/templates/static/robots.txt.j2 +7 -0
  136. steppy/templates/static/video.html.j2 +135 -0
  137. steppy/templates/test_scenario.py.j2 +767 -0
  138. steppy/testids.py +166 -0
  139. steppy/totp.py +26 -0
  140. steppy/tour_spec.py +163 -0
  141. steppy/tours/__init__.py +22 -0
  142. steppy/tours/actions.py +402 -0
  143. steppy/tours/compiler.py +144 -0
  144. steppy/tours/diagnostics.py +137 -0
  145. steppy/tours/emit.py +239 -0
  146. steppy/tours/ids.py +40 -0
  147. steppy/tours/inference.py +399 -0
  148. steppy/tours/model.py +120 -0
  149. steppy/tours/paths.py +126 -0
  150. steppy/tours/pipeline.py +277 -0
  151. steppy/tours/publish.py +124 -0
  152. steppy/tours/replay.py +234 -0
  153. steppy/tours/routes.py +175 -0
  154. steppy/tours/settings.py +164 -0
  155. steppy/tours/text.py +237 -0
  156. steppy/tours_cli.py +219 -0
  157. steppy/tts/__init__.py +280 -0
  158. steppy/tts/cache.py +185 -0
  159. steppy/tts/presets.py +125 -0
  160. steppy/tts/providers/__init__.py +6 -0
  161. steppy/tts/providers/http.py +184 -0
  162. steppy/tts/providers/local.py +207 -0
  163. steppy/tts/providers/null.py +28 -0
  164. steppy/tts/registry.py +201 -0
  165. steppy/tts/track.py +158 -0
  166. steppy/tts/types.py +94 -0
  167. steppy/update_cli.py +460 -0
  168. steppy/video.py +730 -0
  169. steppy/video_cache.py +239 -0
  170. steppy/video_cli.py +271 -0
  171. steppy/worker.py +301 -0
  172. steppy_dev-0.3.0.dist-info/METADATA +1335 -0
  173. steppy_dev-0.3.0.dist-info/RECORD +177 -0
  174. steppy_dev-0.3.0.dist-info/WHEEL +4 -0
  175. steppy_dev-0.3.0.dist-info/entry_points.txt +2 -0
  176. steppy_dev-0.3.0.dist-info/licenses/LICENSE +202 -0
  177. steppy_dev-0.3.0.dist-info/licenses/NOTICE +13 -0
@@ -0,0 +1,676 @@
1
+ ---
2
+ name: steppy-template
3
+ description: |
4
+ How to STYLE and customize the appearance of Steppy guides — the whole surface, both
5
+ render modes. Use whenever a user wants to change how guides LOOK: "style the guides",
6
+ "brand the guides / match our brand", "change the guide colors / accent / fonts",
7
+ "restyle the step-by-step guide", "customize the static site / navbar / landing page",
8
+ "override a guide template", "theme the interactive player", "use the sidebar layout",
9
+ "set a default template", "hide the cursor in the player", "style the PDF / print layout",
10
+ "brand the handbook", "brand the narrated video", "restyle / brand the run report",
11
+ "theme the in-app tour", "style the tour popover", or edits guide CSS,
12
+ `templates/guide.fragment.html.j2`,
13
+ `templates/static/guides.css`, the `--steppy-*` variables, `<steppy-tour-host>` /
14
+ `::part` rules, or `defaultTemplate` / `cursor` /
15
+ a guide's `template` field in `steppy.guide.config.ts`. Covers: the project `templates/`
16
+ override mechanism, step-by-step (`.steppy-guide`) CSS, the static-site chrome (navbar/index/
17
+ landing pages), the interactive `<steppy-guide>` player templates (overlay/sidebar/inline),
18
+ its cursor modes and `--steppy-*` theme tokens, the in-app tour overlay's
19
+ `--steppy-tour-*` tokens, `::part()` names and chrome strings, the print/PDF + handbook
20
+ layout, and video branding — plus how each maps to the Angular and static build
21
+ targets. For writing the scenario STEPS themselves, see the steppy-scenario-author skill;
22
+ for mounting the tour overlay in the app at all, see steppy-app-tours.
23
+ ---
24
+
25
+ # Styling Steppy guides
26
+
27
+ Steppy renders a guide in up to three modes, and **they are styled by different
28
+ systems** that happen to share the `steppy-` name prefix:
29
+
30
+ | Mode | What it is | Styled by | Isolated? |
31
+ |---|---|---|---|
32
+ | **step-by-step** | A scrollable HTML guide (numbered step cards + animated cursor over screenshots). Default mode. | `.steppy-guide`-scoped CSS in Jinja **templates** you can override. | No — global CSS (`ViewEncapsulation.None` / linked stylesheet). You can target the classes directly. |
33
+ | **interactive** | The Lit `<steppy-guide>` **web component** (a click-through player with overlay/sidebar/inline templates). Opt-in via `modes: ['interactive']`. | `--steppy-*` **CSS custom properties** on the element. | Yes — Shadow DOM. Only custom properties cross the boundary; you cannot target inner classes. |
34
+ | **video** | A narrated MP4 rendered from the interactive manifest. Opt-in via `modes: ['video']` **and** `--video`. | The `video:` block in `config.yaml` (branding, captions, dimensions) — **not CSS**. The page around it is a template. | N/A — it's a baked file. Restyling means re-rendering. |
35
+ | **in-app tour** | The `<steppy-tour-host>` overlay: popovers over **your live app**, compiled from scenarios with `tour:`. Opt-in via `tours.enabled` in `config.yaml`. Not a guide mode — it never appears on a guide page. | `--steppy-tour-*` **CSS custom properties** + `::part()` rules, in the **app's** global stylesheet. | Yes — Shadow DOM, but every visible piece is exposed as a `::part()`. |
36
+
37
+ A guide can use any combination (`modes: ['stepbystep', 'interactive', 'video']`). On top of the
38
+ guide modes there's a **third surface — the site chrome and landing pages** (home/index,
39
+ group landing pages, the onboarding "meta-guide" page, the per-guide onboarding strip, navbar,
40
+ footer). **How that third surface is styled depends entirely on the build target:**
41
+
42
+ - **`target: 'static'`** ships **one stylesheet, `guides.css`, that styles everything** — chrome,
43
+ every landing page, and guide content. Override that one file to rebrand the whole site.
44
+ - **`target: 'angular'` (default) ships NO stylesheet for the chrome/landing pages.** Steppy emits
45
+ the group/onboarding/guide components with bare `.steppy-*` classes (`ViewEncapsulation.None`,
46
+ no `styles:` block), so **your host Angular app owns all of that CSS** — and the home page +
47
+ navbar aren't generated at all (the host renders them from the emitted `nav.json`).
48
+
49
+ Pick the section below for what you're restyling. **Interactive features require steppy ≥ 0.2.0**;
50
+ re-run `steppy build` after upgrading so the new `steppy-player.js` bundle is copied.
51
+
52
+ ---
53
+
54
+ ## The `templates/` override mechanism (step-by-step + static chrome)
55
+
56
+ Steppy renders guides through Jinja templates resolved by a `ChoiceLoader` that checks your
57
+ **project's `templates/` directory first**, then falls back to the bundled copies inside the
58
+ installed `steppy` package. So you restyle the step-by-step guides and the static site by
59
+ dropping an **overriding copy** of a template into your project root, mirroring its path:
60
+
61
+ ```
62
+ your-project/
63
+ steppy.guide.config.ts
64
+ templates/ # ← create this; project copies win
65
+ guide.fragment.html.j2 # step-by-step CSS (Angular + standalone)
66
+ static/
67
+ guides.css # static-site stylesheet (chrome + guide content)
68
+ ```
69
+
70
+ You override **whole files**, not fragments — copy the bundled original as your starting
71
+ point, then edit. Locate the bundled originals to copy from:
72
+
73
+ ```bash
74
+ python -c "import steppy, pathlib; print(pathlib.Path(steppy.__file__).parent / 'templates')"
75
+ ```
76
+
77
+ Because you own the full copy, re-diff it against the bundled version after a `steppy`
78
+ upgrade so you don't miss upstream changes. There is exactly **one** tree you edit — your
79
+ project's `templates/`. (`steppy update` reconciles the rest of the Steppy-owned scaffold
80
+ after an upgrade, but it will never touch a template you have overridden; merging upstream
81
+ template changes is manual, by design.)
82
+
83
+ The same rule serves every command — `generate`, `guide`, and `build` all resolve templates
84
+ this way — and it works **per file and per subdirectory**: a project containing only
85
+ `templates/static/guides.css` overrides that one file and inherits every other bundled
86
+ template, with no top-level `*.j2` required.
87
+
88
+ ---
89
+
90
+ ## Step-by-step guide CSS (`.steppy-guide`)
91
+
92
+ The step-by-step guide markup is scoped under `.steppy-guide`. Its style block lives in
93
+ `templates/guide.fragment.html.j2` (Angular target + the standalone `steppy guide` output
94
+ uses `guide.html.j2`). The class surface:
95
+
96
+ - `.steppy-guide` — root (font, max-width, base color)
97
+ - `.steppy-guide h1` + `.steppy-role-badge` — guide title + role pill
98
+ - `.steppy-step`, `.steppy-step h2`, `.steppy-step-number` (the numbered circle),
99
+ `.steppy-step-subtitle`, `.steppy-step-content` — the step card
100
+ - `.steppy-stage`, `.steppy-cursor`, `.steppy-ripple`, `.steppy-kbd` — the screenshot stage +
101
+ animated cursor
102
+
103
+ **What's a variable vs. hardcoded:** only the cursor and z-index layers are CSS variables
104
+ (set on `.steppy-guide`); the rest are **literal colours** (a blue `#3498db`/`#2563eb`
105
+ accent, slate `#2c3e50`, grey `#7f8c8d`). There is **no palette-token system here** (unlike
106
+ the interactive player).
107
+
108
+ ```css
109
+ /* the only variables the step-by-step layer exposes */
110
+ .steppy-guide {
111
+ --steppy-cursor-size: 24px;
112
+ --steppy-cursor-color: #0f172a;
113
+ --steppy-cursor-accent: #2563eb;
114
+ --steppy-cursor-ring: rgba(37, 99, 235, 0.55);
115
+ --steppy-cursor-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
116
+ --steppy-z-screenshot: 1; --steppy-z-cursor: 10; --steppy-z-ripple: 11; --steppy-z-glyph: 12;
117
+ }
118
+ ```
119
+
120
+ ### Restyling it
121
+
122
+ - **Cursor / stacking** — set the `--steppy-cursor-*` / `--steppy-z-*` variables. You can do
123
+ this from your host app's **global** CSS (`.steppy-guide { --steppy-cursor-color: … }`)
124
+ without overriding any template, because step-by-step CSS is global.
125
+ - **Cards, badges, titles, accent colour** (the hardcoded parts) — two ways:
126
+ - **Angular target:** the styles are global (`ViewEncapsulation.None`), so add global CSS
127
+ rules targeting the classes — `.steppy-guide .steppy-step-number { background: var(--brand); }`,
128
+ `.steppy-guide h1 { border-bottom-color: var(--brand); }`. Match or beat the injected
129
+ block's specificity (the component's `<style>` renders after your `<head>` CSS, so use a
130
+ slightly more specific selector or load your sheet so it wins).
131
+ - **Static target:** restyle in `templates/static/guides.css` (see next section) — the
132
+ static pages don't use the fragment's inline block.
133
+ - **Either, for a deep change:** override `templates/guide.fragment.html.j2` and edit its
134
+ `<style>` block. (`guide.html.j2` for the standalone `steppy guide` HTML.)
135
+
136
+ ---
137
+
138
+ ## Site chrome & landing pages (index, groups, onboarding)
139
+
140
+ This is the third surface: the home page, group landing pages, the onboarding meta-guide page,
141
+ the onboarding strip shown on guide pages, the header/breadcrumb/switchers, and the footer.
142
+ Same class names on both targets — but who provides the CSS differs sharply.
143
+
144
+ The full class inventory (these are what you write/override rules for):
145
+
146
+ | Region | Classes |
147
+ |---|---|
148
+ | Header / breadcrumb | `.steppy-site-header`, `.steppy-site-title`, `.steppy-breadcrumb`, `.steppy-breadcrumb-sep` |
149
+ | Language / layout switch | `.steppy-locale-switch`, `.steppy-layout-switch` |
150
+ | Home / index | `.steppy-index`, `.steppy-index-list`, `.steppy-index-title`, `.steppy-index-eyebrow`, `.steppy-index-meta`, `.steppy-index-desc`, `.steppy-index-empty`, `.steppy-index-onboarding`, `.steppy-index-group` |
151
+ | Group landing | `.steppy-index` (shared) + `.steppy-group-description` |
152
+ | Onboarding landing | `.steppy-onboarding`, `.steppy-onboarding-description`, `.steppy-onboarding-steps`, `.steppy-onboarding-step-number`, `.steppy-onboarding-step-title`, `.steppy-onboarding-cta`, `.steppy-onboarding-start` |
153
+ | Onboarding strip (on guide pages) | `.steppy-onboarding-strip`, `.steppy-onboarding-strip-progress`, `.steppy-onboarding-strip-nav`, `.steppy-onboarding-prev`, `.steppy-onboarding-next`, `.steppy-onboarding-disabled` |
154
+ | Footer | `.steppy-site-footer` |
155
+
156
+ ### Static target — restyle `guides.css`
157
+
158
+ `target: 'static'` styles **all** of the above (plus the guide content) through one stylesheet,
159
+ `templates/static/guides.css` (a real file rendered through the Jinja loader, so a project copy
160
+ overrides it). The page templates (`templates/static/{index,page,group,onboarding,interactive}.html.j2`)
161
+ just `<link>` it. **Override `templates/static/guides.css` to rebrand the whole static site**;
162
+ override a page `.j2` only to change markup/structure. The colours there are all hardcoded
163
+ (blue `#3498db` accent, slate `#2c3e50`) — there's no token layer, so edit the literals. The
164
+ static **interactive** page also has a small inline `<style>` for its `.steppy-interactive-*`
165
+ shell (wrapper, back link) in `interactive.html.j2`.
166
+
167
+ ### Angular target — the host app owns this CSS
168
+
169
+ `target: 'angular'` emits the group and onboarding landing pages as standalone components
170
+ (`_groups/<group>.group.component.ts`, `_onboarding/onboarding.component.ts`) and the
171
+ onboarding strip inside the guide component — all with `ViewEncapsulation.None` and **no
172
+ `styles:` block**. Steppy ships **no stylesheet** for them. So in Angular:
173
+
174
+ - **Style the landing pages + onboarding strip in your host app's global CSS**, targeting the
175
+ classes above. The fastest start is to **copy the relevant blocks out of the bundled
176
+ `static/guides.css`** into your global stylesheet and rebrand from there.
177
+ - **The home page and navbar are not generated** — your app renders them from the emitted
178
+ `<role>.nav.json` (a tree of `{titleKey, path, kind}` nodes resolved through ngx-translate).
179
+ Their look is 100% your app's.
180
+ - **The mode/layout switchers are PrimeNG `<p-selectButton>`** inside a `.guide-toolbar` wrapper
181
+ (no Steppy CSS) — they inherit your app's **PrimeNG theme**. Style `.guide-toolbar` for layout;
182
+ theme the buttons via PrimeNG.
183
+
184
+ ---
185
+
186
+ ## Interactive player — choosing a template
187
+
188
+ The interactive player has three swappable **templates**. They apply **only** to guides built
189
+ with `modes: ['interactive']` (step-by-step ignores them).
190
+
191
+ | Template | Layout | Best for |
192
+ |---|---|---|
193
+ | `overlay` *(default)* | Full-bleed preview + a floating callout pinned to the target + a bottom bar. | Short, spatial flows. |
194
+ | `sidebar` | Left rail (title, "N steps · ~duration", a step **timeline**, the current description) beside the preview. | Longer guides; overview + jump-to-step. |
195
+ | `inline` | Full-bleed preview + one slim bottom bar (counter, description, sub-label, nav). | Minimal, embedded walkthroughs. |
196
+
197
+ Set the default in `steppy.guide.config.ts` (both fields optional):
198
+
199
+ ```ts
200
+ export default defineGuideConfig({
201
+ // …
202
+ defaultTemplate: 'sidebar', // project-wide (falls back to 'overlay')
203
+ guides: [
204
+ { slug: 'settings', title: '…', scenario: 'editor/settings.md',
205
+ modes: ['stepbystep', 'interactive'],
206
+ template: 'inline' }, // per-guide override; beats defaultTemplate
207
+ ],
208
+ });
209
+ ```
210
+
211
+ **Resolution order** (what a viewer sees): explicit `template="…"` attribute → the viewer's
212
+ persisted choice in `localStorage['steppy:active-template']` → the guide's `defaultTemplate`
213
+ → `'overlay'`. The player renders its own `[Sidebar | Overlay | Inline]` switcher (plus
214
+ language + viewport switchers), so your configured default is only the **initial** template;
215
+ the viewer's pick persists across reloads and never resets the current step.
216
+
217
+ ### Make `guide-text` fit the template
218
+
219
+ The templates read the three structured `guide-text` fields (see steppy-scenario-author):
220
+
221
+ | field | overlay | sidebar | inline |
222
+ |---|---|---|---|
223
+ | `title` | (unused) | **timeline heading**; falls back to `text` | (unused) |
224
+ | `text` | callout body | description panel | bar description |
225
+ | `subtitle` | mono sub-label | mono sub-label per row | mono sub-label |
226
+
227
+ **If a guide uses `sidebar`, author a short structured `title` (+ a `subtitle`) on every
228
+ step** — otherwise the timeline heading falls back to the long description.
229
+
230
+ ---
231
+
232
+ ## Interactive player — cursor mode
233
+
234
+ Orthogonal to the template: how much pointer chrome the player draws, and **how the viewer
235
+ advances**. Set it project-wide with `cursor`, per guide with `guides[].cursor`.
236
+
237
+ | `cursor` | What's drawn | How a step advances |
238
+ |---|---|---|
239
+ | `arrow` *(default)* | Animated arrow travelling to the target, ripple on action, keyboard-glyph overlay, pulsing hotspot | Viewer clicks the hotspot |
240
+ | `hotspot` | No arrow, ripple, or glyph — just the pulsing hotspot | Viewer clicks the hotspot (Guideflow-style) |
241
+ | `off` | Nothing — no arrow, no hotspot | **Auto-advances** after each step's `waitAfter`, like a passive walkthrough |
242
+
243
+ ```ts
244
+ export default defineGuideConfig({
245
+ // …
246
+ cursor: 'hotspot', // project-wide (falls back to 'arrow')
247
+ guides: [
248
+ { slug: 'tour', title: '…', scenario: 'editor/tour.md',
249
+ modes: ['interactive'],
250
+ cursor: 'off' }, // per-guide override
251
+ ],
252
+ });
253
+ ```
254
+
255
+ `off` is the one that changes *behaviour*, not just looks — nothing is clickable, so the guide
256
+ plays itself. Pick it for a lobby-screen or embedded-demo loop; avoid it when the point is to
257
+ teach someone where to click. `cursor` applies to the interactive player only; the step-by-step
258
+ cursor is styled through `--steppy-cursor-*` (see above) and never disappears.
259
+
260
+ ---
261
+
262
+ ## Interactive player — theming with `--steppy-*` tokens
263
+
264
+ The `<steppy-guide>` web component exposes its whole palette + type scale as CSS custom
265
+ properties. Because custom properties **inherit through the Shadow DOM boundary**, you theme
266
+ it by setting them on the `steppy-guide` element from your host's **global** CSS — you cannot
267
+ target its inner classes (they're shadow-isolated), only the tokens.
268
+
269
+ ```css
270
+ steppy-guide {
271
+ --steppy-accent: #0067b8; /* highlight, current node, counters */
272
+ --steppy-accent-weak: rgba(0, 103, 184, 0.35); /* the highlight glow — alpha of accent */
273
+ --steppy-success: #2e7d32; /* completed-step node (sidebar) */
274
+ --steppy-bg: #fff; --steppy-surface: #fff; --steppy-chrome-bg: #f3f4f6;
275
+ --steppy-fg: #111827; --steppy-muted: #6b7280; --steppy-border: #e5e7eb;
276
+ --steppy-font-serif: 'Inter', Georgia, serif; /* titles */
277
+ --steppy-font-sans: 'Inter', system-ui, sans-serif; /* body/buttons */
278
+ --steppy-font-mono: 'JetBrains Mono', ui-monospace, monospace; /* counters/sub-labels */
279
+ }
280
+ ```
281
+
282
+ | Token | Controls |
283
+ |---|---|
284
+ | `--steppy-accent` / `--steppy-accent-weak` | Highlight outline + its glow, current node, counters, focus |
285
+ | `--steppy-success` | Completed-step node in the sidebar timeline |
286
+ | `--steppy-bg` / `--steppy-surface` / `--steppy-chrome-bg` | Background / panels-rail / control bars |
287
+ | `--steppy-fg` / `--steppy-muted` / `--steppy-border` | Primary text / secondary / hairlines |
288
+ | `--steppy-font-serif` / `-sans` / `-mono` | Titles / body / counters + sub-labels |
289
+ | `--steppy-cursor-color` / `--steppy-tooltip-bg` / `--steppy-tooltip-fg` | Cursor + overlay callout (default to fg/surface) |
290
+
291
+ Set only what differs from the warm-cream/rust/green defaults. Use **system font stacks** or
292
+ fonts your host already loads — the player bundles none and makes **no network requests**.
293
+
294
+ **Where to put the rule:**
295
+ - **Angular target** *(default)*: a **global** stylesheet (e.g. `src/styles.scss`). Not a
296
+ component's scoped styles — those don't reach the custom element.
297
+ - **Static target:** add the `steppy-guide { … }` rule to your `templates/static/guides.css`
298
+ override (it's already linked by `interactive.html.j2`).
299
+
300
+ ---
301
+
302
+ ## In-app tour overlay — tokens, parts, strings
303
+
304
+ In-app tours (opt-in: `tours.enabled: true` in `config.yaml`) render through a **different
305
+ element** from the guide player: `<steppy-tour-host>`, which the app imports into its own
306
+ bundle (`import '@steppy/player/tour'`) and mounts once in its shell. It draws popovers over
307
+ the live application, so it is styled from the **app's global stylesheet** — no `templates/`
308
+ file and no `steppy build` step is involved. It is Shadow DOM with exactly two ways in, both
309
+ deliberate: public custom properties and `::part()`. (Mounting it — the packages, the Angular
310
+ adapter, CSP, the language — is `steppy-app-tours`.)
311
+
312
+ ### Custom properties
313
+
314
+ Set them on `:root` or on the element — custom properties inherit through the shadow boundary.
315
+
316
+ | Property | Default | Controls |
317
+ |---|---|---|
318
+ | `--steppy-tour-bubble-bg` | `#1f1d1a` | Popover background; falls back to `--steppy-tooltip-bg` |
319
+ | `--steppy-tour-bubble-fg` | `#fbf9f4` | Popover text; falls back to `--steppy-tooltip-fg` |
320
+ | `--steppy-tour-muted` | `#c9c5bd` | Progress text ("Step 2 of 5") |
321
+ | `--steppy-tour-accent` | `#a8410e` | Primary button, spotlight ring |
322
+ | `--steppy-tour-accent-fg` | `#ffffff` | Primary button text |
323
+ | `--steppy-tour-focus` | `#f09a6a` | Focus ring |
324
+ | `--steppy-tour-scrim` | `rgba(31, 29, 26, 0.45)` | The dimming around the spotlight |
325
+ | `--steppy-tour-radius` | `6px` | Corner radius |
326
+ | `--steppy-font-family` | inherited | Unset, the bubble uses **your app's font** |
327
+
328
+ ```css
329
+ /* the app's global stylesheet */
330
+ :root {
331
+ --steppy-tour-accent: #0b5cad; /* primary button + spotlight ring — 6.7:1 against white text */
332
+ --steppy-tour-radius: 10px;
333
+ }
334
+ ```
335
+
336
+ The defaults are a dark bubble with a rust accent. Moving to a light bubble means setting
337
+ `-bubble-bg`, `-bubble-fg`, `-muted` and `-focus` **together** — the default muted grey and
338
+ focus ring are tuned for the dark background. A project that already sets
339
+ `--steppy-tooltip-bg` / `-fg` on `:root` gets a matching bubble for free.
340
+
341
+ ### Parts
342
+
343
+ Every visible piece is a `::part()`:
344
+
345
+ | Part | Element |
346
+ |---|---|
347
+ | `layer` | The full-page layer holding everything (the top-layer popover) |
348
+ | `spotlight` | The ring and scrim around the target |
349
+ | `bubble` | The popover itself — a labelled, non-modal dialog |
350
+ | `title`, `body`, `progress` | Its heading, its text, and "Step 2 of 5" |
351
+ | `button` | Every button, which also carries `primary` or `secondary` (its style) and one of `next`, `close`, `skip`, `end`, `resume` (which button it is) |
352
+ | `pill` | The "Tour paused" pill, holding `resume` and `close` |
353
+ | `guide-link` | The "Open the full guide" link |
354
+
355
+ ```css
356
+ steppy-tour-host::part(bubble) { border-radius: 12px; }
357
+ steppy-tour-host::part(primary) { font-weight: 600; }
358
+ ```
359
+
360
+ `::part()` matches the part element itself and nothing inside it — `::part(bubble) span` is an
361
+ invalid selector, not a narrower one. Style the named parts (`title`, `body`, `progress`)
362
+ directly; anything that is not a part is not styleable, on purpose.
363
+
364
+ ### Strings
365
+
366
+ The chrome strings — Next, Done, "Step {current} of {total}", Close the tour, Skip this step,
367
+ End the tour, Resume tour, "Can't find this element", "Open the full guide", and the generic
368
+ per-action hint a step shows when its scenario has no text in that locale — ship in **English,
369
+ French, Dutch and German**. They resolve by the **tour file's** locale, never the page's, so the
370
+ buttons and the step text are always in one language; a regional locale reads its base language
371
+ (`fr-BE` → `fr`), and an unknown one falls back to English. Override keys, or add a locale, with
372
+ the host's `strings` property (a JavaScript property, not an attribute):
373
+
374
+ ```js
375
+ const host = document.querySelector('steppy-tour-host');
376
+ host.strings = {
377
+ en: { next: 'Continue', done: 'Got it' },
378
+ es: { next: 'Siguiente', done: 'Listo', progress: 'Paso {current} de {total}' },
379
+ };
380
+ ```
381
+
382
+ Keys: `next`, `progress` (`{current}`, `{total}`), `done`, `close`, `skipStep`, `endTour`,
383
+ `resume`, `paused`, `dialogLabel`, `openGuide`, `notFoundTitle`, `notFoundBody`, and
384
+ `instructionClick` / `instructionInput` / `instructionChange` / `instructionVisible` /
385
+ `instructionManual`. A key you leave out keeps the bundled value (English for a locale Steppy
386
+ does not ship — en, fr, nl and de are shipped; the compiler warns `chrome-strings-missing` for
387
+ any other tour language, so give that language every key). Step text itself is not a string
388
+ here — it comes from the scenario's `guide-text` / `tour-text`.
389
+
390
+ **Direction.** The layer carries the tour file's `lang` and `dir`, so an Arabic or Hebrew tour
391
+ lays the bubble out right to left. When you restyle parts, use logical properties
392
+ (`margin-inline-start`, `padding-inline-end`, `inset-inline-end`) rather than `left`/`right`,
393
+ or the override breaks mirroring. Placement around the target stays physical.
394
+
395
+ ### Stacking and contrast
396
+
397
+ - **Stacking.** The layer uses the browser's **top layer** (`popover`) where available, so no
398
+ z-index of yours can sit above it — there is nothing to fight. Where the top layer is
399
+ unavailable it falls back to `baseZIndex` (the `base-z-index` attribute, default
400
+ `2147483000`); adjust that, never your own elements' z-index.
401
+ - **Contrast.** The defaults meet WCAG AA; keep that when you override them. Text
402
+ (`-bubble-fg` and `-muted` on `-bubble-bg`) and the primary button (`-accent-fg` on
403
+ `-accent`) need **at least 4.5:1**. The shipped accent `#a8410e` gives 6.13:1 on white; a
404
+ brighter "on-brand" orange is the classic way to break it.
405
+ - **Focus and screen readers are behaviour, not styling.** Focus moves into the popover only for
406
+ manual steps and the "can't find" fallback; each step is announced through a polite live
407
+ region. The focus ring is an outline in `--steppy-tour-focus` — recolour it there rather
408
+ than removing it with a `::part(button)` rule.
409
+
410
+ ---
411
+
412
+ ## Print / PDF layout (`--pdf`, `--handbook`)
413
+
414
+ Steppy lays out no PDF of its own. `--pdf` prints the **already-rendered guide HTML** through
415
+ headless Chromium — so **the `@media print` blocks in the templates *are* the PDF layout**, and
416
+ restyling the page restyles the PDF for free. There is no separate PDF stylesheet to maintain.
417
+
418
+ Three surfaces carry a `@media print` block; edit whichever one your target uses:
419
+
420
+ | File | Applies to |
421
+ |---|---|
422
+ | `static/guides.css` | Static site pages + everything `build --pdf` prints |
423
+ | `guide.fragment.html.j2` | Angular-embedded guides |
424
+ | `guide.html.j2` | Standalone `steppy guide` HTML |
425
+
426
+ What those blocks already do, and what you inherit by not fighting them:
427
+
428
+ - **Site chrome is hidden** (`.steppy-site-header`, `.steppy-breadcrumb`, both switchers, the
429
+ onboarding strip, the PDF link, the footer). That is exactly what lets `build --pdf` print
430
+ the *published* page as-is instead of needing a print-only page. Inactive layouts are already
431
+ `display:none`, so a multi-layout page prints the layout the reader is looking at.
432
+ - **`print-color-adjust: exact`** on `body`, so step badges and the role chip keep their colour
433
+ when the viewer has background graphics off. Drop this and your accent silently disappears
434
+ from every PDF.
435
+ - **`page-break-inside: avoid`** on each `.steppy-step` and each screenshot.
436
+ - **The cursor, ripple, and keyboard glyph are hidden** — they animate, and a frozen arrow on
437
+ paper reads as an artifact.
438
+
439
+ ### The one token you will actually need
440
+
441
+ ```css
442
+ :root { --steppy-print-max-image-height: 180mm; } /* default */
443
+ ```
444
+
445
+ **Chromium cannot split an image across pages.** An uncapped full-page screenshot taller than
446
+ the printable area gets *clipped*, not continued — so screenshots are capped at this height.
447
+ Symptoms and fixes:
448
+
449
+ - **Screenshots look too small in the PDF** → raise it (`220mm`), or switch `pdf.landscape: true`.
450
+ - **A screenshot is cut off at the page edge** → your override raised it past the printable
451
+ height. Lower it, or add margin headroom in the `pdf:` block.
452
+
453
+ Paper itself (`format`, `margin`, `landscape`, `scale`, `print_background`) is **config, not
454
+ CSS** — the `pdf:` block in `config.yaml`. Keep `print_background: true`; Playwright's own
455
+ default is `false`, which would drop every step badge.
456
+
457
+ ### Handbook
458
+
459
+ `--handbook` renders `guide.book.html.j2`: a cover, a table of contents, then every guide
460
+ fragment in order. It **inlines `static/guides.css`** and adds its own `.steppy-book`,
461
+ `.steppy-book-cover`, `.steppy-book-subtitle`, `.steppy-book-toc`, `.steppy-book-section`,
462
+ `.steppy-book-guide` classes. So:
463
+
464
+ - **Rebranding `static/guides.css` rebrands the handbook automatically** — there is no fourth
465
+ copy of the guide CSS to keep in sync.
466
+ - **Restyle the cover / TOC** by overriding `guide.book.html.j2` and editing its own `<style>`
467
+ block.
468
+
469
+ ---
470
+
471
+ ## Narrated video (`modes: ['video']`)
472
+
473
+ A video is a **baked MP4**, so none of the CSS above reaches it — restyling means re-rendering
474
+ (`steppy build --video` / `steppy guide --video`). Its look is configured, not styled, in the
475
+ `video:` block of `config.yaml`:
476
+
477
+ ```yaml
478
+ video:
479
+ width: 1280 # display width; also affects duration — cursor travel is measured in display px
480
+ max_height: null
481
+ captions: band # band (default) | off
482
+ branding:
483
+ logo: null # path to a logo image
484
+ accent: null # accent colour used by the intro/outro and caption band
485
+ font_family: null
486
+ stamp:
487
+ enabled: false # burn a version/date stamp into the frame
488
+ text: "{version} · {date}"
489
+ version: dev
490
+ ```
491
+
492
+ - **`captions: band`** draws the caption band over the footage; `off` removes it (a `.vtt`
493
+ track still ships beside the MP4, so a player can render its own).
494
+ - **`branding.accent`** is the video's equivalent of `--steppy-accent` — set both to the same
495
+ colour or the video will not match the site it links from.
496
+ - **`width` is not only a quality setting**: cursor travel duration is computed from *display*
497
+ distance, so changing it changes the video's length.
498
+
499
+ The **page around** the video is a normal template. The static target's `static/video.html.j2`
500
+ links `assets/guides.css` and adds its own inline `<style>` for the
501
+ `.steppy-video-shell` / `-header` / `-back` / `-player` / `-switch` / `-downloads` classes —
502
+ the same arrangement as `interactive.html.j2`. The Angular target emits
503
+ `angular/component-video.ts.j2`, which your host app styles.
504
+
505
+ **Re-rendering is expensive** (minutes of CPU per video, plus TTS cost). The render cache is
506
+ content-addressed, so a pure styling change to the *page* costs nothing — but touching
507
+ `video.branding`, `width`, or `captions` invalidates every video. Get branding right in one
508
+ pass; `steppy video check` first, and `steppy video cache` to see what you are about to
509
+ invalidate.
510
+
511
+ ---
512
+
513
+ ## Overridable templates — complete map
514
+
515
+ Every file you can drop into your project's `templates/` to change appearance. **Styling**
516
+ = controls look; **structure** = markup/data only (rarely touched for styling).
517
+
518
+ | File | Controls | Kind |
519
+ |---|---|---|
520
+ | `guide.fragment.html.j2` | Step-by-step guide CSS (`.steppy-guide` block) — Angular inlines it, static suppresses it | **styling** |
521
+ | `guide.html.j2` | Standalone `steppy guide` HTML page (self-contained styles) | **styling** |
522
+ | `guide.book.html.j2` | `--handbook` document: cover, TOC, `.steppy-book-*` classes. Inlines `guides.css` | **styling** (cover/TOC only) |
523
+ | `static/guides.css` | **Static target's entire stylesheet** — chrome, all landing pages, onboarding strip, guide content, **and the `@media print` / PDF layout** | **styling** |
524
+ | `static/index.html.j2` | Static home page markup | structure (styling via guides.css) |
525
+ | `static/group.html.j2` | Static group landing markup | structure (styling via guides.css) |
526
+ | `static/onboarding.html.j2` | Static onboarding landing markup | structure (styling via guides.css) |
527
+ | `static/page.html.j2` | Static guide page (header, switchers, onboarding strip markup) | structure (styling via guides.css) |
528
+ | `static/interactive.html.j2` | Static interactive page + inline `.steppy-interactive-*` shell CSS | **styling** (shell) |
529
+ | `static/video.html.j2` | Static video page + inline `.steppy-video-*` shell CSS (player, version switcher, download links). Links `guides.css` | **styling** (shell) |
530
+ | `static/_seo_head.html.j2` | Shared SEO `<head>` fragment (`robots`/canonical/`hreflang`/description/Open Graph) `{% include %}`d by every static page — no visual output | **not styling** |
531
+ | `static/robots.txt.j2` | Emitted by `seo.mode: 'public'` — no visual output | **not styling** |
532
+ | `static/_headers.j2` | `X-Robots-Tag` rules for artifacts that cannot carry a robots meta (PDFs). Read by Netlify / Cloudflare Pages, ignored by GitHub and GitLab Pages — no visual output | **not styling** |
533
+ | `static/_mode_links.html.j2` | The "Step by Step / Interactive / Video" row, `{% include %}`d by all three renderings of a guide. Styled by `.steppy-mode-links` in `guides.css` | structure (`guides.css` styles it) |
534
+ | `static/sitemap.xml.j2` | **Not bundled.** `sitemap.xml` is generated by `src/steppy/sitemap.py`; add this file to the project to override it (context: `urls` with `.loc` / `.alternates[].hreflang` / `.alternates[].href` / `.lastmod`, plus `lastmod`) | **not styling** |
535
+ | `angular/component.ts.j2` | Guide component markup: `.guide-toolbar`, PrimeNG switchers, onboarding strip (no inline CSS) | structure (host CSS styles it) |
536
+ | `angular/component-interactive.ts.j2` | `<steppy-guide>` host + `.steppy-guide-host` inline CSS | **styling** (host wrapper) |
537
+ | `angular/component-video.ts.j2` | Angular host for a guide's narrated video | structure (host CSS styles it) |
538
+ | `angular/group.component.ts.j2` | Group landing component (bare classes, no CSS) | structure (host CSS styles it) |
539
+ | `angular/onboarding.component.ts.j2` | Onboarding landing component (bare classes, no CSS) | structure (host CSS styles it) |
540
+ | `report/report.css` | **The run report's entire stylesheet** — a separate site, see below | **styling** |
541
+ | `report/{index,scenario,coverage}.html.j2` | Run report pages (`steppy report`) | structure (styling via `report/report.css`) |
542
+ | `report/_head.html.j2` | Shared report `<head>` (always `noindex`) — no visual output | **not styling** |
543
+ | `static/cursor.js.j2`, `angular/{content,routes}.ts.j2`, `angular/gitignore.j2`, `guide.md.j2` | Cursor driver / fragments / routes / nav data | **not styling** |
544
+
545
+ > **Angular reality check:** the `angular/*.component.ts.j2` files carry **no CSS** — overriding
546
+ > them changes markup, not look. To restyle Angular landing pages / onboarding strip, write
547
+ > CSS in your **host app** (seed it from `static/guides.css`), not by editing these templates.
548
+
549
+ ---
550
+
551
+ ## The run report is a *separate* site (`steppy report`)
552
+
553
+ `steppy report` renders run results, not guides, and it deliberately does **not** load
554
+ `static/guides.css` — that file is guide-site chrome plus the `@media print` rules that
555
+ *are* the PDF layout, none of which a report inherits. Override
556
+ `templates/report/report.css` instead, and note that only the report pages are affected.
557
+
558
+ It is token-driven, so branding it is usually a handful of custom properties on `:root`
559
+ (the file also redefines them under `@media (prefers-color-scheme: dark)`):
560
+
561
+ ```css
562
+ :root {
563
+ --accent: #2f5fd0; /* links */
564
+ --pass: #1a7f45; /* passed chips + verdict */
565
+ --fail: #c02b2b; /* failed chips, cause counts, the failing step number */
566
+ --warn: #9a6200; /* notices: truncated, no timeline, unknown outcomes */
567
+ --surface: #ffffff; /* cards */
568
+ --bg: #f7f7f8;
569
+ --border: #e3e3e7;
570
+ --radius: 8px;
571
+ }
572
+ ```
573
+
574
+ Class prefixes: `.steppy-report-*` (header, main, section, meta, table),
575
+ `.steppy-chip[data-outcome="passed|failed|error|skipped|…"]`, `.steppy-verdict`,
576
+ `.steppy-cause*`, `.steppy-step*`, `.steppy-shots`, `.steppy-variant*`, `.steppy-notice`,
577
+ `pre.steppy-tail`.
578
+
579
+ > **Two gotchas, in the order they will bite you.**
580
+ >
581
+ > 1. **`report.css` must contain no Jinja delimiters — not even inside a CSS comment.** It
582
+ > is loaded through the Jinja loader (that is what makes the override work), so a `{{`
583
+ > or `{%` anywhere in the file is a `TemplateSyntaxError`. Writing a comment *warning
584
+ > about* this is how you cause it.
585
+ > 2. **The report's Jinja environment has `autoescape=True`**, unlike every other Steppy
586
+ > template. That is deliberate — it renders failure tails, which are arbitrary page
587
+ > content — but it means an interpolated value would come out HTML-escaped. Use CSS
588
+ > custom properties; there is nothing this file needs from Python.
589
+ >
590
+ > Two things are not styling knobs at all: every page is hardcoded `noindex` (a run report
591
+ > has no SEO value, and publishing one to Pages publishes its screenshots), and the images
592
+ > use `width: auto; max-width: 100%` so a dense capture renders at its true pixels and a
593
+ > low-res one is never upscaled — the same rule the guide stage uses.
594
+
595
+ ---
596
+
597
+ ## Responsive & motion
598
+
599
+ - The interactive **screenshot variant** (desktop/mobile capture) is the player's
600
+ "Affichage"/View switcher (driven by the `layouts` you captured) — orthogonal to the
601
+ template. The static site has its own desktop/mobile `.steppy-layout-switch`.
602
+ - The `sidebar` template collapses to a single column (rail above preview) below ~640px of
603
+ **container** width; `overlay`/`inline` are already full-bleed.
604
+ - The interactive player honours `prefers-reduced-motion`. The step-by-step cursor animation
605
+ is driven by a script; reduce its motion by lowering the `guide.cursor.*` timings in the
606
+ scenario frontmatter or omitting `guide.enabled`.
607
+ - The **in-app tour overlay** turns its anchored bubble into a **bottom (or top) sheet** on
608
+ narrow screens (640px viewport width and below), and whenever an anchored bubble would cover
609
+ the target. It honours `prefers-reduced-motion`: **reduced motion removes its transitions and
610
+ smooth scrolling**. Neither needs configuring.
611
+
612
+ ---
613
+
614
+ ## Common mistakes
615
+
616
+ 1. **Confusing the two systems** — step-by-step is global `.steppy-guide` CSS you can target
617
+ directly; the interactive player is shadow-isolated and themeable **only** via `--steppy-*`
618
+ tokens. A rule like `steppy-guide .steppy-step-number {…}` does nothing to the player.
619
+ 2. **Looking for a palette token in step-by-step** — only the cursor/z-index are variables;
620
+ card/badge/accent colours are hardcoded. Override the template or add global class rules.
621
+ 3. **Editing generated output** (`assets/guides.css`, `interactive.html`, components) — it's
622
+ overwritten on the next `steppy build`. Override via `templates/…` or host CSS instead.
623
+ 4. **Theming the player from a scoped component stylesheet** — won't reach the shadow DOM.
624
+ Put `steppy-guide { --steppy-… }` in a **global** stylesheet.
625
+ 5. **Changing `--steppy-accent` but not `--steppy-accent-weak`** — the highlight glow keeps
626
+ the old colour. Set it to an alpha of the new accent.
627
+ 6. **Setting `template` / `defaultTemplate` on a `stepbystep`-only guide** — templates need
628
+ `modes: ['interactive']`.
629
+ 7. **Expecting `defaultTemplate` to stick after a viewer switches** — it's only the *initial*
630
+ template; the viewer's choice persists in `localStorage`. Clear
631
+ `localStorage['steppy:active-template']` to re-test the default.
632
+ 8. **Choosing `sidebar` but writing only `text`** — the timeline heading falls back to the
633
+ long description. Author a short structured `title`.
634
+ 9. **Forgetting to re-run `steppy build` / not on steppy ≥ 0.2.0** — the templated player only
635
+ appears once the new `steppy-player.js` bundle is copied.
636
+ 10. **Overriding a template and never re-diffing after an upgrade** — you own the full copy;
637
+ upstream CSS/markup fixes won't reach you until you merge them.
638
+ 11. **Expecting the Angular landing pages / onboarding strip to come pre-styled** — they don't;
639
+ the Angular components ship bare classes with no CSS. Write the rules in your host app
640
+ (seed them from `static/guides.css`). Editing `angular/*.component.ts.j2` changes markup,
641
+ not look.
642
+ 12. **Trying to style the Angular mode/layout switcher with Steppy CSS** — it's a PrimeNG
643
+ `<p-selectButton>`; theme it through your app's PrimeNG theme (and `.guide-toolbar` for
644
+ layout).
645
+ 13. **Hunting for a generated Angular home page / navbar** — neither is generated. Your app
646
+ renders them from `<role>.nav.json`, and their styling is entirely yours.
647
+ 14. **Looking for a PDF stylesheet** — there isn't one. `--pdf` prints the rendered HTML, so
648
+ the `@media print` block in `static/guides.css` (or the fragment/standalone template) *is*
649
+ the PDF layout.
650
+ 15. **Raising `--steppy-print-max-image-height` until screenshots look big** — past the
651
+ printable height Chromium **clips** the image rather than splitting it. Raise `pdf.margin`
652
+ headroom or switch to `pdf.landscape: true` instead of pushing the cap.
653
+ 16. **Setting `pdf.print_background: false`** (or leaving a viewer's "background graphics" off
654
+ without `print-color-adjust: exact`) — every step badge and role chip prints blank.
655
+ 17. **Trying to restyle a video with CSS** — an MP4 is baked. Its look comes from the `video:`
656
+ config block; changing it means re-rendering with `--video`, and `branding` / `width` /
657
+ `captions` invalidate the whole render cache.
658
+ 18. **Branding the site and forgetting `video.branding.accent`** — the video is the one surface
659
+ that does not inherit `--steppy-accent`, so it ships in the old colour.
660
+ 19. **Reaching for `cursor: 'off'` to "clean up" a teaching guide** — it removes the hotspot
661
+ too, so nothing is clickable and the guide auto-advances. If you only want to drop the
662
+ arrow and ripple, that's `cursor: 'hotspot'`.
663
+ 20. **Styling the in-app tour from an Angular component's `styles`** — component-scoped CSS
664
+ cannot reach into the overlay's shadow DOM, so `.bubble { … }` or `.title { … }` there does
665
+ nothing. Use the `--steppy-tour-*` custom properties or
666
+ `steppy-tour-host::part(…)` rules, in the app's **global** stylesheet.
667
+ 21. **Writing `steppy-tour-host::part(bubble) span { … }`** — parts cannot be descended into;
668
+ the selector is invalid and the whole rule is dropped. Target the named part instead
669
+ (`::part(title)`, `::part(body)`, `::part(progress)`).
670
+ 22. **Lowering `--steppy-tour-accent` below 4.5:1** against `--steppy-tour-accent-fg` —
671
+ the primary button's text fails WCAG AA. The same 4.5:1 floor applies to `-bubble-fg` and
672
+ `-muted` on `-bubble-bg`; check a brand colour before shipping it.
673
+ 23. **Fighting z-index to get above (or below) the tour** — the overlay sits in the browser's
674
+ top layer, where no z-index competes with it. Raising your header's z-index changes
675
+ nothing; where the top layer is unavailable, set the host's `baseZIndex`
676
+ (`base-z-index`) instead.