feat: Hugo engine for iGaming affiliate template (layouts, assets, i18n, verified data, CI build script)

This commit is contained in:
pipeline
2026-07-16 06:07:22 +03:00
parent b9385a1f72
commit 1816ff2093
58 changed files with 5631 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode | default "en" }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="{{ .Site.Data.brand.primaryColor }}">
{{ partial "head-seo.html" . }}
{{/* Brand + locale data exposed to client-side scripts (age gate, locale switcher).
safeJS prevents html/template from escaping the JSON into a quoted string. */}}
<script>
window.__BRAND__ = {{ dict "name" .Site.Data.brand.name "defaultLocale" .Site.Data.brand.defaultLocale "ageRestrictionDefault" .Site.Data.brand.ageRestrictionDefault | jsonify | safeJS }};
window.__LOCALES__ = {{ .Site.Data.locales | jsonify | safeJS }};
</script>
{{/* Add the age-gate class as early as possible to avoid a flash for confirmed visitors. */}}
<script>
(function () {
try {
if (localStorage.getItem("ageConfirmed") !== "1") {
document.documentElement.classList.add("age-gate-active");
}
} catch (e) {
document.documentElement.classList.add("age-gate-active");
}
})();
</script>
{{/* CSS pipeline: inject brand vars (ExecuteAsTemplate) -> Tailwind/PostCSS ->
minify (production only) -> fingerprint. Hugo's [minify] does not minify
Pipes resources, so we minify explicitly for production builds. */}}
{{ $css := resources.Get "css/main.css" | resources.ExecuteAsTemplate "css/main.css" . | css.PostCSS }}
{{ if hugo.IsProduction }}{{ $css = $css | minify }}{{ end }}
{{ $css = $css | fingerprint "sha256" }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}" crossorigin="anonymous">
<link rel="icon" href="{{ "favicon.svg" | relURL }}" type="image/svg+xml">
</head>
<body class="flex min-h-screen flex-col bg-white">
<a href="#main" class="sr-only rounded bg-primary px-4 py-2 text-primary-contrast focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-[110]">{{ i18n "skipToContent" }}</a>
{{ partial "age-gate-banner.html" . }}
{{ partial "header.html" . }}
<main id="main" class="flex-1">
{{ block "main" . }}{{ end }}
</main>
{{ partial "footer.html" . }}
{{ partial "cookie-consent.html" . }}
{{/* App bundle (age gate, cookie consent, games filter). Loads BEFORE Alpine so the
gamesFilter component is registered on the alpine:init event. */}}
{{ $appJs := slice (resources.Get "js/age-gate.js") (resources.Get "js/cookie-consent.js") (resources.Get "js/games-filter.js") | resources.Concat "js/app.js" | minify | fingerprint "sha256" }}
<script defer src="{{ $appJs.RelPermalink }}" integrity="{{ $appJs.Data.Integrity }}" crossorigin="anonymous"></script>
{{/* Alpine.js — vendored first-party (assets/js/vendor/alpine.js, pinned 3.14.8,
refreshed from node_modules by `npm run build:js`) and fingerprinted so it
ships with Subresource Integrity like the other assets. Deferred and placed
AFTER the app bundle so components register on alpine:init before core runs. */}}
{{ $alpine := resources.Get "js/vendor/alpine.js" | fingerprint "sha256" }}
<script defer src="{{ $alpine.RelPermalink }}" integrity="{{ $alpine.Data.Integrity }}" crossorigin="anonymous"></script>
</body>
</html>
+25
View File
@@ -0,0 +1,25 @@
{{ define "main" }}
{{ partial "breadcrumbs.html" . }}
<div class="site-container section">
<header class="mb-8 max-w-3xl">
<h1 class="text-3xl font-extrabold tracking-tight sm:text-4xl">{{ .Params.h1 | default .Title }}</h1>
{{ with .Params.description }}<p class="mt-3 text-lg text-secondary/70">{{ . }}</p>{{ end }}
</header>
{{ with .Content }}<div class="prose-content max-w-3xl">{{ . }}</div>{{ end }}
{{ with .Pages }}
<div class="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{{- range . -}}
<a href="{{ .RelPermalink }}" class="card card-hover block no-underline">
<h2 class="text-lg font-bold">{{ .LinkTitle }}</h2>
<p class="mt-2 text-sm text-secondary/70">{{ .Params.description | default (.Summary | plainify) | truncate 120 }}</p>
</a>
{{- end -}}
</div>
{{ end }}
</div>
{{ partial "related-pages.html" . }}
{{ end }}
+70
View File
@@ -0,0 +1,70 @@
{{ define "main" }}
{{ partial "breadcrumbs.html" . }}
<article class="site-container section">
<header class="mb-8 max-w-3xl">
<h1 class="text-3xl font-extrabold tracking-tight sm:text-4xl">{{ .Params.h1 | default .Title }}</h1>
{{ with .Params.description }}<p class="mt-3 text-lg text-secondary/70">{{ . }}</p>{{ end }}
{{ if not .Lastmod.IsZero }}<p class="mt-2 text-xs text-secondary/70">Last updated {{ .Lastmod.Format "2 January 2006" }}</p>{{ end }}
</header>
{{ with .Content }}<div class="prose-content max-w-3xl">{{ . }}</div>{{ end }}
{{ if or .Params.pros .Params.cons }}
<section class="mt-12 max-w-3xl">
<h2 class="mb-4 text-2xl font-bold">Pros &amp; cons at a glance</h2>
{{ partial "pros-cons-table.html" . }}
</section>
{{ end }}
{{ if .Params.showRatingWidget }}
<section class="mt-12 max-w-3xl">
<h2 class="mb-4 text-2xl font-bold">Our verdict</h2>
{{ partial "rating-widget.html" . }}
</section>
{{ end }}
{{ if .Params.showKycByLocale }}
<section class="mt-12 max-w-4xl">
<h2 class="mb-3 text-2xl font-bold">Verification documents by region</h2>
<p class="mb-4 text-secondary/70">KYC requirements vary by market. Choose your region in the header to highlight your row; the full list is below.</p>
<div class="overflow-x-auto">
<table class="data-table">
<thead><tr><th scope="col">Region</th><th scope="col">Required documents (KYC)</th></tr></thead>
<tbody>
{{- range .Site.Data.locales -}}
<tr data-kyc-row data-locale="{{ .locale }}">
<th scope="row" class="font-medium">{{ .label | default .locale }}</th>
<td><ul class="list-disc space-y-1 pl-4">{{ range .requiredKycDocs }}<li>{{ . }}</li>{{ end }}</ul></td>
</tr>
{{- end -}}
</tbody>
</table>
</div>
</section>
{{ end }}
{{ if .Params.showFaqAccordion }}
<section class="mt-12">
<h2 class="mb-6 text-2xl font-bold">Frequently asked questions</h2>
{{ partial "faq-accordion.html" . }}
</section>
{{ end }}
{{ if .Params.responsibleGamblingSection }}
<section class="mt-12">
{{ partial "responsible-gambling-block.html" (dict "page" . "variant" "full") }}
</section>
{{ end }}
{{ if not .Params.hideCta }}
<div class="mt-12 max-w-3xl rounded-2xl border border-primary/20 bg-primary/5 p-6">
<h2 class="text-xl font-bold">Ready to get started?</h2>
<p class="mt-1 text-secondary/70"><span data-age-value>{{ .Site.Data.brand.ageRestrictionDefault }}</span>+ only. T&amp;Cs apply. Please gamble responsibly.</p>
<div class="mt-4">{{ partial "cta-button.html" (dict "page" . "text" "Visit Casino" "size" "lg") }}</div>
</div>
{{ end }}
</article>
{{ partial "related-pages.html" . }}
{{ end }}