diff --git a/blocks/multiple-variant-table/multiple-variant-table.css b/blocks/multiple-variant-table/multiple-variant-table.css
new file mode 100644
index 0000000..84c2eb8
--- /dev/null
+++ b/blocks/multiple-variant-table/multiple-variant-table.css
@@ -0,0 +1,25 @@
+.multiple-variant-table > div {
+ display: flex;
+ flex-direction: row;
+}
+
+.multiple-variant-table img {
+ width: 100%;
+}
+
+@media (min-width: 900px) {
+ .multiple-variant-table > div {
+ display: flex;
+ align-items: center;
+ flex-direction: unset;
+ }
+
+ .multiple-variant-table > div > div {
+ flex: 1;
+ margin-left: 32px;
+ }
+
+ .multiple-variant-table > div > div:first-of-type {
+ margin-left: unset;
+ }
+}
diff --git a/blocks/multiple-variant-table/multiple-variant-table.js b/blocks/multiple-variant-table/multiple-variant-table.js
new file mode 100644
index 0000000..102b8ea
--- /dev/null
+++ b/blocks/multiple-variant-table/multiple-variant-table.js
@@ -0,0 +1,65 @@
+import {
+ getKey,
+ startsWithTemplateLiteral,
+ endsWithTemplateLiteral,
+ placeholderMap,
+} from '../../scripts/menu-content-parser.js';
+
+/**
+ * Decorate Table HTML with specific class selectors so that,
+ * it can be handled efficiently by the Menu Builder component
+ * @param block
+ */
+function instrumentMultipleVariantTable(block) {
+ if (!block.children) {
+ return;
+ }
+
+ const blockImmediateChildren = [...block.children];
+
+ blockImmediateChildren.forEach((row) => {
+ const placeholdersDivs = [...row.querySelectorAll('div')].filter(
+ (div) => div.innerText.includes(startsWithTemplateLiteral)
+ && div.innerText.includes(endsWithTemplateLiteral),
+ );
+
+ if (placeholdersDivs.length > 0) {
+ if (row.children.length === 3) {
+ // handling three column placement
+ for (let i = 0; i < placeholdersDivs.length; i += 1) {
+ placeholdersDivs[i].style.display = 'none';
+ if (row.children[1] === placeholdersDivs[i]) {
+ placeholderMap.set(
+ `${getKey(placeholdersDivs[i].textContent)}:variant1-price`,
+ placeholdersDivs[i],
+ );
+ } else if (row.children[2] === placeholdersDivs[i]) {
+ placeholderMap.set(
+ `${getKey(placeholdersDivs[i].textContent)}:variant2-price`,
+ placeholdersDivs[i],
+ );
+ }
+ }
+ }
+
+ if (row.children.length === 1) {
+ // handling single column placement
+
+ for (let i = 0; i < placeholdersDivs.length; i += 1) {
+ placeholdersDivs[i].style.display = 'none';
+ placeholderMap.set(
+ getKey(placeholdersDivs[i].textContent),
+ placeholdersDivs[i],
+ );
+ }
+ }
+ }
+ });
+}
+
+export default function decorate(block) {
+ const cols = [...block.firstElementChild.children];
+ block.classList.add(`columns-${cols.length}-cols`);
+
+ instrumentMultipleVariantTable(block);
+}
diff --git a/blocks/spinner/spinner.css b/blocks/spinner/spinner.css
new file mode 100644
index 0000000..2c5c3c1
--- /dev/null
+++ b/blocks/spinner/spinner.css
@@ -0,0 +1,75 @@
+/* Calibrating text on page load to qualify PSI score */
+
+/* container */
+.spinner {
+ position: absolute;
+ top: 50%;
+ right: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: row;
+ transform: translate(50%, -50%);
+ font-size: 4rem;
+}
+
+.spinner-letter {
+ /* font-size: 88px; */
+ font-weight: normal;
+ letter-spacing: 4px;
+ text-transform: uppercase;
+ animation-name: bounce;
+ animation-duration: 2s;
+ animation-iteration-count: infinite;
+}
+
+.spinner-letter:nth-child(2) {
+ animation-delay: .1s;
+}
+
+.spinner-letter:nth-child(3) {
+ animation-delay: .2s;
+}
+
+.spinner-letter:nth-child(4) {
+ animation-delay: .3s;
+}
+
+.spinner-letter:nth-child(5) {
+ animation-delay: .4s;
+}
+
+.spinner-letter:nth-child(6) {
+ animation-delay: .5s;
+}
+
+.spinner-letter:nth-child(7) {
+ animation-delay: .6s;
+}
+
+.spinner-letter:nth-child(8) {
+ animation-delay: .8s;
+}
+
+.spinner-letter:nth-child(9) {
+ animation-delay: 1s;
+}
+
+.spinner-letter:nth-child(10) {
+ animation-delay: 1.2s;
+}
+
+@keyframes bounce {
+ 0% {
+ transform: translateY(0)
+ }
+
+ 40% {
+ transform: translateY(-10px);
+ }
+
+ 80%,
+ 100% {
+ transform: translateY(0);
+ }
+}
\ No newline at end of file
diff --git a/blocks/spinner/spinner.js b/blocks/spinner/spinner.js
new file mode 100644
index 0000000..b546bd5
--- /dev/null
+++ b/blocks/spinner/spinner.js
@@ -0,0 +1,30 @@
+/**
+ * Relocate Spinner HTML element at the body so that main
+ * content can be hidden (opacity:0) until calibration is in process
+ * @param block
+ */
+function splitIntoChildren(block) {
+ if (!block.children) {
+ return;
+ }
+
+ const spinnerTextArray = [...block.innerText.trim().concat('...')];
+
+ const parentDivElement = document.createElement('div');
+ parentDivElement.classList += block.classList;
+ document.querySelector('body').append(parentDivElement);
+
+ block.closest('.section').remove();
+
+ spinnerTextArray.forEach((letter) => {
+ const divElement = document.createElement('div');
+ divElement.classList.add('spinner-letter');
+ divElement.textContent = letter;
+ parentDivElement.append(divElement);
+ });
+}
+
+export default function decorate(block) {
+ block.classList.add('spinner');
+ splitIntoChildren(block);
+}
diff --git a/blocks/table/table.css b/blocks/table/table.css
new file mode 100644
index 0000000..24f0f41
--- /dev/null
+++ b/blocks/table/table.css
@@ -0,0 +1,25 @@
+.table > div {
+ display: flex;
+ flex-direction: row;
+}
+
+.table img {
+ width: 100%;
+}
+
+@media (min-width: 900px) {
+ .table > div {
+ display: flex;
+ align-items: center;
+ flex-direction: unset;
+ }
+
+ .table > div > div {
+ flex: 1;
+ margin-left: 32px;
+ }
+
+ .table > div > div:first-of-type {
+ margin-left: unset;
+ }
+}
diff --git a/blocks/table/table.js b/blocks/table/table.js
new file mode 100644
index 0000000..d85ced7
--- /dev/null
+++ b/blocks/table/table.js
@@ -0,0 +1,40 @@
+import {
+ getKey,
+ startsWithTemplateLiteral,
+ endsWithTemplateLiteral,
+ placeholderMap,
+} from '../../scripts/menu-content-parser.js';
+
+/**
+ * Decorate Table HTML with specific class selectors so that,
+ * it can be handled efficiently by the Menu Builder component
+ * @param block
+ */
+function instrumentSingleVariantTable(block) {
+ if (!block.children) {
+ return;
+ }
+
+ const blockImmediateChildren = [...block.children];
+
+ blockImmediateChildren.forEach((row) => {
+ const placeholdersDivs = [...row.querySelectorAll('div')].filter(
+ (div) => div.innerText.includes(startsWithTemplateLiteral)
+ && div.innerText.includes(endsWithTemplateLiteral),
+ );
+ for (let i = 0; i < placeholdersDivs.length; i += 1) {
+ placeholdersDivs[i].style.display = 'none';
+ placeholderMap.set(
+ getKey(placeholdersDivs[i].textContent),
+ placeholdersDivs[i],
+ );
+ }
+ });
+}
+
+export default function decorate(block) {
+ const cols = [...block.firstElementChild.children];
+ block.classList.add(`columns-${cols.length}-cols`);
+
+ instrumentSingleVariantTable(block);
+}
diff --git a/head.html b/head.html
index e34c617..fd92c8f 100644
--- a/head.html
+++ b/head.html
@@ -1,4 +1,6 @@
+
+
diff --git a/scripts/delayed.js b/scripts/delayed.js
index 920b4ad..947b37f 100644
--- a/scripts/delayed.js
+++ b/scripts/delayed.js
@@ -1,7 +1,57 @@
// eslint-disable-next-line import/no-cycle
-import { sampleRUM } from './lib-franklin.js';
+import { sampleRUM, isMenuBoardTemplate } from './lib-franklin.js';
+
+const MENU_CAFE_FONT_SIZE_CACHE_KEY = 'menu-cafe-fontSize';
// Core Web Vitals RUM collection
sampleRUM('cwv');
-// add more delayed functionality here
+// FixMe: workaround to set the fluid/responsiveness typography
+
+async function isScrollbarHidden(element) {
+ return element.offsetHeight < element.scrollHeight - 2; // threshhold = 2
+}
+
+/* function delayTimer(ms) {
+ return new Promise((res) => setTimeout(res, ms));
+} */
+
+/**
+ * Temporary Workaround: Increase the fontSize of the page until Scroll Bar appears
+ * @returns {Promise}
+ */
+async function checkAndSetTypography() {
+ const htmlElement = document.querySelector('html');
+ const cachedFontSize = localStorage.getItem(MENU_CAFE_FONT_SIZE_CACHE_KEY);
+ if (cachedFontSize) {
+ htmlElement.style.fontSize = `${cachedFontSize}%`;
+ } else {
+ let fontSize = 73; // default fontSize for 1200x800 resolution
+ /* eslint-disable no-await-in-loop */
+ while (await isScrollbarHidden(htmlElement)) {
+ if (fontSize > 200) {
+ break;
+ }
+ fontSize += 1;
+ localStorage.setItem(MENU_CAFE_FONT_SIZE_CACHE_KEY, fontSize.toString());
+ htmlElement.style.fontSize = `${fontSize}%`;
+ window.dispatchEvent(new Event('resize'));
+ // await delayTimer(100); // add delay if needed on specific native platforms
+ }
+ }
+ htmlElement.querySelector('.beverages-menu').style.backgroundColor = '#601014'; // background-color: #601014;
+ htmlElement.querySelector('.food-menu').style.backgroundColor = '#000'; // background-color: #000;
+
+ htmlElement.querySelector('.spinner').style.display = 'none';
+
+ // remove the vertical scroll once menu is calibrated
+ htmlElement.style.overflow = 'hidden';
+ // unhide the main element once menu is ready
+ htmlElement.querySelector('main').style.opacity = '1';
+}
+
+setTimeout(() => {
+ if(isMenuBoardTemplate(document)) {
+ checkAndSetTypography();
+ }
+}, 1);
diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js
index a059188..aaaae0b 100644
--- a/scripts/lib-franklin.js
+++ b/scripts/lib-franklin.js
@@ -129,6 +129,7 @@ export function toCamelCase(name) {
}
const ICONS_CACHE = {};
+const MENUBOARD_TEMPLATE = 'menuboard';
/**
* Replace icons with inline SVG and prefix with codeBasePath.
* @param {Element} [element] Element containing icons
@@ -439,6 +440,26 @@ export async function loadBlocks(main) {
}
}
+export function getTemplateName(doc) {
+ let template = '';
+ const head = doc.querySelector('head');
+ for (const meta of [...head.querySelectorAll(':scope > meta')]) {
+ if(meta.getAttribute('name') === 'template') {
+ template = meta.getAttribute('content');
+ }
+ }
+ return template;
+}
+
+/**
+ * Checks if the doc is menuboard or not
+ * @param doc
+ * @returns
+ */
+export function isMenuBoardTemplate(doc) {
+ return getTemplateName(doc) === MENUBOARD_TEMPLATE;
+}
+
/**
* Returns a picture element with webp and fallbacks
* @param {string} src The image URL
diff --git a/scripts/menu-builder.js b/scripts/menu-builder.js
new file mode 100644
index 0000000..e3f419a
--- /dev/null
+++ b/scripts/menu-builder.js
@@ -0,0 +1,119 @@
+/**
+ * this method generates the HTML structure of the Menu layout
+ * @param rootDocument
+ * @returns {Promise}
+ */
+export const layout = async function createMenusHtmlLayout(rootDocument) {
+ const beveragesFirstSectionSelector = '.section.beverages-heading';
+ const beveragesFirstSectionElement = rootDocument.querySelector(
+ beveragesFirstSectionSelector,
+ );
+
+ const beveragesSecondSectionSelector = '.section.beverages-content';
+ const beveragesSecondSectionElement = rootDocument.querySelector(
+ beveragesSecondSectionSelector,
+ );
+
+ if (beveragesFirstSectionElement && beveragesSecondSectionElement) {
+ // create a new root div element for Beverages Menu
+ const beveragesMenu = document.createElement('div');
+ beveragesMenu.className = 'beverages-menu';
+ rootDocument.querySelector('main').append(beveragesMenu);
+
+ beveragesMenu.appendChild(beveragesFirstSectionElement);
+ beveragesMenu.appendChild(beveragesSecondSectionElement);
+ }
+
+ const foodFirstSectionSelector = '.section.sweets';
+ const foodFirstSectionElement = rootDocument.querySelector(
+ foodFirstSectionSelector,
+ );
+
+ const foodSecondSectionSelector = '.section.brioche-savory';
+ const foodSecondSectionElement = rootDocument.querySelector(
+ foodSecondSectionSelector,
+ );
+
+ const foodThirdSectionSelector = '.section.sides';
+ const foodThirdSectionElement = rootDocument.querySelector(
+ foodThirdSectionSelector,
+ );
+
+ if (
+ foodFirstSectionElement
+ && foodSecondSectionElement
+ && foodThirdSectionElement
+ ) {
+ // create a new root div element for Beverages Menu
+ const foodMenu = document.createElement('div');
+ foodMenu.className = 'food-menu';
+ rootDocument.querySelector('main').append(foodMenu);
+
+ foodMenu.appendChild(foodFirstSectionElement);
+ foodMenu.appendChild(foodSecondSectionElement);
+ foodMenu.appendChild(foodThirdSectionElement);
+ }
+};
+
+/**
+ * This method creates nested html elements to place grid
+ * & flex items in their respective containers
+ * @param rootDocument
+ * @returns {Promise}
+ */
+export const nestedTable = async function createAlcoholBevarageNestedTable(
+ rootDocument,
+) {
+ const rootSelector = '.section.beverages-content';
+ const beverageContentDocumentRoot = rootDocument.querySelector(rootSelector);
+
+ if (!beverageContentDocumentRoot) {
+ return;
+ }
+
+ const coffeeTable = beverageContentDocumentRoot.querySelector(
+ `${rootSelector} > div:nth-last-child(-n + 5)`,
+ );
+ const alcoholicBeverageTableHeading = beverageContentDocumentRoot.querySelector(
+ `${rootSelector} > div:nth-last-child(-n + 4)`,
+ );
+ const wineTable = beverageContentDocumentRoot.querySelector(
+ `${rootSelector} > div:nth-last-child(-n + 3)`,
+ );
+ const champagneTable = beverageContentDocumentRoot.querySelector(
+ `${rootSelector} > div:nth-last-child(-n + 2)`,
+ );
+ const beerTable = beverageContentDocumentRoot.querySelector(
+ `${rootSelector} > div:nth-last-child(-n + 1)`,
+ );
+
+ // create a new parents table div for top section
+ const beverageContentCoffeeTableDiv = document.createElement('div');
+ beverageContentCoffeeTableDiv.className = 'beverages-content-coffee';
+ rootDocument
+ .querySelector(`${rootSelector}`)
+ .append(beverageContentCoffeeTableDiv);
+ beverageContentCoffeeTableDiv.append(coffeeTable);
+
+ // create a new parents table div for bottom section
+ const beverageContentAlcoholTableDiv = document.createElement('div');
+ beverageContentAlcoholTableDiv.className = 'beverages-content-alcohol';
+ rootDocument
+ .querySelector(`${rootSelector}`)
+ .append(beverageContentAlcoholTableDiv);
+
+ // create a new nested table div element
+ const alcoholBeverageNestedTableDiv = document.createElement('div');
+ alcoholBeverageNestedTableDiv.className = 'alcohol-beverages-table';
+ beverageContentAlcoholTableDiv.append(alcoholBeverageNestedTableDiv);
+
+ // create a new nested table div element
+ const wineChampagneNestedTableDiv = document.createElement('div');
+ wineChampagneNestedTableDiv.className = 'wine-champagne-left-table';
+ wineChampagneNestedTableDiv.appendChild(wineTable);
+ wineChampagneNestedTableDiv.appendChild(champagneTable);
+
+ alcoholBeverageNestedTableDiv.appendChild(alcoholicBeverageTableHeading);
+ alcoholBeverageNestedTableDiv.appendChild(wineChampagneNestedTableDiv);
+ alcoholBeverageNestedTableDiv.appendChild(beerTable);
+};
diff --git a/scripts/menu-content-parser.js b/scripts/menu-content-parser.js
new file mode 100644
index 0000000..c19efaf
--- /dev/null
+++ b/scripts/menu-content-parser.js
@@ -0,0 +1,118 @@
+const POS_ENDPOINT = '/content/screens/menus/pos-data.json';
+
+export const startsWithTemplateLiteral = '{{';
+export const endsWithTemplateLiteral = '}}';
+
+export const placeholderMap = new Map();
+
+export function getKey(keyLabel) {
+ if (
+ keyLabel.startsWith(startsWithTemplateLiteral)
+ && keyLabel.endsWith(endsWithTemplateLiteral)
+ ) {
+ return keyLabel;
+ }
+ if (
+ keyLabel.includes(startsWithTemplateLiteral)
+ && keyLabel.includes(endsWithTemplateLiteral)
+ ) {
+ return keyLabel.substring(
+ keyLabel.indexOf(startsWithTemplateLiteral),
+ keyLabel.indexOf(endsWithTemplateLiteral) + 2,
+ );
+ }
+ return keyLabel;
+}
+
+/**
+ * This method will replace the placeholders (i.e.variables) with respective prices
+ * and unhide the Menu item
+ * @param elements
+ * @param beveragesCoffeeEntries
+ */
+function updateMenuItem(SKU, targetElement, targetPrice, isOutOfStock) {
+ if (
+ isOutOfStock.trim().toLowerCase() === 'true'
+ || isOutOfStock.trim().toLowerCase() === 'yes'
+ ) {
+ targetElement.parentElement.style.display = 'none'; // already set
+ return;
+ }
+
+ targetElement.textContent = targetElement.textContent.replace(
+ startsWithTemplateLiteral + SKU + endsWithTemplateLiteral,
+ targetPrice,
+ );
+ targetElement.style.display = ''; // unhide the element after setting the price
+}
+
+function processBeveragesFoodMenuSections(menuJsonPayload) {
+ // Access the "Beverages" category's data
+ const beveragesData = menuJsonPayload.Beverages.data;
+
+ // Loop through the Beverages Menu section data to access individual product details
+ beveragesData.forEach((menuItem) => {
+ const variant1Price = menuItem.Variant1_price;
+ const variant2Price = menuItem.Variant2_price;
+ const sku = menuItem.SKU;
+ const { isOutOfStock } = menuItem;
+ if (placeholderMap.has(`{{${sku}}}`)) {
+ updateMenuItem(
+ sku,
+ placeholderMap.get(`{{${sku}}}`),
+ variant1Price,
+ isOutOfStock,
+ );
+ }
+ if (placeholderMap.has(`{{${sku}}}:variant1-price`)) {
+ updateMenuItem(
+ sku,
+ placeholderMap.get(`{{${sku}}}:variant1-price`),
+ variant1Price,
+ isOutOfStock,
+ );
+ }
+ if (placeholderMap.has(`{{${sku}}}:variant2-price`)) {
+ updateMenuItem(
+ sku,
+ placeholderMap.get(`{{${sku}}}:variant2-price`),
+ variant2Price,
+ isOutOfStock,
+ );
+ }
+ });
+
+ // Access the "Food" category's data
+ const foodData = menuJsonPayload.Food.data;
+
+ // Loop through the Food Menu Section data to access individual product details
+ foodData.forEach((product) => {
+ const variant1Price = product.Variant1_price;
+ const sku = product.SKU;
+ const { isOutOfStock } = product;
+
+ const targetElement = placeholderMap.get(`{{${sku}}}`);
+
+ if (targetElement) {
+ updateMenuItem(sku, targetElement, variant1Price, isOutOfStock);
+ }
+ });
+}
+
+/**
+ * parse value JSON and mutate Menu HTML content
+ * @param elements
+ * @returns {Promise}
+ */
+export async function populateValuesContent() {
+ fetch(POS_ENDPOINT)
+ .then((response) => response.json())
+ .then((menu) => {
+ processBeveragesFoodMenuSections(menu);
+ })
+ .catch((error) => {
+ // Handle any errors that occurred during the HTTP request
+ // eslint-disable-next-line no-console
+ console.warn(` Error in processing the menu item prices : ${error}`);
+ });
+}
diff --git a/scripts/scripts.js b/scripts/scripts.js
index 3a490a8..6163130 100644
--- a/scripts/scripts.js
+++ b/scripts/scripts.js
@@ -8,10 +8,16 @@ import {
decorateTemplateAndTheme,
waitForLCP,
loadBlocks,
+ isMenuBoardTemplate,
loadCSS,
} from './lib-franklin.js';
+import { layout, nestedTable } from './menu-builder.js';
+
+import { populateValuesContent } from './menu-content-parser.js';
+
const LCP_BLOCKS = []; // add your LCP blocks to the list
+window.hlx.RUM_GENERATION = 'project-1'; // add your RUM generation information here
/**
* Builds hero block and prepends to main in a new section.
@@ -87,11 +93,14 @@ export function addFavIcon(href) {
}
}
+
/**
* Loads everything that doesn't need to be delayed.
* @param {Element} doc The container element
*/
async function loadLazy(doc) {
+ const isMenuBoardCase = isMenuBoardTemplate(doc);
+
const main = doc.querySelector('main');
await loadBlocks(main);
@@ -99,8 +108,20 @@ async function loadLazy(doc) {
const element = hash ? doc.getElementById(hash.substring(1)) : false;
if (hash && element) element.scrollIntoView();
- loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`);
+ if(isMenuBoardCase) {
+ await nestedTable(doc);
+ await layout(doc);
+ await populateValuesContent();
+ }
+
+ if(isMenuBoardCase) {
+ loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles-menuboard.css`);
+ loadCSS(`${window.hlx.codeBasePath}/styles/styles-menuboard.css`);
+ } else {
+ loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`);
+ }
addFavIcon(`${window.hlx.codeBasePath}/styles/favicon.svg`);
+
sampleRUM('lazy');
sampleRUM.observe(main.querySelectorAll('div[data-block-name]'));
sampleRUM.observe(main.querySelectorAll('picture > img'));
diff --git a/styles/fonts/JosefinSans/JosefinSans-Bold.woff b/styles/fonts/JosefinSans/JosefinSans-Bold.woff
new file mode 100644
index 0000000..2d58d14
Binary files /dev/null and b/styles/fonts/JosefinSans/JosefinSans-Bold.woff differ
diff --git a/styles/fonts/JosefinSans/JosefinSans-Bold.woff2 b/styles/fonts/JosefinSans/JosefinSans-Bold.woff2
new file mode 100644
index 0000000..551e334
Binary files /dev/null and b/styles/fonts/JosefinSans/JosefinSans-Bold.woff2 differ
diff --git a/styles/fonts/JosefinSans/JosefinSans-Light.woff b/styles/fonts/JosefinSans/JosefinSans-Light.woff
new file mode 100644
index 0000000..ec74887
Binary files /dev/null and b/styles/fonts/JosefinSans/JosefinSans-Light.woff differ
diff --git a/styles/fonts/JosefinSans/JosefinSans-Regular.woff b/styles/fonts/JosefinSans/JosefinSans-Regular.woff
new file mode 100644
index 0000000..877b9fa
Binary files /dev/null and b/styles/fonts/JosefinSans/JosefinSans-Regular.woff differ
diff --git a/styles/lazy-styles-menuboard.css b/styles/lazy-styles-menuboard.css
new file mode 100644
index 0000000..9c344fb
--- /dev/null
+++ b/styles/lazy-styles-menuboard.css
@@ -0,0 +1,269 @@
+/* stylelint-disable no-descending-specificity */
+
+/* below the fold CSS goes here */
+
+@import url("https://fonts.googleapis.com/css?family=Montserrat:200,300,400,500,600,700,800|Open+Sans:400,600,700|Oswald:600&display=swap");
+
+/* Josefin Sans Fonts */
+@font-face {
+ font-family: "Josefin Sans";
+ src: url("../styles/fonts/JosefinSans/JosefinSans-Bold.woff2") format("woff2"), url("../styles/fonts/JosefinSans/JosefinSans-Bold.woff") format("woff");
+ font-weight:700;
+ font-style:normal;
+ font-display: swap;
+}
+
+* {
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+}
+
+/* Beverages Menu */
+
+.beverages-menu {
+ /* background-color: #601014; */
+ display: grid;
+ grid-gap: 1rem;
+ grid-template-columns: 7rem 1fr;
+ flex: 1;
+}
+
+.section.beverages-heading {
+ writing-mode: vertical-rl;
+ text-orientation: revert;
+ font-size: 5rem;
+ font-weight: lighter;
+ padding: 0;
+}
+
+.section.beverages-heading p {
+ margin: 0;
+ transform: scale(-1, -1);
+}
+
+.section.beverages-content {
+ display: grid;
+ grid-template-columns: 100%;
+ grid-template-rows: 1fr auto;
+ padding: 0;
+}
+
+.section.beverages-content .coffee-table > div > div:first-of-type {
+ width: 60%;
+}
+
+.section.beverages-content .coffee-table > div > div:nth-of-type(2) {
+ width: 20%;
+}
+
+.section.beverages-content .coffee-table > div > div:nth-of-type(3) {
+ width: 20%;
+}
+
+.section.beverages-content > .beverages-content-coffee {
+ font-size: larger;
+ margin: 0;
+ align-self: start;
+}
+
+.section.beverages-content > .beverages-content-alcohol {
+ margin: 0;
+ align-self: start;
+}
+
+.section.beverages-content .coffee-table {
+ /* font-size: 110%; */
+ margin-top: 2%;
+}
+
+.section.beverages-content h3 {
+ margin: 0;
+}
+
+.section.beverages-content .coffee-table > div h3 {
+ margin: 0;
+}
+
+.section.beverages-content .alcoholic-table {
+ border: solid;
+ margin-top: 2rem;
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: flex-start;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table {
+ border: solid;
+ margin: 1%;
+ padding: 2%;
+ display: flex;
+ flex-flow: wrap;
+ align-self: end;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > div:first-of-type {
+ width: 100%;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > div:nth-of-type(2) {
+ width: 50%;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > div:nth-of-type(3) {
+ width: 50%;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > .wine-champagne-left-table .wine-table > div:nth-of-type(2) {
+ margin-left: 1em;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > .wine-champagne-left-table .wine-table > div:nth-of-type(3) {
+ margin-left: 1em;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > .wine-champagne-left-table .wine-table > div:nth-of-type(2) > div:nth-of-type(2) {
+ text-align: center;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > .wine-champagne-left-table .wine-table > div:nth-of-type(3) > div:nth-of-type(2) {
+ text-align: center;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > .wine-champagne-left-table .champagne-table > div:nth-of-type(2) {
+ margin-left: 1em;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table > .wine-champagne-left-table .champagne-table > div:nth-of-type(2) > div:nth-of-type(2) {
+ text-align: center;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table .beer-table > div:nth-of-type(2) {
+ margin-left: 1em;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table .beer-table > div:nth-of-type(3) {
+ margin-left: 1em;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table .beer-table > div:nth-of-type(4) {
+ margin-left: 1em;
+}
+
+.section.beverages-content .beverages-content-alcohol > .alcohol-beverages-table .beer-table > div:nth-of-type(5) {
+ margin-left: 1em;
+}
+
+
+/* Food Menu */
+
+.food-menu {
+ display: grid;
+ grid-template-rows: auto auto auto;
+
+ /* background-color: #000; */
+ grid-template-columns: 100%;
+ flex: 1;
+}
+
+
+.section.sweets, .section.brioche-savory, .section.sides {
+ padding: 0;
+}
+
+.section.sweets {
+ align-self: start;
+}
+
+.section.brioche-savory {
+ align-self: center;
+}
+
+.section.sides {
+ padding: 0;
+ align-self: end;
+ justify-content: space-between;
+ display: flex;
+}
+
+.section.sweets .sweets-table {
+ border: solid;
+ padding: 2%;
+ margin: 2%;
+}
+
+.section.brioche-savory .brioche-savory-table {
+ border: solid;
+ padding: 2%;
+ margin: 2%;
+}
+
+.section.sides .sides-table {
+ border: solid;
+ padding: 2%;
+ margin: 2%;
+ width: 95%;
+}
+
+.section.sides > div:first-of-type {
+ order: 1;
+ margin: 0;
+}
+
+.section.sides > div:nth-of-type(2) {
+ margin: 0;
+ width: 100%;
+}
+
+.section.sides p {
+ writing-mode: vertical-rl;
+ text-orientation: revert;
+ transform: scale(-1, -1);
+ font-size: 5rem;
+ font-weight: lighter;
+ padding: 0;
+ margin: 2rem 0 0;
+}
+
+.section.sweets .sweets-table > div:nth-of-type(2) {
+ margin-left: 1em;
+ word-spacing: 0.3em;
+}
+
+.section.sweets .sweets-table > div:nth-of-type(3) {
+ margin-left: 1em;
+}
+
+.section.brioche-savory .brioche-savory-table > div:nth-of-type(2) {
+ margin-left: 1em;
+ word-spacing: 0.3em;
+}
+
+.section.brioche-savory .brioche-savory-table > div:nth-of-type(4) {
+ margin-left: 1em;
+}
+
+.section.brioche-savory .brioche-savory-table > div:nth-of-type(5) {
+ margin-left: 1em;
+}
+
+.section.brioche-savory .brioche-savory-table > div:nth-of-type(6) {
+ margin-left: 1em;
+}
+
+.section.brioche-savory .brioche-savory-table > div:nth-of-type(7) {
+ margin-left: 2em;
+ word-spacing: 0.3em;
+}
+
+.section.sides .sides-table > div:nth-of-type(2) {
+ margin-left: 1em;
+}
+
+.section.sides .sides-table > div:nth-of-type(3) {
+ margin-left: 1em;
+}
+
+.section.sides .sides-table > div:nth-of-type(4) {
+ margin-left: 1em;
+}
diff --git a/styles/styles-menuboard.css b/styles/styles-menuboard.css
new file mode 100644
index 0000000..4d6f316
--- /dev/null
+++ b/styles/styles-menuboard.css
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2020 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+:root {
+ /* colors */
+ --link-color: #035fe6;
+ --link-hover-color: #136ff6;
+ --overlay-background-color: #eee;
+ --highlight-background-color: #ccc;
+ --text-color: #fff;
+
+ /* fonts */
+ --body-font-family: 'Montserrat',sans-serif;
+ --heading-font-family: var(--body-font-family);
+ --fixed-font-family: 'Roboto Mono', menlo, consolas, 'Liberation Mono', monospace;
+
+ /* --body-font-size-m: 22px;
+ --body-font-size-s: 18px;
+ --body-font-size-xs: 16px; */
+
+ /* body sizes in relative units */
+ --body-font-size-m: 1.375rem;
+ --body-font-size-s: 1.125rem;
+ --body-font-size-xs: 1rem;
+
+ /* --heading-font-size-xxl: 48px;
+ --heading-font-size-xl: 40px;
+ --heading-font-size-l: 32px;
+ --heading-font-size-m: 24px;
+ --heading-font-size-s: 20px;
+ --heading-font-size-xs: 18px; */
+
+ /* heading sizes in relative units */
+ --heading-font-size-xxl: 3rem;
+ --heading-font-size-xl: 2.5rem;
+ --heading-font-size-l: 2rem;
+ --heading-font-size-m: 1.5rem;
+ --heading-font-size-s: 1.25rem;
+ --heading-font-size-xs: 1.125rem;
+
+ /* nav height */
+ --nav-height: 48px;
+
+}
+
+body {
+ font-weight: lighter;
+ font-size: var(--body-font-size-m);
+ margin: 0;
+ font-family: var(--body-font-family);
+ line-height: 1.6;
+ color: var(--text-color);
+ background-color: var(--background-color);
+ position: relative;
+}
+
+body.appear {
+ display: unset;
+}
+
+header {
+ height: var(--nav-height);
+ display: none;
+}
+
+h1, h2, h3,
+h4, h5, h6 {
+ font-family: var(--heading-font-family);
+ font-weight: 600;
+ line-height: 1.25;
+ margin-top: 1em;
+ margin-bottom: .5em;
+ scroll-margin: calc(var(--nav-height) + 1em);
+}
+
+h1 { font-size: var(--heading-font-size-xxl) }
+h2 { font-size: var(--heading-font-size-xl) }
+h3 { font-size: var(--heading-font-size-l) }
+h4 { font-size: var(--heading-font-size-m) }
+h5 { font-size: var(--heading-font-size-s) }
+h6 { font-size: var(--heading-font-size-xs) }
+
+p, dl, ol, ul, pre, blockquote {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+a:any-link {
+ color: var(--link-color);
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+ color: var(--link-hover-color);
+}
+
+code, pre, samp {
+ font-family: var(--fixed-font-family);
+ font-size: var(--body-font-size-s);
+}
+
+code, samp {
+ padding: .125em;
+}
+
+pre {
+ overflow: scroll;
+}
+
+/* buttons */
+
+a.button:any-link, button {
+ font-family: var(--body-font-family);
+ display: inline-block;
+ box-sizing: border-box;
+ text-decoration: none;
+ border: 2px solid transparent;
+ padding: 5px 30px;
+ text-align: center;
+ font-style: normal;
+ font-weight: 600;
+ cursor: pointer;
+ color: var(--background-color);
+ background-color: var(--link-color);
+ margin: 16px 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-radius: 30px;
+}
+
+a.button:hover, a.button:focus, button:hover, button:focus {
+ background-color: var(--link-hover-color);
+ cursor: pointer;
+}
+
+button:disabled, button:disabled:hover {
+ background-color: var(--overlay-background-color);
+ cursor: unset;
+}
+
+a.button.secondary, button.secondary {
+ background-color: unset;
+ border: 2px solid currentcolor;
+ color: var(--text-color)
+}
+
+
+main input {
+ font-size: 1.25rem;
+ width: 100%;
+ max-width: 50rem;
+ display: block;
+ margin-bottom: 1rem;
+ padding: 0.75rem 0.6rem;
+ border-radius: 0.25rem;
+ box-sizing: border-box;
+ border: 1px solid var(--text-color);
+ color: var(--text-color);
+ background-color: var(--background-color);
+}
+
+main input:hover {
+ border: 1px solid var(--text-color);
+}
+
+main .section {
+ padding: 64px 16px;
+}
+
+main pre {
+ background-color: var(--overlay-background-color);
+ padding: 1em;
+ border-radius: .25em;
+ overflow-x: auto;
+ white-space: pre;
+}
+
+main blockquote {
+ font-style: italic;
+ margin: 3rem;
+ text-indent: -1rem;
+ hanging-punctuation: first;
+}
+
+main blockquote p::before {
+ content: "“";
+ line-height: 0;
+}
+
+main blockquote p::after {
+ content: "”";
+ line-height: 0;
+}
+
+hr {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+ border: 0;
+ border-bottom: 1px solid var(--overlay-background-color);
+}
+
+main img {
+ max-width: 100%;
+ width: auto;
+ height: auto;
+}
+
+@media (min-width: 600px) {
+ main .section {
+ padding: 64px 32px;
+ }
+}
+
+@media (min-width: 900px) {
+ :root {
+ /* --heading-font-size-xxl: 60px;
+ --heading-font-size-xl: 48px;
+ --heading-font-size-l: 36px;
+ --heading-font-size-m: 30px;
+ --heading-font-size-s: 24px;
+ --heading-font-size-xs: 22px; */
+
+ /* heading sizes in relative units */
+ --heading-font-size-xxl: 3.75rem;
+ --heading-font-size-xl: 3rem;
+ --heading-font-size-l: 2.25rem;
+ --heading-font-size-m: 1.875rem;
+ --heading-font-size-s: 1.5rem;
+ --heading-font-size-xs: 1.375rem;
+ }
+
+ .section > div {
+ /* max-width: 1200px; */
+ margin: auto;
+ }
+}
+
+/* progressive section appearance */
+main .section[data-section-status='loading'],
+main .section[data-section-status='initialized'] {
+ display: none;
+}
+
+main .section.highlight {
+ background-color: var(--highlight-background-color);
+}
+
+main {
+ display: flex;
+ opacity: 0;
+}
+
+html {
+ font-size: 73%;
+ background: linear-gradient(90deg, rgb(82 5 9 / 100%) 0%, rgb(0 0 0 / 100%) 100%);
+}
+
+@media (max-width: 1200px) {
+ main {
+ width: 1200px;
+ }
+}
+
+@media (orientation: portrait) or (max-width: 900px) {
+ main {
+ flex-direction: column;
+ }
+}