Create a simple hero section with title, subtitle, and call-to-action

button Add a features section with icons and descriptions Include a
stats section with live data Add a responsive design with modern CSS
features
This commit is contained in:
Ruben 2025-11-27 21:31:47 +01:00
parent f0d50ff8bf
commit 0e19040473
101 changed files with 1356 additions and 7532 deletions

View file

@ -1,69 +1,55 @@
<?php
$startTime = microtime(true);
$publicDir = realpath($_SERVER['DOCUMENT_ROOT']);
$customCssPath = __DIR__ . '/../../custom/styles/base.css';
$defaultCssPath = __DIR__ . '/../styles/base.css';
$cssPath = file_exists($customCssPath) ? $customCssPath : $defaultCssPath;
$cssUrl = file_exists($customCssPath) ? '/app/styles/base.css' : '/app/default-styles/base.css';
$cssHash = file_exists($cssPath) ? hash_file('md5', $cssPath) : 'file_not_found';
if (isset($GLOBALS['_SERVER']['SCRIPT_FILENAME'])) { $includingFile = $_SERVER['SCRIPT_FILENAME']; }
if (!empty($includingFile)) { $pageName = pathinfo($includingFile, PATHINFO_FILENAME); }
if (!in_array(basename(dirname($includingFile)), ['latest', 'live', 'frozen']) && basename(dirname($includingFile)) !== '') { $dirName = basename(dirname($includingFile)); }
function getActiveClass($href) { return rtrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/') === rtrim($href, '/') ? 'active' : ''; }
?>
<!DOCTYPE html>
<html lang="<?= htmlspecialchars($currentLang ?? 'en') ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php if (!empty($metaDescription)): ?>
<meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
<?php endif; ?>
<link rel="stylesheet" href="<?= $cssUrl ?>?v=<?= $cssHash ?>">
<?php if (!empty($pageCssUrl)): ?>
<link rel="stylesheet" href="<?= $pageCssUrl ?>?v=<?= $pageCssHash ?? '' ?>">
<?php endif; ?>
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" href="/favicon.png" type="image/png">
<title><?= htmlspecialchars($pageTitle ?? 'Site Title') ?></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($pageTitle ?? 'FolderWeb') ?></title>
<?php if (!empty($metaDescription)): ?>
<meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
<?php endif; ?>
<?php if (!empty($socialImageUrl)): ?>
<meta property="og:image" content="<?= htmlspecialchars($socialImageUrl) ?>">
<?php endif; ?>
<link rel="stylesheet" href="/app/default/styles/styles.css">
<?php if (!empty($pageCssUrl)): ?>
<link rel="stylesheet" href="<?= htmlspecialchars($pageCssUrl) ?>?v=<?= htmlspecialchars($pageCssHash ?? '') ?>">
<?php endif; ?>
</head>
<body>
<header>
<nav>
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/"><?= htmlspecialchars($homeLabel ?? ($translations['home'] ?? 'Home')) ?></a>
<?php if (!empty($navigation)): ?>
<?php foreach ($navigation as $item): ?>
<a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a>
<?php endforeach; ?>
<?php endif; ?>
</nav>
<?php if (!empty($languageUrls) && count($languageUrls) > 1): ?>
<nav class="language-switcher" aria-label="Language">
<?php foreach ($languageUrls as $lang => $url): ?>
<a href="<?= htmlspecialchars($url) ?>" <?= ($lang === $currentLang) ? 'aria-current="true"' : '' ?>><?= htmlspecialchars(strtoupper($lang)) ?></a>
<?php endforeach; ?>
</nav>
<?php endif; ?>
</header>
<body class="<?php if (isset($dirName)) echo 'section-' . $dirName . ' '; ?><?php if (isset($pageName)) echo 'page-' . $pageName; ?>">
<div class="grid-container">
<header class="contain">
<div>
<div class="logo">
<a href="/">
<svg width="200" height="60" viewBox="0 0 200 60" xmlns="http://www.w3.org/2000/svg">
<text x="10" y="40" font-family="Arial, sans-serif" font-size="32" font-weight="bold" fill="currentColor">FolderWeb</text>
</svg>
</a>
</div>
<nav>
<ul>
<li><a href="/" class="button <?php echo getActiveClass('/'); ?>"><?= htmlspecialchars($translations['home'] ?? 'Home') ?></a></li>
<?php if (!empty($navigation)): ?>
<?php foreach ($navigation as $item): ?>
<li><a href="<?= htmlspecialchars($item['url']) ?>" class="button <?php echo getActiveClass($item['url']); ?>"><?= htmlspecialchars($item['title']) ?></a></li>
<?php endforeach; ?>
<?php endif; ?>
</ul>
</nav>
</div>
</header>
<main>
<?= $content ?>
</main>
<main>
<?php echo $content ?? ''; ?>
</main>
<footer>
<div class="contain">
<p><?= htmlspecialchars($translations['footer_text'] ?? 'Footer content goes here') ?></p>
<?php $endTime = microtime(true); $pageLoadTime = round(($endTime - $startTime) * 1000, 2); ?>
<p class="generated"><?= htmlspecialchars($translations['footer_handcoded'] ?? 'This page was generated in') ?> <?php echo $pageLoadTime; ?><?= htmlspecialchars($translations['footer_page_time'] ?? 'ms') ?></p>
</div>
</footer>
</div>
<footer>
<nav>
<a href="https://mastodon.social/@example" rel="me">Mastodon</a>
<a href="https://bsky.app/profile/example.bsky.social">Bluesky</a>
</nav>
<p>
<?php if (!empty($translations['footer_handcoded'])): ?>
<?= htmlspecialchars($translations['footer_handcoded']) ?> <?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) ?><?= htmlspecialchars($translations['footer_page_time'] ?? 'ms') ?>
<?php else: ?>
Generated in <?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) ?>ms
<?php endif; ?>
</p>
</footer>
</body>
</html>

