Skip to content

Commit c4fe3f7

Browse files
shsteimerclaude
andcommitted
fix: simplify footer CSS for embed compatibility, add content guards
- Replace child combinator selectors (> div > div:nth-child) with .section:nth-child in footer CSS so styles work regardless of DOM nesting depth (normal loadFragment vs aem-embed buildBlock) - Add textContent guard to footer.js to skip loadFragment when aem-embed has already provided content - Add textContent guard to header.js with fallback nav setup - Remove nav wrapper from aem-embed handleFooter (copy-paste from handleHeader) - Refactor embed tester to use Preact AemEmbed component with smart URL resolution (localhost vs live) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 505382c commit c4fe3f7

File tree

7 files changed

+78
-59
lines changed

7 files changed

+78
-59
lines changed

blocks/footer/footer.css

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ footer .footer p {
1919
}
2020

2121
/* Row 1: Disclaimer */
22-
footer .footer > div > div:first-child {
22+
footer .footer .section:first-child {
2323
padding-bottom: var(--space-m);
2424
font-size: var(--body-font-size-s);
2525
font-weight: 400;
2626
}
2727

2828
/* Row 2: Tagline */
29-
footer .footer > div > div:nth-child(2) p {
29+
footer .footer .section:nth-child(2) p {
3030
font-family: var(--font-family-serif);
3131
font-weight: 400;
3232
font-size: var(--body-font-size-s);
@@ -35,13 +35,13 @@ footer .footer > div > div:nth-child(2) p {
3535
}
3636

3737
/* Row 3: Copyright + social icons (merged) */
38-
footer .footer > div > div:nth-child(3) {
38+
footer .footer .section:nth-child(3) {
3939
margin-top: var(--space-m);
4040
border-top: 1px solid rgb(255 255 255 / 15%);
4141
padding-top: var(--space-m);
4242
}
4343

44-
footer .footer > div > div:nth-child(3) .default-content-wrapper {
44+
footer .footer .section:nth-child(3) .default-content-wrapper {
4545
display: flex;
4646
align-items: center;
4747
justify-content: center;
@@ -54,7 +54,7 @@ footer .footer .footer-separator {
5454
margin-inline: var(--space-xs);
5555
}
5656

57-
footer .footer > div > div:nth-child(3) ul {
57+
footer .footer .section:nth-child(3) ul {
5858
margin: 0;
5959
padding: 0;
6060
list-style: none;
@@ -63,21 +63,21 @@ footer .footer > div > div:nth-child(3) ul {
6363
gap: var(--space-s);
6464
}
6565

66-
footer .footer > div > div:nth-child(3) li {
66+
footer .footer .section:nth-child(3) li {
6767
margin: 0;
6868
}
6969

7070
footer .footer a:any-link {
7171
color: var(--color-parchment);
7272
}
7373

74-
footer .footer > div > div:nth-child(3) ul a {
74+
footer .footer .section:nth-child(3) ul a {
7575
text-decoration: none;
7676
color: inherit;
7777
opacity: 0.7;
7878
}
7979

80-
footer .footer > div > div:nth-child(3) ul a:hover {
80+
footer .footer .section:nth-child(3) ul a:hover {
8181
opacity: 1;
8282
}
8383

blocks/footer/footer.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ import { loadFragment } from '../fragment/fragment.js';
66
* @param {Element} block The footer block element
77
*/
88
export default async function decorate(block) {
9-
// load footer as fragment
10-
const footerMeta = getMetadata('footer');
11-
const footerPath = footerMeta ? new URL(footerMeta, window.location).pathname : '/footer';
12-
const fragment = await loadFragment(footerPath);
9+
// load footer as fragment (skip if aem-embed already provided content)
10+
if (block.textContent === '') {
11+
const footerMeta = getMetadata('footer');
12+
const footerPath = footerMeta ? new URL(footerMeta, window.location).pathname : '/footer';
13+
const fragment = await loadFragment(footerPath);
1314

14-
// decorate footer DOM
15-
block.textContent = '';
16-
const footer = document.createElement('div');
17-
while (fragment.firstElementChild) footer.append(fragment.firstElementChild);
15+
block.textContent = '';
16+
const footer = document.createElement('div');
17+
while (fragment.firstElementChild) footer.append(fragment.firstElementChild);
18+
block.append(footer);
19+
}
1820

1921
// merge social icons into copyright row
20-
const sections = footer.querySelectorAll(':scope > .section');
22+
const sections = block.querySelectorAll('.section');
2123
if (sections.length >= 4) {
2224
const copyrightSection = sections[2];
2325
const socialSection = sections[3];
@@ -31,6 +33,4 @@ export default async function decorate(block) {
3133
socialSection.remove();
3234
}
3335
}
34-
35-
block.append(footer);
3636
}

blocks/fragment/fragment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import dynamicBlocks from '../dynamic/index.js';
1717
/**
1818
* Loads a fragment.
1919
* @param {string} path The path to the fragment
20-
* @returns {HTMLElement} The root element of the fragment
20+
* @returns {Promise<HTMLElement>} The root element of the fragment
2121
*/
2222
export async function loadFragment(path) {
2323
if (path && path.startsWith('/') && !path.startsWith('//')) {

blocks/header/header.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -358,16 +358,26 @@ function toggleMobile(nav, open) {
358358
const NAV_ITEMS = '.default-content-wrapper > ul > li';
359359

360360
export default async function decorate(block) {
361-
const fragment = await loadFragment(getNavPath());
362-
if (!fragment) return;
363-
364-
block.textContent = '';
365-
const nav = document.createElement('nav');
366-
nav.id = 'nav';
367-
nav.setAttribute('aria-label', 'Main');
368-
nav.setAttribute('aria-expanded', 'false');
361+
// Load nav content (skip if aem-embed already provided content)
362+
if (block.textContent === '') {
363+
const fragment = await loadFragment(getNavPath());
364+
if (!fragment) return;
365+
366+
block.textContent = '';
367+
const nav = document.createElement('nav');
368+
nav.id = 'nav';
369+
nav.setAttribute('aria-label', 'Main');
370+
nav.setAttribute('aria-expanded', 'false');
371+
372+
while (fragment.firstElementChild) nav.append(fragment.firstElementChild);
373+
block.append(nav);
374+
}
369375

370-
while (fragment.firstElementChild) nav.append(fragment.firstElementChild);
376+
const nav = block.querySelector('nav');
377+
if (!nav) return;
378+
if (!nav.id) nav.id = 'nav';
379+
if (!nav.getAttribute('aria-label')) nav.setAttribute('aria-label', 'Main');
380+
if (!nav.getAttribute('aria-expanded')) nav.setAttribute('aria-expanded', 'false');
371381

372382
['brand', 'sections', 'tools'].forEach((c, i) => nav.children[i]?.classList.add(`nav-${c}`));
373383

scripts/aem-embed.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@ export class AEMEmbed extends HTMLElement {
8686
footer.append(block);
8787

8888
const cell = block.firstElementChild.firstElementChild;
89-
const nav = document.createElement('nav');
90-
cell.append(nav);
91-
while (main.firstElementChild) nav.append(main.firstElementChild);
89+
while (main.firstElementChild) cell.append(main.firstElementChild);
9290
main.remove();
9391

9492
await this.loadBlock(body, block, 'footer', origin);

tools/embed-tester/embed.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@
99
<script type="module" src="./scripts.js"></script>
1010
</head>
1111
<body>
12-
<aem-embed url="https://feat-embed-content--diyfire--cloudadoption.aem.page/nav" type="header"></aem-embed>
13-
1412
<div id="app"></div>
15-
16-
<aem-embed url="https://feat-embed-content--diyfire--cloudadoption.aem.page/footer" type="footer"></aem-embed>
17-
1813
</body>
1914
</html>

tools/embed-tester/scripts.js

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,34 @@ import {
66
useEffect,
77
} from 'https://esm.sh/htm/preact/standalone';
88

9-
const BASE_URL = 'https://feat-embed-content--diyfire--cloudadoption.aem.page';
9+
const LIVE_URL = 'https://main--diyfire--cloudadoption.aem.live';
10+
11+
function getBaseUrl() {
12+
const { origin } = window.location;
13+
if (origin.includes('localhost')) return origin;
14+
return LIVE_URL;
15+
}
16+
17+
function resolveUrl(path) {
18+
if (path.startsWith('http')) return path;
19+
const base = getBaseUrl();
20+
return `${base}${path.startsWith('/') ? '' : '/'}${path}`;
21+
}
22+
23+
function AemEmbed({ path, type = 'main' }) {
24+
const ref = useRef(null);
25+
26+
useEffect(() => {
27+
if (!path || !ref.current) return;
28+
ref.current.replaceChildren();
29+
const el = document.createElement('aem-embed');
30+
el.setAttribute('url', resolveUrl(path));
31+
el.setAttribute('type', type);
32+
ref.current.appendChild(el);
33+
}, [path, type]);
34+
35+
return html`<div ref=${ref}></div>`;
36+
}
1037

1138
function Card({ title, children }) {
1239
return html`
@@ -51,27 +78,12 @@ function CardGrid() {
5178

5279
function FragmentEmbedder() {
5380
const [path, setPath] = useState('');
54-
const [embedUrl, setEmbedUrl] = useState(null);
55-
const previewRef = useRef(null);
56-
57-
useEffect(() => {
58-
if (!embedUrl || !previewRef.current) return;
59-
previewRef.current.innerHTML = '';
60-
const embed = document.createElement('aem-embed');
61-
embed.setAttribute('url', embedUrl);
62-
embed.setAttribute('type', 'main');
63-
previewRef.current.appendChild(embed);
64-
}, [embedUrl]);
81+
const [activePath, setActivePath] = useState(null);
6582

6683
const handleEmbed = () => {
6784
const trimmed = path.trim();
6885
if (!trimmed) return;
69-
const url = trimmed.startsWith('http') ? trimmed : `${BASE_URL}${trimmed.startsWith('/') ? '' : '/'}${trimmed}`;
70-
setEmbedUrl(url);
71-
};
72-
73-
const handleKeyDown = (e) => {
74-
if (e.key === 'Enter') handleEmbed();
86+
setActivePath(trimmed);
7587
};
7688

7789
return html`
@@ -86,16 +98,18 @@ function FragmentEmbedder() {
8698
placeholder="/fragments/example or full URL"
8799
value=${path}
88100
onInput=${(e) => setPath(e.target.value)}
89-
onKeyDown=${handleKeyDown}
101+
onKeyDown=${(e) => e.key === 'Enter' && handleEmbed()}
90102
/>
91103
</div>
92104
<button onClick=${handleEmbed}>Embed</button>
93105
</div>
94-
<div class="preview-container" ref=${previewRef}>
95-
${!embedUrl && html`<div class="preview-placeholder">Enter a path and click Embed to preview content.</div>`}
106+
<div class="preview-container">
107+
${activePath
108+
? html`<${AemEmbed} path=${activePath} />`
109+
: html`<div class="preview-placeholder">Enter a path and click Embed to preview content.</div>`}
96110
</div>
97111
<p class="hint">
98-
Paths are resolved against <code>${BASE_URL}</code>.
112+
Paths are resolved against <code>${getBaseUrl()}</code>.
99113
CORS headers must be configured on the source for cross-origin embedding.
100114
</p>
101115
</section>
@@ -104,6 +118,7 @@ function FragmentEmbedder() {
104118

105119
function App() {
106120
return html`
121+
<${AemEmbed} path="/nav" type="header" />
107122
<main class="app-main">
108123
<${Hero} />
109124
<${CardGrid} />
@@ -115,6 +130,7 @@ function App() {
115130
</p>
116131
</div>
117132
</main>
133+
<${AemEmbed} path="/footer" type="footer" />
118134
`;
119135
}
120136

0 commit comments

Comments
 (0)