Web Components
HTML custom elements with Shadow DOM and CSS custom properties
Installation
npm install @aho-sdk/components-web
<script src="https://js.aho.com/v1"></script>
<script src="https://js.aho.com/v1/components-web"></script>
Quick Start
Add a verification button to your page. First, include the SDK:
<script src="https://js.aho.com/v1"></script>
Then add a credential button:
<credential-button
publishable-key="aho_pub_xxx"
claims="age_over_21">
Verify Age
</credential-button>
Request multiple claims:
<credential-button
publishable-key="aho_pub_xxx"
claims="given_name,family_name,age_over_21">
Verify Identity
</credential-button>
Using npm
Or install via npm and use ES modules:
npm install @aho-sdk/verify
import { configure } from '@aho-sdk/verify';
configure({ publishableKey: 'aho_pub_xxx' });
Examples
Custom Styling
Customize button appearance with CSS custom properties.
<style nonce="...">
.custom-button {
--aho-button-bg: #059669;
--aho-button-bg-hover: #047857;
--aho-button-radius: 9999px;
}
</style>
<credential-button
class="custom-button"
publishable-key="aho_pub_xxx"
claims="given_name,family_name">
Verify Identity
</credential-button>
Event Handling
Listen for verification lifecycle events.
Click button to see events...
btn.addEventListener('aho:credential:verified', (e) => {
console.log('Verified:', e.detail.result);
});
btn.addEventListener('aho:credential:error', (e) => {
console.error('Error:', e.detail.message);
});
Credential Display
After verification, display the credential card automatically.
<credential-button
id="verify-btn"
publishable-key="aho_pub_xxx"
claims="all">
Verify Identity
</credential-button>
<div id="result-container" class="mt-4 hidden">
<p class="text-green-600 font-medium mb-2">Verification successful!</p>
<credential-display id="verified-credential" enable-download></credential-display>
</div>
<script nonce="...">
(function() {
const btn = document.getElementById('verify-btn');
const resultDiv = document.getElementById('result-container');
const display = document.getElementById('verified-credential');
if (!btn || !resultDiv || !display) {
console.error('Missing elements:', { btn: !!btn, resultDiv: !!resultDiv, display: !!display });
return;
}
btn.addEventListener('aho:credential:verified', (e) => {
if (e.detail.result?.renderUrl) {
display.setAttribute('src', e.detail.result.renderUrl);
resultDiv.classList.remove('hidden');
}
});
})();
</script>
Display Size Variants
Display credentials in compact (280px), default (400px), or large (600px) max-widths.
Compact (max-width: 280px)
Default (max-width: 400px)
Large (max-width: 600px)
<div class="space-y-6">
<div>
<p class="text-sm font-medium text-gray-500 mb-2">Compact (max-width: 280px)</p>
<credential-display
size="compact"
data='{"format":"svg","content":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 318 200\"\u003e \u003crect width=\"318\" height=\"200\" rx=\"8\" fill=\"#fafafa\" stroke=\"#22c55e\" stroke-width=\"1.5\"/\u003e \u003crect width=\"318\" height=\"32\" rx=\"8\" fill=\"#22c55e\"/\u003e\u003crect y=\"8\" width=\"318\" height=\"24\" fill=\"#22c55e\"/\u003e \u003ctext x=\"10\" y=\"22\" font-family=\"system-ui\" font-size=\"11\" font-weight=\"700\" fill=\"white\"\u003eDRIVER LICENSE\u003c/text\u003e\u003ctext x=\"309\" y=\"24\" font-size=\"10\" fill=\"white\" text-anchor=\"end\"\u003eCOMPACT\u003c/text\u003e \u003crect x=\"10\" y=\"42\" width=\"52\" height=\"65\" rx=\"3\" fill=\"#e2e8f0\"/\u003e \u003ccircle cx=\"36\" cy=\"62\" r=\"12\" fill=\"#94a3b8\"/\u003e\u003cellipse cx=\"36\" cy=\"95\" rx=\"18\" ry=\"12\" fill=\"#94a3b8\"/\u003e \u003ctext x=\"72\" y=\"58\" font-family=\"system-ui\" font-size=\"14\" font-weight=\"700\" fill=\"#1e293b\"\u003eJane Doe\u003c/text\u003e \u003ctext x=\"72\" y=\"75\" font-family=\"system-ui\" font-size=\"9\" fill=\"#64748b\"\u003e123 Main St, San Francisco\u003c/text\u003e \u003ctext x=\"72\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eDOB: 01/15/1990\u003c/text\u003e \u003ctext x=\"170\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eLIC: D1234567\u003c/text\u003e \u003cline x1=\"0\" y1=\"165\" x2=\"318\" y2=\"165\" stroke=\"#e5e7eb\"/\u003e \u003ctext x=\"10\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eCLASS: C\u003c/text\u003e \u003ctext x=\"80\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eEXP: 01/2030\u003c/text\u003e \u003c/svg\u003e","template":{"key":"mobile_drivers_license","source":"built_in","verified":true}}'>
</credential-display>
</div>
<div>
<p class="text-sm font-medium text-gray-500 mb-2">Default (max-width: 400px)</p>
<credential-display
data='{"format":"svg","content":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 318 200\"\u003e \u003crect width=\"318\" height=\"200\" rx=\"8\" fill=\"#fafafa\" stroke=\"#0ea5e9\" stroke-width=\"1.5\"/\u003e \u003crect width=\"318\" height=\"32\" rx=\"8\" fill=\"#0ea5e9\"/\u003e\u003crect y=\"8\" width=\"318\" height=\"24\" fill=\"#0ea5e9\"/\u003e \u003ctext x=\"10\" y=\"22\" font-family=\"system-ui\" font-size=\"11\" font-weight=\"700\" fill=\"white\"\u003eDRIVER LICENSE\u003c/text\u003e\u003ctext x=\"309\" y=\"24\" font-size=\"10\" fill=\"white\" text-anchor=\"end\"\u003eDEFAULT\u003c/text\u003e \u003crect x=\"10\" y=\"42\" width=\"52\" height=\"65\" rx=\"3\" fill=\"#e2e8f0\"/\u003e \u003ccircle cx=\"36\" cy=\"62\" r=\"12\" fill=\"#94a3b8\"/\u003e\u003cellipse cx=\"36\" cy=\"95\" rx=\"18\" ry=\"12\" fill=\"#94a3b8\"/\u003e \u003ctext x=\"72\" y=\"58\" font-family=\"system-ui\" font-size=\"14\" font-weight=\"700\" fill=\"#1e293b\"\u003eJane Doe\u003c/text\u003e \u003ctext x=\"72\" y=\"75\" font-family=\"system-ui\" font-size=\"9\" fill=\"#64748b\"\u003e123 Main St, San Francisco\u003c/text\u003e \u003ctext x=\"72\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eDOB: 01/15/1990\u003c/text\u003e \u003ctext x=\"170\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eLIC: D1234567\u003c/text\u003e \u003cline x1=\"0\" y1=\"165\" x2=\"318\" y2=\"165\" stroke=\"#e5e7eb\"/\u003e \u003ctext x=\"10\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eCLASS: C\u003c/text\u003e \u003ctext x=\"80\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eEXP: 01/2030\u003c/text\u003e \u003c/svg\u003e","template":{"key":"mobile_drivers_license","source":"built_in","verified":true}}'>
</credential-display>
</div>
<div>
<p class="text-sm font-medium text-gray-500 mb-2">Large (max-width: 600px)</p>
<credential-display
size="large"
data='{"format":"svg","content":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 318 200\"\u003e \u003crect width=\"318\" height=\"200\" rx=\"8\" fill=\"#fafafa\" stroke=\"#8b5cf6\" stroke-width=\"1.5\"/\u003e \u003crect width=\"318\" height=\"32\" rx=\"8\" fill=\"#8b5cf6\"/\u003e\u003crect y=\"8\" width=\"318\" height=\"24\" fill=\"#8b5cf6\"/\u003e \u003ctext x=\"10\" y=\"22\" font-family=\"system-ui\" font-size=\"11\" font-weight=\"700\" fill=\"white\"\u003eDRIVER LICENSE\u003c/text\u003e\u003ctext x=\"309\" y=\"24\" font-size=\"10\" fill=\"white\" text-anchor=\"end\"\u003eLARGE\u003c/text\u003e \u003crect x=\"10\" y=\"42\" width=\"52\" height=\"65\" rx=\"3\" fill=\"#e2e8f0\"/\u003e \u003ccircle cx=\"36\" cy=\"62\" r=\"12\" fill=\"#94a3b8\"/\u003e\u003cellipse cx=\"36\" cy=\"95\" rx=\"18\" ry=\"12\" fill=\"#94a3b8\"/\u003e \u003ctext x=\"72\" y=\"58\" font-family=\"system-ui\" font-size=\"14\" font-weight=\"700\" fill=\"#1e293b\"\u003eJane Doe\u003c/text\u003e \u003ctext x=\"72\" y=\"75\" font-family=\"system-ui\" font-size=\"9\" fill=\"#64748b\"\u003e123 Main St, San Francisco\u003c/text\u003e \u003ctext x=\"72\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eDOB: 01/15/1990\u003c/text\u003e \u003ctext x=\"170\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eLIC: D1234567\u003c/text\u003e \u003cline x1=\"0\" y1=\"165\" x2=\"318\" y2=\"165\" stroke=\"#e5e7eb\"/\u003e \u003ctext x=\"10\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eCLASS: C\u003c/text\u003e \u003ctext x=\"80\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eEXP: 01/2030\u003c/text\u003e \u003c/svg\u003e","template":{"key":"mobile_drivers_license","source":"built_in","verified":true}}'>
</credential-display>
</div>
</div>
Controlled Mode
Pass pre-fetched render data directly to skip the fetch.
Static (inline data)
<credential-display
data='{"format":"svg","content":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 318 200\"\u003e \u003crect width=\"318\" height=\"200\" rx=\"8\" fill=\"#fafafa\" stroke=\"#10b981\" stroke-width=\"1.5\"/\u003e \u003crect width=\"318\" height=\"32\" rx=\"8\" fill=\"#10b981\"/\u003e\u003crect y=\"8\" width=\"318\" height=\"24\" fill=\"#10b981\"/\u003e \u003ctext x=\"10\" y=\"22\" font-family=\"system-ui\" font-size=\"11\" font-weight=\"700\" fill=\"white\"\u003eDRIVER LICENSE\u003c/text\u003e\u003ctext x=\"309\" y=\"24\" font-size=\"10\" fill=\"white\" text-anchor=\"end\"\u003eSAMPLE\u003c/text\u003e \u003crect x=\"10\" y=\"42\" width=\"52\" height=\"65\" rx=\"3\" fill=\"#e2e8f0\"/\u003e \u003ccircle cx=\"36\" cy=\"62\" r=\"12\" fill=\"#94a3b8\"/\u003e\u003cellipse cx=\"36\" cy=\"95\" rx=\"18\" ry=\"12\" fill=\"#94a3b8\"/\u003e \u003ctext x=\"72\" y=\"58\" font-family=\"system-ui\" font-size=\"14\" font-weight=\"700\" fill=\"#1e293b\"\u003eJohn Smith\u003c/text\u003e \u003ctext x=\"72\" y=\"75\" font-family=\"system-ui\" font-size=\"9\" fill=\"#64748b\"\u003e123 Main St, San Francisco\u003c/text\u003e \u003ctext x=\"72\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eDOB: 01/15/1990\u003c/text\u003e \u003ctext x=\"170\" y=\"100\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eLIC: D1234567\u003c/text\u003e \u003cline x1=\"0\" y1=\"165\" x2=\"318\" y2=\"165\" stroke=\"#e5e7eb\"/\u003e \u003ctext x=\"10\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eCLASS: C\u003c/text\u003e \u003ctext x=\"80\" y=\"185\" font-family=\"system-ui\" font-size=\"8\" fill=\"#94a3b8\"\u003eEXP: 01/2030\u003c/text\u003e \u003c/svg\u003e","template":{"key":"mobile_drivers_license","source":"built_in","verified":true}}'>
</credential-display>
Dynamic (fetch then set)
<credential-button
id="verify-btn"
publishable-key="aho_pub_xxx"
claims="given_name,family_name">
Verify Identity
</credential-button>
<credential-display id="credential-display"></credential-display>
<script nonce="...">
(function() {
const btn = document.getElementById('verify-btn');
const display = document.getElementById('credential-display');
btn.addEventListener('aho:credential:verified', async (e) => {
const response = await fetch(e.detail.result.renderUrl);
const svg = await response.text();
display.setAttribute('data', JSON.stringify({ format: 'svg', content: svg }));
});
})();
</script>
Display Styling
Customize display appearance with CSS custom properties.
<style nonce="...">
.custom-display {
--aho-display-bg: #1f2937;
--aho-display-border: 2px solid #4ade80;
--aho-display-radius: 1rem;
}
</style>
<credential-display
class="custom-display"
data='{"format":"svg","content":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 300 150\"\u003e\u003crect fill=\"#374151\" width=\"300\" height=\"150\" rx=\"12\"/\u003e\u003ctext x=\"150\" y=\"65\" fill=\"#4ade80\" text-anchor=\"middle\" font-size=\"14\" font-weight=\"bold\"\u003eCUSTOM STYLED\u003c/text\u003e\u003ctext x=\"150\" y=\"95\" fill=\"white\" text-anchor=\"middle\" font-size=\"12\"\u003eDark theme with green accent\u003c/text\u003e\u003c/svg\u003e"}'>
</credential-display>
Display Events
Listen for display lifecycle events.
display.addEventListener('aho:display:loaded', (e) => {
console.log('Loaded:', e.detail.format, e.detail.templateKey);
});
display.addEventListener('aho:display:error', (e) => {
console.error('Error:', e.detail.message);
});
display.addEventListener('aho:display:download', (e) => {
console.log('Downloaded:', e.detail.format, e.detail.filename);
});
API Reference
<credential-button>
A button that initiates the credential verification flow.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
publishable-key |
string | — | Publishable key (aho_pub_xxx). Required unless set via configure() or AhoProvider. |
claims |
string | string[] | — | Claims to request. Comma-separated string or array. Use 'all' for all claims. |
return-claims |
boolean | 'all' | string[] | true | Which claims to return in the result. true=all requested, false=none, 'all'=all, array=specific claims. Must be subset of claims. |
document-types |
string | string[] | all | Accepted document types (e.g., 'mDL', 'passport'). Defaults to all. |
client-name |
string | — | Display name shown in wallet dialog. |
base-url |
string | https://api.aho.com | API base URL. Defaults to production. |
unsupported |
'hide' | 'disable' | 'fallback' | disable | Behavior when browser doesn't support Digital Credentials API. |
disabled |
boolean | false | Disable the button. |
Events
| Event | Detail | Description |
|---|---|---|
aho:credential:started |
{ claims: string[] } | Verification process started. |
aho:credential:statechange |
{ state: VerifyState, previousState: VerifyState } | Verification state changed. |
aho:credential:verified |
{ result: VerifyResult } | Verification completed successfully. |
aho:credential:failed |
{ error: Error } | Verification failed (credential rejected). |
aho:credential:error |
{ error: Error } | Error occurred during verification. |
aho:credential:cancelled |
{} | User cancelled the verification. |
aho:credential:unsupported |
{ reason: string, platform: Platform, apiAvailable: boolean } | Browser doesn't support Digital Credentials API. |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--aho-button-bg |
#2563eb | Background color |
--aho-button-bg-hover |
#1d4ed8 | Hover background |
--aho-button-bg-verifying |
#6b7280 | Verifying state background |
--aho-button-bg-success |
#059669 | Success state background |
--aho-button-bg-error |
#dc2626 | Error state background |
--aho-button-color |
white | Text color |
--aho-button-padding |
0.75rem 1.5rem | Padding |
--aho-button-radius |
0.5rem | Border radius |
--aho-button-gap |
0.5rem | Gap between icon and text |
--aho-button-border |
none | Border style |
--aho-button-font |
inherit | Font family |
--aho-button-font-size |
1rem | Font size |
--aho-button-font-weight |
500 | Font weight |
--aho-button-line-height |
1.25 | Line height |
--aho-button-disabled-opacity |
0.5 | Disabled state opacity |
--aho-button-outline |
2px solid #60a5fa | Focus outline |
--aho-button-outline-offset |
2px | Focus outline offset |
--aho-button-transition |
background-color 0.15s, opacity 0.15s | Transition effects |
--aho-button-shadow |
none | Box shadow |
--aho-button-shadow-hover |
none | Box shadow on hover |
--aho-button-transform-hover |
none | Transform on hover |
<credential-display>
Displays a rendered credential.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
src |
string | — | URL to fetch rendered credential from. |
data |
string | object | — | Pre-fetched render response. JSON string (web) or object (React/Vue). |
size |
'compact' | 'default' | 'large' | default | Display size variant. Controls max-width. |
enable-download |
boolean | false | Show download button on hover. |
loading-text |
string | Loading... | Text shown while loading. |
error-text |
string | Failed to load credential | Text shown on error. |
verify-authenticity |
boolean | false | Verify credential authenticity and show trust indicator with link to verification page. |
Events
| Event | Detail | Description |
|---|---|---|
aho:display:loaded |
{ format: string, templateKey?: string } | Content loaded successfully. |
aho:display:error |
{ error: Error, message: string } | Error loading content. |
aho:display:download |
{ format: string, filename: string } | Download triggered. |
aho:display:authenticity-verified |
{ verifyUrl: string } | Credential authenticity verified successfully. |
aho:display:authenticity-failed |
{ error: Error, message: string } | Credential authenticity verification failed. |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--aho-display-bg |
#ffffff | Container background |
--aho-display-border |
1px solid #e5e7eb | Container border |
--aho-display-radius |
0.75rem | Border radius |
--aho-display-compact-width |
280px | Compact size max-width |
--aho-display-default-width |
400px | Default size max-width |
--aho-display-large-width |
600px | Large size max-width |
--aho-display-loading-bg |
rgba(255, 255, 255, 0.9) | Loading overlay background |
--aho-display-spinner-color |
#e5e7eb | Spinner track color |
--aho-display-spinner-active |
#3b82f6 | Spinner active color |
--aho-display-error-color |
#dc2626 | Error text color |
--aho-display-download-bg |
rgba(255, 255, 255, 0.9) | Download button background |
--aho-display-download-border |
1px solid #e5e7eb | Download button border |
--aho-display-download-radius |
0.375rem | Download button radius |
--aho-display-download-bg-hover |
#f3f4f6 | Download button hover background |
--aho-display-download-icon |
#374151 | Download icon color |
--aho-display-trust-bg |
#10b981 | Trust indicator background |
--aho-display-trust-color |
#ffffff | Trust indicator text color |