View file

@ -1,82 +0,0 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<?= $pageContent ?>
</article>
<?php endif; ?>
<section class="list-card-grid-wrapper">
<div class="list-card-grid">
<?php foreach ($items as $item): ?>
<article>
<?php if ($item['cover']): ?>
<a href="<?= htmlspecialchars($item['pdf'] ?? $item['url']) ?>">
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="<?= htmlspecialchars($item['title']) ?>">
</a>
<?php endif; ?>
<h1>
<a href="<?= htmlspecialchars($item['redirect'] ?? $item['url']) ?>">
<?= htmlspecialchars($item['title']) ?>
</a>
</h1>
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
<p><?= htmlspecialchars($item['date']) ?></p>
<?php endif; ?>
<?php if ($item['summary']): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
<div class="card-actions">
<?php if (!empty($item['pdf'])): ?>
<a href="<?= htmlspecialchars($item['pdf']) ?>" class="button" download><?= htmlspecialchars($translations['download_pdf'] ?? 'Download PDF') ?></a>
<?php endif; ?>
<?php if (!empty($item['redirect'])): ?>
<a href="<?= htmlspecialchars($item['redirect']) ?>" class="button"><?= htmlspecialchars($translations['read_article'] ?? 'Read article') ?></a>
<?php else: ?>
<a href="<?= htmlspecialchars($item['url']) ?>" class="button"><?= htmlspecialchars($translations['read_more'] ?? 'Read more') ?></a>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<style>
main > section.list-card-grid-wrapper {
margin-top: 1.3em;
.list-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(clamp(15rem, 45%, 20rem), 1fr));
gap: clamp(1rem, 3vw, 2rem);
}
.list-card-grid > article {
background-color: white;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
> :not(img, a) {
padding-left: 1rem;
padding-right: 1rem;
}
h1 {
margin-top: 1rem;
font-size: clamp(1.5rem, 4vw, 2rem);
}
.card-actions {
margin-top: auto;
display: flex;
gap: 0.3rem;
flex-wrap: wrap;
padding-bottom: 1rem;
a {
margin-top: 1.3rem;
}
}
}
}
</style>

View file

