Spaces:
Running
Running
--- | |
interface Props { | |
citationText: string; | |
bibtex: string; | |
licence?: string; | |
doi?: string; | |
} | |
const { citationText, bibtex, licence, doi } = Astro.props as Props; | |
--- | |
<footer class="footer"> | |
<div class="footer-inner"> | |
<section class="citation-block"> | |
<h3>Citation</h3> | |
<p>For attribution in academic contexts, please cite this work as</p> | |
<pre class="citation short">{citationText}</pre> | |
<p>BibTeX citation</p> | |
<pre class="citation long">{bibtex}</pre> | |
</section> | |
{doi && ( | |
<section class="doi-block"> | |
<h3>DOI</h3> | |
<p><a href={`https://doi.org/${doi}`} target="_blank" rel="noopener noreferrer">{doi}</a></p> | |
</section> | |
)} | |
{licence && ( | |
<section class="reuse-block"> | |
<h3>Reuse</h3> | |
<p set:html={licence}></p> | |
</section> | |
)} | |
<section class="references-block"> | |
<slot /> | |
</section> | |
</div> | |
</footer> | |
<script is:inline> | |
(() => { | |
const getFooter = () => document.currentScript?.closest('footer') || document.querySelector('footer.footer'); | |
const footer = getFooter(); | |
if (!footer) return; | |
const target = footer.querySelector('.references-block'); | |
if (!target) return; | |
const contentRoot = document.querySelector('section.content-grid main') || document.querySelector('main') || document.body; | |
const ensureHeading = (text) => { | |
const exists = Array.from(target.children).some((c) => c.tagName === 'H3' && c.textContent.trim().toLowerCase() === text.toLowerCase()); | |
if (!exists) { | |
const h = document.createElement('h3'); | |
h.textContent = text; | |
target.appendChild(h); | |
} | |
}; | |
const moveIntoFooter = (element, headingText) => { | |
if (!element) return false; | |
// Remove an eventual heading already included inside the block (avoid duplicates) | |
const firstHeading = element.querySelector(':scope > h1, :scope > h2, :scope > h3'); | |
if (firstHeading) { | |
const txt = (firstHeading.textContent || '').trim().toLowerCase(); | |
const targetTxt = headingText.trim().toLowerCase(); | |
if (txt === targetTxt || txt.includes('reference') || txt.includes('bibliograph')) { | |
firstHeading.remove(); | |
} | |
} | |
ensureHeading(headingText); | |
target.appendChild(element); | |
return true; | |
}; | |
const run = () => { | |
const findFirstOutsideFooter = (selectors) => { | |
for (const sel of selectors) { | |
const el = contentRoot.querySelector(sel); | |
if (el && !footer.contains(el)) return el; | |
} | |
return null; | |
}; | |
const referencesEl = findFirstOutsideFooter(['#references', '.references', '.bibliography']); | |
const footnotesEl = findFirstOutsideFooter(['.footnotes']); | |
const movedRefs = moveIntoFooter(referencesEl, 'References'); | |
const movedNotes = moveIntoFooter(footnotesEl, 'Footnotes'); | |
return movedRefs || movedNotes; | |
}; | |
// Try now; if not found yet, try again on DOM ready | |
const done = run(); | |
if (!done) { | |
const onReady = () => run(); | |
if (document.readyState === 'loading') { | |
document.addEventListener('DOMContentLoaded', onReady, { once: true }); | |
} else { | |
setTimeout(onReady, 0); | |
} | |
} | |
// Resize on window changes (e.g., fonts, layout) | |
// No textarea auto-resize needed for <pre> blocks | |
})(); | |
</script> | |
<style is:global> | |
.footer { | |
contain: layout style; | |
font-size: 0.8em; | |
line-height: 1.7em; | |
margin-top: 60px; | |
margin-bottom: 0; | |
border-top: 1px solid rgba(0, 0, 0, 0.1); | |
color: rgba(0, 0, 0, 0.5); | |
} | |
.footer-inner { | |
max-width: 1280px; | |
margin: 0 auto; | |
padding: 60px 16px 48px; | |
display: grid; | |
grid-template-columns: 220px minmax(0, 680px) 260px; | |
gap: 32px; | |
align-items: start; | |
} | |
/* Use the parent grid (3 columns like .content-grid) */ | |
.citation-block, | |
.references-block, | |
.reuse-block, | |
.doi-block { | |
display: contents; | |
} | |
.citation-block > h3, | |
.references-block > h3, | |
.reuse-block > h3, | |
.doi-block > h3 { | |
grid-column: 1; | |
font-size: 15px; | |
margin: 0; | |
text-align: right; | |
padding-right: 30px; | |
} | |
.citation-block > :not(h3), | |
.references-block > :not(h3), | |
.reuse-block > :not(h3), | |
.doi-block > :not(h3) { | |
grid-column: 2; | |
} | |
.citation-block h3 { | |
margin: 0 0 8px; | |
} | |
.citation-block h4 { | |
margin: 16px 0 8px; | |
font-size: 14px; | |
text-transform: uppercase; | |
color: var(--muted-color); | |
} | |
.citation-block p, | |
.reuse-block p, | |
.doi-block p, | |
.footnotes ol, | |
.footnotes ol p, | |
.references { | |
margin-top: 0; | |
} | |
/* Distill-like appendix citation styling */ | |
.citation { | |
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; | |
font-size: 11px; | |
line-height: 15px; | |
border-left: 1px solid rgba(0, 0, 0, 0.1); | |
padding-left: 18px; | |
border: 1px solid rgba(0,0,0,0.1); | |
background: rgba(0, 0, 0, 0.02); | |
padding: 10px 18px; | |
border-radius: 3px; | |
color: rgba(150, 150, 150, 1); | |
overflow: hidden; | |
margin-top: -12px; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
} | |
.citation a { | |
color: rgba(0, 0, 0, 0.6); | |
text-decoration: underline; | |
} | |
.citation.short { | |
margin-top: -4px; | |
} | |
.references-block h3 { | |
margin: 0; | |
} | |
/* Distill-like list styling for references/footnotes */ | |
.references-block ol { | |
padding: 0 0 0 15px; | |
} | |
@media (min-width: 768px) { | |
.references-block ol { | |
padding: 0 0 0 30px; | |
margin-left: -30px; | |
} | |
} | |
.references-block li { | |
margin-bottom: 1em; | |
} | |
/* Évite le double affichage des numéros: masque le marqueur natif */ | |
.references-block li::marker { | |
content: ''; | |
} | |
.references-block a { | |
color: var(--text-color); | |
} | |
[data-theme="dark"] .footer { border-top-color: rgba(255,255,255,.15); color: rgba(200,200,200,.8); } | |
[data-theme="dark"] .citation { background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,.15); color: rgba(200,200,200,1); } | |
[data-theme="dark"] .citation a { color: rgba(255,255,255,0.75); } | |
/* Footer links: use primary color consistently */ | |
.footer a { | |
color: var(--primary-color); | |
border-bottom: 1px solid var(--link-underline); | |
text-decoration: none; | |
} | |
.footer a:hover { | |
color: var(--primary-color-hover); | |
border-bottom-color: var(--link-underline-hover); | |
} | |
[data-theme="dark"] .footer a { | |
color: var(--primary-color); | |
} | |
</style> | |