@smileid/web-components 11.5.0 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js +5398 -0
  2. package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js.map +1 -0
  3. package/dist/esm/{Navigation-Xg565kcu.js → Navigation-6DH3vF4-.js} +2 -2
  4. package/dist/esm/{Navigation-Xg565kcu.js.map → Navigation-6DH3vF4-.js.map} +1 -1
  5. package/dist/esm/{PoweredBySmileId-CxbaihMu.js → PoweredBySmileId-DoKwoPUd.js} +424 -6
  6. package/dist/esm/PoweredBySmileId-DoKwoPUd.js.map +1 -0
  7. package/dist/esm/{SelfieCaptureScreens-D3KuMzZA.js → SelfieCaptureScreens-CtX-4Tco.js} +5 -6
  8. package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js.map +1 -0
  9. package/dist/esm/combobox.js +1 -1
  10. package/dist/esm/document.js +1 -1
  11. package/dist/esm/end-user-consent.js +713 -2
  12. package/dist/esm/end-user-consent.js.map +1 -1
  13. package/dist/esm/index-BqyuTk9f.js +1366 -0
  14. package/dist/esm/{index-CUwa6MPI.js.map → index-BqyuTk9f.js.map} +1 -1
  15. package/dist/esm/localisation.js +1 -1
  16. package/dist/esm/main.js +14 -14
  17. package/dist/esm/navigation.js +1 -1
  18. package/dist/esm/{package-BmVbDNny.js → package-CjZI-cNQ.js} +177 -172
  19. package/dist/esm/{package-BmVbDNny.js.map → package-CjZI-cNQ.js.map} +1 -1
  20. package/dist/esm/selfie.js +1 -1
  21. package/dist/esm/smart-camera-web.js +32 -18
  22. package/dist/esm/smart-camera-web.js.map +1 -1
  23. package/dist/esm/totp-consent.js +731 -2
  24. package/dist/esm/totp-consent.js.map +1 -1
  25. package/dist/esm/validate.js +31 -0
  26. package/dist/esm/validate.js.map +1 -0
  27. package/dist/smart-camera-web.js +696 -321
  28. package/dist/smart-camera-web.js.map +1 -1
  29. package/dist/types/main.d.ts +7 -1
  30. package/dist/types/validate.d.ts +21 -0
  31. package/lib/components/document/src/DocumentCaptureScreens.js +97 -18
  32. package/lib/components/document/src/assets/lottie.d.ts +12 -0
  33. package/lib/components/document/src/assets/svg-inline.d.ts +8 -0
  34. package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.stories.js +75 -0
  35. package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.tsx +1458 -0
  36. package/lib/components/document/src/document-auto-capture/README.md +73 -0
  37. package/lib/components/document/src/document-auto-capture/assets/Greenbook_Shimmer.svg +42 -0
  38. package/lib/components/document/src/document-auto-capture/assets/ID_Back_Shimmer.svg +8 -0
  39. package/lib/components/document/src/document-auto-capture/assets/ID_Front_Shimmer.svg +20 -0
  40. package/lib/components/document/src/document-auto-capture/assets/Passport-Shimmer.svg +143 -0
  41. package/lib/components/document/src/document-auto-capture/assets/shimmers.ts +21 -0
  42. package/lib/components/document/src/document-auto-capture/assets/svg-raw.d.ts +4 -0
  43. package/lib/components/document/src/document-auto-capture/components/CaptureButton.tsx +122 -0
  44. package/lib/components/document/src/document-auto-capture/components/Overlay.tsx +167 -0
  45. package/lib/components/document/src/document-auto-capture/components/TuningPanel.tsx +856 -0
  46. package/lib/components/document/src/document-auto-capture/constants/captureLayout.ts +58 -0
  47. package/lib/components/document/src/document-auto-capture/detection/cvErrorRecovery.ts +40 -0
  48. package/lib/components/document/src/document-auto-capture/detection/documentAspect.ts +20 -0
  49. package/lib/components/document/src/document-auto-capture/detection/qualityScoring.ts +35 -0
  50. package/lib/components/document/src/document-auto-capture/detection/seamRejection.ts +209 -0
  51. package/lib/components/document/src/document-auto-capture/detection/synthesisTiming.ts +10 -0
  52. package/lib/components/document/src/document-auto-capture/hooks/useCamera.ts +117 -0
  53. package/lib/components/document/src/document-auto-capture/hooks/useCardDetection.ts +3059 -0
  54. package/lib/components/document/src/document-auto-capture/index.ts +4 -0
  55. package/lib/components/document/src/document-auto-capture/theme.ts +40 -0
  56. package/lib/components/document/src/document-auto-capture/utils/debug.ts +25 -0
  57. package/lib/components/document/src/document-auto-capture/utils/opencvLoader.ts +86 -0
  58. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.tsx +327 -244
  59. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +153 -189
  60. package/lib/components/document/src/document-capture-submission/DocumentCaptureSubmission.tsx +432 -0
  61. package/lib/components/document/src/document-capture-submission/index.js +3 -0
  62. package/lib/components/selfie/README.md +13 -0
  63. package/lib/components/signature-pad/package.json +1 -1
  64. package/lib/components/smart-camera-web/src/README.md +11 -0
  65. package/lib/components/smart-camera-web/src/SmartCameraWeb.js +25 -1
  66. package/lib/components/totp-consent/src/TotpConsent.js +1 -1
  67. package/package.json +8 -4
  68. package/dist/esm/DocumentCaptureScreens-ucJDu5nH.js +0 -2232
  69. package/dist/esm/DocumentCaptureScreens-ucJDu5nH.js.map +0 -1
  70. package/dist/esm/EndUserConsent-CsiwoThZ.js +0 -717
  71. package/dist/esm/EndUserConsent-CsiwoThZ.js.map +0 -1
  72. package/dist/esm/PoweredBySmileId-CxbaihMu.js.map +0 -1
  73. package/dist/esm/SelfieCaptureScreens-D3KuMzZA.js.map +0 -1
  74. package/dist/esm/TotpConsent-CRtmtudl.js +0 -734
  75. package/dist/esm/TotpConsent-CRtmtudl.js.map +0 -1
  76. package/dist/esm/index-CUwa6MPI.js +0 -1363
  77. package/dist/esm/styles-BTEClL7R.js +0 -419
  78. package/dist/esm/styles-BTEClL7R.js.map +0 -1
  79. /package/lib/components/document/src/assets/lottie/{taking photo of green book passport.lottie → greenbook.lottie} +0 -0
  80. /package/lib/components/document/src/assets/lottie/{taking photo of ID FLIP 2D.lottie → id-card-flip.lottie} +0 -0
  81. /package/lib/components/document/src/assets/lottie/{taking photo of ID.lottie → id-card.lottie} +0 -0
  82. /package/lib/components/document/src/assets/lottie/{taking photo of passport 2.lottie → passport.lottie} +0 -0