@ -0,0 +1,61 @@
<?php if (!empty($pageContent)): ?>
<div class="list-intro">
<?= $pageContent ?>
</div>
<?php endif; ?>
<ul class="compact-list">
<?php foreach ($items as $item): ?>
<li>
<a href="<?= htmlspecialchars($item['url']) ?>">
<strong><?= htmlspecialchars($item['title']) ?></strong>
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
<time><?= htmlspecialchars($item['date']) ?></time>
<?php endif; ?>
</a>
<?php if (!empty($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<style>
.compact-list {
list-style: none;
padding: 0;
max-width: var(--size-content);
& li {
border-block-end: 1px solid var(--color-border);
padding-block: var(--space-s);
&:first-child {
padding-block-start: 0;
}
}
& a {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--space-s);
text-decoration: none;
&:hover strong {
text-decoration: underline;
}
}
& time {
white-space: nowrap;
font-size: 0.875rem;
}
& p {
margin-block-start: var(--space-xs);
color: var(--color-muted);
font-size: 0.9rem;
}
}
</style>

View file

@ -1,141 +0,0 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<?= $pageContent ?>
</article>
<?php endif; ?>
<section class="list-faq-wrapper">
<div class="list-faq">
<?php foreach ($items as $item): ?>
<details class="faq-item">
<summary>
<h2><?= htmlspecialchars($item['title']) ?></h2>
<span class="toggle-icon" aria-hidden="true"></span>
</summary>
<div class="faq-content">
<?php if ($item['summary']): ?>
<p><strong><?= htmlspecialchars($translations['summary'] ?? 'Summary') ?>:</strong> <?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
<a href="<?= htmlspecialchars($item['url']) ?>" class="button"><?= htmlspecialchars($translations['read_full_answer'] ?? 'Read full answer') ?></a>
</div>
</details>
<?php endforeach; ?>
</div>
</section>
<style>
main > section.list-faq-wrapper {
margin-top: 1.3em;
.list-faq {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.faq-item {
background-color: white;
border: 1px solid #e0e0e0;
border-radius: 0.5rem;
overflow: hidden;
transition: border-color 0.2s ease;
&:hover {
border-color: var(--color-primary);
}
&[open] {
border-color: var(--color-primary);
}
}
summary {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.5rem;
cursor: pointer;
list-style: none;
user-select: none;
&::-webkit-details-marker {
display: none;
}
h2 {
margin: 0;
font-size: 1.3rem;
font-weight: 400;
color: var(--color-grey);
flex: 1;
}
.toggle-icon {
width: 1.5rem;
height: 1.5rem;
flex-shrink: 0;
position: relative;
margin-left: 1rem;
&::before,
&::after {
content: '';
position: absolute;
background-color: var(--color-primary);
transition: transform 0.3s ease;
}
&::before {
top: 50%;
left: 0;
right: 0;
height: 2px;
transform: translateY(-50%);
}
&::after {
left: 50%;
top: 0;
bottom: 0;
width: 2px;
transform: translateX(-50%);
}
}
&:hover h2 {
color: var(--color-primary);
}
}
.faq-item[open] summary .toggle-icon::after {
transform: translateX(-50%) rotate(90deg);
opacity: 0;
}
.faq-content {
padding: 0 1.5rem 1.5rem 1.5rem;
animation: slideDown 0.3s ease;
p {
margin-top: 0;
color: var(--color-grey);
line-height: 1.6;
}
.button {
margin-top: 1rem;
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-0.5rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
</style>

View file

@ -1,94 +1,48 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<?= $pageContent ?>
</article>
<div class="list-intro">
<?= $pageContent ?>
</div>
<?php endif; ?>
<section class="list-grid-wrapper">
<div class="list-grid">
<?php foreach ($items as $item): ?>
<article>
<?php if ($item['cover']): ?>
<a href="<?= htmlspecialchars($item['url']) ?>">
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="<?= htmlspecialchars($item['title']) ?>">
</a>
<?php endif; ?>
<h1>
<a href="<?= htmlspecialchars($item['url']) ?>">
<?= htmlspecialchars($item['title']) ?>
</a>
</h1>
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
<p><?= htmlspecialchars($item['date']) ?></p>
<?php endif; ?>
<?php if ($item['summary']): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
<div class="grid-actions">
<?php if (!empty($item['pdf'])): ?>
<a href="<?= htmlspecialchars($item['pdf']) ?>" class="button" download><?= htmlspecialchars($translations['download_pdf'] ?? 'Download PDF') ?></a>
<?php endif; ?>
<a href="<?= htmlspecialchars($item['url']) ?>" class="button"><?= htmlspecialchars($translations['read_more'] ?? 'Read more') ?></a>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<div class="grid">
<?php foreach ($items as $item): ?>
<article class="card">
<?php if (!empty($item['cover'])): ?>
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
<?php endif; ?>
<h3><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h3>
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
<time><?= htmlspecialchars($item['date']) ?></time>
<?php endif; ?>
<?php if (!empty($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>
<style>
main > section.list-grid-wrapper {
margin-top: 1.3em;
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
gap: var(--space-m);
}
.list-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(clamp(15rem, 45%, 20rem), 1fr));
gap: clamp(1rem, 3vw, 2rem);
}
.list-grid > article {
background-color: white;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
> :not(img, a) {
padding-left: 1rem;
padding-right: 1rem;
}
h1 {
margin-top: 1rem;
font-size: clamp(1.5rem, 4vw, 2rem);
}
.grid-actions {
margin-top: auto;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
padding-bottom: 1rem;
a {
margin-top: 1.3rem;
border-radius: 1rem;
background-color: var(--color-grey);
padding: 0.35rem 1rem;
color: white;
text-decoration: none;
&:hover {
background-color: var(--color-primary);
color: white;
}
&:focus {
outline: 0.1rem var(--color-primary) solid;
color: var(--color-grey);
background-color: white;
}
}
}
}
.card {
border: 1px solid var(--color-border);
border-radius: 0.5rem;
padding: var(--space-s);
& img {
margin-block-end: var(--space-s);
border-radius: 0.25rem;
}
& h3 {
margin-block-start: 0;
}
}
</style>

View file

@ -1,55 +1,25 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<?= $pageContent ?>
</article>
<div class="list-intro">
<?= $pageContent ?>
</div>
<?php endif; ?>
<article>
<?php foreach ($items as $item): ?>
<article>
<?php if ($item['cover']): ?>
<a href="<?= htmlspecialchars($item['url']) ?>">
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="<?= htmlspecialchars($item['title']) ?>">
</a>
<?php endif; ?>
<h1>
<a href="<?= htmlspecialchars($item['url']) ?>">
<?= htmlspecialchars($item['title']) ?>
</a>
</h1>
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
<p><?= htmlspecialchars($item['date']) ?></p>
<?php endif; ?>
<?php if ($item['summary']): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
<a href="<?= htmlspecialchars($item['url']) ?>" class="button"><?= htmlspecialchars($translations['read_more'] ?? 'Read more') ?></a>
</article>
<?php endforeach; ?>
</article>
<style>
main > article {
> article {
background-color: white;
padding: 0;
padding-bottom: 1.3rem;
margin-bottom: 1.5rem;
overflow: hidden;
> :not(img, a) {
padding-left: 1rem;
padding-right: 1rem;
}
h1 {
margin-top: 1rem;
}
> .button {
margin-left: 1rem;
margin-top: 1rem;
}
}
}
</style>
<div class="list">
<?php foreach ($items as $item): ?>
<article class="list-item">
<?php if (!empty($item['cover'])): ?>
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
<?php endif; ?>
<h2><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h2>
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
<time><?= htmlspecialchars($item['date']) ?></time>
<?php endif; ?>
<?php if (!empty($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>

View file

@ -1,25 +1,3 @@
<?= $content ?>
<?php if ($pageMetadata && (isset($pageMetadata['tags']) || isset($pageMetadata['categories']))): ?>
<aside class="metadata">
<?php if (!empty($pageMetadata['categories'])): ?>
<div class="categories">
<strong><?= htmlspecialchars($translations['categories'] ?? 'Categories') ?>:</strong>
<?php
$categories = array_map('trim', explode(',', $pageMetadata['categories']));
echo implode(', ', array_map('htmlspecialchars', $categories));
?>
</div>
<?php endif; ?>
<?php if (!empty($pageMetadata['tags'])): ?>
<div class="tags">
<strong><?= htmlspecialchars($translations['tags'] ?? 'Tags') ?>:</strong>
<?php
$tags = array_map('trim', explode(',', $pageMetadata['tags']));
echo implode(', ', array_map('htmlspecialchars', $tags));
?>
</div>
<?php endif; ?>
</aside>
<?php endif; ?>
<article>
<?= $content ?>
</article>