diff --git a/blocks/columns/columns.css b/blocks/columns/columns.css index 472e99b9..4b09aeea 100644 --- a/blocks/columns/columns.css +++ b/blocks/columns/columns.css @@ -11,7 +11,8 @@ .columns > div { align-items: center; flex-direction: unset; - gap: 32px; + gap: var(--padding-vertical) var(--padding-horizontal); + padding: var(--padding-vertical) var(--padding-horizontal); } .columns > div > div:nth-child(1) { diff --git a/blocks/columns/columns.js b/blocks/columns/columns.js index 22747504..823c6c18 100644 --- a/blocks/columns/columns.js +++ b/blocks/columns/columns.js @@ -1,22 +1,18 @@ -export default function decorate(block) { - const id = `gen${crypto.randomUUID().split('-')[0]}`; - block.id = id; +import { + addCssVariables, +} from '../../scripts/lib-franklin.js'; +export default function decorate(block) { const columns = block.querySelectorAll(':scope > div > div'); const columnCount = columns.length; // following line regex matches partition sizes separated by dashes like 1-2-3 const columnPartionRegex = /^\d{1,}(?:-\d{1,})*$/; const columnPartions = [...block.classList].find((c) => columnPartionRegex.test(c))?.split('-') || []; - let variables = ''; + const variables = {}; for (let i = 0; i < columnCount; i += 1) { const partition = columnPartions.length > i ? columnPartions[i] : 1; - variables += `--column${i}-flex: ${partition};`; + variables[`column${i}-flex`] = partition; } - - const style = document.createElement('style'); - style.textContent = `#${id} { - ${variables} - }`; - block.parentNode.insertBefore(style, block); + addCssVariables(block, variables); } diff --git a/blocks/grid/grid.css b/blocks/grid/grid.css new file mode 100644 index 00000000..d7f192a4 --- /dev/null +++ b/blocks/grid/grid.css @@ -0,0 +1,29 @@ +.section.grid { + display: grid; + grid-template-columns: var(--grid-template-columns, unset); + grid-template-rows: var(--grid-template-rows, unset); +} + +.section.grid .element { + grid-column: var(--grid-column-start-position, auto) / var(--grid-column-end-position, auto); + grid-row: var(--grid-row-start-position, auto) / var(--grid-row-end-position, auto); + width: 100%; + max-width: unset; + background-color: var(--background-color, transparent); + color: var(--text-color); + margin: auto; +} + +.section.grid .element .image-wrapper { + margin: 0; + padding: 0; +} + +.section.grid .element img { + width: 100%; +} + +.section.grid .element p { + margin: 0; + padding: var(--padding-vertical) var(--padding-horizontal); +} diff --git a/blocks/grid/grid.js b/blocks/grid/grid.js new file mode 100644 index 00000000..fafdfefc --- /dev/null +++ b/blocks/grid/grid.js @@ -0,0 +1,33 @@ +import { + addCssVariables, +} from '../../scripts/lib-franklin.js'; + +export default function decorate(block) { + const elements = [...block.querySelectorAll(':scope > div')]; + + const columnTemplate = elements.find((e) => e.dataset.gridColumns)?.dataset.gridColumns; + const rowTemplate = elements.find((e) => e.dataset.gridRows)?.dataset.gridRows; + if (columnTemplate || rowTemplate) { + const variables = {}; + if (columnTemplate) { + variables['grid-template-columns'] = columnTemplate; + } + if (rowTemplate) { + variables['grid-template-rows'] = rowTemplate; + } + addCssVariables(block, variables); + } + + elements.forEach((e) => { + e.classList.add('element'); + + const [[startColumnPosition, startRowPosition], [endColumnPosition, endRowPosition]] = e.dataset.gridPosition.split(/\s*\/\s*/).map((p) => p.split(/\s*-\s*/)); + + addCssVariables(e, { + 'grid-column-start-position': startColumnPosition, + 'grid-row-start-position': startRowPosition, + 'grid-column-end-position': endColumnPosition, + 'grid-row-end-position': endRowPosition, + }); + }); +} diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 80b042e0..a5e00564 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -329,43 +329,70 @@ export function readBlockConfig(block) { return config; } +export function addCssVariables(element, variables) { + if (!element.id) { + const id = `gen${crypto.randomUUID().split('-')[0]}`; + element.id = id; + } + + const style = document.createElement('style'); + style.textContent = `#${element.id} { + ${Object.keys(variables).map((k) => `--${k}: ${variables[k]};`).join(' ')} + }`; + + element.parentNode.insertBefore(style, element); +} + +export function decorateSection(section) { + const wrappers = []; + let defaultContent = false; + [...section.children].forEach((e) => { + if (e.tagName === 'DIV' || !defaultContent) { + const wrapper = document.createElement('div'); + wrappers.push(wrapper); + defaultContent = e.tagName !== 'DIV'; + if (defaultContent) wrapper.classList.add('default-content-wrapper'); + } + wrappers[wrappers.length - 1].append(e); + }); + wrappers.forEach((wrapper) => section.append(wrapper)); + section.classList.add('section'); + section.dataset.sectionStatus = 'initialized'; + section.style.display = 'none'; + + /* process section metadata */ + const sectionMeta = section.querySelector('div.section-metadata'); + if (sectionMeta) { + const meta = readBlockConfig(sectionMeta); + Object.keys(meta).forEach((key) => { + if (key === 'style') { + const styles = meta.style.split(',').map((style) => toClassName(style.trim())); + styles.forEach((style) => section.classList.add(style)); + } else { + section.dataset[toCamelCase(key)] = meta[key]; + } + }); + sectionMeta.parentNode.remove(); + } + + if (section.dataset.textColor || section.dataset.background) { + const variables = {}; + if (section.dataset.textColor) { + variables['text-color'] = section.dataset.textColor; + } + if (section.dataset.background) { + variables['background-color'] = section.dataset.background; + } + addCssVariables(section, variables); + } +} + /** * Decorates all sections in a container element. * @param {Element} main The container element */ export function decorateSections(main) { - main.querySelectorAll(':scope > div').forEach((section) => { - const wrappers = []; - let defaultContent = false; - [...section.children].forEach((e) => { - if (e.tagName === 'DIV' || !defaultContent) { - const wrapper = document.createElement('div'); - wrappers.push(wrapper); - defaultContent = e.tagName !== 'DIV'; - if (defaultContent) wrapper.classList.add('default-content-wrapper'); - } - wrappers[wrappers.length - 1].append(e); - }); - wrappers.forEach((wrapper) => section.append(wrapper)); - section.classList.add('section'); - section.dataset.sectionStatus = 'initialized'; - section.style.display = 'none'; - - /* process section metadata */ - const sectionMeta = section.querySelector('div.section-metadata'); - if (sectionMeta) { - const meta = readBlockConfig(sectionMeta); - Object.keys(meta).forEach((key) => { - if (key === 'style') { - const styles = meta.style.split(',').map((style) => toClassName(style.trim())); - styles.forEach((style) => section.classList.add(style)); - } else { - section.dataset[toCamelCase(key)] = meta[key]; - } - }); - sectionMeta.parentNode.remove(); - } - }); + main.querySelectorAll(':scope > div').forEach((section) => decorateSection(section)); } /** diff --git a/scripts/scripts.js b/scripts/scripts.js index f6d84985..bd9af7ce 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -5,6 +5,7 @@ import { loadFooter, decorateButtons, decorateIcons, + decorateSection, decorateSections, decorateBlocks, decorateTemplateAndTheme, @@ -30,6 +31,22 @@ function buildHeroBlock(main) { } } +function buildGrid(main) { + const gridElements = main.querySelectorAll('.section[data-grid-position]'); + gridElements.forEach((e) => { + e.classList.remove('section'); + delete e.dataset.sectionStatus; + e.removeAttribute('style'); + }); + const grid = document.createElement('div'); + grid.classList.add('block', 'grid'); + grid.dataset.blockName = 'grid'; + grid.dataset.blockStatus = 'initialized'; + main.insertBefore(grid, gridElements[0]); + decorateSection(grid); + grid.replaceChildren(...gridElements); +} + /** * load fonts.css and set a session storage flag */ @@ -55,6 +72,14 @@ function buildAutoBlocks(main) { } } +function decorateImages(main) { + main.querySelectorAll('picture').forEach((i) => { + if (i.parentElement.tagName === 'P' && i.parentElement.children.length === 1) { + i.parentElement.classList.add('image-wrapper'); + } + }); +} + /** * Decorates the main element. * @param {Element} main The main element @@ -67,6 +92,8 @@ export function decorateMain(main) { buildAutoBlocks(main); decorateSections(main); decorateBlocks(main); + decorateImages(main); + buildGrid(main); } /** diff --git a/styles/styles.css b/styles/styles.css index a0d8b819..b3b7d4f3 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -39,6 +39,8 @@ /* nav height */ --nav-height: 64px; + --padding-horizontal: 32px; + --padding-vertical: 16px; } @@ -229,23 +231,6 @@ main img { } main .section { - padding: 64px 16px; -} - -@media (min-width: 600px) { - main .section { - padding: 64px 32px; - } -} - -@media (min-width: 900px) { - .section > div { - max-width: 1200px; - margin: auto; - } -} - -/* section metadata */ -main .section.highlight { - background-color: var(--highlight-background-color); + background-color: var(--background-color, transparent); + color: var(--text-color); }