Styling and branding your reservation widget is easy with eat app and just requires some quick configuration
To Style and Brand your widget, go to the Eat App Widget configuration section by:
- Clicking on the side menu at https://app.eatapp.co
- Select the online channels icon
- Click or tap the 'widget' option
Different ways to Customize your online booking widget:
- Update your logo and background
- Add your own custom styles via CSS
- Add your own custom behaviour and styles with javascript
- Remove the time slots on your online waitlist (waitlist only for current time/walkins)
- Lock the continue button so it floats at the bottom of the screen when scrolling
- Enable a scrollable gallery for your events, preferences and tickets
- Add blacktext on highlighted cells (for supporting dark styles and backgrounds)
- Make your top logo 30% size
- Change the style of the widget for a specific event on a specific day
- Change the wording on the confirmation page if there is a payment
- Change the wording on the confirmation page if the party size is large
- Remove Venue name at the top of widget
- Make the waitlist button to always show on the widget
- Make the cover selector row sticky to the top
- Hide timeslots that are not available
Updating the Logo and Background
You can customize the logo and background by going to the look and feel section of the widget here:
Custom CSS Mode
You can customize all the CSS in your widget by toggling the CSS Style option in your 'widget look and feel settings' by choosing widget on the side menu here:
Then go to 'Look and feel' on your widget config of your choice and toggle on the customized CSS field.
You can then add any custom css you want to customize your widget!
Copy and paste this example below to see how far your customization can go:
body {
font-family: arial;
background-color: #f5f5f5;
corner-radius: 4px;
}
.text-primary {
color: #dd8e00 !important;
font-family: arial;
}
.timeslot {
border-radius: 4px !important;
}
.search-cover-selector {
}
.search-date-selector {
}
.search-preference-selector {
}
.search-container {
background-color: #f5f5f5;
}
.search-cover-selector,
.search-date-selector,
.search-preference-selector {
background-color: #FFFFFF !important;
border-color: transparent !important;
}
/* .timeslots - unchecked */
.timeslots .slot {
font-size: 0.875rem;
color: #111111 !important;
border: 1px solid #320606;
border-radius: 4px;
cursor: pointer;
font-weight: 100;
line-height: 1.4;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* .timeslots - checked */
.timeslots input[type=checkbox]:checked+label.slot,
.timeslots input[type=radio]:checked+label.slot {
background-color: #EEEEEE !important;
border: 2px solid #dd8e00 !important;
}
/* timeslots - disabled */
.timeslots input[type='radio']:disabled+label.slot {
color: #a0aec0;
cursor: not-allowed;
background-color: #128849 !important;
}
button.primary,
.btn.primary,
.btn-sm.primary,
.btn-md.primary,
.btn-lg.primary,
.bg-primary,
.info-icon.primary::before,
.radio input:checked {
background-color: #320606 !important;
border-color: transparent !important;
font-family: arial;
}
.button-container {
display: flex;
justify-content: center;
}
button.primary,
lg.primary {
border-color: transparent;
}
.bg-widget {
background-color: #f5f5f5 !important;
}
/* the country dropdown */
button[data-action="click->dropdown-menu#toggle"] {
background-color: transparent !important;
}
/ * the swiper buttons */ .swiper-button-next,
swiper-button-prev {
color: #333333 !important;
}
.swiper-button-prev {
margin-left: 6px;
color: #333333 !important;
}
.swiper-button-next {
margin-right: 6px;
color: #333333 !important;
}
.image-description-container {
display: flex;
justify-content: center;
}
.image-text {
color: #EEEEEE;
font-style: italic;
}
.logo {
width: 30%;
height: 100%;
object-fit: contain;
}
#results {
margin-bottom: 72px;
}
Javascript mode
- Make sure you have the 'Embedded Tracking Installation' section of the widget config enabled (if the embedded tracking section of the widget isn't enabled for you, please speak with our support team to get it enabled before beginning this tutorial).
- Watch this short video and use this code sample to get started in styling your widget:
1) You can watch the video above to learn more about the background styles, element styles, font styling and top image configuration.
2) Copy and paste the code below into the embedded tracking field named 'Embedded Tracking Installation'.
This is where the tutorial might need your marketer or web developer to get involved.
<script>
// Define variables for font URL and styles
const fontUrl = 'https://fonts.googleapis.com/css2?family=Rowdies:wght@300&display=swap';
// Background styles for elements
const backgroundColorWidget = 'transparent !important';
const backgroundColorLabel = 'grey !important';
const backgroundColorCheckedRadio = 'white !important';
const backgroundColorAppBody = '#E2E2E2CC !important';
// Common styles for the widget
const fontFamilyRowdies = "Rowdies, Arial, Charcoal, sans-serif";
const borderRadius2px = '2px';
// Create the Image
const imgElement = document.createElement('img');
imgElement.src = 'https://i.imgur.com/5A1xXod.png';
imgElement.width = 120;
imgElement.height = 120;
window.onload = function () {
// Create and append a link element for the font
const fontLink = document.createElement('link');
fontLink.href = fontUrl;
fontLink.rel = 'stylesheet';
document.head.appendChild(fontLink);
// Define styles using variables
const styles = `
.widget,
#widget-tabs,
#menu-button,
select,
input[type='text'],
input[type='tel'],
input[type='date'],
textarea,
.bg-widget {
background-color: ${backgroundColorAppBody};
},
label * {
background-color: ${backgroundColorLabel};
},
.timeslots input[type='radio']:checked + label.slot {
background-color: ${backgroundColorCheckedRadio};
},
.dropdown {border-radius: ${borderRadius2px};},
.reveal-multiselect button {border-radius: ${borderRadius2px};},
.reveal-multiselect ul,
.tabs-title > a:focus,
.tabs-title > a:hover {
background-color: ${backgroundColorWidget};
},
input[type='radio']:checked, label.slot, timeslots {
background-color: ${backgroundColorWidget};
},
#app-body {
background-color: ${backgroundColorAppBody};
}
`;
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
};
// Set common styles for all elements
const allElements = document.querySelectorAll('*');
allElements.forEach(function (element) {
element.style.borderRadius = borderRadius2px;
element.style.backgroundColor = backgroundColorWidget;
element.style.fontFamily = fontFamilyRowdies;
});
// Set background color for specific elements
document.querySelectorAll("body.pg-bg-widget, #app-body").forEach(function (element) {
element.style.backgroundColor = backgroundColorWidget;
});
// Select the .px-4 element
let px4 = document.querySelector('.px-4');
// Check if the px-4 element exists
if (px4) {
// Create a container div for the image
let imgContainer = document.createElement('div');
imgContainer.style.display = 'flex';
imgContainer.style.justifyContent = 'center'; // center the image horizontally
imgContainer.style.marginTop = '10px';
imgContainer.appendChild(imgElement);
// Insert the container div before the "px-4" element
px4.parentElement.insertBefore(imgContainer, px4);
}
</script>
Remove time slots on your online waitlist
If you want to hide disabled time slots and only show the available ones, you can do it with this script addition:
<script>
// make it so that waitlist timeslots and text are hidden
document.addEventListener("DOMContentLoaded", function() {
if (window.location.href.includes("wait_list")) {
// Function to hide elements that contain specific text
function hideElementsContainingText(className, searchText) {
// Get all elements with the specified class
var elements = document.querySelectorAll('.' + className.replace(/\s+/g, '.'));
// Loop through the elements using forEach
elements.forEach(function(element) {
// Check if the element's text includes the desired text
if (element.textContent.includes(searchText)) {
// Hide the element
element.style.display = 'none';
}
});
}
// Call the function with the specific class and text
hideElementsContainingText('px-4 text-xs text-grey-700', 'Select your preferred time');
// find the timeslots and hide them
const timeslots = document.querySelectorAll('.timeslots');
timeslots.forEach(function(timeslot) {
timeslot.style.display = 'none';
});
// Check the first timeslot
const firstTimeslot = document.querySelector('.timeslots input[type="radio"]');
if (firstTimeslot) {
firstTimeslot.checked = true;
}
// Enable the continue button
const continueButton = document.querySelector('button[data-widget--results-target="confirm"]');
if (continueButton) {
continueButton.disabled = false;
}
}
});
</script>
Lock the continue button to the bottom of the screen when scrolling
// fix the bottom button position when scrolling
document.addEventListener("DOMContentLoaded", function() {
var containers = document.querySelectorAll('.button-container');
function applyStyles(container) {
if (container.querySelector('button')) {
container.style.position = 'fixed';
container.style.bottom = '0';
container.style.left = '0';
container.style.width = '100%';
container.style.zIndex = '1000';
container.style.boxShadow = '0 -2px 5px rgba(0,0,0,0.2)';
}
if (container.querySelector('.flex justify-center')) {
container.style.position = 'fixed';
container.style.bottom = '0';
container.style.left = '0';
container.style.width = '100%';
container.style.zIndex = '1000';
container.style.boxShadow = '0 -2px 5px rgba(0,0,0,0.2)';
}
}
Array.prototype.forEach.call(containers, function(container) {
applyStyles(container);
});
});
Make sure to add 72px to the bottom of the widget so your scrollable button does not cover text at the bottom of the screen. To make sure that the button text is easy to read on a large screen, you can also use text-align: center and justify-content: center.
copy and paste the css below and add it to your custom css section:
#results {
margin-bottom: 72px;
}
.guest-form {
margin-bottom: 72px;
}
button {
text-align; center !important;
justify-content: center !important;
}
Show a scrollable gallery for your events, tickets and preferences
Follow this guide here to advertise all your preferences, events and tickets within a scrollable gallery.
Add black text on a highlighted timeslot (for supporting dark mode)
.timeslots input[type=radio]:checked + label.slot {
color: black !important;
}
Sort all the options / preferences by price
<script>
document.addEventListener('DOMContentLoaded', function () {
// Start sort preference
let preferenceSelectElement = document.getElementById('preference_id');
if (preferenceSelectElement) {
if (preferenceSelectElement.type !== 'hidden') {
// Convert options to an array
let preferenceSelectOptions = Array.from(
preferenceSelectElement?.options || []
);
// Sort the array based on the 'data-amount' attribute
preferenceSelectOptions.sort(function (a, b) {
let aAmount = a.getAttribute('data-amount') || 0;
let bAmount = b.getAttribute('data-amount') || 0;
return Number(aAmount) - Number(bAmount);
});
// Remove all options
while (preferenceSelectElement.firstChild) {
preferenceSelectElement.firstChild.remove();
}
// Append the sorted options
preferenceSelectOptions.forEach(function (option) {
preferenceSelectElement.appendChild(option);
});
let hasSelectedOption = false;
let params = new URLSearchParams(window.location.search);
let preferenceId = params.get('preference_id');
if (!!preferenceId) {
preferenceSelectOptions.forEach(function (option) {
if (option?.value === preferenceId) {
option.selected = true;
hasSelectedOption = true;
}
});
}
if (!hasSelectedOption) {
if (preferenceSelectOptions[0]) {
preferenceSelectOptions[0].selected = true;
}
}
}
}
// End sort preference
});
</script>
Change the text when selecting events and time slots
<script>
// Convert the HTMLCollection to an array
const elements = Array.from(document.body.getElementsByTagName('*'));
// If you aren't advertising events, change the subtitle text
elements.forEach(function(element) {
// Check if the element contains the text "Other Events"
if (element.innerHTML.includes("Other Events")) {
// Replace "Other Events" with "Packages, Events & Set Menus"
element.innerHTML = element.innerHTML.replace("Other Events", "Packages, Events & Set Menus");
}
});
// If 'Select a time' is too basic for you, you can get more creative
elements.forEach(function(element) {
// Check if the element contains the text "Select a time"
if (element.innerHTML.includes("Select a time")) {
// Replace "Select a time" with "What time would you like your experience?"
element.innerHTML = element.innerHTML.replace("Select a time", "What time would you like your experience?");
}
});
</script>
make the logo smaller
.logo {
width: 30%;
height: 100%;
object-fit: contain;
}
Create a script which changes the style for a selected day
<script>
// First, select the element using its class, ID, or any other selector that uniquely identifies it.
var dateSelector = document.querySelector('.search-date-selector');
// Then, use getAttribute to get the value of the data-date attribute.
var selectedDate = dateSelector.getAttribute('data-date');
// Now, create the if statement to check if the selected date is February 14, 2024.
if (selectedDate === '2024-02-14') {
// Define variables for font URL and styles
const fontUrl = 'https://fonts.googleapis.com/css2?family=Rowdies:wght@300&display=swap';
// Background styles for elements
const backgroundColorWidget = 'transparent !important';
const backgroundColorLabel = 'grey !important';
const backgroundColorCheckedRadio = 'white !important';
const backgroundColorAppBody = '#FFEEF0 !important';
// Common styles for the widget
const fontFamilyRowdies = "Rowdies, Arial, Charcoal, sans-serif";
const borderRadius2px = '2px';
// Create the Image
const imgElement = document.createElement('img');
imgElement.src = '';
imgElement.width = 120;
imgElement.height = 120;
window.onload = function () {
// Create and append a link element for the font
const fontLink = document.createElement('link');
fontLink.href = fontUrl;
fontLink.rel = 'stylesheet';
document.head.appendChild(fontLink);
// Define styles using variables
const styles = `
.widget,
#widget-tabs,
#menu-button,
select,
input[type='text'],
input[type='tel'],
input[type='date'],
textarea,
.bg-widget {
background-color: ${backgroundColorAppBody};
},
label * {
background-color: ${backgroundColorLabel};
},
.timeslots input[type='radio']:checked + label.slot {
background-color: ${backgroundColorCheckedRadio};
},
.dropdown {border-radius: ${borderRadius2px};},
.reveal-multiselect button {border-radius: ${borderRadius2px};},
.reveal-multiselect ul,
.tabs-title > a:focus,
.tabs-title > a:hover {
background-color: ${backgroundColorWidget};
},
input[type='radio']:checked, label.slot, timeslots {
background-color: ${backgroundColorWidget};
},
#app-body {
background-color: ${backgroundColorAppBody};
}
`;
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
};
// Set common styles for all elements
const allElements = document.querySelectorAll('*');
allElements.forEach(function (element) {
element.style.borderRadius = borderRadius2px;
element.style.backgroundColor = backgroundColorWidget;
element.style.fontFamily = fontFamilyRowdies;
});
// Set background color for specific elements
document.querySelectorAll("body.pg-bg-widget, #app-body").forEach(function (element) {
element.style.backgroundColor = backgroundColorWidget;
});
}
</script>
Change the wording on the payment confirmation page
Change the wording on the confirmation page if the party is large
document.addEventListener('DOMContentLoaded', function() {
// Function to check if a is greater than b without using the > and && operators
function isGreaterThan(a, b) {
// If the absolute difference plus b equals a, and a is not equal to b, then a is greater than b
return Math.abs(a - b) + b === a;
}
// Function to update reservation status based on the number of guests
function updateReservationStatus(numberOfGuests) {
// Use the isGreaterThan function for comparison
if (isGreaterThan(numberOfGuests, 4)) {
// Find the element that contains the reservation status
const reservationStatusElement = document.querySelector('.text-xl.font-bold');
if (reservationStatusElement) {
// Change the text to 'Reservation pending'
reservationStatusElement.textContent = 'Reservation pending';
}
// Find and remove the image indicating the reservation status
const reservationImage = document.querySelector('img[alt="Reservation confirmed"]');
if (reservationImage) {
reservationImage.parentNode.removeChild(reservationImage);
}
}
}
// Find the element that contains the number of guests
const guestElement = document.querySelector('.pt-1');
if (guestElement) {
// Extract the number of guests from the element's text
const guestText = guestElement.textContent || ''; // e.g., "5 guests"
const numberOfGuests = parseInt(guestText);
// Call the function to update the reservation status based on the number of guests
updateReservationStatus(numberOfGuests);
}
});
Removing Venue name at the top of the Widget can be done using simple css.
Add the following in the CSS section of your widget under Look & feel tab.
.flex .heading-1 {
display: none;
}
Always show the join waitlist button
Even if you are fully booked, or your waitlist is turned off, or there are no slots available, show the join waitlist button anyway
<script>
document.addEventListener("DOMContentLoaded", function() {
// Find all existing 'Join waitlist' links
var waitlistLinks = document.querySelectorAll('.px-4.my-6 a[href*="date="]');
var existingDates = new Set();
// Populate a set with dates from existing waitlist links
waitlistLinks.forEach(function(link) {
var url = new URL(link.href);
var date = url.searchParams.get("date");
existingDates.add(date);
});
// Assume we extract a date from a URL parameter or a form field on the page
var urlParams = new URLSearchParams(window.location.search);
var dateToAdd = urlParams.get('date'); // Assuming 'date' is a URL query parameter
if (!dateToAdd) {
// Fallback or default date if not specified, or you might want to handle this case differently
dateToAdd = new Date().toISOString().slice(0, 10); // Default to today if no date parameter found
}
// Create URL for new 'Join waitlist' link with the selected date
var newWaitlistUrl = `https://eatapp.co/reserve/the-restaurant-at-buckingham-s-choice-83b28a/wait_list?covers=2&date=${dateToAdd}&locale=en`;
// Check if a waitlist link with the selected date already exists
if (!existingDates.has(dateToAdd)) {
var newHtml = `
<div class="px-4 my-6">
<p class="font-semibold text-sm mb-5">Can’t find the time you want?</p>
<a class="font-semibold text-sm text-primary no-underline cursor-pointer mr-5" href="${newWaitlistUrl}">Join waitlist</a>
</div>
`;
// Select the terms and conditions block
var termsAndConditions = document.querySelector('.my-6.px-4.text-custom-primary');
if (termsAndConditions) {
// Insert the new HTML block above the terms and conditions
termsAndConditions.insertAdjacentHTML('beforebegin', newHtml);
}
} else {
console.log("A waitlist link for the date " + dateToAdd + " with the same URL already exists.");
}
});
</script>
Make the cover selector row sticky to the top.
Add the following in the CSS section of your widget under Look & feel tab.
#search {
position:sticky !important;
}
.overflow-auto {
overflow:visible;
}
Hide dates that are not available for a selected preference or shift
Add the following in the embedded tracking section of your widget under Look & feel tab.
<script>
document.addEventListener('DOMContentLoaded', (event) => {
// Get the select element by its id
var selectElement = document.getElementById('date');
// Loop through each option in the select element
for (var i = 0; i < selectElement.options.length; i++) {
var option = selectElement.options[i];
// Check if the option is disabled
if (option.disabled) {
// Hide the disabled option
option.style.display = 'none';
}
}
});
</script>
You may also want to remove the shift titles when hiding timeslots that are not available. This is because it looks strange to have shift titles that are visible when there are no timeslots.
<script>
document.addEventListener("DOMContentLoaded", function() {
// Get all time slot sections
const timeSlotSections = document.querySelectorAll(".timeslots");
timeSlotSections.forEach(section => {
// Get all slots in this section
const slots = section.querySelectorAll("div.flex");
// Check if all slots are hidden
const allSlotsHidden = Array.from(slots).every(slot => slot.style.display === "none");
if (allSlotsHidden) {
// Get the shift title (the previous sibling of the section)
const shiftTitle = section.previousElementSibling;
if (shiftTitle && shiftTitle.classList.contains("font-semibold")) {
// Remove the shift title
shiftTitle.style.display = "none";
}
}
});
});
</script>
Hide timeslots that are not available for a selected preference or shift
Add the following in the embedded tracking section of your widget under Look & feel tab.
<script>
document.addEventListener('DOMContentLoaded', function() {
// Select all input elements with the name 'start_time'
const timeSlotInputs = document.querySelectorAll('input[name="start_time"]');
// Loop through each time slot input
timeSlotInputs.forEach(function(input) {
// Check if the input is disabled
if (input.disabled) {
// Hide the parent div that contains the input and label
input.parentElement.style.display = 'none';
}
});
});
</script>