1. Validating fields in a Identity Management template
Identity Management input fields are not part of a standard HTML
tag, so client-side validation requires JavaScript wired into the template via . Piano internally uses Apache Commons Validator as the reference for built-in checks, but custom validation has to be added by the publisher.
Email validation pattern
The canonical pattern uses a regex tested against the input value, with the result driving the disabled state of the submit button and a visible error message:
<p class="lead" formTitle hideIfInsideCheckout></p>
<p class="sub-lead" hideIfInsideCheckout>
<span><t>Please fill in the form</t></span>
</p>
<errors-list></errors-list>
<custom-field id="email" fieldName="email"></custom-field>
<span class="errorMessage" id="valEmailMsg"></span>
<custom-fields></custom-fields>
<p hideIfInsideCheckout>
<button actionSubmit external-event="submitForm" id="sendButton" class="btn prime"><t>Send</t></button>
</p>
<p class="final" hideIfInsideCheckout>
<a actionSkip class="link"><t>Skip</t></a>
</p>
<custom-script>
var regexEmail = /^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
var emailValid;
function validateForm() {
var activateButton = document.getElementById('sendButton');
if (emailValid == true) {
activateButton.removeAttribute('disabled');
} else {
activateButton.setAttribute('disabled', 'disabled');
}
}
function validateEmail() {
var parentEmail = document.getElementById('email');
var emailInput = parentEmail.children[0]; // this is the input
var valEmailMsg = document.getElementById('valEmailMsg');
if (!regexEmail.test(emailInput.value)) {
emailInput.setAttribute('style', 'border: .25px solid #FF0000;');
valEmailMsg.innerHTML = 'Please enter a valid email address.';
emailValid = false;
} else {
emailInput.removeAttribute('style', 'border: .25px solid #FF0000;');
valEmailMsg.innerHTML = '';
emailValid = true;
}
validateForm();
}
setTimeout(function() {
var parent = document.getElementById('email');
var child = parent.children[0];
child.setAttribute('onkeyup', 'validateEmail()');
}, 100);
</custom-script>
The same skeleton — setTimeout to grab the rendered input after Angular mounts, then attach a validator on onkeyup — works for first / last name validation, custom regex requirements, or excluding emojis / special characters.
Important: cover paste, not just keyup
If you rely only on keyup, paste operations are missed. Paste fires paste and input (and change on blur), but not keyup. Attach the validator to all four events so iOS WKWebView and paste flows work consistently:
<custom-script>
function updateEmail() { validateEmail(); }
setTimeout(function() {
var emailInput = document.querySelector('#email input');
['input', 'paste', 'keyup', 'change'].forEach(function(evt) {
emailInput.addEventListener(evt, updateEmail, false);
});
}, 100);
</custom-script>
This specifically addresses the iOS bug where pasting an email into Identity Management inside a WKWebView caused an "email required" error on submit.
Preserving expected attributes
Do not rename or remove the email input's required attributes — Identity Management needs them to read the value:
<input type="email" name="email" id="email" autocomplete="email">
If you revert to the default Piano ID login template and the bug disappears, the cause is your customization.
Blocking emails by domain or pattern
Two layered approaches:
-
Identity Management
Allowed email regexsetting — Java regex specifying what the email must match. Failures return a validation error and block registration. This is the supported, server-side enforcement. -
Template-level JavaScript blocklist — in addition (or as a stop-gap), use a that checks the entered email against a list of blocked addresses or domains and hides the login button or displays an error message if it matches.
The regex setting is enforced everywhere; the script-only path is bypassable, so do not rely on it alone for compliance use cases.
Custom fields and unique validators
For named fields beyond email (for example, first / last name with regex restrictions on emoji / special characters), define custom fields in Identity Management with unique validators. The same validate-then-toggle-button pattern applies.
2. Customizing placeholder text
Placeholder text on profile inputs (First Name, Last Name, etc.) is customized in the template editor of the relevant template:
-
My Account User Profile Components template, or
-
Piano ID profile in My Account template.
Edit the placeholder string directly in the template HTML.
Removing placeholder text in custom forms
If the light-gray placeholder text inside Piano ID custom form inputs needs to go, the typical cause is a configuration in the custom form template. To remove it:
-
Identify whether the placeholder is template-driven (confirm by reviewing the template HTML).
-
Roll back recent template changes if the placeholder started appearing after a template edit in production.
-
Mirror any production change in sandbox so the two stay in sync.
-
Communicate the rollback to anyone who was depending on the placeholder behavior.
A custom-script alternative — strip the empty class so the rendered placeholder is dismissed at load time:
<custom-script>
setTimeout(function() {
document.querySelectorAll('.custom-field-input').forEach(function(ele) {
ele.classList.remove('empty');
});
}, 1000);
</custom-script>
Tune the timeout so it runs after the form's elements have rendered.
3. Controlling autocomplete and autofill
Disabling email autofill in login / register modals
To stop browsers from suggesting saved emails inside the create-account or login modals, modify the Piano ID register page and Piano ID login page templates. Locate the email input — it may appear as:
<input fieldRegisterEmail type="email" value="">
<input fieldLoginEmail type="email" value="">
Add the autofill-suppression attribute set:
<!-- Registration -->
<input fieldRegisterEmail
type="email"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
data-lpignore="true"
value="">
<!-- Login -->
<input fieldLoginEmail
type="email"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
data-lpignore="true"
value="">
If autocomplete="off" is ignored (Chrome in particular can ignore it for known-credential fields), try autocomplete="new-email" or autocomplete="nope". Adding autocomplete="off" to the surrounding
tag may also help. As a stronger override, change the input type from "email" to "text" with inputmode="email" — but only if you keep email validation in place. Save / publish and test across Chrome, Safari, Firefox, Edge, and their mobile equivalents.
Trade-off: heavy autofill suppression also disables password-manager helpers; weigh that against the autofill behavior you are trying to remove.
Enabling autocomplete on Piano forms
Conversely, to enable autocomplete on Piano's own forms (so user data fills in automatically when logged in), add the autocomplete attribute to the specific field in the template. Note that external services (e.g., Jotform) cannot pull from Piano's stored user data to autocomplete their own forms — that integration is not supported.
4. Casing matters in template directives
Identity Management template directives are case-sensitive. For example, showIfInsideCheckout must be written with capital I and C. Lowercased versions (e.g., showifinsidecheckout) are silently unrecognized — elements then appear on all screens instead of only during the checkout flow. Always copy directive names exactly from the documentation.
5. Handling special characters in template HTML
If the dashboard preview and the rendered page diverge on characters like < and >, replace them with the HTML entities < and >. This is the standard fix for any template where literal angle brackets are part of the user-facing text — the dashboard editor sometimes tolerates the raw character while the browser does not.
6. Encoding curly braces in tracking URLs
Some third-party platforms (Sourcepoint and similar) reject URLs containing { or }. Two patterns address this:
-
encodeURIComponentfilter — encode template variables in place:
{{params.trackingId | encodeURIComponent}} This produces %7B / %7D and other escapes automatically.
-
_ptidparameter — auto-encodesparams.trackingIdwhen appended:
html link
If a downstream component needs to read the encoded value, decode it on the receiving side:
const urlParams = new URLSearchParams(window.location.search);
const pianoTrackingID = urlParams.get('_ptid');
7. Multi-select field display
By default, a multi-select custom field in Identity Management renders as a collapsed dropdown. To force the options to render directly (always visible), add to the CSS tab of the custom form template:
.pn-dropdown.pn-selectbox.pn-dropdown--flexible {
position: relative !important;
top: 0 !important;
display: block !important;
}
.pn-dropdown__item-list { max-height: unset !important; }
.input.with-tooltip-icon.with-placeholder { pointer-events: none !important; }
This rule applies to every form that uses this template — you cannot scope it to a single custom field instance.
8. Checkbox styling
Checkboxes in Identity Management login / register render inside #piano-id-container and inherit from Piano ID Layout. To override them:
-
Edit Manage → Templates → Piano ID Layout → CSS.
-
Use high-specificity selectors such as
#piano-id-container .field-checkbox-label checkbox-control, or!importantwhere necessary. -
Style both base and checked states. You can visually hide the native input and style the visual control instead — but leave the native input in the DOM so keyboard navigation and screen readers still work.
If your changes don't appear, force a hard refresh / incognito session — checkbox rules can be cached.
9. OTP / 2FA digital code input layout
The digital code input boxes for OTP and 2FA default to a vertical layout on some templates. The fix is to apply CSS to Piano ID Layout (not the 2FA Email Digital Code or DOI Digital Code template — those don't have their own CSS tab, so the styling must be inherited from Layout).
Minimal fix — make the boxes horizontal
.digital-code-input { display: flex; }
Add !important if the rule is overridden by defaults.
Full styling rule for the OTP / DOI verification code
.digital-code-input {
display: flex;
align-items: center;
justify-content: center;
margin: 0 -6px;
}
.digital-code-input input {
margin: 0 3px;
width: 35px;
height: 48px;
padding: 11px 8px;
text-align: center;
border-radius: 2px;
}
.digital-code-input input:nth-child(3n) {
margin: 0 13px 0 3px;
}
.digital-code-input input:last-child {
margin: 0 3px;
}
digital-code-input-base .digital-code-input input[type='number'] {
color: var(--code-color);
background: transparent;
border-color: var(--border-input-field);
}
digital-code-input-base .digital-code-input input[type='number']:focus {
border-color: var(--border-input-field-focused);
}
Before applying the full rule, reset Piano ID Layout and the Identity Management Digital Code templates to default so they are on the latest version — older versions of those templates have inline CSS that fights the flex rule.
10. Disabling the datepicker UI
To hide the calendar UI on date input fields (so users type the date directly), add to Piano ID Layout (for registration) or Piano ID profile in My Account (for the profile tab):
.pn-datepicker__content {
display: none;
}
The input remains mandatory; only the calendar overlay is suppressed.
11. Fixing the "Blocked autofocusing" console error
If Chrome DevTools shows:
Blocked autofocusing on a <input> element in a cross-origin subframe.
…then the standard HTML autofocus attribute is being applied inside a Identity Management modal (which runs in a cross-origin iframe). Replace autofocus with Piano's own pn-autofocus directive — that is the supported way to set focus inside Identity Management templates without triggering the browser block.
Templates that may need the change:
-
Piano ID initiate password reset
-
Piano ID login page
-
Piano ID login confirm page
-
Piano ID register page
-
Piano ID register confirm page
Find each value="" autofocus occurrence and rewrite as value="" pn-autofocus.
12. Customizing OTP input box appearance
Beyond layout, the OTP input box appearance — borders, sizing, colors — is also styled via the .digital-code-input class set in Piano ID Layout. The full rule in section 9 covers the standard styling levers; raise specificity or use !important if your rule is being overridden by defaults.