| """SEO page components: hero, FAQ, related Spaces, disclaimer, footer.""" | |
| import html as _html | |
| from .config import SpaceConfig, COLLECTION_TITLE, COLLECTION_URL | |
| def hero_html(config: SpaceConfig) -> str: | |
| """Single H1 + one-sentence outcome promise. The only H1 on the page.""" | |
| return ( | |
| '<div class="lf-page">' | |
| f"<h1>{_html.escape(config.h1)}</h1>" | |
| f'<p class="lf-tagline">{_html.escape(config.tagline)}</p>' | |
| "</div>" | |
| ) | |
| def section_html(heading: str, body_html: str, level: int = 2) -> str: | |
| tag = f"h{level}" | |
| return ( | |
| '<div class="lf-page">' | |
| f"<{tag}>{_html.escape(heading)}</{tag}>{body_html}</div>" | |
| ) | |
| def faq_html(faqs: list[tuple[str, str]]) -> str: | |
| """Accessible <details> FAQ. faqs = [(question, answer_html)].""" | |
| items = "".join( | |
| f"<details><summary>{_html.escape(q)}</summary><p>{a}</p></details>" | |
| for q, a in faqs | |
| ) | |
| return ( | |
| '<div class="lf-page"><h2>Frequently Asked Questions</h2>' | |
| f'<div class="lf-faq">{items}</div></div>' | |
| ) | |
| def related_html(config: SpaceConfig) -> str: | |
| if not config.related: | |
| return "" | |
| cards = "".join( | |
| f'<a class="lf-related-card" href="{r.url}">' | |
| f'<div class="lf-related-title">{_html.escape(r.title)}</div>' | |
| f'<div class="lf-related-desc">{_html.escape(r.description)}</div></a>' | |
| for r in config.related | |
| ) | |
| return ( | |
| '<div class="lf-page"><h2>More Free D&D Tools</h2>' | |
| f'<div class="lf-related">{cards}</div>' | |
| f'<p>Browse the full collection: <a href="{COLLECTION_URL}">' | |
| f"{_html.escape(COLLECTION_TITLE)}</a></p></div>" | |
| ) | |
| def disclaimer_html() -> str: | |
| return ( | |
| '<p class="lf-disclaimer">' | |
| "This is an unofficial fan tool. Dungeons & Dragons and D&D are " | |
| "trademarks of Wizards of the Coast LLC. This tool is not affiliated with, " | |
| "endorsed, or sponsored by Wizards of the Coast. Portions of the results are " | |
| "generated by AI: review everything with your DM and adapt it to your table " | |
| "before play. This tool is built and maintained by " | |
| '<a href="https://loreify.ai">Loreify</a>, an AI session-notes app for D&D ' | |
| "campaigns.</p>" | |
| ) | |