Overview
Hakama.js is a lightweight, ES5-compatible JavaScript library that adds virtual try-on functionality to your e-commerce site. It automatically injects action buttons into product cards, opens a modal for image upload, and optionally integrates with your Try-On API to generate virtual try-on previews.
Key Features
Image Management
- Upload exactly ONE user image (jpeg/jpg/png/webp)
- Automatic file type and size validation
- Persist in IndexedDB (best practice) with base64 fallback
- Auto-load previously saved image whenever the modal opens
- Remove/Clear saved photo and allow re-upload
Modal Experience
- Full-page overlay with customizable styling
- Side-by-side thumbnail preview (clothing + user photo)
- Full-width result display area
- Close on Escape key or backdrop click
- Scroll lock when modal is open
Try-On Integration
- POST multipart/form-data with { person, clothing } images
- Automatic preparation of image blobs
- Cloudflare Turnstile token injection
- Custom API headers support
- Error handling and retry logic
Developer Experience
- ES5-compatible (works in older browsers)
- No dependencies required
- Automatic DOM observation for dynamic content
- Custom button templates
- Comprehensive event callbacks
- TypeScript-friendly API
Installation
CDN (Recommended for Quick Setup)
<script src="https://hakama.io/hakama.min.js"></script>
Quick Start
Basic Setup
The simplest way to get started:
<!-- Your product cards -->
<div class="card">
<img src="product1.jpg" alt="Product 1">
<h3>Blue Jacket</h3>
<div class="actions"></div>
</div>
<!-- Include Hakama -->
<script src="https://hakama.io/hakama.min.js"></script>
<script>
Hakama.init({
cardSelector: '.card',
insertIntoSelector: '.actions',
buttonText: 'Try On',
buttonClass: 'btn btn-primary',
key: 'your-api-key',
});
</script>
With Auto-Initialization
Define configuration before loading the script:
<script>
window.HakamaConfig = {
cardSelector: '.card',
buttonText: 'Try On',
tryOnEnabled: true,
tryOnEndpoint: '/api/try-on'
};
</script>
<script src="hakama.js"></script>
Configuration Options
Hakama.js provides extensive configuration options to customize behavior, appearance, and integration with your Try-On API.
Card & Button Configuration
Control how buttons are injected into your product cards:
| Option | Type | Default | Description |
|---|---|---|---|
cardSelector |
String | '.card' |
CSS selector for product cards |
insertIntoSelector |
String | null |
Target element within card (e.g., '.actions') |
createActionsIfMissing |
Boolean | false |
Create container if insertIntoSelector not found |
createdActionsTag |
String | 'div' |
HTML tag for created container |
createdActionsClass |
String | 'hakama-actions' |
Class for created container |
buttonText |
String | 'Action' |
Button text content |
buttonClass |
String | '' |
CSS classes for button |
ariaLabel |
String | null |
Accessibility label (defaults to buttonText) |
buttonTemplate |
Function | null |
Custom button generator function |
placement |
String | 'append' |
'append' or 'prepend' within target |
Hakama.init({
buttonTemplate: function(opts, card) {
var btn = document.createElement('button');
btn.className = 'custom-tryon-btn';
btn.innerHTML = '<span>👔</span> Try It On';
return btn;
}
});
Filter Options
| Option | Type | Default | Description |
|---|---|---|---|
injectedAttr |
String | 'data-action-injected' |
Attribute to mark injected cards |
skipSelector |
String | '[data-action-disabled]' |
Skip cards matching this selector |
cardMustAlsoMatch |
String | null |
Additional selector cards must match |
DOM Observation
| Option | Type | Default | Description |
|---|---|---|---|
root |
Element | document |
Root element for querying cards |
observe |
Boolean | true |
Watch for dynamically added cards |
Click Behavior
| Option | Type | Default | Description |
|---|---|---|---|
onClick |
Function | null |
function(cardEl, event) - Custom click handler |
dispatchEventName |
String | null |
Custom event name to dispatch (e.g., 'card:action') |
dispatchTarget |
String | 'card' |
'card' (bubbles from card) or 'document' |
Modal Configuration
Customize the modal appearance and behavior:
| Option | Type | Default | Description |
|---|---|---|---|
modal |
Boolean | true |
Enable modal functionality |
modalTitle |
String | 'Preview' |
Default modal title |
closeOnEsc |
Boolean | true |
Close modal on Escape key |
closeOnBackdrop |
Boolean | true |
Close modal on backdrop click |
renderModal |
Function | null |
function(cardEl) => { title, html } |
onModalOpen |
Function | null |
function(dialogEl, cardEl, imageUrl) |
onModalClose |
Function | null |
function(dialogEl, cardEl) |
Upload Configuration
Control image upload behavior:
| Option | Type | Default | Description |
|---|---|---|---|
enableUpload |
Boolean | true |
Enable upload functionality |
uploadText |
String | 'Upload photo' |
Upload button text |
uploadAccept |
String | 'image/jpeg,image/jpg,image/png,image/webp' |
Accepted file types |
uploadButtonClass |
String | '' |
CSS classes for upload button |
maxFileSizeBytes |
Number | null |
Maximum file size in bytes |
onUpload |
Function | null |
function(file, cardEl, imageUrl) |
Remove/Clear Configuration
| Option | Type | Default | Description |
|---|---|---|---|
enableRemove |
Boolean | true |
Enable remove functionality |
removeText |
String | 'Remove photo' |
Remove button text |
removeButtonClass |
String | '' |
CSS classes for remove button |
onRemove |
Function | null |
function(cardEl) |
Storage Configuration
Configure where and how user images are stored:
| Option | Type | Default | Description |
|---|---|---|---|
storageKey |
String | 'hakama:user-image' |
localStorage key for fallback storage |
idb |
Object | { dbName, storeName, key } |
IndexedDB configuration |
idb.dbName |
String | 'HakamaDB' |
IndexedDB database name |
idb.storeName |
String | 'images' |
IndexedDB object store name |
idb.key |
String | 'user-image' |
IndexedDB key for user image |
Try-On API Configuration
Configure integration with your virtual try-on API:
| Option | Type | Default | Description |
|---|---|---|---|
tryOnEnabled |
Boolean | false |
Enable Try-On functionality |
tryOnButtonText |
String | 'Generate Preview' |
Try-On button text |
tryOnButtonClass |
String | '' |
CSS classes for Try-On button |
tryOnHeaders |
Object | null |
Additional HTTP headers (DO NOT set Content-Type) |
clothingImageSelector |
String | 'img' |
How to find clothing image in card |
getClothingImage |
Function | null |
Custom clothing image extractor |
onTryOnStart |
Function | null |
function(cardEl) |
onTryOnResult |
Function | null |
function(cardEl, resultObjectURL) |
onTryOnError |
Function | null |
function(cardEl, error) |
Styling Configuration
Control CSS injection and styling:
| Option | Type | Default | Description |
|---|---|---|---|
injectStyles |
String | 'minimal' |
'minimal' (inject basic styles) or 'none' |
namespace |
String | 'hakama' |
CSS class prefix |
zIndexBase |
Number | 2147483000 |
Base z-index for modal (very high by default) |
API Reference
Initialize Hakama with configuration options. This method is idempotent (safe to call multiple times).
Parameters: options (Object)
Returns: Hakama instance (chainable)
Re-scan the DOM and inject buttons into any new cards that match the selector.
Returns: Hakama instance (chainable)
Disconnect the MutationObserver and stop watching for new cards.
Returns: Hakama instance (chainable)
Remove all injected buttons from cards and clear the injected attribute.
Returns: Hakama instance (chainable)
Programmatically open the modal with custom content.
Parameters: payload (Object, optional) - { title, html }
Returns: Hakama instance (chainable)
Programmatically close the modal.
Returns: Hakama instance (chainable)
Storage System
How It Works
Hakama uses a dual-storage strategy to ensure user photos persist reliably across sessions:
Usage Examples
Basic Implementation
<script src="hakama.js"></script>
<script>
Hakama.init({
cardSelector: '.card',
buttonText: 'Try On',
buttonClass: 'btn btn-primary'
});
</script>
E-commerce Integration
Hakama.init({
cardSelector: '.product-card',
buttonText: '👔 Virtual Try-On',
tryOnEnabled: true,
tryOnEndpoint: '/api/virtual-tryon',
key: 'api-key',
maxFileSizeBytes: 10 * 1024 * 1024,
onTryOnResult: function(card, resultUrl) {
gtag('event', 'tryon_success');
}
});