Custom Sales Room Template Development
This guide provides technical documentation for developers looking to create custom sales room templates. It covers the template structure, available variables, and best practices for building secure, functional templates.
All code snippets and blocks in this guide are examples to demonstrate possibilities. Your sales room templates can include any features and functionality you need - these are not limitations, just starting points.
For a complete, working example of a sales room template with detailed documentation, see the Example Sales Room Documentation. The example includes Lead With Contact API integration, proforma calculator implementation, and responsive design patterns.
Template Architecture
SendSquared sales rooms use a Pug (formerly Jade) template engine with Tailwind CSS for styling. Templates are server-side rendered with dynamic data interpolation. All styling is implemented inline to ensure complete template portability.
Core Template Structure
doctype html
html(lang="en")
head
meta(charset='utf-8')
meta(name='viewport' content='width=device-width, initial-scale=1')
title #{contact.first_name} #{contact.last_name} - #{company.name}
// Tailwind CSS
script(src="https://cdn.tailwindcss.com")
// Font imports
link(rel="preconnect" href="https://fonts.googleapis.com")
link(rel="preconnect" href="https://fonts.gstatic.com" crossorigin)
link(rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap")
// Inline styles for custom theming
style.
body {
font-family: 'Open Sans', sans-serif;
}
:root {
--primary-color: #{company.primary_color || '#1E40AF'};
--secondary-color: #{company.secondary_color || '#6366F1'};
--accent-color: #{company.accent_color || '#F59E0B'};
}
.bg-primary { background-color: var(--primary-color); }
.text-primary { color: var(--primary-color); }
.border-primary { border-color: var(--primary-color); }
.bg-secondary { background-color: var(--secondary-color); }
.text-secondary { color: var(--secondary-color); }
.bg-accent { background-color: var(--accent-color); }
.text-accent { color: var(--accent-color); }
// Maps API - SendSquared will provide the API key
script(src="https://maps.googleapis.com/maps/api/js?key=API_KEY")
body.bg-gray-50
nav
// Navigation structure
main
// Content sections
footer
// Footer content
Image Handling
Inline Base64 Encoded Images
Since all CSS must be inline, the recommended approach for images is to embed them directly in the HTML using Base64 encoding. This creates completely self-contained templates with no external dependencies.
How Base64 Image Encoding Works
Base64 encoding converts binary image data into ASCII text that can be embedded directly in HTML. This follows the data URI scheme defined in RFC 2397.
// Example: Inline Base64 encoded image
img(src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' alt='1x1 red pixel')
// For JPEG images
img(src='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...' alt='Company Logo')
// For SVG images (can be included as plain text)
img(src='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"%3E%3Ccircle cx="50" cy="50" r="40" fill="%23ff0000"/%3E%3C/svg%3E' alt='Red Circle')
Converting Images to Base64
You can convert images to Base64 using:
- Online tools - Search for "image to base64 converter"
- Command line (macOS/Linux):
base64 -i image.png -o encoded.txt
- Node.js:
const fs = require("fs");
const base64 = fs.readFileSync("image.png", "base64");
console.log(`data:image/png;base64,${base64}`);
Size Considerations
- Base64 encoding increases file size by approximately 33%
- Keep images optimized and compressed before encoding
- Consider using SVG for logos and icons (smaller file size)
- For large images, you may still want to use external hosting
Alternative: External Image Hosting
If Base64 encoding isn't suitable for your use case:
- Your own hosting - Use your company's web hosting or CDN
- Third-party services - Services like Cloudinary, Imgur, or AWS S3
// Example: Reference externally hosted images
img(src='https://your-domain.com/images/hero-image.jpg' alt='Hero Image')
img(src='https://cdn.your-company.com/assets/logo.png' alt='Company Logo')
Available Context Objects
The template system provides access to several context objects containing property and contact information:
contact
Object
Contains information about the prospect/lead:
contact.first_name
- First namecontact.last_name
- Last namecontact.primary_email
- Email addresscontact.mobile_phone
- Mobile phone numbercontact.address_1
- Street address line 1contact.address_2
- Street address line 2contact.locality
- Citycontact.region
- State/Provincecontact.postal
- ZIP/Postal codecontact.country
- Country
unit
Object
Contains information about the property being showcased:
unit.name
- Property nameunit.address_1
- Property street addressunit.address_2
- Unit/Suite numberunit.locality
- Cityunit.region
- State/Provinceunit.postal
- ZIP/Postal codeunit.country
- Countryunit.lat
- Latitude coordinateunit.lng
- Longitude coordinateunit.taxable_value
- Property tax assessment valueunit.proforma_value_low
- Low revenue projection (in cents)unit.proforma_value_high
- High revenue projection (in cents)unit.building_name
- Building/complex name
company
Object
Contains information about your company:
company.name
- Company namecompany.address_1
- Company street addresscompany.address_2
- Company suite/unitcompany.locality
- Company citycompany.region
- Company state/provincecompany.postal
- Company ZIP/postal codecompany.country
- Company country
Map Integration
Sales room templates often include interactive maps to showcase property locations:
Basic Map Implementation
function initMap() {
var location = {lat: #{unit.lat}, lng: #{unit.lng}};
var map = new google.maps.Map(document.getElementById('map'), {
center: location,
zoom: 18,
mapTypeId: google.maps.MapTypeId.SATELLITE
});
new google.maps.Marker({
map: map,
position: location,
title: "#{unit.address_1}, #{unit.address_2} #{unit.locality}, #{unit.region} #{unit.postal}"
});
}
Map Container
#map(style='height: 500px;')
Revenue Projection Components
Proforma Calculator Integration
For vacation rental management companies, integrate the proforma calculator:
SendSquared will provide the appropriate proforma coefficients for your market area. These coefficients are used to calculate accurate revenue projections based on property characteristics and location. Contact support to get your market-specific coefficients configured.
script(src="https://your-domain.com/js/oa-proforma.bundled.js")
// Configure proforma styling with inline styles
style.
:root {
--oa-primary-color: #57c5cf;
--oa-block-text-color: #ffffff;
--oa-mobile-slide-height: 50vh;
}
// Add proforma component
oa-proforma(
position='right'
sticky='true'
taxValue=unit.taxable_value
unitType="house"
bedrooms='3'
bathrooms='2'
accommodates='6'
locationQuality="3"
propertyQuality="3"
viewQuality="3"
buttonText='Click here to schedule a consultation'
buttonUrl='https://calendly.com/your-link'
location='Your Location'
chartData='[9.64,12.82,16.64,7.75,7.07,9.48,8.38,4.19,3.53,4.63,6.19,9.67]'
)
Revenue Display
Display revenue projections prominently using Tailwind classes:
The revenue display section is optional and will only be shown if revenue gating is configured in your SendSquared account. If no revenue gates are set, this section won't appear. Similarly, the proforma component will respect the same pricing gates if configured.
// Only display if revenue projections are available
if unit.proforma_value_low && unit.proforma_value_high
.bg-primary.text-white.p-8.rounded-lg.text-center.shadow-lg
h2.text-3xl.font-bold.mb-2 #{contact.first_name} #{contact.last_name}, earn between
h2.text-4xl.font-bold.text-accent.mb-4 $#{unit.proforma_value_low / 100}k - $#{unit.proforma_value_high / 100}k
p.text-lg of income from #{unit.address_1}#{unit.address_2 ? ' ' + unit.address_2 : ''}, #{unit.locality} #{unit.region} #{unit.postal}
Content Sections
Hero Section
section.relative.h-screen.flex.items-center.justify-center(style='background:url(https://your-domain.com/images/hero-image.jpg) center center/cover no-repeat;')
.absolute.inset-0.bg-black.bg-opacity-50
.container.mx-auto.px-4.relative.z-10
.text-center.text-white
h1.text-5xl.font-bold.mb-4 Welcome #{contact.first_name} #{contact.last_name}
p.text-xl Your personalized property overview for #{unit.name}
Feature Sections
Create modular content sections using Tailwind's grid system:
section.py-16.bg-white
.container.mx-auto.px-4
.grid.grid-cols-1.md-grid-cols-2.gap-8
.text-center.p-6
.w-20.h-20.mx-auto.mb-4
img.w-full.h-full(src='https://your-domain.com/images/feature-icon.png' alt='Feature')
h3.text-2xl.font-bold.mb-2 Feature Title
p.text-gray-600 Feature description with personalized content for #{contact.first_name}
.text-center.p-6
.w-20.h-20.mx-auto.mb-4
img.w-full.h-full(src='https://your-domain.com/images/feature-icon-2.png' alt='Feature 2')
h3.text-2xl.font-bold.mb-2 Another Feature
p.text-gray-600 More details about your property at #{unit.address_1}
Call-to-Action Sections
section.py-16.bg-primary.text-white
.container.mx-auto.px-4
.text-center
h2.text-4xl.font-bold.mb-4 Ready to Get Started?
p.text-xl.mb-8 Schedule a personalized consultation
.space-x-4
a.inline-block.bg-white.text-primary.px-8.py-3.rounded-lg.font-semibold.hover-bg-gray-100.transition(href='tel:+1234567890') Call Us Today
a.inline-block.bg-secondary.text-white.px-8.py-3.rounded-lg.font-semibold.hover-bg-opacity-90.transition(href='https://calendly.com/your-link') Schedule Online
Form Integration
Contact Form Example
form#contact-form.max-w-lg.mx-auto(method='post' action='/submit-lead')
.mb-6
label.block.text-gray-700.text-sm.font-bold.mb-2(for='name') Full Name
input#name.w-full.px-3.py-2.border.border-gray-300.rounded-md.focus-outline-none.focus-ring-2.focus-ring-primary(
type='text'
name='name'
value='#{contact.first_name} #{contact.last_name}'
required
)
.mb-6
label.block.text-gray-700.text-sm.font-bold.mb-2(for='email') Email
input#email.w-full.px-3.py-2.border.border-gray-300.rounded-md.focus-outline-none.focus-ring-2.focus-ring-primary(
type='email'
name='email'
value='#{contact.primary_email}'
required
)
.mb-6
label.block.text-gray-700.text-sm.font-bold.mb-2(for='phone') Phone
input#phone.w-full.px-3.py-2.border.border-gray-300.rounded-md.focus-outline-none.focus-ring-2.focus-ring-primary(
type='tel'
name='phone'
value='#{contact.mobile_phone}'
required
)
.mb-6
label.block.text-gray-700.text-sm.font-bold.mb-2(for='property') Property Interest
input#property.w-full.px-3.py-2.border.border-gray-300.rounded-md.bg-gray-100(
type='text'
name='property'
value='#{unit.name} - #{unit.address_1}'
readonly
)
button.w-full.bg-primary.text-white.py-3.px-4.rounded-md.font-semibold.hover-bg-opacity-90.transition(type='submit') Submit Interest
Analytics and Tracking
SendSquared Analytics
// Analytics tracking
script(src='https://api.sendsquared.com/tracking/v1/bse-analytics-es3.js')
SMS Chat Widget
script(src='https://api.sendsquared.com/sms-chat/v1/s2-sms-chat.min.js')
script.
document.addEventListener('DOMContentLoaded', async function (event) {
S2SmsChat.init(
'YOUR-WIDGET-ID',
'YOUR-COMPANY-ID'
);
});
Google Analytics
// Google tag (gtag.js)
script(async='' src='https://www.googletagmanager.com/gtag/js?id=YOUR-GA-ID')
script.
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'YOUR-GA-ID');
Responsive Design Patterns
Mobile-First Approach
// Desktop/tablet view
.hidden.md-block
img(src='https://your-domain.com/images/desktop-image.jpg' alt='Feature')
// Mobile view
.block.md-hidden
img(src='https://your-domain.com/images/mobile-image.jpg' alt='Feature')
Responsive Grid Layouts
.grid.grid-cols-1.md-grid-cols-2.lg-grid-cols-3.gap-6
.p-4
// Content scales across devices
.p-4
// Responsive columns
.p-4
// Flexible layouts
Image and Asset Management
Image Optimization
// Responsive images with multiple sources
picture
source(media='(min-width: 768px)' srcset='https://your-domain.com/images/hero-desktop@2x.jpg')
source(media='(min-width: 480px)' srcset='https://your-domain.com/images/hero-tablet@2x.jpg')
img(src='https://your-domain.com/images/hero-mobile@2x.jpg' alt='Hero Image')
Lazy Loading
img.w-full.h-auto(
data-src='https://your-domain.com/images/image.jpg'
alt='Description'
loading='lazy'
)
Custom Styling
Inline Theme Configuration
All styling should be implemented inline within the template's style tag:
style.
/* Theme Variables */
:root {
--primary-color: #{company.primary_color || '#1E40AF'};
--secondary-color: #{company.secondary_color || '#6366F1'};
--accent-color: #{company.accent_color || '#F59E0B'};
--text-color: #1F2937;
--background-color: #F3F4F6;
}
/* Custom utility classes */
.bg-primary { background-color: var(--primary-color); }
.text-primary { color: var(--primary-color); }
.border-primary { border-color: var(--primary-color); }
.bg-secondary { background-color: var(--secondary-color); }
.text-secondary { color: var(--secondary-color); }
.bg-accent { background-color: var(--accent-color); }
.text-accent { color: var(--accent-color); }
/* Hover states */
.hover-bg-primary:hover { background-color: var(--primary-color); }
.hover-text-primary:hover { color: var(--primary-color); }
.hover-bg-opacity-90:hover { opacity: 0.9; }
/* Focus states */
.focus-ring-primary:focus {
--tw-ring-color: var(--primary-color);
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
/* Additional custom styles */
body {
font-family: 'Open Sans', sans-serif;
}
/* Smooth transitions */
.transition {
transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
Best Practices
Security Guidelines
- Input Sanitization: Always escape user inputs
- HTTPS: Ensure all resources load over HTTPS
- API Key Protection: Never expose API keys in client-side code
- Form Validation: Implement both client and server-side validation
Performance Optimization
- Asset Compression: Optimize all images and assets
- CDN Usage: Leverage CDNs for static assets
- Minification: Minify CSS and JavaScript
- Caching: Implement appropriate cache headers
Accessibility
- Alt Text: Provide descriptive alt text for all images
- Form Labels: Use proper label elements for forms
- Color Contrast: Ensure sufficient color contrast ratios
- Keyboard Navigation: Test keyboard accessibility
SEO Considerations
- Meta Tags: Include relevant meta descriptions
- Structured Data: Add schema.org markup where appropriate
- Mobile Optimization: Ensure mobile-friendly design
- Page Speed: Optimize for fast loading times
Testing Guidelines
Cross-Browser Testing
Test templates across:
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome Android)
Device Testing
Verify functionality on:
- Desktop (1920x1080, 1366x768)
- Tablet (768x1024)
- Mobile (375x667, 414x896)
Variable Testing
Test with various data scenarios:
- Missing optional fields
- Long text values
- Special characters in names
- Different property types
- Various revenue projections
Deployment Considerations
Asset Hosting
- Upload static assets to your own CDN or website
- Use full URLs for all image references in your template
- Ensure proper CORS headers for cross-origin resources
- Implement versioning for cache busting
- All CSS must remain inline within the template's style tag
Recommended CDN Services
For hosting your template assets (images, scripts, fonts), consider these popular CDN options:
Free/Low-Cost Options:
- Cloudflare - Free tier available with generous bandwidth limits
- jsDelivr - Free for open source projects, works with npm, GitHub, and WordPress
- unpkg - Free CDN for everything on npm
- cdnjs - Free CDN for popular JavaScript libraries and CSS frameworks
Premium CDN Services:
- AWS CloudFront - Pay-as-you-go pricing, integrates with S3
- Google Cloud CDN - Global edge locations, integrates with Cloud Storage
- Bunny CDN - Affordable pricing starting at $0.01/GB
- Fastly - Real-time analytics and instant purging capabilities
When choosing a CDN, consider:
- Geographic coverage for your target audience
- SSL/HTTPS support (required for sales room templates)
- Bandwidth costs and limits
- Ease of integration with your existing workflow
- Cache control and purging capabilities
Template Validation
- Validate Pug syntax before deployment
- Test all variable interpolations
- Verify form submissions work correctly
- Check analytics tracking implementation
This documentation provides the foundation for building effective sales room templates that convert prospects into customers while maintaining security and performance standards.