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; + } +}