@@ -4,107 +4,65 @@ import register from 'preact-custom-element';
4
4
  import { DotLottie } from '@lottiefiles/dotlottie-web';
5
5
  import { getBoolProp } from '../../../../utils/props';
6
6
  import { getDirection, t } from '../../../../domain/localisation';
7
+ // Registers the shared <smileid-navigation> custom element (back button,
8
+ // localised label, RTL arrow flip) used across the SDK's capture screens.
9
+ import '../../../navigation/src';
10
+ import idCardLottie from '../assets/lottie/id-card.lottie?inline';
11
+ import passportLottie from '../assets/lottie/passport.lottie?inline';
12
+ import greenbookLottie from '../assets/lottie/greenbook.lottie?inline';
13
+ import idCardFlipLottie from '../assets/lottie/id-card-flip.lottie?inline';
14
+ import idCardGood from '../assets/icons/guidelines/id-card/good.svg?inline';
15
+ import idCardNotCropped from '../assets/icons/guidelines/id-card/not-cropped.svg?inline';
16
+ import idCardNotBlurry from '../assets/icons/guidelines/id-card/not-blurry.svg?inline';
17
+ import idCardNotReflective from '../assets/icons/guidelines/id-card/not-reflective.svg?inline';
18
+ import passportGood from '../assets/icons/guidelines/passport/good.svg?inline';
19
+ import passportNotCropped from '../assets/icons/guidelines/passport/not-cropped.svg?inline';
20
+ import passportNotBlurry from '../assets/icons/guidelines/passport/not-blurry.svg?inline';
21
+ import passportNotReflective from '../assets/icons/guidelines/passport/not-reflective.svg?inline';
22
+ import greenbookGood from '../assets/icons/guidelines/greenbook/good.svg?inline';
23
+ import greenbookNotCropped from '../assets/icons/guidelines/greenbook/not-cropped.svg?inline';
24
+ import greenbookNotBlurry from '../assets/icons/guidelines/greenbook/not-blurry.svg?inline';
25
+ import greenbookNotReflective from '../assets/icons/guidelines/greenbook/not-reflective.svg?inline';
7
26
 
8
27
  // ── Visual assets ────────────────────────────────────────────────────────────
9
- const HERO_ID_CARD_LOTTIE_URL = new URL(
10
- '../assets/lottie/taking photo of ID.lottie',
11
- import.meta.url,
12
- ).href;
28
+ const HERO_ID_CARD_LOTTIE_URL = idCardLottie;
29
+ const HERO_PASSPORT_LOTTIE_URL = passportLottie;
30
+ const HERO_GREENBOOK_LOTTIE_URL = greenbookLottie;
13
31
 
14
- const HERO_PASSPORT_LOTTIE_URL = new URL(
15
- '../assets/lottie/taking photo of passport 2.lottie',
16
- import.meta.url,
17
- ).href;
18
-
19
- const HERO_GREENBOOK_LOTTIE_URL = new URL(
20
- '../assets/lottie/taking photo of green book passport.lottie',
21
- import.meta.url,
22
- ).href;
23
-
24
- const HERO_IMAGE_FALLBACK_URL =
25
- 'https://www.figma.com/api/mcp/asset/be898e16-cf3f-4c91-86ed-fbd2aa436e49';
32
+ // Card-flip animation shown on the back-of-ID instruction screen.
33
+ const FLIP_LOTTIE_URL = idCardFlipLottie;
26
34
 
27
35
  type DocumentVariant = 'id-card' | 'passport' | 'greenbook';
28
36
  type GuidelineKey = 'good' | 'not-cropped' | 'not-blurry' | 'not-reflective';
29
37
 
30
38
  interface HeroAssetConfig {
31
39
  animationSrc: string;
32
- fallbackAlt: string;
33
- fallbackSrc: string;
34
40
  }
35
41
 
36
42
  const HERO_ASSETS: Record<DocumentVariant, HeroAssetConfig> = {
37
- 'id-card': {
38
- animationSrc: HERO_ID_CARD_LOTTIE_URL,
39
- fallbackAlt: 'Phone capturing an ID card on a desk',
40
- fallbackSrc: HERO_IMAGE_FALLBACK_URL,
41
- },
42
- passport: {
43
- animationSrc: HERO_PASSPORT_LOTTIE_URL,
44
- fallbackAlt: 'Phone capturing a passport on a desk',
45
- fallbackSrc: HERO_IMAGE_FALLBACK_URL,
46
- },
47
- greenbook: {
48
- animationSrc: HERO_GREENBOOK_LOTTIE_URL,
49
- fallbackAlt: 'Phone capturing a green book passport on a desk',
50
- fallbackSrc: HERO_IMAGE_FALLBACK_URL,
51
- },
43
+ 'id-card': { animationSrc: HERO_ID_CARD_LOTTIE_URL },
44
+ passport: { animationSrc: HERO_PASSPORT_LOTTIE_URL },
45
+ greenbook: { animationSrc: HERO_GREENBOOK_LOTTIE_URL },
52
46
  };
53
47
 
54
48
  const GUIDELINE_ICONS: Record<DocumentVariant, Record<GuidelineKey, string>> = {
55
49
  'id-card': {
56
- good: new URL(
57
- '../assets/icons/guidelines/id-card/good.svg',
58
- import.meta.url,
59
- ).href,
60
- 'not-cropped': new URL(
61
- '../assets/icons/guidelines/id-card/not-cropped.svg',
62
- import.meta.url,
63
- ).href,
64
- 'not-blurry': new URL(
65
- '../assets/icons/guidelines/id-card/not-blurry.svg',
66
- import.meta.url,
67
- ).href,
68
- 'not-reflective': new URL(
69
- '../assets/icons/guidelines/id-card/not-reflective.svg',
70
- import.meta.url,
71
- ).href,
50
+ good: idCardGood,
51
+ 'not-cropped': idCardNotCropped,
52
+ 'not-blurry': idCardNotBlurry,
53
+ 'not-reflective': idCardNotReflective,
72
54
  },
73
55
  passport: {
74
- good: new URL(
75
- '../assets/icons/guidelines/passport/good.svg',
76
- import.meta.url,
77
- ).href,
78
- 'not-cropped': new URL(
79
- '../assets/icons/guidelines/passport/not-cropped.svg',
80
- import.meta.url,
81
- ).href,
82
- 'not-blurry': new URL(
83
- '../assets/icons/guidelines/passport/not-blurry.svg',
84
- import.meta.url,
85
- ).href,
86
- 'not-reflective': new URL(
87
- '../assets/icons/guidelines/passport/not-reflective.svg',
88
- import.meta.url,
89
- ).href,
56
+ good: passportGood,
57
+ 'not-cropped': passportNotCropped,
58
+ 'not-blurry': passportNotBlurry,
59
+ 'not-reflective': passportNotReflective,
90
60
  },
91
61
  greenbook: {
92
- good: new URL(
93
- '../assets/icons/guidelines/greenbook/good.svg',
94
- import.meta.url,
95
- ).href,
96
- 'not-cropped': new URL(
97
- '../assets/icons/guidelines/greenbook/not-cropped.svg',
98
- import.meta.url,
99
- ).href,
100
- 'not-blurry': new URL(
101
- '../assets/icons/guidelines/greenbook/not-blurry.svg',
102
- import.meta.url,
103
- ).href,
104
- 'not-reflective': new URL(
105
- '../assets/icons/guidelines/greenbook/not-reflective.svg',
106
- import.meta.url,
107
- ).href,
62
+ good: greenbookGood,
63
+ 'not-cropped': greenbookNotCropped,
64
+ 'not-blurry': greenbookNotBlurry,
65
+ 'not-reflective': greenbookNotReflective,
108
66
  },
109
67
  };
