@zitadel/config 0.1.0-alpha.16 → 0.1.0-alpha.18

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 (44) hide show
  1. package/defaults/README-branding.md +62 -0
  2. package/defaults/README-flows.md +0 -8
  3. package/defaults/branding/centered/login.liquid +164 -0
  4. package/defaults/branding/hero/login.liquid +222 -0
  5. package/defaults/branding/minimal/login.liquid +154 -0
  6. package/defaults/branding/split/login.liquid +178 -0
  7. package/defaults/branding/split-right/login.liquid +178 -0
  8. package/defaults/default-human-user.json +0 -15
  9. package/defaults/default-login.json +1 -4
  10. package/defaults/presets/passkey-first/human-user.json +0 -15
  11. package/defaults/presets/passkey-first/login.json +1 -4
  12. package/dist/defaults-Bh4_xVbh.d.mts +84 -0
  13. package/dist/defaults-Bh4_xVbh.d.mts.map +1 -0
  14. package/dist/{defaults-BQej3KIY.mjs → defaults-CXTnFMHj.mjs} +193 -65
  15. package/dist/defaults-CXTnFMHj.mjs.map +1 -0
  16. package/dist/defaults.d.mts +2 -2
  17. package/dist/defaults.mjs +2 -2
  18. package/dist/index.d.mts +5 -4
  19. package/dist/index.mjs +5 -4
  20. package/dist/{meta-schemas-8nlyVL7g.mjs → meta-schemas-B4YiHAX_.mjs} +71 -3
  21. package/dist/meta-schemas-B4YiHAX_.mjs.map +1 -0
  22. package/dist/meta-schemas.d.mts +6 -1
  23. package/dist/meta-schemas.d.mts.map +1 -1
  24. package/dist/meta-schemas.mjs +2 -2
  25. package/dist/schemas.d.mts +14 -1
  26. package/dist/schemas.d.mts.map +1 -1
  27. package/dist/schemas.mjs +60 -2
  28. package/dist/schemas.mjs.map +1 -1
  29. package/dist/template.d.mts +76 -0
  30. package/dist/template.d.mts.map +1 -0
  31. package/dist/template.mjs +145 -0
  32. package/dist/template.mjs.map +1 -0
  33. package/dist/validate.d.mts.map +1 -1
  34. package/dist/validate.mjs +6 -1
  35. package/dist/validate.mjs.map +1 -1
  36. package/meta-schemas/branding.json +52 -0
  37. package/meta-schemas/property-name.json +7 -0
  38. package/meta-schemas/user-property.json +6 -1
  39. package/meta-schemas/user-schema.json +4 -1
  40. package/package.json +12 -4
  41. package/dist/defaults-BQej3KIY.mjs.map +0 -1
  42. package/dist/defaults-CSjBjBkQ.d.mts +0 -41
  43. package/dist/defaults-CSjBjBkQ.d.mts.map +0 -1
  44. package/dist/meta-schemas-8nlyVL7g.mjs.map +0 -1
