โ† Back to Examples

๐Ÿท๏ธ Badges & Tooltips

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.

Overview: Three Ways to Add Badges & Tooltips

The component provides multiple approaches to display badges and tooltips, each with different use cases and capabilities.

MethodUse CaseSupports HTMLComplexity
specialDates arrayStatic dates known at initialization (holidays, events)No (text only)Simple
getDateMetadataCallbackDynamic data based on date calculations (pricing, availability)No (text only)Medium
badgeTooltipCallback / dayTooltipCallbackAdvanced HTML tooltips with rich formattingYes (HTML)Advanced
New in latest version: The DateInfo interface has been refactored for clarity:
  • disabled โ†’ isDisabled
  • label โ†’ badgeText
  • tooltip โ†’ badgeTooltip + dayTooltip
  • class โ†’ badgeClass + dayClass

Method 1: Static Special Dates Array

special_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.

Basic Usage

Holidays marked with emojis and custom styling
Some dates are already booked (disabled)
// 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' }
];

Badge Implementation Methods

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.

Best for: Known dates defined at initialization (holidays, events) Uses: specialDates array property
Best for: Computed values based on date properties (pricing, availability) Uses: getDateMetadataCallback
Best for: Integrating existing data without reshaping (legacy systems, APIs) Uses: badgeMemberMapping with custom data
Best for: Complex tooltip content with HTML formatting Uses: 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';
};

Using Custom Property Names (Member Mapping)

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.

This example uses a custom data structure with properties like 'displayDate', 'icon', 'description', 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;
Available Member Properties:
  • 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')

Method 2: Dynamic Metadata Callback

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.

Prices calculated based on day of week and demand
Shows remaining spots with color coding
// 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;
};
Return Value: The callback returns a DateInfo object with these properties:
  • badgeText - Text shown in badge row
  • badgeClass - CSS class for badge cell
  • badgeTooltip - Plain text tooltip for badge
  • dayClass - CSS class for entire day cell
  • dayTooltip - Plain text tooltip for day cell
  • isDisabled - Whether the date is disabled

Method 3: Advanced HTML Tooltips

JS 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.

Hover over dates to see detailed booking information
HTML tooltips with event schedules and details
// 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;
};
Security Note: Tooltip callbacks return HTML that is rendered with innerHTML. Always sanitize user-generated content to prevent XSS attacks. Only use trusted data in tooltips.

Combining Multiple Methods

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.

Static holidays + dynamic pricing + HTML tooltips all working together
// 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;
};
Priority: When multiple methods provide data for the same date:
  • specialDates is checked first
  • getDateMetadataCallback is checked if no special date found
  • *TooltipCallback functions override text tooltips from above methods

Consumer-Data Classifier Classes (built-in and custom)

The 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".

Built-in conventions: the shipped CSS includes default styling for five common consumer-data class names so you get something polished without writing CSS:
  • 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 badge
  • badgeClass: 'badge-count' โ€” red count badge
  • badgeClass: 'badge-text' โ€” gray uppercase text badge
Any other string (e.g., 'salsa', 'rumba') is rendered as-is โ€” you ship the matching CSS via customStylesCallback.

Demo 1 โ€” Built-in dayClass tints

Shipped .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' },
];

Demo 2 โ€” Built-in badgeClass badge types

Three shipped badge styles: badge-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' },
];

Demo 3 โ€” Custom classifiers (your own vocabulary)

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.

Three custom 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' },
];
Why 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 Best Practices

When to Use Each Tooltip Type

Tooltip TypeBest ForExample Content
badgeTooltipExplaining what the badge means"Price: $150", "5 spots remaining", "Holiday"
dayTooltipAdditional date information"Available", "Fully booked", "Weekend rate applies"
badgeTooltipCallbackRich badge explanations with HTMLFormatted price breakdowns, availability charts
dayTooltipCallbackDetailed date information with HTMLEvent schedules, booking details, weather forecasts

Accessibility Tips

  • Keep tooltip text concise and informative
  • Ensure sufficient color contrast in custom badge styles
  • Don't rely solely on color to convey meaning (use icons/text too)
  • Test tooltip readability on mobile devices

Performance Considerations

  • Callbacks are called for every visible day - keep logic fast
  • Cache expensive calculations outside the callback
  • Use specialDates array for static data (better performance)
  • Avoid complex DOM manipulation in tooltip HTML
๐Ÿ”Œ Server round-trip
0
The LiveView server saw 0 event(s) from the wrapper.
(pick a date anywhere on this page)