Display badges and tooltips using static arrays, dynamic callbacks, or HTML tooltips
Comprehensive guide to displaying badges, tooltips, and special date indicators using the date range picker component.
The component provides multiple approaches to display badges and tooltips, each with different use cases and capabilities.
| Method | Use Case | Supports HTML | Complexity |
|---|---|---|---|
specialDates array | Static dates known at initialization (holidays, events) | No (text only) | Simple |
getDateMetadataCallback | Dynamic data based on date calculations (pricing, availability) | No (text only) | Medium |
badgeTooltipCallback / dayTooltipCallback | Advanced HTML tooltips with rich formatting | Yes (HTML) | Advanced |
DateInfo interface has been refactored for clarity:
disabled โ isDisabledlabel โ badgeTexttooltip โ badgeTooltip + dayTooltipclass โ badgeClass + dayClassspecial_dates={[%{date:, badge_text:, badge_tooltip:, badge_class:}]} (or set el.specialDates)
The simplest approach: define an array of special dates with labels, tooltips, and CSS classes. Perfect for holidays, events, and other dates known at initialization time.
// In HEEx this is the special_dates attr (declarative, needs hook={true}) โ no JS.
// Shown as JS here to mirror the vanilla demo; this page sets it in demos.js.
const picker = document.getElementById('holidays-demo');
picker.specialDates = [
{ date: '2025-12-25', badgeClass: 'holiday', badgeText: '๐', badgeTooltip: 'Christmas Day' },
{ date: '2025-01-01', badgeClass: 'holiday', badgeText: '๐', badgeTooltip: 'New Year\'s Day' },
{ date: '2025-07-04', badgeClass: 'holiday', badgeText: '๐', badgeTooltip: 'Independence Day' }
];
Four different technical approaches for displaying badges above dates. Choose the method that fits your use case.
See the real code for these four pickers in
test_app/priv/static/assets/demos.js โ the
BadgesTooltipsDemo phx-hook, blocks
set("method-static", โฆ), set("method-dynamic", โฆ),
set("method-mapping", โฆ), set("method-tooltips", โฆ).
The snippet below is the same logic, extracted for reading.
specialDates array property
getDateMetadataCallback
badgeMemberMapping with custom data
badgeTooltipCallback
// Client-side JS โ goes in a phx-hook's mounted() (assets/js/app.js), NOT the LiveView.
// Callbacks aren't wrapper attrs; this page wires them in priv/static/assets/demos.js.
// Method 1: Static Array
picker.specialDates = [
{ date: '2025-12-25', badgeText: '๐', badgeTooltip: 'Christmas' }
];
// Method 2: Dynamic Calculation with Custom Styles
picker.customStylesCallback = () => {
return `.price-high { background: #fee2e2; color: #991b1b; }
.price-low { background: #d1fae5; color: #065f46; }`;
};
picker.getDateMetadataCallback = ({ date }) => {
const price = (date.getDay() === 0 || date.getDay() === 6) ? 220 : 150;
const priceClass = price >= 220 ? 'price-high' : 'price-low';
return { badgeText: `$${price}`, badgeClass: `badge-number ${priceClass}` };
};
// Method 3: Member Mapping
picker.dateMember = 'eventDate';
picker.badgeTextMember = 'icon';
picker.specialDates = [
{ id: 1, eventDate: '2025-12-25', icon: '๐', label: 'Christmas' }
];
// Method 4: HTML Tooltips
picker.specialDates = [
{ date: '2025-12-25', badgeText: '๐', badgeClass: 'holiday' }
];
picker.badgeTooltipCallback = (data) => {
return '<strong>Christmas Day</strong><br>๐ Gift exchange<br>๐ฝ๏ธ Family dinner';
};
map your keys: date_member="displayDate", badge_text_member="icon", โฆ
Already have your own data structure? No need to reshape your data! Use member mapping properties to tell the picker which properties contain the date, badge text, tooltips, etc.
// In HEEx: the special_dates attr + the *_member attrs (date_member, badge_text_member, โฆ),
// all declarative (needs hook={true}). Shown as JS here to mirror the vanilla demo.
// Your existing data structure (no need to reshape!)
const myHolidays = [
{ id: 1, displayDate: '2025-12-25', icon: '๐', description: 'Christmas - Office Closed', style: 'holiday', disabled: true },
{ id: 2, displayDate: '2025-12-31', icon: '๐ฅณ', description: 'New Year Eve - Office Closed', style: 'holiday', disabled: true },
{ id: 3, displayDate: '2025-07-04', icon: '๐', description: 'Independence Day', style: 'event', disabled: true },
{ id: 4, displayDate: '2025-01-01', icon: '๐', description: 'New Year Day - Office Closed', style: 'holiday', disabled: true },
{ id: 5, displayDate: '2025-02-14', icon: '๐', description: 'Valentine\'s Day', style: 'event', disabled: false },
{ id: 6, displayDate: '2025-11-27', icon: '๐ฆ', description: 'Thanksgiving - Office Closed', style: 'holiday', disabled: true },
{ id: 7, displayDate: '2025-11-28', icon: '๐๏ธ', description: 'Black Friday', style: 'event', disabled: false }
];
const picker = document.getElementById('member-mapping-example');
// Map your property names to picker fields
picker.dateMember = 'displayDate'; // Which property contains the date
picker.badgeTextMember = 'icon'; // Which property contains the badge text
picker.badgeClassMember = 'style'; // Which property contains the CSS class
picker.badgeTooltipMember = 'description'; // Which property contains the tooltip
picker.isDisabledMember = 'disabled'; // Which property contains the disabled flag
// Assign your data as-is!
picker.specialDates = myHolidays;
dateMember - Property containing the date (default: 'date')badgeTextMember - Property containing badge text (default: 'badgeText')badgeClassMember - Property containing badge CSS class (default: 'badgeClass')dayClassMember - Property containing day CSS class (default: 'dayClass')badgeTooltipMember - Property containing badge tooltip (default: 'badgeTooltip')dayTooltipMember - Property containing day tooltip (default: 'dayTooltip')isDisabledMember - Property containing disabled flag (default: 'isDisabled')JS callback: el.getDateMetadataCallback = (ctx) => { ... } ยท ctx.date is the day
Use getDateMetadataCallback to generate badge and tooltip content dynamically based on date calculations, external data, or business logic. This is perfect for pricing calendars, availability systems, or computed values.
// Client-side JS โ goes in a phx-hook's mounted() (assets/js/app.js), NOT the LiveView.
// Callbacks aren't wrapper attrs; this page wires them in priv/static/assets/demos.js.
const picker = document.getElementById('dynamic-pricing');
// Pricing data from API or database
const priceData = {
'2025-11-15': 180,
'2025-11-16': 200,
'2025-11-22': 250,
'2025-11-23': 280
};
const toLocalISO = (d) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
picker.getDateMetadataCallback = ({ date }) => {
const dateStr = toLocalISO(date);
const price = priceData[dateStr];
if (price) {
let badgeClass = 'badge-number';
if (price > 250) badgeClass += ' price-high';
else if (price > 200) badgeClass += ' price-medium';
else badgeClass += ' price-low';
return {
badgeText: `$${price}`,
badgeClass: badgeClass,
badgeTooltip: `Price: $${price}/night`
};
}
return null;
};
DateInfo object with these properties:
badgeText - Text shown in badge rowbadgeClass - CSS class for badge cellbadgeTooltip - Plain text tooltip for badgedayClass - CSS class for entire day celldayTooltip - Plain text tooltip for day cellisDisabled - Whether the date is disabledJS callback: el.dayTooltipCallback / el.badgeTooltipCallback return HTML
For rich, formatted tooltips with HTML content, use badgeTooltipCallback or dayTooltipCallback. These callbacks return HTML strings that are rendered with full formatting support.
// Client-side JS โ goes in a phx-hook's mounted() (assets/js/app.js), NOT the LiveView.
// Callbacks aren't wrapper attrs; this page wires them in priv/static/assets/demos.js.
const picker = document.getElementById('html-tooltips-booking');
const bookingInfo = {
'2025-11-15': { price: 180, available: 5, rating: 4.5 },
'2025-11-16': { price: 200, available: 2, rating: 4.8 },
'2025-11-22': { price: 250, available: 1, rating: 4.9 }
};
const toLocalISO = (d) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
picker.getDateMetadataCallback = ({ date }) => {
const info = bookingInfo[toLocalISO(date)];
if (info) return { badgeText: `$${info.price}`, badgeClass: 'badge-number' };
return null;
};
// Add rich HTML tooltips via callback
picker.dayTooltipCallback = (data) => {
const info = bookingInfo[data.dateString];
if (info) {
return `
<div style="padding: 0.5rem;">
<strong style="display: block; margin-bottom: 0.5rem;">${data.dateString}</strong>
<div style="font-size: 0.875rem;">
<div>๐ฐ Price: $${info.price}/night</div>
<div>๐ Available: ${info.available} rooms</div>
<div>โญ Rating: ${info.rating}/5.0</div>
</div>
</div>
`;
}
return null;
};
innerHTML. Always sanitize user-generated content to prevent XSS attacks. Only use trusted data in tooltips.
You can combine different methods for maximum flexibility. For example, use specialDates for static holidays and getDateMetadataCallback for dynamic pricing, with dayTooltipCallback for rich tooltips.
// Client-side JS โ goes in a phx-hook's mounted() (assets/js/app.js), NOT the LiveView.
// Callbacks aren't wrapper attrs; this page wires them in priv/static/assets/demos.js.
const picker = document.getElementById('combined-example');
// 1. Static holidays via specialDates
picker.specialDates = [
{ date: '2025-12-25', badgeClass: 'holiday', badgeText: '๐', badgeTooltip: 'Christmas Day' },
{ date: '2025-12-31', badgeClass: 'holiday', badgeText: '๐ฅณ', badgeTooltip: 'New Year\'s Eve' }
];
// 2. Dynamic pricing via getDateMetadataCallback
const priceData = { /* ... */ };
const toLocalISO = (d) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
picker.getDateMetadataCallback = ({ date }) => {
const price = priceData[toLocalISO(date)];
if (price) return { badgeText: `$${price}`, badgeClass: 'badge-number' };
return null;
};
// 3. Rich tooltips via dayTooltipCallback
picker.dayTooltipCallback = (data) => {
const price = priceData[data.dateString];
if (price) return `<strong>$${price}/night</strong><br>Click to book`;
return null;
};
specialDates is checked firstgetDateMetadataCallback is checked if no special date found*TooltipCallback functions override text tooltips from above methodsThe picker reads dayClass / badgeClass from your specialDates data and applies whatever string you hand it as a literal class on the day cell or badge cell. You drive the styling from data โ the component itself never decides what a date "means".
dayClass: 'holiday' โ tints the whole day cell red (consumes --drp-holiday-color)dayClass: 'event' โ tints the whole day cell green (consumes --drp-event-color)badgeClass: 'badge-number' โ accent-colored numeric badgebadgeClass: 'badge-count' โ red count badgebadgeClass: 'badge-text' โ gray uppercase text badge'salsa', 'rumba') is rendered as-is โ you ship the matching CSS via customStylesCallback.
dayClass tints.drp__day.holiday / .drp__day.event rules fire automatically.
// In HEEx this is the special_dates attr (declarative, needs hook={true}) โ no JS.
// Shown as JS here to mirror the vanilla demo; this page sets it in demos.js.
const picker = document.getElementById('dayclass-builtin-demo');
picker.specialDates = [
{ date: dayOffset(-7), dayClass: 'holiday', dayTooltip: 'Office closed' },
{ date: dayOffset(-3), dayClass: 'event', dayTooltip: 'Team offsite' },
{ date: dayOffset(2), dayClass: 'event', dayTooltip: 'Product launch' },
{ date: dayOffset(7), dayClass: 'holiday', dayTooltip: 'Public holiday' },
];
badgeClass badge typesbadge-number, badge-count, badge-text.
// In HEEx this is the special_dates attr (declarative, needs hook={true}) โ no JS.
// Shown as JS here to mirror the vanilla demo; this page sets it in demos.js.
const picker = document.getElementById('badgeclass-builtin-demo');
picker.specialDates = [
{ date: dayOffset(-5), badgeClass: 'badge-number', badgeText: '12', badgeTooltip: 'Slot number' },
{ date: dayOffset(-2), badgeClass: 'badge-count', badgeText: '3', badgeTooltip: '3 alerts' },
{ date: dayOffset(1), badgeClass: 'badge-text', badgeText: 'NEW', badgeTooltip: 'New offering' },
{ date: dayOffset(4), badgeClass: 'badge-number', badgeText: '7', badgeTooltip: 'Capacity' },
{ date: dayOffset(8), badgeClass: 'badge-count', badgeText: '15', badgeTooltip: '15 bookings' },
];
When you need domain-specific classes (a fitness studio's schedule, a venue's class types, etc.), use whatever strings make sense and ship the matching CSS via customStylesCallback.
dayClass values: salsa, rumba, pilates.
// Client-side JS โ goes in a phx-hook's mounted() (assets/js/app.js), NOT the LiveView.
// customStylesCallback / callbacks aren't wrapper attrs; this page wires them in priv/static/assets/demos.js.
const picker = document.getElementById('custom-classifier-demo');
// 1. Ship the CSS for your class names โ injected into the picker's shadow DOM.
picker.customStylesCallback = () => `
.drp__day.salsa {
background: color-mix(in srgb, #fb7185 18%, transparent);
border-color: #fb7185;
}
.drp__day.rumba {
background: color-mix(in srgb, #f59e0b 18%, transparent);
border-color: #f59e0b;
}
.drp__day.pilates {
background: color-mix(in srgb, #6366f1 18%, transparent);
border-color: #6366f1;
}
`;
// 2. Hand the picker your data with those class names.
picker.specialDates = [
{ date: dayOffset(-6), dayClass: 'salsa', dayTooltip: 'Salsa basics' },
{ date: dayOffset(-3), dayClass: 'rumba', dayTooltip: 'Rumba intermediate' },
{ date: dayOffset(0), dayClass: 'pilates', dayTooltip: 'Pilates flow' },
{ date: dayOffset(3), dayClass: 'salsa', dayTooltip: 'Salsa advanced' },
{ date: dayOffset(6), dayClass: 'pilates', dayTooltip: 'Pilates flow' },
{ date: dayOffset(10), dayClass: 'rumba', dayTooltip: 'Rumba show night' },
];
customStylesCallback instead of a regular stylesheet?
The picker lives inside a shadow DOM, so document-level CSS can't reach .drp__day. Either inject your styles via this callback, or theme via --drp-* CSS variables on the host element. See the Theming examples for the variable approach.
| Tooltip Type | Best For | Example Content |
|---|---|---|
badgeTooltip | Explaining what the badge means | "Price: $150", "5 spots remaining", "Holiday" |
dayTooltip | Additional date information | "Available", "Fully booked", "Weekend rate applies" |
badgeTooltipCallback | Rich badge explanations with HTML | Formatted price breakdowns, availability charts |
dayTooltipCallback | Detailed date information with HTML | Event schedules, booking details, weather forecasts |
specialDates array for static data (better performance)(pick a date anywhere on this page)