Converti chill-theme en dossier classique

This commit is contained in:
Boris Waaub
2026-01-28 09:40:37 +01:00
parent 577786f22d
commit cc60b64505
118 changed files with 11868 additions and 1 deletions
@@ -0,0 +1,25 @@
{{ $title := .Get "title" }}
{{ $subtitle := .Get "subtitle" }}
<div class="text-center mb-16">
<h2 class="text-3xl font-bold mb-4">{{ $title }}</h2>
<p class="text-xl text-gray-600">{{ $subtitle }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
{{ range $index := (seq 1 10) }}
{{ $benefit := printf "benefit%d" $index }}
{{ with $.Get $benefit }}
{{ $parts := split . "|" }}
{{ if ge (len $parts) 4 }}
<div class="p-6 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow">
<div class="w-12 h-12 rounded-lg flex items-center justify-center mb-4" style="background-color: {{ index $parts 1 }}15;">
{{ partial "icons" (dict "name" (index $parts 0) "color" (index $parts 1) "size" "6") }}
</div>
<h3 class="text-xl font-bold mb-2">{{ index $parts 2 }}</h3>
<p class="text-gray-600">{{ index $parts 3 }}</p>
</div>
{{ end }}
{{ end }}
{{ end }}
</div>
@@ -0,0 +1,15 @@
<div class="not-prose">
<div class="bg-white rounded-2xl shadow-xl overflow-hidden">
<div class="grid grid-cols-1 md:grid-cols-2">
<div class="p-8 md:p-12">
<div class="inline-block px-4 py-2 rounded-full text-sm font-semibold mb-6" style="background-color: {{ .Get 0 }}15; color: {{ .Get 0 }};">{{ .Get 1 }}</div>
<div class="text-3xl font-bold mb-4 !mt-0">{{ .Get 2 }}</div>
<div class="text-gray-600 mb-6">{{ .Get 3 }}</div>
<a href="{{ .Get 5 }}" class="inline-block px-6 py-3 text-white font-bold rounded-lg transition-colors hover:opacity-90 !no-underline !text-white" style="background-color: {{ .Get 0 }};">{{ .Get 4 }}</a>
</div>
<div class="p-8 md:p-12 flex items-center justify-center" style="background: linear-gradient(135deg, {{ .Get 0 }}03, {{ .Get 0 }}30);">
<img src="{{ .Get 6 }}" alt="{{ .Get 7 }}" class="max-w-[200px] !my-0 !rounded-none !shadow-none">
</div>
</div>
</div>
</div>
@@ -0,0 +1,82 @@
<section class="border-y border-gray-100 overflow-hidden">
<div class="container mx-auto">
<div class="py-12">
<p class="text-center text-3xl md:text-2xl font-bold mb-6">{{ .Get "title" | default (i18n "trustedByCompanies") }}</p>
<div class="logo-scroll">
<div class="logos-slide{{ if ne (.Get "animate" | default "true") "false" }} animate{{ end }}">
{{ range .Page.Params.client_logos }}
<img src="{{ .logo | relURL }}" class="h-20" alt="{{ .name }}" />
{{ end }}
{{ if ne (.Get "animate" | default "true") "false" }}
{{ range .Page.Params.client_logos }}
<img src="{{ .logo | relURL }}" class="h-20" alt="{{ .name }}" />
{{ end }}
{{ end }}
</div>
</div>
</div>
</div>
</section>
<style>
.logo-scroll {
overflow: hidden;
padding: 20px 0;
white-space: nowrap;
position: relative;
}
.logo-scroll.static {
display: flex;
justify-content: center;
overflow: visible;
}
.logo-scroll.static .logos-slide {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 40px;
}
.logos-slide {
display: inline-block;
}
.logos-slide.animate {
animation: 30s slide infinite linear;
}
.logos-slide img {
max-height: 1.5rem;
margin: 0 40px;
display: inline-block;
vertical-align: middle;
filter: grayscale(100%);
opacity: 0.6;
}
.logo-scroll.static .logos-slide img {
margin: 0;
}
@keyframes slide {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(-100% / 2));
}
}
.logo-scroll:hover .logos-slide.animate {
animation-play-state: paused;
}
/* Mobile-specific animation speed */
@media (max-width: 768px) {
.logos-slide.animate {
animation: 15s slide infinite linear;
}
}
</style>
@@ -0,0 +1,19 @@
{{ $lang := .Get 0 }}
{{ $code := .Inner }}
{{ $filename := .Get 1 }}
<div class="not-prose my-8 overflow-hidden rounded-lg bg-gray-900 shadow-lg">
{{ with $filename }}
<div class="flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700">
<div class="text-sm text-gray-200 font-mono">{{ . }}</div>
<div class="flex space-x-2">
<div class="w-3 h-3 rounded-full bg-red-500"></div>
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
<div class="w-3 h-3 rounded-full bg-green-500"></div>
</div>
</div>
{{ end }}
<div class="p-4 overflow-x-auto">
{{ highlight $code $lang "linenos=table,linenostart=1,hl_lines=,lineanchors=line" }}
</div>
</div>
@@ -0,0 +1,17 @@
{{ $params := dict
"title" (.Get "title")
"description" (.Get "description")
"primary_button" (dict
"text" (.Get "primary_button_text")
"url" (.Get "primary_button_url")
)
"secondary_button" (dict
"text" (.Get "secondary_button_text")
"url" (.Get "secondary_button_url")
)
"gradient_from" (.Get "gradient-from")
"gradient_to" (.Get "gradient-to")
"gradient_angle" (.Get "gradient-angle")
}}
{{ partial "components/cta" (dict "Site" .Site "Params" (dict "cta" $params "enable" true)) }}
@@ -0,0 +1,31 @@
{{ $data := .Inner | transform.Unmarshal }}
<section class="bg-white">
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
<div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900">{{ $data.title | default "Frequently Asked Questions" }}</h2>
{{ with $data.description }}
<p class="mb-5 font-light text-gray-500 sm:text-xl">{{ . }}</p>
{{ end }}
</div>
<div class="space-y-6">
{{ range $data.questions }}
<div class="border rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-md transition-shadow duration-200">
<button class="w-full flex justify-between items-center p-6 text-left hover:bg-gray-50 transition-colors duration-200 focus:outline-none"
onclick="this.parentElement.querySelector('.faq-content').classList.toggle('hidden');
this.querySelector('svg').classList.toggle('rotate-180')">
<span class="text-lg font-medium text-gray-900">{{ .question }}</span>
<svg class="w-5 h-5 text-gray-500 transform transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="faq-content hidden border-t">
<div class="p-6 prose prose-sm sm:prose lg:prose-lg max-w-none">
{{ .answer | markdownify }}
</div>
</div>
</div>
{{ end }}
</div>
</div>
</section>
@@ -0,0 +1,51 @@
{{/* Feature Shortcode */}}
{{ $title := .Get "title" }}
{{ $description := .Get "description" }}
{{ $badge := .Get "badge" }}
{{ $badgeColor := .Get "badgeColor" | default "#5573df" }}
{{ $image := .Get "image" }}
{{ $buttonText := .Get "buttonText" | default "Learn More" }}
{{ $buttonLink := .Get "buttonLink" | default "#" }}
{{ $imagePosition := .Get "imagePosition" | default "right" }}
{{ $features := split (.Get "features") "," }}
<div class="grid lg:grid-cols-2 gap-12 items-center" style="--badge-color: {{ $badgeColor }}">
{{ if eq $imagePosition "left" }}
<div class="order-2 lg:order-1">
<img src="{{ $image | relURL }}" alt="{{ $title }}" class="rounded-xl shadow-elevation">
</div>
{{ end }}
<div class="space-y-6 {{ if eq $imagePosition "left" }}order-1 lg:order-2{{ end }}">
<style>
.badge {
background-color: color-mix(in srgb, var(--badge-color) 10%, transparent);
color: var(--badge-color);
}
.badge-icon {
color: var(--badge-color);
}
</style>
<div class="badge inline-block px-4 py-2 rounded-full font-medium">{{ $badge }}</div>
<h3 class="text-2xl md:text-3xl font-bold">{{ $title }}</h3>
<p class="text-lg text-gray-600">{{ $description }}</p>
<ul class="space-y-4">
{{ range $features }}
<li class="flex items-center space-x-3">
<svg class="badge-icon w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>{{ . | default "" }}</span>
</li>
{{ end }}
</ul>
<a href="{{ $buttonLink }}" class="btn-primary inline-block">{{ $buttonText }}</a>
</div>
{{ if ne $imagePosition "left" }}
<div>
<img src="{{ $image | relURL }}" alt="{{ $title }}" class="rounded-xl shadow-elevation">
</div>
{{ end }}
</div>
@@ -0,0 +1,81 @@
{{/*
Usage :
{{< features-carousel title="Vos fonctionnalités clés" >}}
{
"features": [
{
"title": "Performance",
"titleBtn": "Performance",
"description": "Leverage Hugo's blazing-fast build times and optimized output.",
"badge": "Performance",
"badgeColor": "#2563eb",
"image": "/images/feature-1.svg",
"buttonText": "Learn More",
"buttonLink": "/features/performance/",
"features": "Sub-second page loads,Optimized assets,Minimal JavaScript,CDN-ready output",
"imagePosition": "right"
},
]
}
{{< /features-carousel >}}
*/}}
{{ $title := .Get "title" | default "" }}
{{ $data := .Inner | transform.Unmarshal }}
{{ $background_color := .Get "background-color" }}
<section id="features-carousel" class="section{{ if not $background_color }} bg-gray-50{{ end }}" {{ if $background_color }}style="background-color:{{ $background_color }};"{{ end }}>
<div class="container">
{{ if $title }}
<h1 class="text-3xl font-bold mb-4 text-center">{{ $title }}</h1>
{{ end }}
<div class="features-carousel__nav"></div>
<div class="features-carousel__slides">
{{ range $i, $f := $data.features }}
<div class="feature" data-title-btn="{{ $f.titleBtn }}">
{{ partial "shortcodes/feature.html" (dict
"title" $f.title
"titleBtn" $f.titleBtn
"description" $f.description
"badge" $f.badge
"badgeColor" $f.badgeColor
"image" $f.image
"buttonText" $f.buttonText
"buttonLink" $f.buttonLink
"features" $f.features
"imagePosition" $f.imagePosition
) }}
</div>
{{ end }}
</div>
</div>
</section>
<script>
(function() {
const carousel = document.getElementById('features-carousel');
if (!carousel) return;
const slides = carousel.querySelectorAll('.feature');
const nav = carousel.querySelector('.features-carousel__nav');
let current = 0;
slides.forEach((slide, i) => {
const btn = document.createElement('button');
btn.innerText = slide.dataset.titleBtn || `Feature ${i+1}`;
btn.onclick = () => {
current = i;
showSlide(current);
};
nav.appendChild(btn);
});
function showSlide(idx) {
slides.forEach((slide, i) => {
slide.style.display = i === idx ? 'block' : 'none';
});
Array.from(nav.children).forEach((btn, i) => {
btn.classList.toggle('active', i === idx);
});
}
showSlide(current);
})();
</script>
@@ -0,0 +1,26 @@
{{ $title := .Get "title" }}
{{ $color := .Get "color" | default "#2563eb" }}
<div class="max-w-3xl mx-auto">
<h2 class="text-3xl font-bold text-center !mb-24">{{ $title }}</h2>
<div class="space-y-12">
{{ range $index := (seq 1 10) }}
{{ $feature := printf "feature%d" $index }}
{{ with $.Get $feature }}
{{ $parts := split . "|" }}
{{ if ge (len $parts) 2 }}
<div class="flex gap-6">
<div class="w-10 h-10 rounded-xl flex items-center justify-center flex-shrink-0" style="background-color: {{ $color }}15;">
{{ partial "icons" (dict "name" "check" "color" $color "size" "6") }}
</div>
<div>
<h3 class="text-2xl font-bold mb-3 !mt-0">{{ index $parts 0 }}</h3>
<p class="text-gray-600 text-lg leading-relaxed">{{ index $parts 1 }}</p>
</div>
</div>
{{ end }}
{{ end }}
{{ end }}
</div>
</div>
@@ -0,0 +1,17 @@
{{/* Features Section Wrapper Shortcode */}}
{{ $title := .Get "title" | default "Powerful Features for Modern Teams" }}
{{ $description := .Get "description" | default "Discover how our platform helps you understand and optimize every aspect of your user experience." }}
<section class="section">
<div class="container">
<div class="text-center max-w-3xl mx-auto mb-16">
<h2 class="text-3xl md:text-4xl font-bold mb-6">{{ $title }}</h2>
<p class="text-xl text-gray-600">{{ $description }}</p>
</div>
<!-- Feature Grid -->
<div class="space-y-32">
{{ .Inner }}
</div>
</div>
</section>
@@ -0,0 +1,18 @@
{{ $src := .Get "src" }}
{{ $alt := .Get "alt" }}
{{ $caption := .Get "caption" }}
{{ $class := .Get "class" | default "w-full" }}
<figure class="my-8">
<img
src="{{ $src }}"
alt="{{ $alt }}"
class="{{ $class }} rounded-lg shadow-lg"
loading="lazy"
>
{{ with $caption }}
<figcaption class="mt-2 text-center text-sm text-gray-600">
{{ . | markdownify }}
</figcaption>
{{ end }}
</figure>
@@ -0,0 +1,3 @@
<div class="not-prose">
<img src="{{ .Get 0 }}" alt="{{ .Get 1 }}" class="w-full no-prose-img">
</div>
@@ -0,0 +1,51 @@
{{ $headline := .Get "headline" }}
{{ $sub_headline := .Get "sub_headline" }}
{{ $primary_button_text := .Get "primary_button_text" }}
{{ $primary_button_url := .Get "primary_button_url" }}
{{ $secondary_button_text := .Get "secondary_button_text" }}
{{ $secondary_button_url := .Get "secondary_button_url" }}
{{ $hero_image := .Get "hero_image" }}
{{ $background_image := .Get "background_image" }}
{{ $custom_class := .Get "custom_class" }}
<section class="relative overflow-hidden {{ $custom_class }}" >
{{ if $background_image }}
<div class="absolute inset-0">
<img src="{{ $background_image | relURL }}" alt="Background" class="w-full h-full object-cover">
</div>
{{ end }}
<div class="container pt-16 pb-20 md:pt-24 md:pb-28">
<div class="grid lg:grid-cols-2 gap-12 items-center">
<div class="space-y-8">
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight">
{{ $headline | safeHTML }}
</h1>
<p class="text-xl text-gray-200">
{{ $sub_headline | safeHTML }}
</p>
<div class="flex flex-col sm:flex-row gap-4">
{{ if and $primary_button_text $primary_button_url }}
<a href="{{ $primary_button_url }}" class="btn-primary text-center">
{{ $primary_button_text }}
</a>
{{ end }}
{{ if and $secondary_button_text $secondary_button_url }}
<a href="{{ $secondary_button_url }}" class="btn-outline text-center">
{{ $secondary_button_text }}
</a>
{{ end }}
</div>
</div>
<div class="relative">
{{ if $hero_image }}
<div class="relative z-10">
<img src="{{ $hero_image | relURL }}" alt="Hero Image" class="rounded-xl shadow-elevation">
</div>
{{ end }}
<!-- Background decoration -->
<div class="absolute -top-20 -right-20 w-64 h-64 bg-primary-100 rounded-full filter blur-3xl opacity-50"></div>
<div class="absolute -bottom-20 -left-20 w-64 h-64 bg-secondary-100 rounded-full filter blur-3xl opacity-50"></div>
</div>
</div>
</div>
</section>
@@ -0,0 +1,7 @@
<div class="investor-logo p-6 flex items-center justify-center">
{{ if .Get "image" }}
<img src="{{ .Get "image" }}" alt="{{ .Get "name" }}" class="max-h-12 grayscale hover:grayscale-0 transition-all duration-300">
{{ else }}
<div class="text-xl font-bold text-gray-400">{{ .Get "name" }}</div>
{{ end }}
</div>
@@ -0,0 +1,63 @@
{{ $json := .Inner | transform.Unmarshal }}
<section class="py-16 bg-white">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
{{ with $json.title }}
<div class="text-center">
<h2 class="text-3xl font-bold text-gray-900">{{ . }}</h2>
{{ with $json.description }}
<p class="mt-4 text-xl text-gray-600">{{ . }}</p>
{{ end }}
</div>
{{ end }}
<div class="mt-16">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
{{ range $json.plans }}
<div class="w-full">
<div class="h-full flex flex-col rounded-2xl border border-gray-200 bg-white shadow-sm hover:shadow-lg transition-shadow duration-300 relative">
{{ if .featured }}
<div class="absolute top-4 right-4 inline-block rounded-full bg-blue-100 px-4 py-1 text-sm font-semibold text-blue-800">
{{ i18n "popular" }}
</div>
{{ end }}
<div class="p-8 flex flex-col flex-grow">
<div class="mb-6">
<h3 class="text-2xl font-bold text-gray-900">{{ .name }}</h3>
<p class="mt-2 text-gray-500">{{ .description }}</p>
</div>
<div class="mb-8">
<p class="flex items-baseline">
<span class="text-4xl font-bold tracking-tight text-gray-900">{{ .price }}</span>
<span class="ml-2 text-gray-500">{{ i18n "perMonth" }}</span>
</p>
<p class="flex items-baseline">
<span class="font-bold tracking-tight text-gray-900">{{ .additional_price }}</span>
<span class="ml-2 text-gray-500">{{ i18n "perMonth" }}</span>
</p>
</div>
<ul class="space-y-4 text-gray-600 flex-grow">
{{ range .features }}
<li class="flex items-start">
<svg class="h-6 w-6 flex-shrink-0 text-blue-500" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/>
</svg>
<span class="ml-3">{{ . }}</span>
</li>
{{ end }}
</ul>
{{ with .additional_description }}
<div class="mt-4 text-sm text-gray-500">{{ . }}</div>
{{ end }}
<div class="mt-8">
<a href="{{ .button.url }}" class="btn btn-primary w-full">
{{ .button.text }}
</a>
</div>
</div>
</div>
</div>
{{ end }}
</div>
</div>
</div>
</section>
@@ -0,0 +1,60 @@
{{ $data := .Inner | unmarshal }}
<section class="bg-white">
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
{{ with $data.title }}
<div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900">{{ . }}</h2>
{{ with $data.description }}
<p class="mb-5 font-light text-gray-500 sm:text-xl">{{ . }}</p>
{{ end }}
</div>
{{ end }}
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
{{ range $index, $plan := $data.plans }}
<div class="relative flex flex-col p-6 {{ if $plan.featured }}bg-primary-600 text-white{{ else }}bg-white{{ end }} rounded-2xl shadow-xl transform hover:-translate-y-1 transition duration-300">
{{ if $plan.featured }}
<div class="absolute -top-4 left-1/2 transform -translate-x-1/2">
<span class="bg-yellow-400 text-gray-900 text-xs font-semibold px-4 py-1 rounded-full">{{ i18n "mostPopular" }}</span>
</div>
{{ end }}
<div class="mb-4">
<h3 class="text-2xl font-bold {{ if $plan.featured }}text-white{{ else }}text-gray-900{{ end }}">{{ $plan.name }}</h3>
<p class="mt-2 {{ if not $plan.featured }}text-gray-500{{ end }}">{{ $plan.description }}</p>
</div>
{{ if $plan.price }}
<div class="mb-6">
<div class="flex items-baseline">
<span class="text-5xl font-extrabold tracking-tight {{ if not $plan.featured }}text-gray-900{{ end }}">{{ $plan.price }}</span>
<span class="ml-1 {{ if not $plan.featured }}text-gray-500{{ end }}">{{ i18n $plan.price_unit }}</span>
</div>
<div class="flex items-baseline">
<span class="font-extrabold tracking-tight {{ if not $plan.featured }}text-gray-900{{ end }}">{{ $plan.additional_price }}</span>
</div>
</div>
{{ end }}
<ul class="mb-8 space-y-4 flex-grow">
{{ range $plan.features }}
<li class="flex items-center">
<svg class="w-5 h-5 {{ if $plan.featured }}text-white{{ else }}text-green-500{{ end }} mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="{{ if not $plan.featured }}text-gray-600{{ end }}">{{ . }}</span>
</li>
{{ end }}
</ul>
{{ with .additional_description }}
<div class="text-sm text-gray-500">{{ . }}</div>
{{ end }}
<a href="{{ $plan.button.url }}" class="mt-4 text-center w-full px-5 py-3 rounded-lg {{ if $plan.featured }}bg-white text-primary-600 hover:bg-gray-100{{ else }}bg-primary-600 text-white hover:bg-primary-700{{ end }} font-medium transition duration-300">
{{ $plan.button.text }}
</a>
</div>
{{ end }}
</div>
</div>
</section>
@@ -0,0 +1,5 @@
<section class="{{ .Get "class" }}">
<div class="container mx-auto px-4">
{{ .Inner }}
</div>
</section>
@@ -0,0 +1,4 @@
<div class="stat-card">
<div class="text-3xl md:text-4xl font-bold text-blue-600 mb-2">{{ .Get "number" }}</div>
<div class="text-gray-600 font-medium">{{ .Get "label" }}</div>
</div>
@@ -0,0 +1,22 @@
<div class="team-member text-center">
<div class="relative mb-4 rounded-lg overflow-hidden aspect-square">
{{ if .Get "image" }}
<img src="{{ .Get "image" }}" alt="{{ .Get "name" }}" class="w-full h-full object-cover">
{{ else }}
<div class="w-full h-full bg-gray-200 flex items-center justify-center">
<svg class="w-20 h-20 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 14c3.31 0 6-2.69 6-6s-2.69-6-6-6-6 2.69-6 6 2.69 6 6 6zm0 2c-4 0-12 2-12 6v2h24v-2c0-4-8-6-12-6z"/>
</svg>
</div>
{{ end }}
{{ if .Get "linkedin" }}
<a href="{{ .Get "linkedin" }}" target="_blank" rel="noopener" class="absolute bottom-2 right-2 bg-white rounded-full p-2 shadow-md hover:shadow-lg transition-shadow">
<svg class="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/>
</svg>
</a>
{{ end }}
</div>
<h3 class="text-xl font-bold mb-1">{{ .Get "name" }}</h3>
<p class="text-gray-600">{{ .Get "title" }}</p>
</div>
@@ -0,0 +1,183 @@
{{ $background_color := .Get "background-color" }}
<section class="section{{ if not $background_color }} bg-gray-50{{ end }}" {{ if $background_color }}style="background-color:{{ $background_color }};"{{ end }}>
<div class="container">
<div class="text-center max-w-3xl mx-auto mb-16">
<h2 class="text-3xl md:text-4xl font-bold mb-6">{{ .Get "title" | default (i18n "lovedByTeams") }}</h2>
<p class="text-xl text-gray-600">{{ .Get "description" | default (i18n "testimonialsDescription") }}</p>
</div>
<div class="testimonials-container overflow-hidden">
<div class="testimonials-track{{ if ne (.Get "animate" | default "true") "false" }} animate{{ end }}">
{{ range .Page.Params.testimonials }}
<div class="testimonial-card">
<div class="flex items-center space-x-4 mb-6">
<img src="{{ .avatar | relURL }}" alt="{{ .name }}" class="w-12 h-12 rounded-full">
<div>
<h4 class="font-bold">{{ .name }}</h4>
<p class="text-gray-600">{{ .title }}</p>
</div>
</div>
<p class="text-gray-600">{{ .quote }}</p>
</div>
{{ end }}
{{ if ne (.Get "animate" | default "true") "false" }}
{{ range .Page.Params.testimonials }}
<div class="testimonial-card" aria-hidden="true">
<div class="flex items-center space-x-4 mb-6">
<img src="{{ .avatar | relURL }}" alt="{{ .name }}" class="w-12 h-12 rounded-full">
<div>
<h4 class="font-bold">{{ .name }}</h4>
<p class="text-gray-600">{{ .title }}</p>
</div>
</div>
<p class="text-gray-600">{{ .quote }}</p>
</div>
{{ end }}
{{ end }}
</div>
</div>
</div>
</section>
{{ if ne (.Get "animate" | default "true") "false" }}
<script>
(function() {
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTestimonialsScroll);
} else {
initTestimonialsScroll();
}
function initTestimonialsScroll() {
const tracks = document.querySelectorAll('.testimonials-track.animate');
tracks.forEach(track => {
// Remove CSS animation and use JS for smoother control
track.style.animation = 'none';
const container = track.parentElement;
// Calculate the width of one set of testimonials (50% of track)
const trackWidth = track.scrollWidth;
const halfWidth = trackWidth / 2;
// Ensure track has valid dimensions before animating
if (trackWidth === 0 || halfWidth === 0) {
return;
}
let position = 0; // Current position in pixels
let isPaused = false;
let lastTime = performance.now();
// Check for mobile and set duration - responsive to window resize
function getDuration() {
return window.innerWidth <= 768 ? 15000 : 40000;
}
// Speed in pixels per millisecond (recalculated on resize)
let speed = halfWidth / getDuration();
function animate(currentTime) {
if (!isPaused) {
const delta = currentTime - lastTime;
position += speed * delta;
// Reset position seamlessly when we've moved one full set
if (position >= halfWidth) {
position = position - halfWidth;
}
track.style.transform = `translateX(-${position}px)`;
}
lastTime = currentTime;
requestAnimationFrame(animate);
}
// Start animation
requestAnimationFrame(animate);
// Pause on hover
container.addEventListener('mouseenter', () => {
isPaused = true;
});
container.addEventListener('mouseleave', () => {
isPaused = false;
lastTime = performance.now(); // Reset lastTime to avoid jump
});
// Handle window resize to adjust speed
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
speed = halfWidth / getDuration();
}, 250); // Debounce resize events
});
});
}
})();
</script>
{{ end }}
<style>
.testimonials-container {
padding: 20px 0;
position: relative;
}
.testimonials-container.static {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
overflow: visible;
}
.testimonials-track {
display: flex;
gap: 2rem;
width: fit-content;
}
.testimonials-track.animate {
animation: 40s testimonials-scroll infinite linear;
will-change: transform;
}
.testimonial-card {
flex: 0 0 auto;
width: 300px;
background: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.testimonials-container.static .testimonial-card {
width: 100%;
}
@keyframes testimonials-scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
.testimonials-container:hover .testimonials-track.animate {
animation-play-state: paused;
}
/* Mobile-specific animation speed */
@media (max-width: 768px) {
.testimonials-track.animate {
animation: 15s testimonials-scroll infinite linear;
will-change: transform;
}
}
</style>
@@ -0,0 +1,4 @@
<div class="bg-gray-50 p-6 rounded-lg mb-8">
<h2 class="text-xl font-semibold mb-4">{{ i18n "tableOfContents" }}</h2>
{{ .Page.TableOfContents }}
</div>
@@ -0,0 +1,21 @@
<div class="value-card bg-white p-8 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div class="mb-4">
{{ $icon := .Get "icon" }}
{{ if eq $icon "lightbulb" }}
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
</svg>
{{ else if eq $icon "users" }}
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg>
{{ else if eq $icon "eye" }}
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
{{ end }}
</div>
<h3 class="text-xl font-bold mb-2">{{ .Get "title" }}</h3>
<p class="text-gray-600">{{ .Get "description" }}</p>
</div>