@@ -0,0 +1,62 @@
1
+ # Branding
2
+
3
+ This directory owns how your login UI looks: `branding.json` (layout preset
4
+ and asset URLs) plus `login.liquid`, the LiquidJS template the
5
+ `<zitadel-login>` component renders for every step.
6
+
7
+ ## Workflow
8
+
9
+ 1. Edit `login.liquid` (and `branding.json` for logo/font/hero URLs).
10
+ 2. `zitadel plan` — validates the template (LiquidJS parse, banned patterns,
11
+ the required `{% mandatory_gates %}` tag) and shows the pending revision.
12
+ 3. `zitadel apply` — publishes an immutable branding revision. The login UI
13
+ picks up the newest revision on the next step response.
14
+
15
+ Every edit publishes a new revision; there is no in-place update. Roll back
16
+ by re-applying an earlier template.
17
+
18
+ ## Rules of the template
19
+
20
+ - Compose `<zl-*>` atoms and plain structural HTML. No `<script>`, no
21
+ `<style>`, no inline event handlers — theming happens through design
22
+ tokens, not template CSS.
23
+ - User-facing copy goes through translation keys: `{{ "key" | t }}`.
24
+ - Keep the trailing `{% mandatory_gates %}` tag: it appends any required
25
+ field, gate, or submit action your template forgot, so a broken template
26
+ still yields a submittable step.
27
+
28
+ ## Make it yours
29
+
30
+ - In the split-family designs (`split`, `split-right`, `hero`) the
31
+ `.zl-split__brand` pane is yours: structural HTML plus inline `style=""`
32
+ attributes are allowed; `button`, `input`, and `form` tags are stripped —
33
+ use `<a>` for landing CTAs. The `hero` design ships a full landing-page
34
+ starting point on token-styled `zl-hero__*` classes.
35
+ - On narrow viewports the brand pane collapses and the `.zl-split__compact`
36
+ node inside the form pane takes over (logo or brand line) — keep one so
37
+ your identity survives on phones.
38
+ - The split chrome is tunable from your template root's `style` attribute
39
+ (`--zl-split-columns`, `--zl-split-align`, `--zl-split-brand-mobile`), and
40
+ `zl-split--right` on the wrapper mirrors the panes. Knob reference:
41
+ `docs/design/branding/templates.md` in the Zitadel repo.
42
+ - The "Secured with Zitadel" attribution is licence-gated and on by default.
43
+ - Back-navigation: the engine injects a `kind: "back"` action on steps
44
+ that can return to their predecessor, and the browser's back gesture
45
+ submits it automatically — the shipped designs deliberately render **no
46
+ visible back control**. Your template may render one if you want it;
47
+ select the action by kind (never by name) and submit it like any other
48
+ action:
49
+
50
+ ```liquid
51
+ {% assign back = actions | where: "kind", "back" | first %}
52
+ {% if back %}
53
+ <a href="#" class="zl-card-nav__link" data-action="{{ back.name }}">{{ back.text_key | t }}</a>
54
+ {% endif %}
55
+ ```
56
+
57
+ If you list actions generically, exclude `a.kind == 'back'` from button
58
+ loops so the injected action doesn't render as a stray secondary button.
59
+
60
+ Start over anytime with `zitadel branding eject --design <name>` (designs:
61
+ `centered`, `split`, `split-right`, `hero`, `minimal`; add `--force` to
62
+ overwrite).
@@ -76,14 +76,6 @@ enters login on a fields-less passkey step with an email → password
76
76
  fallback path. The preset only decides the starting point — edit
77
77
  anything here afterwards.
78
78
 
79
- ## Presets
80
-
81
- `zitadel setup` scaffolds this folder from a preset (`--preset
82
- password-first` or `--preset passkey-first`). The passkey-first flow
83
- enters login on a fields-less passkey step with an email → password
84
- fallback path. The preset only decides the starting point — edit
85
- anything here afterwards.
86
-
87
79
  ## Schema revisions
88
80
 
89
81
  Editing a schema publishes a new immutable revision. When you `apply` a
