diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..16b277f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,31 @@ +# AGENTS Guide + +## Project Overview + +- **Name:** mlut website +- **Stack:** Eleventy, mlut, ejs, Sass, Javascript +- **Architecture:** SSG (Static site generation) + +## Project Structure + +- `src/` - editable sources +- `src/_includes/` - partials and reusable templates +- `src/_data/` - global Eleventy data +- `src/assets/` - styles, scripts, images, and other static assets +- `src/layouts/` - reusable generic layouts for pages +- `src/arts/` - pure CSS arts +- `dist/` - generated output + +## Setup and commands + +- **Install dependencies:** `npm i` +- **Build project:** `npm run build` +- **Start dev server:** `npm start` +- **Start mlut CLI in watch mode:** `npm run mlut` + +## Working rules + +- Always try to address the cause, not the effect +- Use modern vanilla JS with custom elements and without frameworks. See examples of components in `src/assets/script/` +- Use Atomic CSS aproach with mlut for writing styles +- Do not install new third-party packages without asking the user first diff --git a/src/_data/arts.json b/src/_data/arts.json index daa21d9..0199483 100644 --- a/src/_data/arts.json +++ b/src/_data/arts.json @@ -2,52 +2,61 @@ { "artId":"ghost", "bgColor":"Bgc-$artGhost", - "showInMain": true + "showInMain": true, + "size":"standard" }, { "artId":"flushed-emoji", "bgColor":"Bgc-$artFlushedEmoji", - "showInMain": true + "showInMain": true, + "size":"standard" }, { "artId":"penrose-triangle", "bgColor":"Bgc-$artPenroseTriangle", - "showInMain": true + "showInMain": true, + "size":"wide" }, { "artId":"exploding-head", "bgColor":"Bgc-$artExplodingHead", - "showInMain": true + "showInMain": true, + "size":"wide" }, { "artId":"clown-emoji", "bgColor":"Bgc-$artClownEmoji", - "showInMain": false + "showInMain": false, + "size":"standard" }, { "artId":"mushroom", "bgColor":"Bgc-$artMushroom", - "showInMain": false + "showInMain": false, + "size":"tall" }, { "artId":"robot-hare", "bgColor":"Bgc-$artRobotHare", - "showInMain": true + "showInMain": true, + "size":"tall" }, { "artId":"funny-cake", "bgColor":"Bgc-$artFunnyCake", - "showInMain": false + "showInMain": false, + "size":"wide" }, { "artId":"cat", "bgColor":"Bgc-$artCat", - "showInMain": false + "showInMain": false, + "size":"wide" }, { "artId":"corgi", "bgColor":"Bgc-$artCorgi", - "showInMain": true + "showInMain": true, + "size":"wide" } - -] \ No newline at end of file +] diff --git a/src/arts.ejs b/src/arts.ejs index 6e849c4..bd08705 100644 --- a/src/arts.ejs +++ b/src/arts.ejs @@ -4,23 +4,91 @@ area: Arts --- <% const artsPageCSS = { - card:"W100p H20gg Tsd-$shortTs @:hv_Tsd-$longTs", - artWrapper:"-Sz100p Ov-h Plcc-c Plci-c D-f", + card:"D H20gg Bgc-$core800 Tsd-$shortTs @:hv_Tsd-$longTs", button: "W12gg lg_W9gg Mxw50u xl_Mxw55u Ai-str" + }; + + // Grid spans are expressed as [columns, rows]. + const tileSizes = { + standard: [1, 2], + wide: [2, 2], + tall: [1, 4] + }; + const colsOnScreen = { + md: 2, + xl: 4, + }; + + const masonryItems = [ + ...it.arts, + { artId: 'add-art', size: 'standard' } + ]; + + // Represent every occupied grid cell with a stable row:column key. + const getCells = (row, column, width, height) => Array.from( + { length: width * height }, + (_, index) => `${row + Math.floor(index / width)}:${column + (index % width)}` + ); + + function getTileDimensions(size, columns) { + const [preferredWidth, height] = tileSizes[size] || tileSizes.standard; + const wideTileOnMdScreen = size === 'wide' && columns === colsOnScreen.md; + const width = wideTileOnMdScreen ? 1 : Math.min(preferredWidth, columns); + + return { width, height }; + } + + function findFirstAvailableArea(occupied, columns, width, height) { + // Preserve the row-first, then column-first placement order. + for (let row = 1; ; row += 1) { + for (let column = 1; column <= columns - width + 1; column += 1) { + const cells = getCells(row, column, width, height); + const isAvailable = cells.every((cell) => !occupied.has(cell)); + + if (isAvailable) return { column, row, cells }; + } + } } - function setCtaLength(){ - return it.arts.length % 2 != 0 ? 'md_W50p' : '' + function packMasonry(columns) { + const occupied = new Set(); + + return masonryItems.reduce((layout, item) => { + const { width, height } = getTileDimensions(item.size, columns); + const { column, row, cells } = findFirstAvailableArea( + occupied, + columns, + width, + height + ); + + cells.forEach((cell) => occupied.add(cell)); + layout[item.artId] = { column, row, width, height }; + return layout; + }, {}); } + // Save positions for the two responsive grid configurations. + const masonryLayouts = { + md: packMasonry(colsOnScreen.md), + xl: packMasonry(colsOnScreen.xl) + }; + + function getMasonryStyle(artId) { + // Serialize server-calculated positions as CSS custom properties. + return Object.entries(masonryLayouts).map(([breakpoint, layout]) => { + const { column, row, width, height } = layout[artId]; + return `--masonry-column-${breakpoint}:${column}/span ${width};--masonry-row-${breakpoint}:${row}/span ${height};`; + }).join(''); + } %>

Pure CSS art

-
- <% it.arts.forEach((art, index) => {%> - +
+ <% it.arts.forEach((art) => { %> +
<%- include(`./arts/${art.artId}.ejs`) %>
@@ -43,9 +111,9 @@ area: Arts
- <% }) %> + <% }); %> -