@incodetech/prizma-core-screens 0.2.4 → 0.2.7
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.
- package/dist/CurpEnterScreen.d.ts +7 -0
- package/dist/CurpEnterScreen.js +17 -3
- package/dist/CurpGenerateScreen.d.ts +8 -0
- package/dist/CurpGenerateScreen.js +28 -13
- package/dist/CurpLayout.css +8 -34
- package/dist/CurpStrings.d.ts +16 -2
- package/dist/EkybAddressDetailsScreen.d.ts +7 -0
- package/dist/EkybAddressDetailsScreen.js +25 -7
- package/dist/EkybBusinessDetailsScreen.d.ts +6 -0
- package/dist/EkybBusinessDetailsScreen.js +23 -6
- package/dist/EkybCountryScreen.d.ts +13 -5
- package/dist/EkybCountryScreen.js +27 -9
- package/dist/EkybOwnersScreen.d.ts +8 -0
- package/dist/EkybOwnersScreen.js +26 -3
- package/dist/EkybStrings.d.ts +27 -10
- package/dist/EkycCountryCatalog.d.ts +4 -1
- package/dist/EkycCountryCatalog.js +5 -3
- package/dist/EkycCountryFormScreen.d.ts +12 -3
- package/dist/EkycCountryFormScreen.js +28 -10
- package/dist/EkycDetailsScreen.d.ts +7 -0
- package/dist/EkycDetailsScreen.js +21 -4
- package/dist/EkycStrings.d.ts +14 -2
- package/dist/ElectronicSignatureLayout.css +6 -0
- package/dist/EmailInputStrings.d.ts +11 -1
- package/dist/EmailInputVerifyScreen.d.ts +7 -0
- package/dist/EmailInputVerifyScreen.js +17 -3
- package/dist/FaceMatchComparisonScreen.d.ts +3 -3
- package/dist/FaceMatchLayout.css +21 -12
- package/dist/FormScreen.css +16 -0
- package/dist/FormsLongFormScreen.d.ts +8 -0
- package/dist/FormsLongFormScreen.js +34 -13
- package/dist/FormsStrings.d.ts +11 -1
- package/dist/GeolocationBrowserStepsScreen.d.ts +6 -2
- package/dist/GeolocationBrowserStepsScreen.js +7 -3
- package/dist/GeolocationStrings.d.ts +3 -0
- package/dist/IdCaptureLayout.css +40 -0
- package/dist/ListScreen.d.ts +2 -0
- package/dist/ListScreen.js +1 -1
- package/dist/PhoneInputLayout.css +10 -9
- package/dist/PhoneInputStrings.d.ts +11 -1
- package/dist/PhoneInputVerifyScreen.d.ts +8 -0
- package/dist/PhoneInputVerifyScreen.js +19 -6
- package/dist/QualifiedSignatureAcceptScreen.js +6 -1
- package/dist/QualifiedSignatureDocumentScreen.d.ts +13 -9
- package/dist/QualifiedSignatureDocumentScreen.js +18 -16
- package/dist/QualifiedSignatureLayout.css +76 -0
- package/dist/QualifiedSignatureStrings.d.ts +10 -0
- package/dist/ScreenNav.css +8 -0
- package/dist/ScreenNav.d.ts +15 -2
- package/dist/ScreenNav.js +10 -3
- package/dist/VideoSelfieIdCaptureScreen.d.ts +10 -3
- package/dist/VideoSelfieIdCaptureScreen.js +44 -13
- package/dist/VideoSelfieLayout.css +164 -33
- package/dist/VideoSelfieSelfieCaptureScreen.d.ts +44 -27
- package/dist/VideoSelfieSelfieCaptureScreen.js +65 -39
- package/dist/VideoSelfieSpeakScreen.js +18 -1
- package/dist/VideoSelfieStrings.d.ts +78 -12
- package/dist/VideoSelfieTurnHeadScreen.d.ts +51 -0
- package/dist/VideoSelfieTurnHeadScreen.js +63 -0
- package/dist/VideoSelfieTutorialScreen.js +13 -5
- package/dist/VideoSelfieWaveScreen.d.ts +53 -0
- package/dist/VideoSelfieWaveScreen.js +62 -0
- package/dist/WatchlistBusinessFormScreen.d.ts +11 -7
- package/dist/WatchlistBusinessFormScreen.js +27 -13
- package/dist/WatchlistBusinessStrings.d.ts +21 -7
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +3 -1
- package/package.json +14 -3
- package/samples/video-selfie-room.png +0 -0
- package/src/CurpEnterScreen.tsx +21 -4
- package/src/CurpGenerateScreen.tsx +46 -22
- package/src/CurpLayout.css +8 -34
- package/src/CurpStrings.ts +22 -2
- package/src/EkybAddressDetailsScreen.tsx +32 -4
- package/src/EkybBusinessDetailsScreen.tsx +23 -5
- package/src/EkybCountryScreen.tsx +28 -7
- package/src/EkybOwnersScreen.tsx +34 -3
- package/src/EkybStrings.ts +29 -10
- package/src/EkycCountryCatalog.ts +9 -4
- package/src/EkycCountryFormScreen.tsx +51 -12
- package/src/EkycDetailsScreen.tsx +22 -1
- package/src/EkycStrings.ts +14 -2
- package/src/ElectronicSignatureLayout.css +6 -0
- package/src/EmailInputStrings.ts +12 -1
- package/src/EmailInputVerifyScreen.tsx +21 -4
- package/src/FaceMatchComparisonScreen.tsx +3 -3
- package/src/FaceMatchLayout.css +21 -12
- package/src/FormScreen.css +16 -0
- package/src/FormsLongFormScreen.tsx +41 -13
- package/src/FormsStrings.ts +12 -1
- package/src/GeolocationBrowserStepsScreen.tsx +6 -3
- package/src/GeolocationStrings.ts +3 -0
- package/src/IdCaptureLayout.css +40 -0
- package/src/ListScreen.tsx +10 -1
- package/src/PhoneInputLayout.css +10 -9
- package/src/PhoneInputStrings.ts +12 -1
- package/src/PhoneInputVerifyScreen.tsx +21 -5
- package/src/QualifiedSignatureAcceptScreen.tsx +12 -1
- package/src/QualifiedSignatureDocumentScreen.tsx +27 -17
- package/src/QualifiedSignatureLayout.css +76 -0
- package/src/QualifiedSignatureStrings.ts +10 -0
- package/src/ScreenNav.css +8 -0
- package/src/ScreenNav.tsx +35 -5
- package/src/VideoSelfieIdCaptureScreen.tsx +116 -30
- package/src/VideoSelfieLayout.css +164 -33
- package/src/VideoSelfieSelfieCaptureScreen.tsx +104 -59
- package/src/VideoSelfieSpeakScreen.tsx +25 -14
- package/src/VideoSelfieStrings.ts +85 -12
- package/src/VideoSelfieTurnHeadScreen.tsx +151 -0
- package/src/VideoSelfieTutorialScreen.tsx +34 -23
- package/src/VideoSelfieWaveScreen.tsx +135 -0
- package/src/WatchlistBusinessFormScreen.tsx +35 -16
- package/src/WatchlistBusinessStrings.ts +21 -7
- package/src/index.ts +14 -0
- package/src/types.ts +3 -1
|
@@ -33,7 +33,9 @@ const PRESET_ROWS = {
|
|
|
33
33
|
{ key: 'idNumber', kind: 'text', requirement: 'required' },
|
|
34
34
|
{ key: 'email', kind: 'email', requirement: 'required' },
|
|
35
35
|
{ key: 'residence', kind: 'country', requirement: 'required' },
|
|
36
|
-
|
|
36
|
+
// Date of birth carries NO asterisk (owner Elena, 2026-09-30), so the
|
|
37
|
+
// `emptyError` state flags the other three rows and leaves it at rest.
|
|
38
|
+
{ key: 'dob', kind: 'date', requirement: 'none' },
|
|
37
39
|
],
|
|
38
40
|
};
|
|
39
41
|
/**
|
|
@@ -85,10 +87,19 @@ const OPTIONAL_VARIANT = {
|
|
|
85
87
|
* into the CTA band — the half a module stylesheet kept losing. It is a no-op
|
|
86
88
|
* on the short `name` preset, whose rows never overflow. DEVIATIONS #DSC-2,
|
|
87
89
|
* restating #CURP-1.
|
|
90
|
+
*
|
|
91
|
+
* `emptyError` is the GUIDELINES §6-E empty-submit rule (Elena 2026-09-30):
|
|
92
|
+
* the user pressed the CTA with the form still empty. Every REQUIRED row
|
|
93
|
+
* renders empty, with no placeholder, in Core's own error state carrying
|
|
94
|
+
* `strings.requiredError`; optional and unmarked rows stay on their resting
|
|
95
|
+
* surface with no error. The CTA greys there and ONLY there: the resting
|
|
96
|
+
* `default` keeps it enabled, so the user can press it to reach the error —
|
|
97
|
+
* the `FormsFieldScreen` `emptyError` precedent applied to a stack.
|
|
88
98
|
*/
|
|
89
99
|
export const FormsLongFormScreen = forwardRef(({ strings, preset = 'eKyc', state = 'default', values, focusedField, options, countries, screenId = `forms-long-form-${preset}-${state}`, className, overrides, onAction, ...shell }, ref) => {
|
|
90
100
|
const copy = strings.longForm[preset];
|
|
91
101
|
const busy = state === 'loading';
|
|
102
|
+
const emptyError = state === 'emptyError';
|
|
92
103
|
const countryList = countries ?? [];
|
|
93
104
|
// The Optional / Required pair documents the eKYC form specifically; no
|
|
94
105
|
// other preset draws it, so the override never leaks to a preset whose
|
|
@@ -106,20 +117,30 @@ export const FormsLongFormScreen = forwardRef(({ strings, preset = 'eKyc', state
|
|
|
106
117
|
const fieldCopy = copy.fields[row.key];
|
|
107
118
|
const label = labelFor(row);
|
|
108
119
|
const required = row.requirement === 'required';
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
// `emptyError` is an EMPTY submit: every row renders empty, whatever
|
|
121
|
+
// values the host passed.
|
|
122
|
+
const value = emptyError ? '' : (values?.[row.key] ?? '');
|
|
123
|
+
const focused = !busy && !emptyError && focusedField === row.key;
|
|
124
|
+
// Only REQUIRED rows flag the empty submit. Their placeholder goes, as
|
|
125
|
+
// on the drawn `forms.IDnumber.error`; optional rows stay at rest.
|
|
126
|
+
const invalid = emptyError && required;
|
|
127
|
+
const errorText = invalid ? strings.requiredError : undefined;
|
|
128
|
+
const placeholder = invalid ? undefined : fieldCopy?.placeholder;
|
|
111
129
|
switch (row.kind) {
|
|
112
130
|
case 'country':
|
|
113
|
-
return (_jsx(PrizmaDropdown, { label: label, required: required, requiredHint: strings.requiredHint, placeholder:
|
|
131
|
+
return (_jsx(PrizmaDropdown, { label: label, required: required, requiredHint: strings.requiredHint, placeholder: placeholder, options: countryList.map((c) => ({ value: c.code, label: c.name })), value: value || null, searchable: true, searchPlaceholder: copy.searchPlaceholder, disabled: busy, error: invalid, errorText: errorText, forceState: focused ? 'focused' : undefined, renderLeading: (option) => {
|
|
114
132
|
const match = countryList.find((c) => c.code === option.value);
|
|
115
133
|
return match ? _jsx(PrizmaCountryFlag, { country: match }) : null;
|
|
116
134
|
} }, row.key));
|
|
117
135
|
case 'select':
|
|
118
|
-
return (_jsx(PrizmaDropdown, { label: label, required: required, requiredHint: strings.requiredHint, placeholder:
|
|
136
|
+
return (_jsx(PrizmaDropdown, { label: label, required: required, requiredHint: strings.requiredHint, placeholder: placeholder, options: options?.[row.key] ?? [], value: value || null, disabled: busy, error: invalid, errorText: errorText, forceState: focused ? 'focused' : undefined }, row.key));
|
|
119
137
|
case 'date':
|
|
120
|
-
return (_jsx(PrizmaDateField, { className: focused ? 'prizma-input-datefield--focused' : undefined, label: label, required: required, requiredHint: strings.requiredHint,
|
|
138
|
+
return (_jsx(PrizmaDateField, { className: focused ? 'prizma-input-datefield--focused' : undefined, label: label, required: required, requiredHint: strings.requiredHint,
|
|
139
|
+
/* Core falls back to its DD/MM/YYYY mask on an undefined
|
|
140
|
+
placeholder, so the error row passes an explicit empty one. */
|
|
141
|
+
placeholder: invalid ? '' : fieldCopy?.placeholder, defaultValue: value, disabled: busy, error: invalid, errorText: errorText, calendarLabel: copy.calendarLabel }, row.key));
|
|
121
142
|
default:
|
|
122
|
-
return (_jsx(PrizmaTextField, { className: focused ? 'prizma-input-textfield--focused' : undefined, label: label, required: required, requiredHint: strings.requiredHint, placeholder:
|
|
143
|
+
return (_jsx(PrizmaTextField, { className: focused ? 'prizma-input-textfield--focused' : undefined, label: label, required: required, requiredHint: strings.requiredHint, placeholder: placeholder, defaultValue: value, type: row.kind === 'email' ? 'email' : 'text', inputMode: row.kind === 'email' ? 'email' : undefined, autoComplete: row.kind === 'email' ? 'email' : 'off', autoCapitalize: row.kind === 'email' ? 'none' : undefined, spellCheck: false, disabled: busy, error: invalid, errorText: errorText,
|
|
123
144
|
/* Figma draws the clear ✕ only on the focused row of
|
|
124
145
|
`longform.focused`; every other row draws none. */
|
|
125
146
|
clearable: focused, clearLabel: copy.clearLabel }, row.key));
|
|
@@ -134,12 +155,12 @@ export const FormsLongFormScreen = forwardRef(({ strings, preset = 'eKyc', state
|
|
|
134
155
|
]
|
|
135
156
|
.filter(Boolean)
|
|
136
157
|
.join(' '), title: copy.title, subtitle: copy.subtitle, ctaLabel: copy.ctaLabel,
|
|
137
|
-
/*
|
|
138
|
-
the
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
ctaDisabled:
|
|
158
|
+
/* GUIDELINES §6-E empty-submit rule (Elena 2026-09-30): the CTA stays
|
|
159
|
+
ENABLED on the resting empty form, every preset alike, so the user
|
|
160
|
+
can press it; it greys only on `emptyError`, the state that press
|
|
161
|
+
leads to. This supersedes the per-section greying #FDE-3 followed
|
|
162
|
+
on `longform.default` and `longform.customizable`. */
|
|
163
|
+
ctaDisabled: emptyError, ctaLoading: busy, ctaLoadingLabel: copy.ctaLoadingLabel,
|
|
143
164
|
/* The four- and five-row stacks are taller than the 844 frame; the
|
|
144
165
|
archetype's variant pans them and keeps Spacing/16 of clear surface
|
|
145
166
|
above the CTA. No module CSS re-declares this. */
|
package/dist/FormsStrings.d.ts
CHANGED
|
@@ -80,8 +80,12 @@ export type FormsLongFormPreset = 'name' | 'profile' | 'eKyc';
|
|
|
80
80
|
* - `loading` — `longform.loading`: the whole form greyed, spinner in the CTA
|
|
81
81
|
* - `optional` — the "Optional / Required" documentation pair: the same form
|
|
82
82
|
* with mixed required and optional markers
|
|
83
|
+
* - `emptyError` — the user pressed the CTA with the form still empty: every
|
|
84
|
+
* REQUIRED row renders empty in Core's error state with
|
|
85
|
+
* `requiredError`, optional and unmarked rows stay clean, CTA
|
|
86
|
+
* greyed (GUIDELINES §6-E empty-submit rule, Elena 2026-09-30)
|
|
83
87
|
*/
|
|
84
|
-
export type FormsLongFormState = 'default' | 'focused' | 'filled' | 'loading' | 'optional';
|
|
88
|
+
export type FormsLongFormState = 'default' | 'focused' | 'filled' | 'loading' | 'optional' | 'emptyError';
|
|
85
89
|
/** Copy for one single-field section. */
|
|
86
90
|
export interface FormsFieldCopy {
|
|
87
91
|
/** H2 ("Enter your title", "Enter your email", "Enter your phone number"). */
|
|
@@ -161,6 +165,12 @@ export interface FormsStrings {
|
|
|
161
165
|
* the label string by the screen. See DEVIATIONS #FDE-4.
|
|
162
166
|
*/
|
|
163
167
|
optionalHint: string;
|
|
168
|
+
/**
|
|
169
|
+
* The error row every REQUIRED field shows on the `emptyError` long-form
|
|
170
|
+
* state ("This field is required"). Maps to the SDK's
|
|
171
|
+
* `dynamicForms.fieldRequired` key. GUIDELINES §6-E empty-submit rule.
|
|
172
|
+
*/
|
|
173
|
+
requiredError: string;
|
|
164
174
|
/** The single-field sections. */
|
|
165
175
|
fields: Record<FormsFieldKey, FormsFieldCopy>;
|
|
166
176
|
/** The choice sections. */
|
|
@@ -31,9 +31,13 @@ export interface GeolocationBrowserStepsScreenProps extends Omit<PrizmaListScree
|
|
|
31
31
|
* The rows are Core's `PrizmaCardListCommonIssues` verbatim; only the leading
|
|
32
32
|
* glyph is chosen here, from the canonical bank (see `STEP_ICONS`).
|
|
33
33
|
*
|
|
34
|
+
* No subtitle: the browser name (`label`) is NOT drawn under the title (owner
|
|
35
|
+
* call, Elena 2026-09-30). The label stays in the strings as the section's
|
|
36
|
+
* identifier for hosts and catalogs; the screen itself shows title + steps.
|
|
37
|
+
*
|
|
34
38
|
* The title block is LEFT-aligned for this whole family. Every
|
|
35
|
-
* `Geolocation.allowLocation.<browser>` section starts the two-line headline
|
|
36
|
-
*
|
|
39
|
+
* `Geolocation.allowLocation.<browser>` section starts the two-line headline
|
|
40
|
+
* flush at the gutter, over left-led step
|
|
37
41
|
* rows; a centred headline above a left-aligned list reads as a mistake. The
|
|
38
42
|
* screen therefore DEFAULTS the archetype's own `align` override to `left`
|
|
39
43
|
* rather than declaring any alignment of its own, and a host that passes
|
|
@@ -55,9 +55,13 @@ const STEP_ICONS = {
|
|
|
55
55
|
* The rows are Core's `PrizmaCardListCommonIssues` verbatim; only the leading
|
|
56
56
|
* glyph is chosen here, from the canonical bank (see `STEP_ICONS`).
|
|
57
57
|
*
|
|
58
|
+
* No subtitle: the browser name (`label`) is NOT drawn under the title (owner
|
|
59
|
+
* call, Elena 2026-09-30). The label stays in the strings as the section's
|
|
60
|
+
* identifier for hosts and catalogs; the screen itself shows title + steps.
|
|
61
|
+
*
|
|
58
62
|
* The title block is LEFT-aligned for this whole family. Every
|
|
59
|
-
* `Geolocation.allowLocation.<browser>` section starts the two-line headline
|
|
60
|
-
*
|
|
63
|
+
* `Geolocation.allowLocation.<browser>` section starts the two-line headline
|
|
64
|
+
* flush at the gutter, over left-led step
|
|
61
65
|
* rows; a centred headline above a left-aligned list reads as a mistake. The
|
|
62
66
|
* screen therefore DEFAULTS the archetype's own `align` override to `left`
|
|
63
67
|
* rather than declaring any alignment of its own, and a host that passes
|
|
@@ -85,7 +89,7 @@ export const GeolocationBrowserStepsScreen = forwardRef(({ strings, browser = 's
|
|
|
85
89
|
...overrides,
|
|
86
90
|
align: overrides?.align ?? 'left',
|
|
87
91
|
};
|
|
88
|
-
return (_jsx(PrizmaListScreen, { ref: ref, title: strings.browserSteps.title,
|
|
92
|
+
return (_jsx(PrizmaListScreen, { ref: ref, title: strings.browserSteps.title, ctaLabel: strings.browserSteps.ctaLabel, secondaryCtaLabel: showSkip ? strings.browserSteps.skipLabel : undefined, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: alignedOverrides, onAction: onAction, ...shell, children: _jsx(PrizmaCardListCommonIssues, { items: (slice?.steps ?? []).map((step) => ({
|
|
89
93
|
icon: STEP_ICONS[step.kind],
|
|
90
94
|
title: step.label,
|
|
91
95
|
showSubtitle: false,
|
|
@@ -76,6 +76,9 @@ export interface GeolocationBrowserSteps {
|
|
|
76
76
|
* rows and would render as one screen three times. Every browser therefore
|
|
77
77
|
* SHOULD carry a label, extending the precedent Figma itself sets on Safari.
|
|
78
78
|
* Still optional so a host that ships its own logo artwork can drop it.
|
|
79
|
+
*
|
|
80
|
+
* 2026-09-30 (Elena): the screen no longer RENDERS it as a subtitle. It is
|
|
81
|
+
* kept as the section's name for catalogs and hosts, not as on-screen copy.
|
|
79
82
|
*/
|
|
80
83
|
label?: string;
|
|
81
84
|
/** The steps, in order. */
|
package/dist/IdCaptureLayout.css
CHANGED
|
@@ -59,6 +59,46 @@
|
|
|
59
59
|
gap: var(--prizma-spacing-8); /* Title → instruction, GUIDELINES §5 */
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/* ── Short screens: the vertical frame gives up height, never the title ──────
|
|
63
|
+
* The vertical ID (288:456) and passport (306:420) frames are tall. On a
|
|
64
|
+
* 320 × 693 phone the column is 272 wide, so the frame alone is ~431 tall and
|
|
65
|
+
* the stage ran 168px past the bottom, under the shutter strip (Camila,
|
|
66
|
+
* 2026-09-28). Here the frame is the ONE part of the column that shrinks: it
|
|
67
|
+
* starts at its full-width height (the column less its two Spacing/24
|
|
68
|
+
* gutters, over the aspect), and when the screen is too short the flex column
|
|
69
|
+
* takes the shortfall out of its height; the aspect ratio then narrows its
|
|
70
|
+
* width to match, so it stays an ID-shaped frame, centred. The title band
|
|
71
|
+
* keeps its content height. With room to spare (390 × 844) the frame never
|
|
72
|
+
* needs to shrink, so it sits exactly where it did.
|
|
73
|
+
*
|
|
74
|
+
* Scoped to ID + passport with `:not(--poa)`: Document Capture renders the
|
|
75
|
+
* same stage as `type="poa" isVertical`, so it carries `--vertical` too, but
|
|
76
|
+
* the PoA frame has NO aspect ratio (it fills the host's height). `width:
|
|
77
|
+
* auto` there collapsed it to a 0-wide sliver, a grey vertical bar down the
|
|
78
|
+
* camera screen (Elena, 2026-09-30). Only showed where this file and Document
|
|
79
|
+
* Capture's CSS ship in the same bundle, i.e. the production build. */
|
|
80
|
+
.prizma-viewfinder__content:has(> .prizma-id-capture--vertical:not(.prizma-id-capture--poa)) {
|
|
81
|
+
min-height: 0;
|
|
82
|
+
}
|
|
83
|
+
.prizma-viewfinder__content:has(> .prizma-id-capture--vertical:not(.prizma-id-capture--poa)) > .id-capture-band--top {
|
|
84
|
+
flex-shrink: 0;
|
|
85
|
+
min-height: min-content;
|
|
86
|
+
}
|
|
87
|
+
.prizma-viewfinder__content > .prizma-id-capture--vertical:not(.prizma-id-capture--poa) {
|
|
88
|
+
flex: 0 1 auto;
|
|
89
|
+
min-height: 0;
|
|
90
|
+
}
|
|
91
|
+
.prizma-viewfinder__content > .prizma-id-capture--vertical:not(.prizma-id-capture--poa) .prizma-id-capture__frame {
|
|
92
|
+
flex: 0 1 auto;
|
|
93
|
+
min-height: 0;
|
|
94
|
+
width: auto;
|
|
95
|
+
max-width: 100%;
|
|
96
|
+
flex-basis: calc((100cqw - 2 * var(--prizma-spacing-24)) * 456 / 288);
|
|
97
|
+
}
|
|
98
|
+
.prizma-viewfinder__content > .prizma-id-capture--passport.prizma-id-capture--vertical .prizma-id-capture__frame {
|
|
99
|
+
flex-basis: calc((100cqw - 2 * var(--prizma-spacing-24)) * 420 / 306);
|
|
100
|
+
}
|
|
101
|
+
|
|
62
102
|
/* ── Camera stage (dark) ───────────────────────────────────────────────────── */
|
|
63
103
|
|
|
64
104
|
/* The stage's nav row carries only the close X (no logo — the camera owns the
|
package/dist/ListScreen.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export interface PrizmaListScreenProps extends Omit<PrizmaScreenShellProps, 'dar
|
|
|
64
64
|
/** "verified by" attribution copy for the footer tag. */
|
|
65
65
|
verifiedLabel: string;
|
|
66
66
|
navLabel?: string;
|
|
67
|
+
/** Opt-in back chevron in the nav (its accessible name). Emits `back`. */
|
|
68
|
+
backLabel?: string;
|
|
67
69
|
screenId?: string;
|
|
68
70
|
overrides?: PrizmaScreenOverrides;
|
|
69
71
|
onAction?: PrizmaScreenActionHandler;
|
package/dist/ListScreen.js
CHANGED
|
@@ -31,7 +31,7 @@ import './ScreenLayout.css';
|
|
|
31
31
|
* Reach for `PrizmaResultScreen` instead when the middle is a centred verdict
|
|
32
32
|
* rather than a list.
|
|
33
33
|
*/
|
|
34
|
-
export const PrizmaListScreen = forwardRef(({ progress, status = 'none', title, subtitle, glyph, children, listAlign = 'start', scrollList = false, disclaimer, ctaLabel, secondaryCtaLabel, linkLabel, verifiedLabel, navLabel, screenId, showHeader = true, showFooter = true, overrides, onAction, ...shell }, ref) => (_jsxs(PrizmaScreenShell, { ref: ref, ...shell, children: [_jsx(PrizmaScreenNav, { reserved: !showHeader, logo: overrides?.logo, navLabel: navLabel }), progress ? (_jsx("div", { className: "prizma-screen-layout__progress", children: progress })) : null, _jsxs("div", { className: "prizma-screen-layout__content", children: [glyph, _jsx(PrizmaTitle, { variant: overrides?.align === 'left' ? 'left' : 'centered', title: title, showSubtitle: Boolean(subtitle), subtitle: subtitle, icon: status === 'loading'
|
|
34
|
+
export const PrizmaListScreen = forwardRef(({ progress, status = 'none', title, subtitle, glyph, children, listAlign = 'start', scrollList = false, disclaimer, ctaLabel, secondaryCtaLabel, linkLabel, verifiedLabel, navLabel, backLabel, screenId, showHeader = true, showFooter = true, overrides, onAction, ...shell }, ref) => (_jsxs(PrizmaScreenShell, { ref: ref, ...shell, children: [_jsx(PrizmaScreenNav, { reserved: !showHeader, logo: overrides?.logo, navLabel: navLabel, backLabel: backLabel, onBack: () => onAction?.({ type: 'back', screenId }) }), progress ? (_jsx("div", { className: "prizma-screen-layout__progress", children: progress })) : null, _jsxs("div", { className: "prizma-screen-layout__content", children: [glyph, _jsx(PrizmaTitle, { variant: overrides?.align === 'left' ? 'left' : 'centered', title: title, showSubtitle: Boolean(subtitle), subtitle: subtitle, icon: status === 'loading'
|
|
35
35
|
? { type: 'spinner' }
|
|
36
36
|
: status === 'none'
|
|
37
37
|
? { type: 'none' }
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
align-items: flex-start;
|
|
22
22
|
gap: var(--prizma-spacing-8);
|
|
23
23
|
width: 100%;
|
|
24
|
+
position: relative; /* the country menu's containing block — see below */
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/* The dial-code dropdown hugs its content ("🇷🇸 +381"), as Figma draws it.
|
|
@@ -47,15 +48,15 @@
|
|
|
47
48
|
min-width: 0;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
/* The open menu
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
51
|
+
/* The open menu spans the WHOLE row (dial code + number field), as Figma's
|
|
52
|
+
* `Input / Combined` draws it: Core anchors the menu overlay to the trigger's
|
|
53
|
+
* `__anchor` (`position: relative`, Dropdown.css), so once the trigger hugs
|
|
54
|
+
* "🇷🇸 +381" the menu came out as narrow as the dial code. Releasing the
|
|
55
|
+
* anchor's positioning hands the overlay's `left: 0; right: 0` to the row
|
|
56
|
+
* above (`position: relative`), so the menu matches the row's width exactly.
|
|
57
|
+
* Core's own overlay offset (`top: 100% + Spacing/8`) is left untouched.
|
|
55
58
|
* DEVIATIONS #PI-1 (same root cause as the hug).
|
|
56
59
|
*/
|
|
57
|
-
.phone-input-combined__country .prizma-
|
|
58
|
-
|
|
59
|
-
min-width: 100%;
|
|
60
|
-
max-width: 342px;
|
|
60
|
+
.phone-input-combined__country .prizma-dropdown__anchor {
|
|
61
|
+
position: static;
|
|
61
62
|
}
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
* - `typing` — partial value, focused field
|
|
8
8
|
* - `filled` — complete value
|
|
9
9
|
* - `loading` — CTA in its loading treatment while the code is requested
|
|
10
|
+
* - `emptyError` — "Continue" pressed with no number typed: the phone field
|
|
11
|
+
* stays empty (no placeholder) in Core's error state with `requiredError`,
|
|
12
|
+
* and the CTA is disabled. The dial code always carries a selection, so it
|
|
13
|
+
* takes no error. GUIDELINES §6-E empty-submit rule (Elena, 2026-09-30).
|
|
10
14
|
*/
|
|
11
|
-
export type PhoneInputVerifyState = 'default' | 'countryCode' | 'typing' | 'filled' | 'loading';
|
|
15
|
+
export type PhoneInputVerifyState = 'default' | 'countryCode' | 'typing' | 'filled' | 'loading' | 'emptyError';
|
|
12
16
|
/**
|
|
13
17
|
* The `phoneNumber.OTP.*` sections — same layout, different field state:
|
|
14
18
|
* - `typing` — partial code
|
|
@@ -41,6 +45,12 @@ export interface PhoneInputStrings {
|
|
|
41
45
|
verifiedLabel: string;
|
|
42
46
|
/** Accessible name for the logo nav row. */
|
|
43
47
|
navLabel?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Error row under the required field when the primary CTA is pressed with it
|
|
50
|
+
* still empty (the verify screen's `emptyError` state): "This field is
|
|
51
|
+
* required". SDK key `dynamicForms.fieldRequired`.
|
|
52
|
+
*/
|
|
53
|
+
requiredError: string;
|
|
44
54
|
/** phoneNumber.verify.* */
|
|
45
55
|
verify: {
|
|
46
56
|
/** H2 ("Verify your phone number"). */
|
|
@@ -36,6 +36,14 @@ export interface PhoneInputVerifyScreenProps extends Omit<PrizmaFormScreenProps,
|
|
|
36
36
|
* primitives (`PrizmaDropdown`, `PrizmaTextField`) at the designed 56px min
|
|
37
37
|
* height and `Border/Radius/small`, so the module layer contributes only the
|
|
38
38
|
* row that pairs them (`PhoneInputLayout.css`) — no primitive is re-implemented.
|
|
39
|
+
*
|
|
40
|
+
* `emptyError` is the empty-submit state (rule from Elena, 2026-09-30,
|
|
41
|
+
* GUIDELINES §6-E empty-submit rule): "Continue" was pressed with no number
|
|
42
|
+
* typed. The phone field renders empty with no placeholder, in Core's error
|
|
43
|
+
* state with `requiredError`, and the primary CTA is disabled, following the
|
|
44
|
+
* drawn precedent in `FormsFieldScreen`. The dial code always carries a
|
|
45
|
+
* selection, so it takes no error. The resting `default` keeps the CTA enabled
|
|
46
|
+
* so the user can reach this state.
|
|
39
47
|
*/
|
|
40
48
|
export declare const PhoneInputVerifyScreen: React.ForwardRefExoticComponent<PhoneInputVerifyScreenProps & React.RefAttributes<HTMLDivElement>>;
|
|
41
49
|
export default PhoneInputVerifyScreen;
|
|
@@ -29,14 +29,24 @@ const DEFAULT_COUNTRIES = [
|
|
|
29
29
|
* primitives (`PrizmaDropdown`, `PrizmaTextField`) at the designed 56px min
|
|
30
30
|
* height and `Border/Radius/small`, so the module layer contributes only the
|
|
31
31
|
* row that pairs them (`PhoneInputLayout.css`) — no primitive is re-implemented.
|
|
32
|
+
*
|
|
33
|
+
* `emptyError` is the empty-submit state (rule from Elena, 2026-09-30,
|
|
34
|
+
* GUIDELINES §6-E empty-submit rule): "Continue" was pressed with no number
|
|
35
|
+
* typed. The phone field renders empty with no placeholder, in Core's error
|
|
36
|
+
* state with `requiredError`, and the primary CTA is disabled, following the
|
|
37
|
+
* drawn precedent in `FormsFieldScreen`. The dial code always carries a
|
|
38
|
+
* selection, so it takes no error. The resting `default` keeps the CTA enabled
|
|
39
|
+
* so the user can reach this state.
|
|
32
40
|
*/
|
|
33
41
|
export const PhoneInputVerifyScreen = forwardRef(({ strings, state = 'default', countries = DEFAULT_COUNTRIES, country, value, screenId = `phone-input-verify-${state}`, overrides, onAction, ...shell }, ref) => {
|
|
34
42
|
const selected = country ?? countries[0]?.value;
|
|
35
43
|
const options = countries.map((c) => ({
|
|
36
44
|
value: c.value,
|
|
37
|
-
// The
|
|
38
|
-
// ("🇷🇸 +381"); the
|
|
39
|
-
|
|
45
|
+
// The open menu row reads flag + dial code + country name
|
|
46
|
+
// ("🇷🇸 +381 Serbia"); the closed field keeps only flag + dial code
|
|
47
|
+
// ("🇷🇸 +381") through Core's `fieldLabel`, exactly as Figma draws it.
|
|
48
|
+
label: [c.flag, c.dialCode, c.label].filter(Boolean).join(' '),
|
|
49
|
+
fieldLabel: [c.flag, c.dialCode].filter(Boolean).join(' '),
|
|
40
50
|
}));
|
|
41
51
|
// Figma treats the states as three distinct field treatments, not one:
|
|
42
52
|
// `loading` greys BOTH fields (the request is in flight and the form is
|
|
@@ -47,9 +57,12 @@ export const PhoneInputVerifyScreen = forwardRef(({ strings, state = 'default',
|
|
|
47
57
|
// for none of them — it would grey the resting states too (DEVIATIONS #PI-6).
|
|
48
58
|
const busy = state === 'loading';
|
|
49
59
|
const focused = state === 'typing';
|
|
60
|
+
// Empty submit: the number field drops its placeholder and takes Core's
|
|
61
|
+
// error state with the shared "required" row; the CTA greys.
|
|
62
|
+
const emptyError = state === 'emptyError';
|
|
50
63
|
return (_jsx(PrizmaFormScreen, { ref: ref, title: strings.verify.title, subtitle: strings.verify.subtitle, ctaLabel: strings.verify.ctaLabel,
|
|
51
64
|
/* Figma's `loading` section swaps the CTA label for Core's spinner. */
|
|
52
|
-
ctaLoading: busy, ctaLoadingLabel: strings.verify.ctaLoadingLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsxs("div", { className: "phone-input-combined", children: [_jsx(PrizmaDropdown, { className: "phone-input-combined__country", "aria-label": strings.verify.countryLabel, options: options, value: selected, searchable: true, searchPlaceholder: strings.verify.countrySearchPlaceholder,
|
|
65
|
+
ctaLoading: busy, ctaDisabled: emptyError, ctaLoadingLabel: strings.verify.ctaLoadingLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsxs("div", { className: "phone-input-combined", children: [_jsx(PrizmaDropdown, { className: "phone-input-combined__country", "aria-label": strings.verify.countryLabel, options: options, value: selected, searchable: true, searchPlaceholder: strings.verify.countrySearchPlaceholder,
|
|
53
66
|
/* The `countryCode` section IS the open-menu spec state. */
|
|
54
67
|
forceOpen: state === 'countryCode', disabled: busy }), _jsx(PrizmaTextField, { className: [
|
|
55
68
|
'phone-input-combined__number',
|
|
@@ -59,11 +72,11 @@ export const PhoneInputVerifyScreen = forwardRef(({ strings, state = 'default',
|
|
|
59
72
|
focused ? 'prizma-input-textfield--focused' : '',
|
|
60
73
|
]
|
|
61
74
|
.filter(Boolean)
|
|
62
|
-
.join(' '), type: "tel", inputMode: "tel", "aria-label": strings.verify.fieldLabel, placeholder: strings.verify.placeholder,
|
|
75
|
+
.join(' '), type: "tel", inputMode: "tel", "aria-label": strings.verify.fieldLabel, placeholder: emptyError ? undefined : strings.verify.placeholder,
|
|
63
76
|
/* Uncontrolled: a screen is one static spec state, and `value`
|
|
64
77
|
without `onChange` would either warn or force `readOnly`, whose
|
|
65
78
|
disabled surface is exactly the grey Figma does NOT draw here. */
|
|
66
|
-
defaultValue: value ?? '', disabled: busy,
|
|
79
|
+
defaultValue: emptyError ? '' : value ?? '', disabled: busy, error: emptyError, errorText: emptyError ? strings.requiredError : undefined,
|
|
67
80
|
/* Figma draws the clear ✕ only while the field is focused. */
|
|
68
81
|
clearable: focused })] }) }));
|
|
69
82
|
});
|
|
@@ -62,6 +62,7 @@ export const QualifiedSignatureAcceptScreen = forwardRef(({ strings, variant = '
|
|
|
62
62
|
// `loading` makes the whole form inert.
|
|
63
63
|
const busy = state === 'loading';
|
|
64
64
|
const ticked = state !== 'default';
|
|
65
|
+
const requiredHint = strings.accept.requiredHint;
|
|
65
66
|
return (_jsxs(PrizmaFormScreen, { ref: ref, className: ['qualified-signature-accept', className].filter(Boolean).join(' '), title: copy.title, subtitle: copy.subtitle, ctaLabel: copy.ctaLabel, ctaDisabled: !ticked, ctaLoading: busy, ctaLoadingLabel: copy.ctaLoadingLabel,
|
|
66
67
|
/* The clause stack pans inside the column — the archetype owns this now
|
|
67
68
|
(`--scroll`), so the module declares no overflow of its own. #QS-1 */
|
|
@@ -80,7 +81,11 @@ export const QualifiedSignatureAcceptScreen = forwardRef(({ strings, variant = '
|
|
|
80
81
|
action outright while the partner's `loading` frame greys it. Two
|
|
81
82
|
Figma readings of one inert state; Core models inert as disabled,
|
|
82
83
|
so both take the greyed treatment and the removal is logged. */
|
|
83
|
-
_jsx(PrizmaCardDocumentContainer, { filename: filename, actionLabel: strings.accept.viewLabel, actionDisabled: busy, onAction: () => onAction?.({ type: 'tertiary', screenId }) })) : null,
|
|
84
|
+
_jsx(PrizmaCardDocumentContainer, { filename: filename, actionLabel: strings.accept.viewLabel, actionDisabled: busy, onAction: () => onAction?.({ type: 'tertiary', screenId }) })) : null, _jsxs("div", { className: "qualified-signature-terms", role: "group", "aria-label": strings.accept.termsLabel, children: [requiredHint && strings.accept.requiredLegend ? (_jsx("p", { className: "qualified-signature-terms__legend", children: strings.accept.requiredLegend })) : null, terms.map((term) => (_jsx(PrizmaCheckbox, { size: "L", name: term.id, value: term.id, checked: ticked, disabled: busy, required: Boolean(requiredHint),
|
|
85
|
+
/* The `*` rides the END of the clause copy, the Data Sharing
|
|
86
|
+
Consent idiom — the L card paints no marker of its own beside
|
|
87
|
+
the box (owner, 2026-09-02). */
|
|
88
|
+
segments: requiredHint ? [...term.segments, ` ${requiredHint}`] : term.segments }, term.id)))] })] }));
|
|
84
89
|
});
|
|
85
90
|
QualifiedSignatureAcceptScreen.displayName = 'QualifiedSignatureAcceptScreen';
|
|
86
91
|
export default QualifiedSignatureAcceptScreen;
|
|
@@ -40,11 +40,13 @@ export interface QualifiedSignatureDocumentScreenProps extends Omit<PrizmaListSc
|
|
|
40
40
|
* overflow constraint, declaring no spacing or size, the same idiom as CURP's
|
|
41
41
|
* taller-than-844 form (DEVIATIONS #CURP-1).
|
|
42
42
|
*
|
|
43
|
-
* THREE deltas from the frames
|
|
44
|
-
* 1. Both sections draw a back chevron at the nav's left
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* THREE deltas from the frames:
|
|
44
|
+
* 1. Both sections draw a back chevron at the nav's left. It is BUILT (owner
|
|
45
|
+
* call 2026-09-29): these are viewers the user opened from the accept
|
|
46
|
+
* screen, so they need a way back. It is Core's chevron through the opt-in
|
|
47
|
+
* `PrizmaScreenNav.backLabel`, emitting `back`; the logo stays centred.
|
|
48
|
+
* `terms` replacing the logo row with a chevron + heading bar is still not
|
|
49
|
+
* followed — the title stays the archetype's H2 below the nav.
|
|
48
50
|
* 2. `viewdoc` draws NO heading. GUIDELINES §4 is explicit that every screen has
|
|
49
51
|
* exactly one H2, and archetype G has no title-less path, so the screen
|
|
50
52
|
* carries one and the Figma frame is the side to change. (An archetype
|
|
@@ -53,10 +55,12 @@ export interface QualifiedSignatureDocumentScreenProps extends Omit<PrizmaListSc
|
|
|
53
55
|
* for attribution chrome (#PI-8, #F-30, #F-34), so both variants keep the
|
|
54
56
|
* header and the footer and nothing shifts between states.
|
|
55
57
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* an
|
|
58
|
+
* Both variants frame the page in the box Electronic Signature's review screen
|
|
59
|
+
* uses, so the title is CENTRED on both, like that screen (owner call
|
|
60
|
+
* 2026-09-29, "take reference from other screens"). Figma's `QSign.T&C` draws
|
|
61
|
+
* the title left over an unframed wall of text; once the text sits in its own
|
|
62
|
+
* left-aligned frame the centred title no longer reads as a mistake. An
|
|
63
|
+
* explicit `overrides.align` from the host still wins.
|
|
60
64
|
*/
|
|
61
65
|
export declare const QualifiedSignatureDocumentScreen: React.ForwardRefExoticComponent<QualifiedSignatureDocumentScreenProps & React.RefAttributes<HTMLDivElement>>;
|
|
62
66
|
export default QualifiedSignatureDocumentScreen;
|
|
@@ -24,11 +24,13 @@ import './QualifiedSignatureLayout.css';
|
|
|
24
24
|
* overflow constraint, declaring no spacing or size, the same idiom as CURP's
|
|
25
25
|
* taller-than-844 form (DEVIATIONS #CURP-1).
|
|
26
26
|
*
|
|
27
|
-
* THREE deltas from the frames
|
|
28
|
-
* 1. Both sections draw a back chevron at the nav's left
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* THREE deltas from the frames:
|
|
28
|
+
* 1. Both sections draw a back chevron at the nav's left. It is BUILT (owner
|
|
29
|
+
* call 2026-09-29): these are viewers the user opened from the accept
|
|
30
|
+
* screen, so they need a way back. It is Core's chevron through the opt-in
|
|
31
|
+
* `PrizmaScreenNav.backLabel`, emitting `back`; the logo stays centred.
|
|
32
|
+
* `terms` replacing the logo row with a chevron + heading bar is still not
|
|
33
|
+
* followed — the title stays the archetype's H2 below the nav.
|
|
32
34
|
* 2. `viewdoc` draws NO heading. GUIDELINES §4 is explicit that every screen has
|
|
33
35
|
* exactly one H2, and archetype G has no title-less path, so the screen
|
|
34
36
|
* carries one and the Figma frame is the side to change. (An archetype
|
|
@@ -37,20 +39,20 @@ import './QualifiedSignatureLayout.css';
|
|
|
37
39
|
* for attribution chrome (#PI-8, #F-30, #F-34), so both variants keep the
|
|
38
40
|
* header and the footer and nothing shifts between states.
|
|
39
41
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* an
|
|
42
|
+
* Both variants frame the page in the box Electronic Signature's review screen
|
|
43
|
+
* uses, so the title is CENTRED on both, like that screen (owner call
|
|
44
|
+
* 2026-09-29, "take reference from other screens"). Figma's `QSign.T&C` draws
|
|
45
|
+
* the title left over an unframed wall of text; once the text sits in its own
|
|
46
|
+
* left-aligned frame the centred title no longer reads as a mistake. An
|
|
47
|
+
* explicit `overrides.align` from the host still wins.
|
|
44
48
|
*/
|
|
45
49
|
export const QualifiedSignatureDocumentScreen = forwardRef(({ strings, variant = 'document', body, screenId = `qualified-signature-document-${variant}`, overrides, onAction, ...shell }, ref) => {
|
|
46
50
|
const copy = variant === 'terms' ? strings.document.terms : strings.document.document;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
53
|
-
return (_jsx(PrizmaListScreen, { ref: ref, title: copy.title, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: alignedOverrides, onAction: onAction, ...shell, children: _jsx("div", { className: "qualified-signature-document", children: body }) }));
|
|
51
|
+
return (_jsx(PrizmaListScreen, { ref: ref, title: copy.title, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, backLabel: strings.document.backLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx("div", { className: [
|
|
52
|
+
'qualified-signature-document',
|
|
53
|
+
'qualified-signature-document--framed',
|
|
54
|
+
`qualified-signature-document--${variant}`,
|
|
55
|
+
].join(' '), role: "region", "aria-label": copy.title, tabIndex: 0, children: body }) }));
|
|
54
56
|
});
|
|
55
57
|
QualifiedSignatureDocumentScreen.displayName = 'QualifiedSignatureDocumentScreen';
|
|
56
58
|
export default QualifiedSignatureDocumentScreen;
|
|
@@ -57,6 +57,26 @@
|
|
|
57
57
|
display: flex;
|
|
58
58
|
flex-direction: column;
|
|
59
59
|
gap: var(--prizma-spacing-4);
|
|
60
|
+
/* The clauses sit at the BOTTOM of the column, on the primary button, not
|
|
61
|
+
* under the document row (Elena, 2026-09-30). The fields are a flex column,
|
|
62
|
+
* so the auto margin takes the free space; when the clauses outgrow the frame
|
|
63
|
+
* (the five-clause reading) there is none and the stack scrolls as before. */
|
|
64
|
+
margin-top: auto;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* The required legend leading the group. Core ships no caption block outside a
|
|
68
|
+
* field, so the line binds the clause copy's own Size-S tokens in the secondary
|
|
69
|
+
* ink — no new size, no new colour. Spacing/8 more below it than the clauses
|
|
70
|
+
* keep between themselves, so it reads as the group's header. */
|
|
71
|
+
.qualified-signature-terms__legend {
|
|
72
|
+
margin: 0 0 var(--prizma-spacing-8);
|
|
73
|
+
font-family: var(--prizma-checkbox-radio-family);
|
|
74
|
+
font-weight: var(--prizma-checkbox-radio-s-font-weight);
|
|
75
|
+
font-size: var(--prizma-checkbox-radio-s-font-size);
|
|
76
|
+
line-height: var(--prizma-checkbox-radio-s-line-height);
|
|
77
|
+
letter-spacing: var(--prizma-checkbox-radio-s-letter-spacing);
|
|
78
|
+
color: var(--prizma-text-body-500-secondary);
|
|
79
|
+
text-align: left;
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
/* ── (c) Uploaded-file preview card ──────────────────────────────────────────
|
|
@@ -181,6 +201,62 @@
|
|
|
181
201
|
overscroll-behavior: contain;
|
|
182
202
|
}
|
|
183
203
|
|
|
204
|
+
/* The document FRAME — the two full-surface document screens draw the page
|
|
205
|
+
* inside the same box Electronic Signature's review screen uses
|
|
206
|
+
* (`.electronic-signature-document`): `Border/Neutral/100` hairline,
|
|
207
|
+
* `Border/Radius/small`, `Surface/Neutral/0`, clipped so the page scrolls INSIDE
|
|
208
|
+
* it. Without it the contract image floated loose on the screen and the legal
|
|
209
|
+
* text ran edge to edge as one grey wall (owner-reported 2026-09-29: "take
|
|
210
|
+
* reference from other screens"). A modifier, not the base class, because the
|
|
211
|
+
* gated-form exploration reuses `.qualified-signature-document` inside its own
|
|
212
|
+
* popup, which already carries a frame.
|
|
213
|
+
*
|
|
214
|
+
* Spacing/16 bottom margin: the gap Electronic Signature's frame keeps above the
|
|
215
|
+
* CTA band, here kept above the anchored tag so the frame never touches it.
|
|
216
|
+
*/
|
|
217
|
+
.qualified-signature-document--framed {
|
|
218
|
+
box-sizing: border-box;
|
|
219
|
+
background-color: var(--prizma-surface-neutral-0);
|
|
220
|
+
border: var(--prizma-border-width-regular) solid var(--prizma-border-neutral-100);
|
|
221
|
+
border-radius: var(--prizma-border-radius-small);
|
|
222
|
+
margin-bottom: var(--prizma-spacing-16);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* `terms` — a TEXT agreement needs a gutter inside the frame (Spacing/16) and
|
|
226
|
+
* the paragraph rhythm Electronic Signature uses (Spacing/12 between blocks). */
|
|
227
|
+
.qualified-signature-document--terms {
|
|
228
|
+
padding: var(--prizma-spacing-16);
|
|
229
|
+
text-align: start;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.qualified-signature-document--terms > * {
|
|
233
|
+
margin: 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.qualified-signature-document.qualified-signature-document--terms > * + * {
|
|
237
|
+
/* Doubled class: `__prose` resets `margin: 0` later in this file at the
|
|
238
|
+
same specificity, which would otherwise swallow the rhythm. */
|
|
239
|
+
margin-top: var(--prizma-spacing-12);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Section headings inside the form — Headline/H5 on Text/Body/800, the same
|
|
243
|
+
* pair as `.electronic-signature-document h4`. */
|
|
244
|
+
.qualified-signature-document--terms h4 {
|
|
245
|
+
font-family: var(--prizma-headline-family);
|
|
246
|
+
font-weight: var(--prizma-headline-h5-font-weight);
|
|
247
|
+
font-size: var(--prizma-headline-h5-font-size);
|
|
248
|
+
line-height: var(--prizma-headline-h5-line-height);
|
|
249
|
+
letter-spacing: var(--prizma-headline-h5-letter-spacing);
|
|
250
|
+
color: var(--prizma-text-body-800-primary);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* Inside the frame the prose drops to Text/Body/500, the secondary ink every
|
|
254
|
+
* block of Core prose uses (`CardList.css`, `Card.css`) — the heading carries
|
|
255
|
+
* the hierarchy, as in Electronic Signature. */
|
|
256
|
+
.qualified-signature-document--terms .qualified-signature-document__prose {
|
|
257
|
+
color: var(--prizma-text-body-500-secondary);
|
|
258
|
+
}
|
|
259
|
+
|
|
184
260
|
/* The contract page is host-supplied media (an <img> / <canvas> of the rendered
|
|
185
261
|
* PDF, GUIDELINES §10's `cameraSurface` idiom) — it fills the column width and
|
|
186
262
|
* keeps its own aspect. */
|
|
@@ -128,6 +128,12 @@ export interface QualifiedSignatureStrings {
|
|
|
128
128
|
viewLabel: string;
|
|
129
129
|
/** Accessible name for the consent stack. */
|
|
130
130
|
termsLabel?: string;
|
|
131
|
+
/** Suffix appended to every clause, all of them required ("*"). Localizable,
|
|
132
|
+
* never baked into the clause copy — Data Sharing Consent's `requiredHint`. */
|
|
133
|
+
requiredHint?: string;
|
|
134
|
+
/** The line that leads the clause group and explains the marker ("All terms
|
|
135
|
+
* marked with * are required."). Only drawn alongside `requiredHint`. */
|
|
136
|
+
requiredLegend?: string;
|
|
131
137
|
/** The generic five-clause reading. */
|
|
132
138
|
accept: QualifiedSignatureAcceptCopy;
|
|
133
139
|
/** The partner two-clause reading. */
|
|
@@ -148,6 +154,10 @@ export interface QualifiedSignatureStrings {
|
|
|
148
154
|
terms: {
|
|
149
155
|
title: string;
|
|
150
156
|
};
|
|
157
|
+
/** Accessible name of the nav's back chevron on both document screens
|
|
158
|
+
* ("Back") — they are opened from the accept screen, so they carry a way
|
|
159
|
+
* back to it. Emits `back`. Omit to render the logo-only nav. */
|
|
160
|
+
backLabel?: string;
|
|
151
161
|
};
|
|
152
162
|
/** The two in-flight screens. */
|
|
153
163
|
processing: {
|