@@ -0,0 +1,164 @@
1
+ <zl-page-shell data-zl-template-root>
2
+ {% if branding.logo_url %}
3
+ <img slot="header" class="zl-card-logo" src="{{ branding.logo_url }}" alt="" />
4
+ {% endif %}
5
+ <zl-card>
6
+ <h1 slot="header" class="zl-card-title">{{ step.texts.title_key | t: identity.display_name }}</h1>
7
+ {% if step.texts.description_key %}
8
+ {% assign card_subtitle = step.texts.description_key | t: identity.display_name %}
9
+ {% if card_subtitle != "" %}
10
+ <p slot="header" class="zl-card-subtitle">{{ card_subtitle }}</p>
11
+ {% endif %}
12
+ {% endif %}
13
+
14
+ {% if errors and errors.size > 0 %}
15
+ {% for err in errors %}
16
+ {% if err | formLevelError %}
17
+ {% assign alert_heading = err.text_key | alertHeading %}
18
+ {% assign alert_body = err.text_key | alertBody %}
19
+ <zl-alert severity="error" dismissible data-zl-step-error{% if alert_heading != "" %} heading="{{ alert_heading | t }}"{% endif %}>
20
+ {% if alert_body != "" %}{{ alert_body | t }}{% elsif err.text_key %}{{ err.text_key | t }}{% else %}{{ err.message }}{% endif %}
21
+ </zl-alert>
22
+ {% endif %}
23
+ {% endfor %}
24
+ {% endif %}
25
+
26
+ {% for f in fields %}
27
+ {% assign field_placeholder = f.text_key | fieldPlaceholder %}
28
+ {% assign field_help = f.text_key | fieldHelp %}
29
+ {% assign field_err = f.name | fieldError: errors %}
30
+ {% case f.type %}
31
+ {% when 'checkbox' %}
32
+ <zl-checkbox
33
+ name="{{ f.name }}"
34
+ data-testid="zitadel-field-{{ f.name | testid }}"
35
+ label="{{ f.text_key | t }}"
36
+ value="true"
37
+ {% if f.value %}checked{% endif %}
38
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
39
+ ></zl-checkbox>
40
+ {% when 'select' %}
41
+ <zl-select
42
+ name="{{ f.name }}"
43
+ data-testid="zitadel-field-{{ f.name | testid }}"
44
+ label="{{ f.text_key | t }}"
45
+ value="{{ f.value }}"
46
+ options='{{ f.validation.enum | selectOptions | json }}'
47
+ {% if f.required %}required{% endif %}
48
+ {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
49
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
50
+ ></zl-select>
51
+ {% else %}
52
+ {% assign autocomplete_val = '' %}
53
+ {% if f.name == 'email' %}
54
+ {% assign autocomplete_val = 'email' %}
55
+ {% elsif f.type == 'password' and step.name contains 'register' %}
56
+ {% assign autocomplete_val = 'new-password' %}
57
+ {% elsif f.type == 'password' %}
58
+ {% assign autocomplete_val = 'current-password' %}
59
+ {% endif %}
60
+ <zl-field
61
+ name="{{ f.name }}"
62
+ data-testid="zitadel-field-{{ f.name | testid }}"
63
+ label="{{ f.text_key | t }}"
64
+ type="{{ f.type }}"
65
+ value="{{ f.value }}"
66
+ {% if autocomplete_val != '' %}autocomplete="{{ autocomplete_val }}"{% endif %}
67
+ {% if f.required %}required{% endif %}
68
+ {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
69
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
70
+ >
71
+ {% if field_help != "" %}
72
+ <span slot="help">{{ field_help }}</span>
73
+ {% endif %}
74
+ </zl-field>
75
+ {% endcase %}
76
+ {% endfor %}
77
+
78
+ {% assign recover = actions | where: "name", "recover" | first %}
79
+ {% if recover %}
80
+ <p class="zl-card-forgot">
81
+ <a href="#" class="zl-card-forgot__link" data-action="recover">{{ recover.text_key | t }}</a>
82
+ </p>
83
+ {% endif %}
84
+
85
+ {% for a in actions %}
86
+ {% if a.primary %}
87
+ <zl-button
88
+ hierarchy="primary"
89
+ size="medium"
90
+ type="submit"
91
+ block
92
+ action="{{ a.name }}"
93
+ data-testid="zitadel-action-{{ a.name }}"
94
+ label="{{ a.text_key | t }}"
95
+ {% if loading %}loading{% endif %}
96
+ ></zl-button>
97
+ {% endif %}
98
+ {% endfor %}
99
+
100
+ {% for a in actions %}
101
+ {% unless a.primary or a.kind == 'back' or a.name == 'passkey' or a.name == 'register' or a.name == 'sign_in' or a.name == 'recover' %}
102
+ <zl-button
103
+ hierarchy="secondary"
104
+ size="medium"
105
+ type="button"
106
+ block
107
+ action="{{ a.name }}"
108
+ data-testid="zitadel-action-{{ a.name }}"
109
+ label="{{ a.text_key | t }}"
110
+ ></zl-button>
111
+ {% endunless %}
112
+ {% endfor %}
113
+
114
+ {% assign passkey = actions | where: "name", "passkey" | first %}
115
+ {% if passkey %}
116
+ {% unless passkey.primary %}
117
+ <zl-button
118
+ hierarchy="secondary"
119
+ size="medium"
120
+ block
121
+ action="passkey"
122
+ data-testid="zitadel-action-passkey"
123
+ label="{{ passkey.text_key | t }}"
124
+ ></zl-button>
125
+ {% endunless %}
126
+ {% endif %}
127
+
128
+
129
+ {% assign register = actions | where: "name", "register" | first %}
130
+ {% if register %}
131
+ <p class="zl-card-nav">
132
+ {{ 'identifier.action.register.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="register" data-testid="zitadel-action-register-link">{{ 'identifier.action.register.link' | t }}</a>
133
+ </p>
134
+ {% endif %}
135
+
136
+ {% assign sign_in = actions | where: "name", "sign_in" | first %}
137
+ {% if sign_in %}
138
+ <p class="zl-card-nav">
139
+ {{ 'register.action.sign_in.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="sign_in" data-testid="zitadel-action-sign_in-link">{{ 'register.action.sign_in.link' | t }}</a>
140
+ </p>
141
+ {% endif %}
142
+
143
+ {% if challenge %}
144
+ {% if challenge.method == "passkey" or challenge.method == "passkey_register" %}
145
+ {% assign ceremony = "authenticate" %}
146
+ {% assign submit_method = challenge.method %}
147
+ {% if challenge.method == "passkey_register" or challenge.options.user %}
148
+ {% assign ceremony = "register" %}
149
+ {% assign submit_method = "passkey_register" %}
150
+ {% endif %}
151
+ <zl-passkey
152
+ ceremony="{{ ceremony }}"
153
+ method="{{ submit_method }}"
154
+ challenge-id="{{ challenge.challenge_id }}"
155
+ options='{{ challenge.options | json }}'
156
+ pending-label="{{ 'passkey.pending.status' | t }}"
157
+ cancel-label="{{ 'action.cancel' | t }}"
158
+ ></zl-passkey>
159
+ {% endif %}
160
+ {% endif %}
161
+
162
+ {% mandatory_gates %}
163
+ </zl-card>
164
+ </zl-page-shell>
@@ -0,0 +1,222 @@
1
+ <zl-page-shell data-zl-template-root>
2
+ <div class="zl-split">
3
+ <aside class="zl-split__brand">
4
+ {% comment %} ──────────────────────────────────────────────────────────
5
+ YOUR LANDING CONTENT — everything inside this aside is yours.
6
+ Edit the copy and structure freely: structural HTML (div, section,
7
+ nav, h1–h6, ul, li, a, img, …) plus inline style="" attributes are
8
+ allowed; button, input, form, script, and style tags are stripped —
9
+ use plain anchors for landing CTAs. The zl-hero__* classes keep
10
+ this pane on design tokens so palettes and theme mode apply.
11
+
12
+ The login card in the section below is fully wired to the flow
13
+ engine; leave its zl-* atoms intact. Layout knobs (set on the root
14
+ element's style attribute): --zl-split-columns, --zl-split-align,
15
+ --zl-split-brand-mobile. Add zl-split--right to the wrapper div to
16
+ mirror the panes. More in .zitadel/branding/README.md.
17
+ ────────────────────────────────────────────────────────────── {% endcomment %}
18
+ <div class="zl-hero">
19
+ <nav class="zl-hero__nav">
20
+ <span class="zl-hero__brand">
21
+ {% if branding.logo_url %}
22
+ <img class="zl-hero__brand-logo" src="{{ branding.logo_url }}" alt="" />
23
+ {% else %}
24
+ <span class="zl-hero__brand-mark" aria-hidden="true"></span>
25
+ {% endif %}
26
+ Your brand
27
+ </span>
28
+ <span class="zl-hero__nav-links">
29
+ <a href="#">Product</a>
30
+ <a href="#">Pricing</a>
31
+ <a href="#">Docs</a>
32
+ </span>
33
+ </nav>
34
+
35
+ <p class="zl-hero__eyebrow">Your product category</p>
36
+ <h2 class="zl-hero__headline">A headline about what you ship.</h2>
37
+ <p class="zl-hero__sub">
38
+ Use this pane to tell visitors why they sign up: what your product
39
+ does, who it is for, and what happens after they sign in. Replace
40
+ every line here before you publish.
41
+ </p>
42
+
43
+ <ul class="zl-hero__bullets">
44
+ <li>Your first selling point</li>
45
+ <li>Your second selling point</li>
46
+ <li>Your third selling point</li>
47
+ </ul>
48
+
49
+ <p class="zl-hero__footnote">
50
+ Replace this line with real social proof, such as customer logos or
51
+ a certification you actually hold.
52
+ </p>
53
+ </div>
54
+ </aside>
55
+ <section class="zl-split__form">
56
+ {% if branding.logo_url %}
57
+ <img class="zl-split__compact" src="{{ branding.logo_url }}" alt="" />
58
+ {% else %}
59
+ <p class="zl-split__compact zl-hero__compact-brand">Your brand</p>
60
+ {% endif %}
61
+ <zl-card>
62
+ <h1 slot="header" class="zl-card-title">{{ step.texts.title_key | t: identity.display_name }}</h1>
63
+ {% if step.texts.description_key %}
64
+ {% assign card_subtitle = step.texts.description_key | t: identity.display_name %}
65
+ {% if card_subtitle != "" %}
66
+ <p slot="header" class="zl-card-subtitle">{{ card_subtitle }}</p>
67
+ {% endif %}
68
+ {% endif %}
69
+
70
+ {% if errors and errors.size > 0 %}
71
+ {% for err in errors %}
72
+ {% if err | formLevelError %}
73
+ {% assign alert_heading = err.text_key | alertHeading %}
74
+ {% assign alert_body = err.text_key | alertBody %}
75
+ <zl-alert severity="error" dismissible data-zl-step-error{% if alert_heading != "" %} heading="{{ alert_heading | t }}"{% endif %}>
76
+ {% if alert_body != "" %}{{ alert_body | t }}{% elsif err.text_key %}{{ err.text_key | t }}{% else %}{{ err.message }}{% endif %}
77
+ </zl-alert>
78
+ {% endif %}
79
+ {% endfor %}
80
+ {% endif %}
81
+
82
+ {% for f in fields %}
83
+ {% assign field_placeholder = f.text_key | fieldPlaceholder %}
84
+ {% assign field_help = f.text_key | fieldHelp %}
85
+ {% assign field_err = f.name | fieldError: errors %}
86
+ {% case f.type %}
87
+ {% when 'checkbox' %}
88
+ <zl-checkbox
89
+ name="{{ f.name }}"
90
+ data-testid="zitadel-field-{{ f.name | testid }}"
91
+ label="{{ f.text_key | t }}"
92
+ value="true"
93
+ {% if f.value %}checked{% endif %}
94
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
95
+ ></zl-checkbox>
96
+ {% when 'select' %}
97
+ <zl-select
98
+ name="{{ f.name }}"
99
+ data-testid="zitadel-field-{{ f.name | testid }}"
100
+ label="{{ f.text_key | t }}"
101
+ value="{{ f.value }}"
102
+ options='{{ f.validation.enum | selectOptions | json }}'
103
+ {% if f.required %}required{% endif %}
104
+ {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
105
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
106
+ ></zl-select>
107
+ {% else %}
108
+ {% assign autocomplete_val = '' %}
109
+ {% if f.name == 'email' %}
110
+ {% assign autocomplete_val = 'email' %}
111
+ {% elsif f.type == 'password' and step.name contains 'register' %}
112
+ {% assign autocomplete_val = 'new-password' %}
113
+ {% elsif f.type == 'password' %}
114
+ {% assign autocomplete_val = 'current-password' %}
115
+ {% endif %}
116
+ <zl-field
117
+ name="{{ f.name }}"
118
+ data-testid="zitadel-field-{{ f.name | testid }}"
119
+ label="{{ f.text_key | t }}"
120
+ type="{{ f.type }}"
121
+ value="{{ f.value }}"
122
+ {% if autocomplete_val != '' %}autocomplete="{{ autocomplete_val }}"{% endif %}
123
+ {% if f.required %}required{% endif %}
124
+ {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
125
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
126
+ >
127
+ {% if field_help != "" %}
128
+ <span slot="help">{{ field_help }}</span>
129
+ {% endif %}
130
+ </zl-field>
131
+ {% endcase %}
132
+ {% endfor %}
133
+
134
+ {% assign recover = actions | where: "name", "recover" | first %}
135
+ {% if recover %}
136
+ <p class="zl-card-forgot">
137
+ <a href="#" class="zl-card-forgot__link" data-action="recover">{{ recover.text_key | t }}</a>
138
+ </p>
139
+ {% endif %}
140
+
141
+ {% for a in actions %}
142
+ {% if a.primary %}
143
+ <zl-button
144
+ hierarchy="primary"
145
+ size="medium"
146
+ type="submit"
147
+ block
148
+ action="{{ a.name }}"
149
+ data-testid="zitadel-action-{{ a.name }}"
150
+ label="{{ a.text_key | t }}"
151
+ {% if loading %}loading{% endif %}
152
+ ></zl-button>
153
+ {% endif %}
154
+ {% endfor %}
155
+
156
+ {% for a in actions %}
157
+ {% unless a.primary or a.kind == 'back' or a.name == 'passkey' or a.name == 'register' or a.name == 'sign_in' or a.name == 'recover' %}
158
+ <zl-button
159
+ hierarchy="secondary"
160
+ size="medium"
161
+ type="button"
162
+ block
163
+ action="{{ a.name }}"
164
+ data-testid="zitadel-action-{{ a.name }}"
165
+ label="{{ a.text_key | t }}"
166
+ ></zl-button>
167
+ {% endunless %}
168
+ {% endfor %}
169
+
170
+ {% assign passkey = actions | where: "name", "passkey" | first %}
171
+ {% if passkey %}
172
+ {% unless passkey.primary %}
173
+ <zl-button
174
+ hierarchy="secondary"
175
+ size="medium"
176
+ block
177
+ action="passkey"
178
+ data-testid="zitadel-action-passkey"
179
+ label="{{ passkey.text_key | t }}"
180
+ ></zl-button>
181
+ {% endunless %}
182
+ {% endif %}
183
+
184
+
185
+ {% assign register = actions | where: "name", "register" | first %}
186
+ {% if register %}
187
+ <p class="zl-card-nav">
188
+ {{ 'identifier.action.register.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="register" data-testid="zitadel-action-register-link">{{ 'identifier.action.register.link' | t }}</a>
189
+ </p>
190
+ {% endif %}
191
+
192
+ {% assign sign_in = actions | where: "name", "sign_in" | first %}
193
+ {% if sign_in %}
194
+ <p class="zl-card-nav">
195
+ {{ 'register.action.sign_in.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="sign_in" data-testid="zitadel-action-sign_in-link">{{ 'register.action.sign_in.link' | t }}</a>
196
+ </p>
197
+ {% endif %}
198
+
199
+ {% if challenge %}
200
+ {% if challenge.method == "passkey" or challenge.method == "passkey_register" %}
201
+ {% assign ceremony = "authenticate" %}
202
+ {% assign submit_method = challenge.method %}
203
+ {% if challenge.method == "passkey_register" or challenge.options.user %}
204
+ {% assign ceremony = "register" %}
205
+ {% assign submit_method = "passkey_register" %}
206
+ {% endif %}
207
+ <zl-passkey
208
+ ceremony="{{ ceremony }}"
209
+ method="{{ submit_method }}"
210
+ challenge-id="{{ challenge.challenge_id }}"
211
+ options='{{ challenge.options | json }}'
212
+ pending-label="{{ 'passkey.pending.status' | t }}"
213
+ cancel-label="{{ 'action.cancel' | t }}"
214
+ ></zl-passkey>
215
+ {% endif %}
216
+ {% endif %}
217
+
218
+ {% mandatory_gates %}
219
+ </zl-card>
220
+ </section>
221
+ </div>
222
+ </zl-page-shell>
@@ -0,0 +1,154 @@
1
+ <zl-page-shell data-zl-template-root>
2
+ <section class="zl-minimal">
3
+ <h1 class="zl-card-title">{{ step.texts.title_key | t: identity.display_name }}</h1>
4
+
5
+ {% if errors and errors.size > 0 %}
6
+ {% for err in errors %}
7
+ {% if err | formLevelError %}
8
+ {% assign alert_heading = err.text_key | alertHeading %}
9
+ {% assign alert_body = err.text_key | alertBody %}
10
+ <zl-alert severity="error" dismissible data-zl-step-error{% if alert_heading != "" %} heading="{{ alert_heading | t }}"{% endif %}>
11
+ {% if alert_body != "" %}{{ alert_body | t }}{% elsif err.text_key %}{{ err.text_key | t }}{% else %}{{ err.message }}{% endif %}
12
+ </zl-alert>
13
+ {% endif %}
14
+ {% endfor %}
15
+ {% endif %}
16
+
17
+ {% for f in fields %}
18
+ {% assign field_placeholder = f.text_key | fieldPlaceholder %}
19
+ {% assign field_help = f.text_key | fieldHelp %}
20
+ {% assign field_err = f.name | fieldError: errors %}
21
+ {% case f.type %}
22
+ {% when 'checkbox' %}
23
+ <zl-checkbox
24
+ name="{{ f.name }}"
25
+ data-testid="zitadel-field-{{ f.name | testid }}"
26
+ label="{{ f.text_key | t }}"
27
+ value="true"
28
+ {% if f.value %}checked{% endif %}
29
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
30
+ ></zl-checkbox>
31
+ {% when 'select' %}
32
+ <zl-select
33
+ name="{{ f.name }}"
34
+ data-testid="zitadel-field-{{ f.name | testid }}"
35
+ label="{{ f.text_key | t }}"
36
+ value="{{ f.value }}"
37
+ options='{{ f.validation.enum | selectOptions | json }}'
38
+ {% if f.required %}required{% endif %}
39
+ {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
40
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
41
+ ></zl-select>
42
+ {% else %}
43
+ {% assign autocomplete_val = '' %}
44
+ {% if f.name == 'email' %}
45
+ {% assign autocomplete_val = 'email' %}
46
+ {% elsif f.type == 'password' and step.name contains 'register' %}
47
+ {% assign autocomplete_val = 'new-password' %}
48
+ {% elsif f.type == 'password' %}
49
+ {% assign autocomplete_val = 'current-password' %}
50
+ {% endif %}
51
+ <zl-field
52
+ name="{{ f.name }}"
53
+ data-testid="zitadel-field-{{ f.name | testid }}"
54
+ label="{{ f.text_key | t }}"
55
+ type="{{ f.type }}"
56
+ value="{{ f.value }}"
57
+ {% if autocomplete_val != '' %}autocomplete="{{ autocomplete_val }}"{% endif %}
58
+ {% if f.required %}required{% endif %}
59
+ {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
60
+ {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
61
+ >
62
+ {% if field_help != "" %}
63
+ <span slot="help">{{ field_help }}</span>
64
+ {% endif %}
65
+ </zl-field>
66
+ {% endcase %}
67
+ {% endfor %}
68
+
69
+ {% assign recover = actions | where: "name", "recover" | first %}
70
+ {% if recover %}
71
+ <p class="zl-card-forgot">
72
+ <a href="#" class="zl-card-forgot__link" data-action="recover">{{ recover.text_key | t }}</a>
73
+ </p>
74
+ {% endif %}
75
+
76
+ {% for a in actions %}
77
+ {% if a.primary %}
78
+ <zl-button
79
+ hierarchy="primary"
80
+ size="medium"
81
+ type="submit"
82
+ block
83
+ action="{{ a.name }}"
84
+ data-testid="zitadel-action-{{ a.name }}"
85
+ label="{{ a.text_key | t }}"
86
+ {% if loading %}loading{% endif %}
87
+ ></zl-button>
88
+ {% endif %}
89
+ {% endfor %}
90
+
91
+ {% for a in actions %}
92
+ {% unless a.primary or a.kind == 'back' or a.name == 'passkey' or a.name == 'register' or a.name == 'sign_in' or a.name == 'recover' %}
93
+ <zl-button
94
+ hierarchy="secondary"
95
+ size="medium"
96
+ type="button"
97
+ block
98
+ action="{{ a.name }}"
99
+ data-testid="zitadel-action-{{ a.name }}"
100
+ label="{{ a.text_key | t }}"
101
+ ></zl-button>
102
+ {% endunless %}
103
+ {% endfor %}
104
+
105
+ {% assign passkey = actions | where: "name", "passkey" | first %}
106
+ {% if passkey %}
107
+ {% unless passkey.primary %}
108
+ <zl-button
109
+ hierarchy="secondary"
110
+ size="medium"
111
+ block
112
+ action="passkey"
113
+ data-testid="zitadel-action-passkey"
114
+ label="{{ passkey.text_key | t }}"
115
+ ></zl-button>
116
+ {% endunless %}
117
+ {% endif %}
118
+
119
+ {% assign register = actions | where: "name", "register" | first %}
120
+ {% if register %}
121
+ <p class="zl-card-nav">
122
+ {{ 'identifier.action.register.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="register" data-testid="zitadel-action-register-link">{{ 'identifier.action.register.link' | t }}</a>
123
+ </p>
124
+ {% endif %}
125
+
126
+ {% assign sign_in = actions | where: "name", "sign_in" | first %}
127
+ {% if sign_in %}
128
+ <p class="zl-card-nav">
129
+ {{ 'register.action.sign_in.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="sign_in" data-testid="zitadel-action-sign_in-link">{{ 'register.action.sign_in.link' | t }}</a>
130
+ </p>
131
+ {% endif %}
132
+
133
+ {% if challenge %}
134
+ {% if challenge.method == "passkey" or challenge.method == "passkey_register" %}
135
+ {% assign ceremony = "authenticate" %}
136
+ {% assign submit_method = challenge.method %}
137
+ {% if challenge.method == "passkey_register" or challenge.options.user %}
138
+ {% assign ceremony = "register" %}
139
+ {% assign submit_method = "passkey_register" %}
140
+ {% endif %}
141
+ <zl-passkey
142
+ ceremony="{{ ceremony }}"
143
+ method="{{ submit_method }}"
144
+ challenge-id="{{ challenge.challenge_id }}"
145
+ options='{{ challenge.options | json }}'
146
+ pending-label="{{ 'passkey.pending.status' | t }}"
147
+ cancel-label="{{ 'action.cancel' | t }}"
148
+ ></zl-passkey>
149
+ {% endif %}
150
+ {% endif %}
151
+
152
+ {% mandatory_gates %}
153
+ </section>
154
+ </zl-page-shell>