110
68
 
@@ -135,15 +93,13 @@ function getTextDirection(dir?: string): 'ltr' | 'rtl' | 'auto' {
135
93
 
136
94
  interface HeroLottieProps {
137
95
  animationSrc: string;
138
- fallbackSrc: string;
139
- fallbackAlt: string;
96
+ // How the animation is scaled within the canvas. 'cover' fills and crops
97
+ // (front hero card); 'contain' fits the whole frame without cropping
98
+ // (flip card, which must stay fully visible).
99
+ fit?: 'cover' | 'contain';
140
100
  }
141
101
 
142
- function HeroLottie({
143
- animationSrc,
144
- fallbackSrc,
145
- fallbackAlt,
146
- }: HeroLottieProps) {
102
+ function HeroLottie({ animationSrc, fit = 'cover' }: HeroLottieProps) {
147
103
  const canvasRef = useRef<HTMLCanvasElement>(null);
148
104
  const [hasError, setHasError] = useState(false);
149
105
 
@@ -161,7 +117,7 @@ function HeroLottie({
161
117
  loop: true,
162
118
  layout: {
163
119
  align: [0.5, 0.5],
164
- fit: 'cover',
120
+ fit,
165
121
  },
166
122
  renderConfig: {
167
123
  autoResize: true,
@@ -170,33 +126,34 @@ function HeroLottie({
170
126
  src: animationSrc,
171
127
  });
172
128
 
173
- animation.addEventListener('loadError', () => {
174
- if (isMounted) {
175
- setHasError(true);
129
+ // Tear the animation down before unmounting the canvas so dotLottie
130
+ // stops trying to render into a detached node (which would otherwise
131
+ // log render errors until the component itself unmounts).
132
+ const handleError = () => {
133
+ if (!isMounted) return;
134
+ try {
135
+ animation.destroy();
136
+ } catch {
137
+ /* destroy may throw if already torn down; ignore */
176
138
  }
177
- });
139
+ setHasError(true);
140
+ };
178
141
 
179
- animation.addEventListener('renderError', () => {
180
- if (isMounted) {
181
- setHasError(true);
182
- }
183
- });
142
+ animation.addEventListener('loadError', handleError);
143
+ animation.addEventListener('renderError', handleError);
184
144
 
185
145
  return () => {
186
146
  isMounted = false;
187
- animation.destroy();
147
+ try {
148
+ animation.destroy();
149
+ } catch {
150
+ /* may already be destroyed by handleError */
151
+ }
188
152
  };
189
- }, [animationSrc]);
153
+ }, [animationSrc, fit]);
190
154
 
191
155
  return (
192
156
  <div class="doc-instr-hero-media">
193
- <img
194
- class="doc-instr-hero-img"
195
- src={fallbackSrc}
196
- alt={fallbackAlt}
197
- loading="eager"
198
- decoding="async"
199
- />
200
157
  {!hasError && (
201
158
  <canvas
202
159
  ref={canvasRef}
@@ -210,27 +167,6 @@ function HeroLottie({
210
167
 
211
168
  // ── Inline SVG helpers ───────────────────────────────────────────────────────
212
169
 
213
- function BackArrowIcon() {
214
- return (
215
- <svg
216
- aria-hidden="true"
217
- width="24"
218
- height="24"
219
- viewBox="0 0 24 24"
220
- fill="none"
221
- xmlns="http://www.w3.org/2000/svg"
222
- >
223
- <path
224
- d="M19 12H5M5 12L12 19M5 12L12 5"
225
- stroke="white"
226
- stroke-width="2"
227
- stroke-linecap="round"
228
- stroke-linejoin="round"
229
- />
230
- </svg>
231
- );
232
- }
233
-
234
170
  function ArrowRightIcon() {
235
171
  return (
236
172
  <svg
@@ -307,7 +243,7 @@ function PoweredBySmileIdLogo() {
307
243
  style={{ width: '90px', height: '9px' }}
308
244
  >
309
245
  <path
310
- d="M0.544 7V1.4H2.616C3.064 1.4 3.43467 1.47467 3.728 1.624C4.02133 1.77333 4.24 1.97867 4.384 2.24C4.528 2.50133 4.6 2.79467 4.6 3.12C4.6 3.42933 4.53067 3.71467 4.392 3.976C4.25333 4.232 4.03733 4.44 3.744 4.6C3.45067 4.75467 3.07467 4.832 2.616 4.832H1.568V7H0.544ZM1.568 4H2.552C2.90933 4 3.16533 3.92267 3.32 3.768C3.48 3.608 3.56 3.392 3.56 3.12C3.56 2.84267 3.48 2.62667 3.32 2.472C3.16533 2.312 2.90933 2.232 2.552 2.232H1.568V4ZM7.08025 7.096C6.69625 7.096 6.34958 7.008 6.04025 6.832C5.73625 6.656 5.49358 6.41333 5.31225 6.104C5.13625 5.78933 5.04825 5.42667 5.04825 5.016C5.04825 4.60533 5.13892 4.24533 5.32025 3.936C5.50158 3.62133 5.74425 3.376 6.04825 3.2C6.35758 3.024 6.70425 2.936 7.08825 2.936C7.46692 2.936 7.80825 3.024 8.11225 3.2C8.42158 3.376 8.66425 3.62133 8.84025 3.936C9.02158 4.24533 9.11225 4.60533 9.11225 5.016C9.11225 5.42667 9.02158 5.78933 8.84025 6.104C8.66425 6.41333 8.42158 6.656 8.11225 6.832C7.80292 7.008 7.45892 7.096 7.08025 7.096ZM7.08025 6.208C7.34692 6.208 7.57892 6.10933 7.77625 5.912C7.97358 5.70933 8.07225 5.41067 8.07225 5.016C8.07225 4.62133 7.97358 4.32533 7.77625 4.128C7.57892 3.92533 7.34958 3.824 7.08825 3.824C6.81625 3.824 6.58158 3.92533 6.38425 4.128C6.19225 4.32533 6.09625 4.62133 6.38425 5.912C6.58158 6.10933 6.81358 6.208 7.08025 6.208ZM10.6632 7L9.50319 3.032H10.5192L11.2072 5.888L12.0072 3.032H13.1432L13.9432 5.888L14.6392 3.032H15.6552L14.4872 7H13.4232L12.5752 4.032L11.7272 7H10.6632ZM18.0886 7.096C17.6886 7.096 17.334 7.01067 17.0246 6.84C16.7153 6.66933 16.4726 6.42933 16.2966 6.12C16.1206 5.81067 16.0326 5.45333 16.0326 5.048C16.0326 4.63733 16.118 4.272 16.2886 3.952C16.4646 3.632 16.7046 3.384 17.0086 3.208C17.318 3.02667 17.6806 2.936 18.0966 2.936C18.486 2.936 18.83 3.02133 19.1286 3.192C19.4273 3.36267 19.6593 3.59733 19.8246 3.896C19.9953 4.18933 20.0806 4.51733 20.0806 4.88C20.0806 4.93867 20.078 5 20.0726 5.064C20.0726 5.128 20.07 5.19467 20.0646 5.264H17.0486C17.07 5.57333 17.1766 5.816 17.3686 5.992C17.566 6.168 17.8033 6.256 18.0806 6.256C18.2886 6.256 18.462 6.21067 18.6006 6.12C18.7446 6.024 18.8513 5.90133 18.9206 5.752H19.9606C19.886 6.00267 19.7606 6.232 19.5846 6.44C19.414 6.64267 19.2006 6.80267 18.9446 6.92C18.694 7.03733 18.4086 7.096 18.0886 7.096ZM18.0966 3.768C17.846 3.768 17.6246 3.84 17.4326 3.984C17.2406 4.12267 17.118 4.336 17.0646 4.624H19.0406C19.0246 4.36267 18.9286 4.15467 18.7526 4C18.5766 3.84533 18.358 3.768 18.0966 3.768ZM20.9419 7V3.032H21.8539L21.9499 3.776C22.0939 3.52 22.2885 3.31733 22.5339 3.168C22.7845 3.01333 23.0779 2.936 23.4139 2.936V4.016H23.1259C22.9019 4.016 22.7019 4.05067 22.5259 4.12C22.3499 4.18933 22.2112 4.30933 22.1099 4.48C22.0139 4.65067 21.9659 4.888 21.9659 5.192V7H20.9419ZM25.9714 7.096C25.5714 7.096 25.2168 7.01067 24.9074 6.84C24.5981 6.66933 24.3554 6.42933 24.1794 6.12C24.0034 5.81067 23.9154 5.45333 23.9154 5.048C23.9154 4.63733 24.0008 4.272 24.1714 3.952C24.3474 3.632 24.5874 3.384 24.8914 3.208C25.2008 3.02667 25.5634 2.936 25.9794 2.936C26.3688 2.936 26.7128 3.02133 27.0114 3.192C27.3101 3.36267 27.5421 3.59733 27.7074 3.896C27.8781 4.18933 27.9634 4.51733 27.9634 4.88C27.9634 4.93867 27.9608 5 27.9554 5.064C27.9554 5.128 27.9528 5.19467 27.9474 5.264H24.9314C24.9528 5.57333 25.0594 5.816 25.2514 5.992C25.4488 6.168 25.6861 6.256 25.9634 6.256C26.1714 6.256 26.3448 6.21067 26.4834 6.12C26.6274 6.024 26.7341 5.90133 26.8034 5.752H27.8434C27.7688 6.00267 27.6434 6.232 27.4674 6.44C27.2968 6.64267 27.0834 6.80267 26.8274 6.92C26.5768 7.03733 26.2914 7.096 25.9714 7.096ZM25.9794 3.768C25.7288 3.768 25.5074 3.84 25.3154 3.984C25.1234 4.12267 25.0008 4.336 24.9474 4.624H26.9234C26.9074 4.36267 26.8114 4.15467 26.6354 4C26.4594 3.84533 26.2408 3.768 25.9794 3.768ZM30.6487 7.096C30.2754 7.096 29.942 7.00533 29.6487 6.824C29.3554 6.64267 29.1234 6.39467 28.9527 6.08C28.782 5.76533 28.6967 5.408 28.6967 5.008C28.6967 4.608 28.782 4.25333 28.9527 3.944C29.1234 3.62933 29.3554 3.384 29.6487 3.208C29.942 3.02667 30.2754 2.936 30.6487 2.936C30.9474 2.936 31.2087 2.992 31.4327 3.104C31.6567 3.216 31.838 3.37333 31.9767 3.576V1.24H33.0007V7H32.0887L31.9767 6.432C31.8487 6.608 31.678 6.76267 31.4647 6.896C31.2567 7.02933 30.9847 7.096 30.6487 7.096ZM30.8647 6.2C31.1954 6.2 31.4647 6.09067 31.6727 5.872C31.886 5.648 31.9927 5.36267 31.9927 5.016C31.9927 4.66933 31.886 4.38667 31.6727 4.168C31.4647 3.944 31.1954 3.832 30.8647 3.832C30.5394 3.832 30.27 3.94133 30.0567 4.16C29.8434 4.37867 29.7367 4.66133 29.7367 5.008C29.7367 5.35467 29.8434 5.64 30.0567 5.864C30.27 6.088 30.5394 6.2 30.8647 6.2ZM38.3017 7.096C38.003 7.096 37.7417 7.04 37.5177 6.928C37.2937 6.816 37.1124 6.65867 36.9737 6.456L36.8617 7H35.9497V1.24H36.9737V3.6C37.1017 3.424 37.2697 3.26933 37.4777 3.136C37.691 3.00267 37.9657 2.936 38.3017 2.936C38.675 2.936 39.0084 3.02667 39.3017 3.208C39.595 3.38933 39.827 3.63733 39.9977 3.952C40.1684 4.26667 40.2537 4.624 40.2537 5.024C40.2537 5.424 40.1684 5.78133 39.9977 6.096C39.827 6.40533 39.595 6.65067 39.3017 6.832C39.0084 7.008 38.675 7.096 38.3017 7.096ZM38.0857 6.2C38.411 6.2 38.6804 6.09067 38.8937 5.872C39.107 5.65333 39.2137 5.37067 39.2137 5.024C39.2137 4.67733 39.107 4.392 38.8937 4.168C38.6804 3.944 38.411 3.832 38.0857 3.832C37.755 3.832 37.483 3.944 37.2697 4.168C37.0617 4.38667 36.9577 4.66933 36.9577 5.016C36.9577 5.36267 37.0617 5.648 37.2697 5.872C37.483 6.09067 37.755 6.2 38.0857 6.2ZM41.3051 8.76L42.2251 6.736H41.9851L40.4411 3.032H41.5531L42.6651 5.824L43.8251 3.032H44.9131L42.3931 8.76H41.3051Z"
246
+ d="M0.544 7V1.4H2.616C3.064 1.4 3.43467 1.47467 3.728 1.624C4.02133 1.77333 4.24 1.97867 4.384 2.24C4.528 2.50133 4.6 2.79467 4.6 3.12C4.6 3.42933 4.53067 3.71467 4.392 3.976C4.25333 4.232 4.03733 4.44 3.744 4.6C3.45067 4.75467 3.07467 4.832 2.616 4.832H1.568V7H0.544ZM1.568 4H2.552C2.90933 4 3.16533 3.92267 3.32 3.768C3.48 3.608 3.56 3.392 3.56 3.12C3.56 2.84267 3.48 2.62667 3.32 2.472C3.16533 2.312 2.90933 2.232 2.552 2.232H1.568V4ZM7.08025 7.096C6.69625 7.096 6.34958 7.008 6.04025 6.832C5.73625 6.656 5.49358 6.41333 5.31225 6.104C5.13625 5.78933 5.04825 5.42667 5.04825 5.016C5.04825 4.60533 5.13892 4.24533 5.32025 3.936C5.50158 3.62133 5.74425 3.376 6.04825 3.2C6.35758 3.024 6.70425 2.936 7.08825 2.936C7.46692 2.936 7.80825 3.024 8.11225 3.2C8.42158 3.376 8.66425 3.62133 8.84025 3.936C9.02158 4.24533 9.11225 4.60533 9.11225 5.016C9.11225 5.42667 9.02158 5.78933 8.84025 6.104C8.66425 6.41333 8.42158 6.656 8.11225 6.832C7.80292 7.008 7.45892 7.096 7.08025 7.096ZM7.08025 6.208C7.34692 6.208 7.57892 6.10933 7.77625 5.912C7.97358 5.70933 8.07225 5.41067 8.07225 5.016C8.07225 4.62133 7.97358 4.32533 7.77625 4.128C7.57892 3.92533 7.34958 3.824 7.08825 3.824C6.81625 3.824 6.58158 3.92533 6.38425 4.128C6.19225 4.32533 6.09625 4.62133 6.09625 5.016C6.09625 5.41067 6.19225 5.70933 6.38425 5.912C6.58158 6.10933 6.81358 6.208 7.08025 6.208ZM10.6632 7L9.50319 3.032H10.5192L11.2072 5.888L12.0072 3.032H13.1432L13.9432 5.888L14.6392 3.032H15.6552L14.4872 7H13.4232L12.5752 4.032L11.7272 7H10.6632ZM18.0886 7.096C17.6886 7.096 17.334 7.01067 17.0246 6.84C16.7153 6.66933 16.4726 6.42933 16.2966 6.12C16.1206 5.81067 16.0326 5.45333 16.0326 5.048C16.0326 4.63733 16.118 4.272 16.2886 3.952C16.4646 3.632 16.7046 3.384 17.0086 3.208C17.318 3.02667 17.6806 2.936 18.0966 2.936C18.486 2.936 18.83 3.02133 19.1286 3.192C19.4273 3.36267 19.6593 3.59733 19.8246 3.896C19.9953 4.18933 20.0806 4.51733 20.0806 4.88C20.0806 4.93867 20.078 5 20.0726 5.064C20.0726 5.128 20.07 5.19467 20.0646 5.264H17.0486C17.07 5.57333 17.1766 5.816 17.3686 5.992C17.566 6.168 17.8033 6.256 18.0806 6.256C18.2886 6.256 18.462 6.21067 18.6006 6.12C18.7446 6.024 18.8513 5.90133 18.9206 5.752H19.9606C19.886 6.00267 19.7606 6.232 19.5846 6.44C19.414 6.64267 19.2006 6.80267 18.9446 6.92C18.694 7.03733 18.4086 7.096 18.0886 7.096ZM18.0966 3.768C17.846 3.768 17.6246 3.84 17.4326 3.984C17.2406 4.12267 17.118 4.336 17.0646 4.624H19.0406C19.0246 4.36267 18.9286 4.15467 18.7526 4C18.5766 3.84533 18.358 3.768 18.0966 3.768ZM20.9419 7V3.032H21.8539L21.9499 3.776C22.0939 3.52 22.2885 3.31733 22.5339 3.168C22.7845 3.01333 23.0779 2.936 23.4139 2.936V4.016H23.1259C22.9019 4.016 22.7019 4.05067 22.5259 4.12C22.3499 4.18933 22.2112 4.30933 22.1099 4.48C22.0139 4.65067 21.9659 4.888 21.9659 5.192V7H20.9419ZM25.9714 7.096C25.5714 7.096 25.2168 7.01067 24.9074 6.84C24.5981 6.66933 24.3554 6.42933 24.1794 6.12C24.0034 5.81067 23.9154 5.45333 23.9154 5.048C23.9154 4.63733 24.0008 4.272 24.1714 3.952C24.3474 3.632 24.5874 3.384 24.8914 3.208C25.2008 3.02667 25.5634 2.936 25.9794 2.936C26.3688 2.936 26.7128 3.02133 27.0114 3.192C27.3101 3.36267 27.5421 3.59733 27.7074 3.896C27.8781 4.18933 27.9634 4.51733 27.9634 4.88C27.9634 4.93867 27.9608 5 27.9554 5.064C27.9554 5.128 27.9528 5.19467 27.9474 5.264H24.9314C24.9528 5.57333 25.0594 5.816 25.2514 5.992C25.4488 6.168 25.6861 6.256 25.9634 6.256C26.1714 6.256 26.3448 6.21067 26.4834 6.12C26.6274 6.024 26.7341 5.90133 26.8034 5.752H27.8434C27.7688 6.00267 27.6434 6.232 27.4674 6.44C27.2968 6.64267 27.0834 6.80267 26.8274 6.92C26.5768 7.03733 26.2914 7.096 25.9714 7.096ZM25.9794 3.768C25.7288 3.768 25.5074 3.84 25.3154 3.984C25.1234 4.12267 25.0008 4.336 24.9474 4.624H26.9234C26.9074 4.36267 26.8114 4.15467 26.6354 4C26.4594 3.84533 26.2408 3.768 25.9794 3.768ZM30.6487 7.096C30.2754 7.096 29.942 7.00533 29.6487 6.824C29.3554 6.64267 29.1234 6.39467 28.9527 6.08C28.782 5.76533 28.6967 5.408 28.6967 5.008C28.6967 4.608 28.782 4.25333 28.9527 3.944C29.1234 3.62933 29.3554 3.384 29.6487 3.208C29.942 3.02667 30.2754 2.936 30.6487 2.936C30.9474 2.936 31.2087 2.992 31.4327 3.104C31.6567 3.216 31.838 3.37333 31.9767 3.576V1.24H33.0007V7H32.0887L31.9767 6.432C31.8487 6.608 31.678 6.76267 31.4647 6.896C31.2567 7.02933 30.9847 7.096 30.6487 7.096ZM30.8647 6.2C31.1954 6.2 31.4647 6.09067 31.6727 5.872C31.886 5.648 31.9927 5.36267 31.9927 5.016C31.9927 4.66933 31.886 4.38667 31.6727 4.168C31.4647 3.944 31.1954 3.832 30.8647 3.832C30.5394 3.832 30.27 3.94133 30.0567 4.16C29.8434 4.37867 29.7367 4.66133 29.7367 5.008C29.7367 5.35467 29.8434 5.64 30.0567 5.864C30.27 6.088 30.5394 6.2 30.8647 6.2ZM38.3017 7.096C38.003 7.096 37.7417 7.04 37.5177 6.928C37.2937 6.816 37.1124 6.65867 36.9737 6.456L36.8617 7H35.9497V1.24H36.9737V3.6C37.1017 3.424 37.2697 3.26933 37.4777 3.136C37.691 3.00267 37.9657 2.936 38.3017 2.936C38.675 2.936 39.0084 3.02667 39.3017 3.208C39.595 3.38933 39.827 3.63733 39.9977 3.952C40.1684 4.26667 40.2537 4.624 40.2537 5.024C40.2537 5.424 40.1684 5.78133 39.9977 6.096C39.827 6.40533 39.595 6.65067 39.3017 6.832C39.0084 7.008 38.675 7.096 38.3017 7.096ZM38.0857 6.2C38.411 6.2 38.6804 6.09067 38.8937 5.872C39.107 5.65333 39.2137 5.37067 39.2137 5.024C39.2137 4.67733 39.107 4.392 38.8937 4.168C38.6804 3.944 38.411 3.832 38.0857 3.832C37.755 3.832 37.483 3.944 37.2697 4.168C37.0617 4.38667 36.9577 4.66933 36.9577 5.016C36.9577 5.36267 37.0617 5.648 37.2697 5.872C37.483 6.09067 37.755 6.2 38.0857 6.2ZM41.3051 8.76L42.2251 6.736H41.9851L40.4411 3.032H41.5531L42.6651 5.824L43.8251 3.032H44.9131L42.3931 8.76H41.3051Z"
311
247
  fill="#001096"
312
248
  />
313
249
  <g clip-path="url(#clip0_du_1)">
@@ -359,6 +295,7 @@ function PoweredBySmileIdLogo() {
359
295
  interface Props {
360
296
  dir?: string;
361
297
  'document-type'?: string;
298
+ 'side-of-id'?: string;
362
299
  title?: string;
363
300
  'hide-attribution'?: string | boolean;
364
301
  'hide-back'?: string | boolean;
@@ -370,6 +307,7 @@ interface Props {
370
307
  const DocumentCaptureInstructions: FunctionComponent<Props> = ({
371
308
  dir,
372
309
  'document-type': documentType = '',
310
+ 'side-of-id': sideOfId = 'Front',
373
311
  title = '',
374
312
  'hide-attribution': hideAttributionProp = false,
375
313
  'hide-back': hideBackProp = false,
@@ -383,6 +321,7 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
383
321
  const direction = getTextDirection(dir);
384
322
  const heroAsset = HERO_ASSETS[documentVariant];
385
323
  const rootRef = useRef<HTMLDivElement>(null);
324
+ const navigationRef = useRef<HTMLElement | null>(null);
386
325
  const guidelineItems: GuidelineItem[] = [
387
326
  { key: 'good', label: t('document.instructions.guidelines.good') },
388
327
  {
@@ -402,7 +341,8 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
402
341
  const dispatchInstructionEvent = (
403
342
  eventName:
404
343
  | 'document-capture-instructions.cancelled'
405
- | 'document-capture-instructions.capture',
344
+ | 'document-capture-instructions.capture'
345
+ | 'document-capture-instructions.skip',
406
346
  ) => {
407
347
  const rootNode = rootRef.current?.getRootNode();
408
348
  const shadowHost = (rootNode as ShadowRoot)?.host as
@@ -420,94 +360,28 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
420
360
  dispatchInstructionEvent('document-capture-instructions.cancelled');
421
361
  };
422
362
 
363
+ // Bridge the <smileid-navigation> element's back event to handleBack.
364
+ useEffect(() => {
365
+ const navigation = navigationRef.current;
366
+ if (!navigation || hideBack) return undefined;
367
+ const onNavigationBack = () => handleBack();
368
+ navigation.addEventListener('navigation.back', onNavigationBack);
369
+ return () =>
370
+ navigation.removeEventListener('navigation.back', onNavigationBack);
371
+ }, [hideBack]);
372
+
423
373
  const handleStartCapture = () => {
424
374
  dispatchInstructionEvent('document-capture-instructions.capture');
425
375
  };
426
376
 
427
- return (
428
- <div ref={rootRef} class="doc-instr-root" dir={direction}>
429
- {/* ── Back button ──────────────────────────────────────── */}
430
- {!hideBack && (
431
- <button
432
- class="back-button"
433
- type="button"
434
- aria-label="Go back"
435
- onClick={handleBack}
436
- >
437
- <BackArrowIcon />
438
- </button>
439
- )}
440
-
441
- {/* ── Scrollable content ───────────────────────────────── */}
442
- <div class="doc-instr-scroll">
443
- {/* ── Title ─────────────────────────────────────────── */}
444
- <div class="doc-instr-title-block">
445
- <h1 class="doc-instr-title">
446
- <span class="doc-instr-title-regular">
447
- {t('document.instructions.captureTitlePrefix')}{' '}
448
- </span>
449
- <span class="doc-instr-title-type">
450
- {displayDocumentType || '<ID Type>'}
451
- </span>
452
- </h1>
453
- </div>
454
-
455
- {/* ── Hero illustration ─────────────────────────────── */}
456
- <div class="doc-instr-hero-card" aria-hidden="true">
457
- <HeroLottie
458
- animationSrc={heroAsset.animationSrc}
459
- fallbackSrc={heroAsset.fallbackSrc}
460
- fallbackAlt={heroAsset.fallbackAlt}
461
- />
462
- </div>
463
-
464
- {/* ── Capture guidelines ────────────────────────────── */}
465
- <div class="doc-instr-guidelines">
466
- <div class="doc-instr-guidelines-header">
467
- <GuidelinesIcon />
468
- <span class="doc-instr-guidelines-label">
469
- {t('document.instructions.captureGuidelines')}
470
- </span>
471
- </div>
472
-
473
- <div class="doc-instr-guidelines-grid">
474
- {guidelineItems.map((item) => (
475
- <div class="doc-instr-guide-item" key={item.label}>
476
- <div class="doc-instr-guide-thumb-wrap">
477
- <GuidelineThumbnail
478
- src={GUIDELINE_ICONS[documentVariant][item.key]}
479
- label={item.label}
480
- />
481
- </div>
482
- <p class="doc-instr-guide-caption">{item.label}</p>
483
- </div>
484
- ))}
485
- </div>
486
- </div>
487
- </div>
377
+ const handleSkip = () => {
378
+ dispatchInstructionEvent('document-capture-instructions.skip');
379
+ };
488
380
 
489
- {/* ── CTA button ───────────────────────────────────────── */}
490
- <div class="doc-instr-footer">
491
- <button
492
- id="take-photo"
493
- class="doc-instr-start-btn"
494
- type="button"
495
- onClick={handleStartCapture}
496
- >
497
- <span>{t('document.instructions.startCapture')}</span>
498
- <ArrowRightIcon />
499
- </button>
381
+ const isBack = String(sideOfId).toLowerCase() === 'back';
500
382
 
501
- {/* ── Attribution ───────────────────────────────────── */}
502
- {!hideAttribution && (
503
- <div class="doc-instr-attribution">
504
- <PoweredBySmileIdLogo />
505
- </div>
506
- )}
507
- </div>
508
-
509
- {/* ── Scoped styles ────────────────────────────────────── */}
510
- <style>{`
383
+ // Scoped styles shared by the front and back (flip) layouts.
384
+ const STYLES = `
511
385
  *,
512
386
  *::before,
513
387
  *::after {
@@ -534,32 +408,13 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
534
408
  overflow: hidden;
535
409
  }
536
410
 
537
- /* ── Back button ─────────────────────────────────────── */
538
- .back-button {
411
+ /* ── Back button (positions the shared <smileid-navigation>;
412
+ the circle, hover and focus styling live in its shadow) ── */
413
+ .back-nav {
539
414
  position: absolute;
540
415
  top: 24px;
541
416
  left: 20px;
542
417
  z-index: 10;
543
- width: 40px;
544
- height: 40px;
545
- border-radius: 50%;
546
- background: #2d2b2a;
547
- border: 1px solid rgba(255, 255, 255, 0.1);
548
- display: flex;
549
- align-items: center;
550
- justify-content: center;
551
- cursor: pointer;
552
- flex-shrink: 0;
553
- transition: opacity 0.15s ease;
554
- }
555
-
556
- .back-button:hover {
557
- opacity: 0.85;
558
- }
559
-
560
- .back-button:focus-visible {
561
- outline: 2px solid #151f72;
562
- outline-offset: 3px;
563
418
  }
564
419
 
565
420
  /* ── Scrollable area ──────────────────────────────────── */
@@ -608,14 +463,6 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
608
463
  justify-content: center;
609
464
  }
610
465
 
611
- .doc-instr-hero-img {
612
- width: 100%;
613
- height: 100%;
614
- object-fit: cover;
615
- display: block;
616
- border-radius: inherit;
617
- }
618
-
619
466
  .doc-instr-hero-media {
620
467
  position: relative;
621
468
  width: 100%;
@@ -731,6 +578,89 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
731
578
  outline-offset: 3px;
732
579
  }
733
580
 
581
+ /* ── Back / flip layout ──────────────────────────────── */
582
+ .doc-instr-root--back {
583
+ background: #ffffff;
584
+ }
585
+
586
+ .doc-instr-back-content {
587
+ flex: 1;
588
+ display: flex;
589
+ flex-direction: column;
590
+ padding: 48px 20px 0;
591
+ min-height: 0;
592
+ }
593
+
594
+ .doc-instr-flip-title {
595
+ font-size: clamp(20px, 5vw, 24px);
596
+ font-weight: 800;
597
+ line-height: 1.25;
598
+ letter-spacing: -0.025em;
599
+ text-align: center;
600
+ color: #0f172b;
601
+ }
602
+
603
+ .doc-instr-flip-title-type {
604
+ color: #151f72;
605
+ }
606
+
607
+ .doc-instr-flip-hero {
608
+ flex: 1;
609
+ display: flex;
610
+ align-items: center;
611
+ justify-content: center;
612
+ min-height: 0;
613
+ padding: 16px 0;
614
+ }
615
+
616
+ .doc-instr-flip-hero-inner {
617
+ width: 100%;
618
+ max-width: 420px;
619
+ aspect-ratio: 1 / 1;
620
+ position: relative;
621
+ }
622
+
623
+ /* ── Skip button (secondary outline) ─────────────────── */
624
+ .doc-instr-back-actions {
625
+ width: 100%;
626
+ display: flex;
627
+ flex-direction: column;
628
+ align-items: center;
629
+ gap: 12px;
630
+ }
631
+
632
+ .doc-instr-skip-btn {
633
+ width: 100%;
634
+ max-width: 400px;
635
+ height: 48px;
636
+ background: #ffffff;
637
+ color: #151f72;
638
+ border: 1px solid #151f72;
639
+ border-radius: 32px;
640
+ font-size: 14px;
641
+ font-weight: 700;
642
+ cursor: pointer;
643
+ display: flex;
644
+ align-items: center;
645
+ justify-content: center;
646
+ box-shadow: 0px 1px 1px 0px rgba(16, 24, 40, 0.05);
647
+ transition: opacity 0.15s ease, transform 0.1s ease;
648
+ letter-spacing: 0.01em;
649
+ }
650
+
651
+ .doc-instr-skip-btn:hover {
652
+ opacity: 0.85;
653
+ }
654
+
655
+ .doc-instr-skip-btn:active {
656
+ transform: scale(0.98);
657
+ }
658
+
659
+ .doc-instr-skip-btn:focus-visible {
660
+ outline: 2px solid #151f72;
661
+ outline-offset: 3px;
662
+ }
663
+
734
664
  /* ── Attribution ─────────────────────────────────────── */
735
665
  .doc-instr-attribution {
736
666
  display: flex;
@@ -751,7 +681,7 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
751
681
  padding-right: 32px;
752
682
  }
753
683
 
754
- .back-button {
684
+ .back-nav {
755
685
  top: 28px;
756
686
  left: 28px;
757
687
  }
@@ -763,6 +693,10 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
763
693
  .doc-instr-guide-caption {
764
694
  font-size: 10px;
765
695
  }
696
+
697
+ .doc-instr-back-content {
698
+ padding-top: 56px;
699
+ }
766
700
  }
767
701
 
768
702
  /* ── Very small screens (< 360px) ────────────────────── */
@@ -778,8 +712,156 @@ const DocumentCaptureInstructions: FunctionComponent<Props> = ({
778
712
  .doc-instr-title {
779
713
  font-size: 18px;
780
714
  }
715
+
716
+ .doc-instr-flip-title {
717
+ font-size: 18px;
718
+ }
781
719
  }
782
- `}</style>
720
+ `;
721
+
722
+ if (isBack) {
723
+ return (
724
+ <div
725
+ ref={rootRef}
726
+ class="doc-instr-root doc-instr-root--back"
727
+ dir={direction}
728
+ >
729
+ {/* ── Content: centered title + flip animation ─────────── */}
730
+ <div class="doc-instr-back-content">
731
+ <h1 class="doc-instr-flip-title">
732
+ <span class="doc-instr-flip-title-regular">
733
+ {t('document.instructions.flipTitlePrefix')}{' '}
734
+ </span>
735
+ <span class="doc-instr-flip-title-type">
736
+ {displayDocumentType || '<ID Type>'}
737
+ </span>
738
+ </h1>
739
+
740
+ <div class="doc-instr-flip-hero" aria-hidden="true">
741
+ <div class="doc-instr-flip-hero-inner">
742
+ <HeroLottie animationSrc={FLIP_LOTTIE_URL} fit="contain" />
743
+ </div>
744
+ </div>
745
+ </div>
746
+
747
+ {/* ── Actions: Capture Back + Skip ─────────────────────── */}
748
+ <div class="doc-instr-footer">
749
+ <div class="doc-instr-back-actions">
750
+ <button
751
+ id="capture-back"
752
+ class="doc-instr-start-btn"
753
+ type="button"
754
+ onClick={handleStartCapture}
755
+ >
756
+ <span>{t('document.instructions.captureBackButton')}</span>
757
+ <ArrowRightIcon />
758
+ </button>
759
+ <button
760
+ id="skip-back"
761
+ class="doc-instr-skip-btn"
762
+ type="button"
763
+ onClick={handleSkip}
764
+ >
765
+ {t('document.instructions.skipButton')}
766
+ </button>
767
+ </div>
768
+
769
+ {!hideAttribution && (
770
+ <div class="doc-instr-attribution">
771
+ <PoweredBySmileIdLogo />
772
+ </div>
773
+ )}
774
+ </div>
775
+
776
+ {/* ── Scoped styles (shared with front layout) ─────────── */}
777
+ <style>{STYLES}</style>
778
+ </div>
779
+ );
780
+ }
781
+
782
+ return (
783
+ <div ref={rootRef} class="doc-instr-root" dir={direction}>
784
+ {/* ── Back button (shared <smileid-navigation>, back only) ─ */}
785
+ {!hideBack && (
786
+ // @ts-expect-error preact-custom-element lacks ref/attr types
787
+ <smileid-navigation
788
+ ref={navigationRef}
789
+ hide-close=""
790
+ class="back-nav"
791
+ style={{
792
+ '--smileid-navigation-button-bg': '#2d2b2a',
793
+ '--smileid-navigation-icon-color': '#ffffff',
794
+ '--smileid-navigation-focus-color': '#151f72',
795
+ }}
796
+ />
797
+ )}
798
+
799
+ {/* ── Scrollable content ───────────────────────────────── */}
800
+ <div class="doc-instr-scroll">
801
+ {/* ── Title ─────────────────────────────────────────── */}
802
+ <div class="doc-instr-title-block">
803
+ <h1 class="doc-instr-title">
804
+ <span class="doc-instr-title-regular">
805
+ {t('document.instructions.captureTitlePrefix')}{' '}
806
+ </span>
807
+ <span class="doc-instr-title-type">
808
+ {displayDocumentType || '<ID Type>'}
809
+ </span>
810
+ </h1>
811
+ </div>
812
+
813
+ {/* ── Hero illustration ─────────────────────────────── */}
814
+ <div class="doc-instr-hero-card" aria-hidden="true">
815
+ <HeroLottie animationSrc={heroAsset.animationSrc} />
816
+ </div>
817
+
818
+ {/* ── Capture guidelines ────────────────────────────── */}
819
+ <div class="doc-instr-guidelines">
820
+ <div class="doc-instr-guidelines-header">
821
+ <GuidelinesIcon />
822
+ <span class="doc-instr-guidelines-label">
823
+ {t('document.instructions.captureGuidelines')}
824
+ </span>
825
+ </div>
826
+
827
+ <div class="doc-instr-guidelines-grid">
828
+ {guidelineItems.map((item) => (
829
+ <div class="doc-instr-guide-item" key={item.label}>
830
+ <div class="doc-instr-guide-thumb-wrap">
831
+ <GuidelineThumbnail
832
+ src={GUIDELINE_ICONS[documentVariant][item.key]}
833
+ label={item.label}
834
+ />
835
+ </div>
836
+ <p class="doc-instr-guide-caption">{item.label}</p>
837
+ </div>
838
+ ))}
839
+ </div>
840
+ </div>
841
+ </div>
842
+
843
+ {/* ── CTA button ───────────────────────────────────────── */}
844
+ <div class="doc-instr-footer">
845
+ <button
846
+ id="take-photo"
847
+ class="doc-instr-start-btn"
848
+ type="button"
849
+ onClick={handleStartCapture}
850
+ >
851
+ <span>{t('document.instructions.startCapture')}</span>
852
+ <ArrowRightIcon />
853
+ </button>
854
+
855
+ {/* ── Attribution ───────────────────────────────────── */}
856
+ {!hideAttribution && (
857
+ <div class="doc-instr-attribution">
858
+ <PoweredBySmileIdLogo />
859
+ </div>
860
+ )}
861
+ </div>
862
+
863
+ {/* ── Scoped styles (shared with the back layout) ─────── */}
864
+ <style>{STYLES}</style>
783
865
  </div>
784
866
  );
785
867
  };
@@ -796,6 +878,7 @@ if (
796
878
  [
797
879
  'dir',
798
880
  'document-type',
881
+ 'side-of-id',
799
882
  'title',
800
883
  'hide-attribution',
801
884
  'hide-back',