mirror of
https://github.com/qdrant/landing_page.git
synced 2026-10-03 18:08:30 +02:00
New homepage (#2172)
* two new sections * enterprise capabilities and enterprise ready sections * sections: engineered-for-builders and performance-by-design * sections: cta-banner, integrations-logos, solutions # Conflicts: # qdrant-landing/themes/qdrant-2024/assets/css/partials/_cta-banner.scss # qdrant-landing/themes/qdrant-2024/layouts/partials/cta-banner.html * remove old sections from index.html * wip: home page * reuse links, carousel width * globe and globe lights * globe lights * finalyzing the globe, add customers icons * new icons set + svg partial update * upd icons in the enterprise-ready section * lines background * globe lights as gradient and svg * fix carousel responsiveness * fix cut box shadow * accordion slider responsiveness * new hero * hero customers * some hero responsiveness * customers layout fix * cards responsiveness * fixes * remove floating github link * hero - bigger screens * refactor css * more hero responsiveness * fix * hero parallax * replace svg as img with embedded svg logos * fix marquee * slider's tabs responsiveness * remove horizontal scroll on the mobile * moved images * images optim * moved paths to markdown * svg -> png * some refactoring and clean up * moved scss * svg to png * more images optimization and cleaning * optimizing png * fix globe cards height * add reveal effect * improve reveal effect * fix for the hero animation in Safari * removed type:page preventing use-cases page from rendering with newer version of Hugo * restore static/img/home/reranking.webp * remove unused files * formatting * hero and reveal fixes * fixes * update cta banner * links and content fix * fixes: content, links, web-ui illustration * canva testimonial * upd link * fix * replace links * slider links
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
{{ with (.Site.GetPage "/headless/main/core-features") }}
|
||||
<section class="core-features">
|
||||
<div class="container">
|
||||
<div class="row gy-4">
|
||||
<div class="col-12">
|
||||
<h2 class="core-features__title mx-auto">{{ .Params.title }}</h2>
|
||||
<p class="core-features__subtitle mx-auto">{{ .Params.description }}</p>
|
||||
<div class="text-start text-lg-center">
|
||||
<a class="link link_xl" href="{{ .Params.link.url }}">{{ .Params.link.text }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<a href="{{ .Params.cloudFeature.link.url }}" class="card core-features__cloud-native">
|
||||
<div>
|
||||
<h4 class="core-features__card-title">{{ .Params.cloudFeature.title }}</h4>
|
||||
<p class="core-features__card-content">{{ .Params.cloudFeature.content }}</p>
|
||||
<span class="link link_dark link_md">
|
||||
{{ .Params.cloudFeature.link.text }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="core-features__cloud-native-image"></div>
|
||||
</a>
|
||||
</div>
|
||||
{{ range .Params.featureCards }}
|
||||
<div class="col-12 col-xl-4">
|
||||
<a href="{{ .link.url }}" class="card core-features__card">
|
||||
<div>
|
||||
{{ .icon | safeHTML }}
|
||||
<h6 class="core-features__card-title">{{ .title }}</h6>
|
||||
<p class="core-features__card-content">{{ .content }}</p>
|
||||
</div>
|
||||
<span class="link link_dark link_md align-self-start"> {{ .link.text }} </span>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -1,9 +1,9 @@
|
||||
{{ $background := default (dict "src" "/img/cta-banner-stars.png" "webp" "/img/cta-banner-stars.webp") .Params.background }}
|
||||
{{ $illustration := default (dict "src" "/img/cta-illustration.png" "alt" "Rocket flying over globe illustration") .Params.illustration }}
|
||||
<section class="cta-banner">
|
||||
<div class="container">
|
||||
<div class="cta-banner__banner">
|
||||
{{ if .Params.background }}
|
||||
<div class="cta-banner__background" style="background-image: url('{{ .Params.background.src }}');"></div>
|
||||
{{ end }}
|
||||
<div class="cta-banner__background" style="background-image: url('{{ $background.src }}');{{ if $background.webp }} background-image: image-set(url('{{ $background.webp }}') type('image/webp'), url('{{ $background.src }}') type('image/png'));{{ end }}"></div>
|
||||
<div class="cta-banner__content">
|
||||
<div class="cta-banner__text">
|
||||
<h2 class="cta-banner__title">{{ .Params.title }}</h2>
|
||||
@@ -14,11 +14,9 @@
|
||||
{{ .Params.button.text }}
|
||||
</a>
|
||||
</div>
|
||||
{{ if .Params.illustration }}
|
||||
<div class="cta-banner__illustration">
|
||||
<img src="{{ .Params.illustration.src }}" alt="{{ .Params.illustration.alt }}" />
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="cta-banner__illustration">
|
||||
{{ partial "picture" (dict "src" $illustration.src "alt" $illustration.alt "loading" "lazy" "decoding" "async") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{{ with (.Site.GetPage "/headless/main/customers") }}
|
||||
<section class="customers">
|
||||
{{ $context := slice }}
|
||||
{{ range .Params.customers }}
|
||||
{{ $context = $context | append (dict "svg" .logo "name" .name) }}
|
||||
{{ end }}
|
||||
{{ partial "marquee.html" (dict "context" $context "marqueeClass" "customers__logos") }}
|
||||
|
||||
|
||||
<div class="customers__description">
|
||||
<span class="customers__title">{{ .Params.customerStories.text }}</span>
|
||||
{{ if .Params.customerStories.url }}
|
||||
<a class="link link_xl" href="{{ .Params.customerStories.url }}"> {{ .Params.customerStories.textLink }} </a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -29,6 +29,17 @@
|
||||
|
||||
{{ partial "css" . }}
|
||||
|
||||
{{ if .IsHome }}
|
||||
{{ with .Site.GetPage "/headless/main/hero" }}
|
||||
{{ with .Params.astronauts }}
|
||||
<link rel="preload" as="image" type="image/webp" fetchpriority="high"
|
||||
imagesrcset="{{ .webpSrcset }}"
|
||||
imagesizes="100vw"
|
||||
/>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ hugo.Generator }}
|
||||
|
||||
{{ with .OutputFormats.Get "RSS" }}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
{{ with (.Site.GetPage "/headless/main/hero") }}
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="hero__content">
|
||||
<h1 class="hero__title">{{ .Params.title }}</h1>
|
||||
<p class="hero__subtitle">{{ .Params.subtitle }}</p>
|
||||
<div class="hero__buttons">
|
||||
<a data-metric-loc="hero" href="{{ .Params.startFree.url }}" class="button button_contained button_lg"
|
||||
>{{ .Params.startFree.text }}</a
|
||||
>
|
||||
<a data-metric-loc="hero" href="{{ .Params.learnMore.url }}" class="button button_outlined button_lg"
|
||||
>{{ .Params.learnMore.text }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ .Params.githubStars.actionUrl }}" type="button" class="hero__stars" target="_blank">
|
||||
{{ .Params.githubStars.logo | safeHTML }}
|
||||
<span class="hero__stars-text">{{ .Params.githubStars.actionText }}</span>
|
||||
<span class="hero__stars-count">
|
||||
{{ .Params.githubStars.starIcon | safeHTML }}
|
||||
{{ with (.Site.GetPage "/headless/stats") }}
|
||||
{{ .Params.stats.githubStars }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hero__image">
|
||||
<picture>
|
||||
{{ range .Params.heroImageSources }}
|
||||
<source
|
||||
{{ if
|
||||
.minWidth
|
||||
}}
|
||||
media="(min-width: {{ .minWidth }})"
|
||||
{{ end }}
|
||||
srcset="{{ .srcset }}"
|
||||
type="{{ .type }}"
|
||||
/>
|
||||
{{ end }}
|
||||
<img src="{{ .Params.fallbackHeroImage.src }}" alt="{{ .Params.fallbackHeroImage.alt }}" draggable="false" />
|
||||
</picture>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,152 @@
|
||||
{{ with (.Site.GetPage "/headless/main/case-studies") }}
|
||||
<section class="case-studies">
|
||||
<div class="container case-studies__container">
|
||||
<div class="case-studies__header">
|
||||
<h2 class="case-studies__title">{{ .Params.title }}</h2>
|
||||
<div class="case-studies__controls">
|
||||
<button type="button" class="case-studies__arrow case-studies__arrow--prev" aria-label="Previous slide">
|
||||
<svg width="24" height="14" viewBox="0 0 17 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.5 1L1.5 5L5.5 9M1.5 5H16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" class="case-studies__arrow case-studies__arrow--next" aria-label="Next slide">
|
||||
<svg width="24" height="14" viewBox="0 0 17 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5 1L15.5 5M15.5 5L11.5 9M15.5 5H1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="case-studies__carousel-outer">
|
||||
<div class="case-studies__carousel">
|
||||
<div class="case-studies__track">
|
||||
{{ range .Params.caseStudies }}
|
||||
<div class="case-studies__slide">
|
||||
{{ if .link }}<a href="{{ .link }}" class="case-studies__card-link">{{ end }}
|
||||
<div class="case-studies__card">
|
||||
<p class="case-studies__card-text">
|
||||
{{ .text }}
|
||||
{{ if .highlight }}
|
||||
<span class="case-studies__phrase{{ if eq (trim .textAfter " ") "." }} case-studies__phrase--no-break{{ end }}"><span class="case-studies__highlight">{{ .highlight }}</span>{{ if ne (trim .textAfter " ") "." }} {{ end }}{{ .textAfter }}</span>
|
||||
{{ end }}
|
||||
{{ if and (not .highlight) .textAfter }}{{ .textAfter }}{{ end }}
|
||||
{{ if .highlight2 }}
|
||||
<span class="case-studies__phrase{{ if eq (trim .textAfter2 " ") "." }} case-studies__phrase--no-break{{ end }}"><span class="case-studies__highlight">{{ .highlight2 }}</span>{{ if ne (trim .textAfter2 " ") "." }} {{ end }}{{ .textAfter2 }}</span>
|
||||
{{ end }}
|
||||
{{ if and (not .highlight2) .textAfter2 }}{{ .textAfter2 }}{{ end }}
|
||||
</p>
|
||||
<div class="case-studies__card-footer">
|
||||
<div class="case-studies__card-logo" role="img" aria-label="{{ .logoAlt }}">
|
||||
{{ partial "svg" (dict "icon" .logoIcon "path" "themes/qdrant-2024/static/img/customer-logo/") }}
|
||||
</div>
|
||||
<div class="case-studies__card-arrow">
|
||||
<svg width="24" height="14" viewBox="0 0 17 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5 1L15.5 5M15.5 5L11.5 9M15.5 5H1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ if .link }}</a>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
(function() {
|
||||
const container = document.querySelector('.case-studies__container');
|
||||
const carousel = document.querySelector('.case-studies__carousel');
|
||||
const track = document.querySelector('.case-studies__track');
|
||||
const prevBtn = document.querySelector('.case-studies__arrow--prev');
|
||||
const nextBtn = document.querySelector('.case-studies__arrow--next');
|
||||
|
||||
if (!container || !carousel || !track || !prevBtn || !nextBtn) return;
|
||||
|
||||
const slides = track.querySelectorAll('.case-studies__slide');
|
||||
const gap = 30;
|
||||
const breakpoints = { sm: 576, md: 768, xl: 1200 };
|
||||
let currentOffset = 0;
|
||||
|
||||
function getVisibleCount() {
|
||||
const w = window.innerWidth;
|
||||
if (w < breakpoints.sm) return 1;
|
||||
if (w < breakpoints.md) return 2;
|
||||
if (w < breakpoints.xl) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
function getContainerContentWidth() {
|
||||
const style = getComputedStyle(container);
|
||||
const paddingLeft = parseFloat(style.paddingLeft) || 0;
|
||||
const paddingRight = parseFloat(style.paddingRight) || 0;
|
||||
return container.offsetWidth - paddingLeft - paddingRight;
|
||||
}
|
||||
|
||||
function getSlideWidth() {
|
||||
const contentWidth = getContainerContentWidth();
|
||||
const count = Math.min(getVisibleCount(), slides.length);
|
||||
const gaps = Math.max(0, count - 1) * gap;
|
||||
return Math.floor((contentWidth - gaps) / count);
|
||||
}
|
||||
|
||||
function applySlideWidth() {
|
||||
const slideWidth = getSlideWidth();
|
||||
carousel.style.setProperty('--case-studies-slide-width', slideWidth + 'px');
|
||||
}
|
||||
|
||||
function getMaxOffset() {
|
||||
applySlideWidth();
|
||||
const slideWidth = getSlideWidth();
|
||||
const trackWidth = slides.length * slideWidth + (slides.length - 1) * gap;
|
||||
const contentWidth = getContainerContentWidth();
|
||||
return Math.max(0, trackWidth - contentWidth);
|
||||
}
|
||||
|
||||
function updateTrack() {
|
||||
const maxOffset = getMaxOffset();
|
||||
currentOffset = Math.min(Math.max(0, currentOffset), maxOffset);
|
||||
track.style.transform = `translateX(-${currentOffset}px)`;
|
||||
}
|
||||
|
||||
function updateButtons() {
|
||||
const maxOffset = getMaxOffset();
|
||||
prevBtn.disabled = currentOffset <= 0;
|
||||
nextBtn.disabled = currentOffset >= maxOffset;
|
||||
}
|
||||
|
||||
prevBtn.addEventListener('click', function() {
|
||||
const slideWidth = getSlideWidth();
|
||||
currentOffset = Math.max(0, currentOffset - (slideWidth + gap));
|
||||
updateTrack();
|
||||
updateButtons();
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', function() {
|
||||
const maxOffset = getMaxOffset();
|
||||
const slideWidth = getSlideWidth();
|
||||
currentOffset = Math.min(maxOffset, currentOffset + (slideWidth + gap));
|
||||
updateTrack();
|
||||
updateButtons();
|
||||
});
|
||||
|
||||
function onResize() {
|
||||
applySlideWidth();
|
||||
updateTrack();
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
let resizeTimer;
|
||||
window.addEventListener('resize', function() {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function() {
|
||||
requestAnimationFrame(onResize);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
applySlideWidth();
|
||||
updateTrack();
|
||||
updateButtons();
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
{{ with (.Site.GetPage "/headless/main/customers") }}
|
||||
<section class="customers">
|
||||
<div class="container">
|
||||
<div class="customers__grid">
|
||||
{{ range .Params.logoRows }}
|
||||
{{ range .logos }}
|
||||
<div class="customers__logo">
|
||||
{{ partial "svg.html" (dict "icon" .src "path" "static" "fill" "#B4BACC") }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,53 @@
|
||||
{{ with (.Site.GetPage "/headless/main/engineered-for-builders") }}
|
||||
<section class="engineered-for-builders">
|
||||
<div class="engineered-for-builders__background" aria-hidden="true"></div>
|
||||
<div class="engineered-for-builders__background-texture" aria-hidden="true"></div>
|
||||
<div class="container">
|
||||
<div class="engineered-for-builders__header">
|
||||
<h2 class="engineered-for-builders__title">{{ .Params.title }}</h2>
|
||||
<p class="engineered-for-builders__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
{{ with .Params.illustration }}
|
||||
<div class="engineered-for-builders__illustration">
|
||||
{{ if .srcset }}
|
||||
<picture>
|
||||
{{ with .webpSrcset }}<source type="image/webp" srcset="{{ . }}" />{{ end }}
|
||||
<img
|
||||
src="{{ .src }}"
|
||||
srcset="{{ .srcset }}"
|
||||
alt="{{ .alt }}"
|
||||
class="engineered-for-builders__illustration-image"
|
||||
width="{{ .width }}"
|
||||
height="{{ .height }}"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
{{ else }}
|
||||
{{ partial "picture" (dict "src" .src "alt" .alt "class" "engineered-for-builders__illustration-image" "loading" "lazy" "decoding" "async" "width" .width "height" .height) }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="engineered-for-builders__features">
|
||||
{{ range .Params.features }}
|
||||
<div class="engineered-for-builders__feature">
|
||||
<h3 class="engineered-for-builders__feature-title">{{ .title }}</h3>
|
||||
<p class="engineered-for-builders__feature-description">{{ .description }}</p>
|
||||
<a href="{{ .link.url }}" class="engineered-for-builders__feature-link">
|
||||
{{ .link.text }}
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M3.33 8H12.67M12.67 8L8.67 4M12.67 8L8.67 12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,45 @@
|
||||
{{ with (.Site.GetPage "/headless/main/enterprise-capabilities") }}
|
||||
{{ $iconsPath := "static/img/components/icons/" }}
|
||||
<section class="enterprise-capabilities">
|
||||
<div class="enterprise-capabilities__stars" aria-hidden="true"></div>
|
||||
|
||||
<div class="container enterprise-capabilities__white-light" aria-hidden="true"></div>
|
||||
<div class="enterprise-capabilities__background" aria-hidden="true">
|
||||
<div class="enterprise-capabilities__globe-blue-light"></div>
|
||||
<div class="enterprise-capabilities__globe-blue-light"></div>
|
||||
<div class="enterprise-capabilities__globe-blue-light"></div>
|
||||
<div class="enterprise-capabilities__globe-front"></div>
|
||||
</div>
|
||||
<div class="container enterprise-capabilities__white-light" aria-hidden="true"></div>
|
||||
<div class="enterprise-capabilities__background" aria-hidden="true">
|
||||
<div class="enterprise-capabilities__globe-front"></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="enterprise-capabilities__content">
|
||||
<div class="enterprise-capabilities__header">
|
||||
<h2 class="enterprise-capabilities__title">{{ .Params.title }}</h2>
|
||||
<p class="enterprise-capabilities__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
<div class="enterprise-capabilities__cards">
|
||||
{{ range .Params.deploymentOptions }}
|
||||
<a href="{{ .link.url }}" class="enterprise-capabilities__card enterprise-capabilities__card--{{ .position }}">
|
||||
{{ if .connector }}
|
||||
<img src="{{ .connector }}" alt="" class="enterprise-capabilities__connector" />
|
||||
{{ end }}
|
||||
<div class="enterprise-capabilities__card-icon">
|
||||
{{ partial "svg" (dict "icon" .icon "path" $iconsPath "color" .iconColor ) }}
|
||||
</div>
|
||||
<div class="enterprise-capabilities__card-content">
|
||||
<h3 class="enterprise-capabilities__card-title">{{ .title }}</h3>
|
||||
<p class="enterprise-capabilities__card-description">{{ .description }}</p>
|
||||
</div>
|
||||
<span class="link link_white link_md enterprise-capabilities__card-link"
|
||||
>{{ .link.text }}</span
|
||||
>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,50 @@
|
||||
{{ with (.Site.GetPage "/headless/main/enterprise-ready") }}
|
||||
<section class="enterprise-ready">
|
||||
<div class="container">
|
||||
<div class="enterprise-ready__header">
|
||||
<h2 class="enterprise-ready__title">{{ .Params.title }}</h2>
|
||||
<p class="enterprise-ready__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
<div class="enterprise-ready__content">
|
||||
{{/* Large cards row */}}
|
||||
<div class="enterprise-ready__row enterprise-ready__row--large">
|
||||
{{ range .Params.largeCards }}
|
||||
{{ if and .link .link.url }}
|
||||
<a href="{{ .link.url }}" class="enterprise-ready__card enterprise-ready__card--large enterprise-ready__card--link"{{ if or (hasPrefix .link.url "http://") (hasPrefix .link.url "https://") }} target="_blank" rel="noopener noreferrer"{{ end }}>
|
||||
{{ else }}
|
||||
<div class="enterprise-ready__card enterprise-ready__card--large">
|
||||
{{ end }}
|
||||
<div class="enterprise-ready__card-visual enterprise-ready__card-visual--{{ .visual }}">
|
||||
{{ if .image }}
|
||||
{{ partial "picture" (dict "src" .image.src "alt" .image.alt "class" "enterprise-ready__card-img" "loading" "lazy" "decoding" "async") }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="enterprise-ready__card-footer">
|
||||
<span class="link link_dark link_md enterprise-ready__card-link">{{ .title }}</span>
|
||||
</div>
|
||||
{{ if and .link .link.url }}</a>{{ else }}</div>{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{/* Small cards row */}}
|
||||
{{ $iconsPath := "static/img/components/icons/" }}
|
||||
<div class="enterprise-ready__row enterprise-ready__row--small">
|
||||
{{ range .Params.smallCards }}
|
||||
{{ if and .link .link.url }}
|
||||
<a href="{{ .link.url }}" class="enterprise-ready__card enterprise-ready__card--small enterprise-ready__card--link"{{ if or (hasPrefix .link.url "http://") (hasPrefix .link.url "https://") }} target="_blank" rel="noopener noreferrer"{{ end }}>
|
||||
{{ else }}
|
||||
<div class="enterprise-ready__card enterprise-ready__card--small">
|
||||
{{ end }}
|
||||
<div class="enterprise-ready__card-icon">
|
||||
{{ if .icon }}
|
||||
{{ partial "svg" (dict "icon" .icon.name "path" $iconsPath "color" .icon.color "stroke" .icon.stroke "fill" .icon.fill) }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<span class="enterprise-ready__card-label">{{ .title }}</span>
|
||||
{{ if and .link .link.url }}</a>{{ else }}</div>{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,95 @@
|
||||
{{ with (.Site.GetPage "/headless/main/hero") }}
|
||||
<section class="hero">
|
||||
<div class="hero__container">
|
||||
<div class="hero__grid"></div>
|
||||
<div class="hero__content">
|
||||
<div class="hero__heading">
|
||||
<h1 class="hero__title">{{ .Params.title }}</h1>
|
||||
<p class="hero__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
<div class="hero__buttons">
|
||||
<a data-metric-loc="hero" href="{{ .Params.startFree.url }}" class="button button_contained button_lg"
|
||||
data-metric-loc="hero">{{ .Params.startFree.text }}</a>
|
||||
<a data-metric-loc="hero" href="{{ .Params.learnMore.url }}" class="button button_outlined button_lg"
|
||||
data-metric-loc="hero">{{ .Params.learnMore.text }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero__planet">
|
||||
{{ with .Params.planet }}
|
||||
<img class="hero__planet-blur" src="{{ .blur }}" alt="" width="570" height="544" decoding="async" />
|
||||
<img
|
||||
class="hero__planet-blur hero__planet-blur--alt"
|
||||
src="{{ .blur2 }}"
|
||||
alt=""
|
||||
width="570"
|
||||
height="544"
|
||||
decoding="async"
|
||||
/>
|
||||
<img class="hero__planet-sphere" src="{{ .sphere }}" alt="" width="514" height="488" decoding="async" />
|
||||
{{ with .texture }}
|
||||
<picture>
|
||||
<source srcset="{{ .webp }}" type="image/webp" />
|
||||
<img
|
||||
class="hero__planet-texture"
|
||||
src="{{ .png }}"
|
||||
alt=""
|
||||
width="1012"
|
||||
height="960"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{ with .Params.blueHaze }}
|
||||
<img class="hero__blue-haze" src="{{ . }}" alt="" width="1200" height="600" decoding="async" />
|
||||
{{ end }}
|
||||
|
||||
|
||||
<div class="hero__astronauts">
|
||||
{{ with .Params.astronauts }}
|
||||
<picture>
|
||||
<source type="image/webp" srcset="{{ .webpSrcset }}" sizes="100vw" />
|
||||
<img
|
||||
src="{{ .fallbackSrc }}"
|
||||
srcset="{{ .pngSrcset }}"
|
||||
sizes="100vw"
|
||||
alt="{{ .alt }}"
|
||||
width="{{ .width }}"
|
||||
height="{{ .height }}"
|
||||
draggable="false"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
</picture>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="hero__fade-top"></div>
|
||||
<div class="hero__fade-bottom"></div>
|
||||
<div class="hero__fade-left"></div>
|
||||
<div class="hero__fade-right"></div>
|
||||
</div>
|
||||
{{ if .Params.valueBadges }}
|
||||
<div class="hero__badges">
|
||||
{{ range .Params.valueBadges }}
|
||||
{{ if .url }}
|
||||
<a href="{{ .url }}" class="hero__badge hero__badge--link"{{ if or (hasPrefix .url "http://") (hasPrefix .url "https://") }} target="_blank" rel="noopener noreferrer"{{ end }}>
|
||||
<img src="{{ .icon }}" alt="" class="hero__badge-icon" width="16" height="16" decoding="async" />
|
||||
<span class="hero__badge-text">{{ .text }}</span>
|
||||
</a>
|
||||
{{ else }}
|
||||
<span class="hero__badge">
|
||||
<img src="{{ .icon }}" alt="" class="hero__badge-icon" width="16" height="16" decoding="async" />
|
||||
<span class="hero__badge-text">{{ .text }}</span>
|
||||
</span>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,46 @@
|
||||
{{ with (.Site.GetPage "/headless/main/integrations-logos") }}
|
||||
{{ $logosFirstRow := .Params.logosFirstRow }}
|
||||
{{ $logosSecondRow := .Params.logosSecondRow }}
|
||||
|
||||
|
||||
<section class="integrations-logos">
|
||||
<div class="container">
|
||||
<p class="integrations-logos__title">{{ .Params.title }}</p>
|
||||
</div>
|
||||
|
||||
<div class="integrations-logos__content">
|
||||
<div class="integrations-logos__cards">
|
||||
<div class="integrations-logos__cards__container">
|
||||
<div class="integrations-logos__cards__block">
|
||||
<div class="integrations-logos__cards__inner integrations-logos__cards__inner_to-left">
|
||||
<span>
|
||||
{{ partial "integrations-logos-cards" (dict "context" $logosFirstRow) }}
|
||||
</span>
|
||||
<span>
|
||||
{{ partial "integrations-logos-cards" (dict "context" $logosFirstRow) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="integrations-logos__cards">
|
||||
<div class="integrations-logos__cards__container">
|
||||
<div class="integrations-logos__cards__block">
|
||||
<div class="integrations-logos__cards__inner integrations-logos__cards__inner_to-right">
|
||||
<span>
|
||||
{{ partial "integrations-logos-cards" (dict "context" $logosSecondRow) }}
|
||||
</span>
|
||||
<span>
|
||||
{{ partial "integrations-logos-cards" (dict "context" $logosSecondRow) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="integrations-logos__gradient-left"></div>
|
||||
<div class="integrations-logos__gradient-right"></div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,110 @@
|
||||
{{ with (.Site.GetPage "/headless/main/performance-by-design") }}
|
||||
<section class="performance-by-design">
|
||||
<div class="container">
|
||||
<div class="performance-by-design__header">
|
||||
<span class="performance-by-design__tagline">{{ .Params.tagline }}</span>
|
||||
<h2 class="performance-by-design__title">{{ .Params.title }}</h2>
|
||||
<p class="performance-by-design__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
|
||||
{{/* Slider layout: visible only below md */}}
|
||||
<div class="performance-by-design__slider">
|
||||
<div class="performance-by-design__content">
|
||||
<div class="performance-by-design__features">
|
||||
{{ range $index, $feature := .Params.features }}
|
||||
<div class="performance-by-design__feature{{ if eq $index 0 }} performance-by-design__feature--active{{ end }}" data-feature-index="{{ $index }}">
|
||||
<h3 class="performance-by-design__feature-title">{{ $feature.title }}</h3>
|
||||
<div class="performance-by-design__feature-body">
|
||||
<p class="performance-by-design__feature-description">{{ $feature.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="performance-by-design__visual">
|
||||
{{ range $index, $feature := .Params.features }}
|
||||
<div class="performance-by-design__image{{ if eq $index 0 }} performance-by-design__image--active{{ end }}" data-image-index="{{ $index }}">
|
||||
{{ if $feature.image }}
|
||||
{{ partial "picture" (dict "src" $feature.image.src "alt" $feature.image.alt "loading" "lazy" "decoding" "async") }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/* Card grid: visible from md up, 2 cards per row */}}
|
||||
<div class="performance-by-design__grid">
|
||||
{{ range .Params.features }}
|
||||
<div class="performance-by-design__card">
|
||||
{{ if .image }}
|
||||
<div class="performance-by-design__card-visual">
|
||||
{{ partial "picture" (dict "src" .image.src "alt" .image.alt "class" "performance-by-design__card-image" "loading" "lazy" "decoding" "async") }}
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="performance-by-design__card-content">
|
||||
<h3 class="performance-by-design__card-title">{{ .title }}</h3>
|
||||
<p class="performance-by-design__card-description">{{ .description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var section = document.querySelector('.performance-by-design');
|
||||
if (!section) return;
|
||||
|
||||
var features = section.querySelectorAll('.performance-by-design__feature');
|
||||
var images = section.querySelectorAll('.performance-by-design__image');
|
||||
if (!features.length) return;
|
||||
|
||||
var currentIndex = 0;
|
||||
var autoplayInterval = null;
|
||||
var AUTOPLAY_DELAY = 5000;
|
||||
|
||||
function goToFeature(index) {
|
||||
if (index < 0 || index >= features.length) return;
|
||||
|
||||
features.forEach(function (f) {
|
||||
f.classList.remove('performance-by-design__feature--active');
|
||||
});
|
||||
images.forEach(function (img) {
|
||||
img.classList.remove('performance-by-design__image--active');
|
||||
});
|
||||
|
||||
features[index].classList.add('performance-by-design__feature--active');
|
||||
if (images[index]) images[index].classList.add('performance-by-design__image--active');
|
||||
|
||||
currentIndex = index;
|
||||
}
|
||||
|
||||
function nextFeature() {
|
||||
var next = (currentIndex + 1) % features.length;
|
||||
goToFeature(next);
|
||||
}
|
||||
|
||||
function startAutoplay() {
|
||||
stopAutoplay();
|
||||
autoplayInterval = setInterval(nextFeature, AUTOPLAY_DELAY);
|
||||
}
|
||||
|
||||
function stopAutoplay() {
|
||||
if (autoplayInterval) {
|
||||
clearInterval(autoplayInterval);
|
||||
autoplayInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
features.forEach(function (feature, i) {
|
||||
feature.addEventListener('click', function () {
|
||||
goToFeature(i);
|
||||
startAutoplay();
|
||||
});
|
||||
});
|
||||
|
||||
startAutoplay();
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{ with (.Site.GetPage "/headless/main/solutions") }}
|
||||
<section class="solutions">
|
||||
<div class="solutions__background"></div>
|
||||
<div class="container">
|
||||
<div class="solutions__header">
|
||||
<span class="solutions__label">{{ .Params.label }}</span>
|
||||
<h2 class="solutions__title">{{ .Params.title }}</h2>
|
||||
<p class="solutions__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ partial "tab-slider" (dict
|
||||
"sectionClass" "solutions"
|
||||
"tabs" .Params.tabs
|
||||
"items" .Params.tabs
|
||||
"slidePartial" "solutions-slide-content"
|
||||
"showControls" true
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,27 @@
|
||||
{{ with (.Site.GetPage "/headless/main/why-qdrant") }}
|
||||
<section class="why-qdrant">
|
||||
<div class="container">
|
||||
<div class="why-qdrant__header">
|
||||
<span class="why-qdrant__pill">{{ .Params.pill }}</span>
|
||||
<h2 class="why-qdrant__title">{{ .Params.title }}</h2>
|
||||
<p class="why-qdrant__subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
<div class="why-qdrant__grid">
|
||||
{{ range .Params.featureCards }}
|
||||
<div class="why-qdrant__card why-qdrant__card--{{ .size }}" data-card-id="{{ .id }}">
|
||||
<div class="why-qdrant__card-visual why-qdrant__card-visual--{{ .id }}">
|
||||
{{ if .image }}
|
||||
{{ partial "picture" (dict "src" .image.src "alt" .image.alt "class" "why-qdrant__card-image" "loading" "lazy" "decoding" "async") }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="why-qdrant__card-content">
|
||||
<h3 class="why-qdrant__card-title">{{ .title }}</h3>
|
||||
<p class="why-qdrant__card-description">{{ .description | safeHTML }}</p>
|
||||
<a href="{{ .link.url }}" class="link link_md why-qdrant__card-link">{{ .link.text }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{ range .context }}
|
||||
<div class="integrations-logos__cards__item" role="img" aria-label="{{ .logo.alt }}">
|
||||
{{ partial "svg" (dict "icon" .logo.icon "path" "themes/qdrant-2024/static/img/customer-logo/") }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ range .context }}
|
||||
<div class="integrations-logos__cards__item" role="img" aria-label="{{ .logo.alt }}">
|
||||
{{ partial "svg" (dict "icon" .logo.icon "path" "themes/qdrant-2024/static/img/customer-logo/") }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,15 +0,0 @@
|
||||
{{ with (.Site.GetPage "/headless/main/integrations") }}
|
||||
<section class="integrations">
|
||||
<div class="container">
|
||||
<h2 class="integrations__title mx-auto">{{ .Params.title }}</h2>
|
||||
<p class="integrations__subtitle">
|
||||
Qdrant integrates with all leading
|
||||
<a class="integrations__link" href="{{ .Params.embedLink.url }}">{{ .Params.embedLink.text }}</a> and
|
||||
<a class="integrations__link" href="{{ .Params.frameworkLink.url }}">{{ .Params.frameworkLink.text }}</a>.
|
||||
</p>
|
||||
<div class="text-center">{{ .Scratch.Set "cssClass" "link_light link_xl" }} {{ partial "link" . }}</div>
|
||||
<div class="integrations__preview mx-auto"></div>
|
||||
<div class="integrations__overlay"></div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
@@ -7,7 +7,12 @@
|
||||
|
||||
<!-- OneTrust Cookies Consent Notice start for local.qdrant.tech -->
|
||||
{{ if .Site.Params.onetrustScriptID }}
|
||||
<script src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js" type="text/javascript" charset="UTF-8" data-domain-script="{{ .Site.Params.onetrustScriptID }}" ></script>
|
||||
<script
|
||||
src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js"
|
||||
type="text/javascript"
|
||||
charset="UTF-8"
|
||||
data-domain-script="{{ .Site.Params.onetrustScriptID }}"
|
||||
></script>
|
||||
<script async type="text/javascript">
|
||||
function OptanonWrapper() {
|
||||
const onetrustAnalyticsChoice = new CustomEvent('onetrust_loaded');
|
||||
@@ -36,6 +41,7 @@
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
|
||||
<!--Google Measurement ID-->
|
||||
{{ $googleJs := resources.Get "js/google-setup.js" | js.Build (dict "params" (dict "gaMeasurementId" .Site.Config.Services.GoogleAnalytics.ID)) | minify | resources.Fingerprint "sha512" }}
|
||||
<script src="{{ $googleJs.RelPermalink }}"></script>
|
||||
@@ -44,6 +50,12 @@
|
||||
<script src="{{ $indexJs.RelPermalink }}"></script>
|
||||
|
||||
{{ if .IsHome }}
|
||||
{{ $homeAnimationsJs := resources.Get "/js/home-animations.js" | js.Build | minify | resources.Fingerprint "sha512" }}
|
||||
<script src="{{ $homeAnimationsJs.RelPermalink }}"></script>
|
||||
|
||||
{{ $scrollRevealJs := resources.Get "/js/scroll-reveal.js" | js.Build | minify | resources.Fingerprint "sha512" }}
|
||||
<script src="{{ $scrollRevealJs.RelPermalink }}"></script>
|
||||
|
||||
<!--splide.js carousel-->
|
||||
{{ $splideJs := resources.Get "/js/vendor/splide.js" | js.Build | minify | resources.Fingerprint "sha512" }}
|
||||
<script src="{{ $splideJs.RelPermalink }}"></script>
|
||||
@@ -153,27 +165,22 @@
|
||||
|
||||
{{ if and (eq .Section "blog") (.IsPage) }}
|
||||
<script>
|
||||
document.addEventListener("scroll", () => {
|
||||
const article = document.getElementById("article");
|
||||
const progress = document.getElementById("progress");
|
||||
document.addEventListener('scroll', () => {
|
||||
const article = document.getElementById('article');
|
||||
const progress = document.getElementById('progress');
|
||||
|
||||
const articleHeight = article.scrollHeight - window.innerHeight;
|
||||
const scrollTop = window.scrollY - article.offsetTop;
|
||||
|
||||
progress.value = Math.min(
|
||||
Math.max((scrollTop / articleHeight) * 100, 0),
|
||||
100
|
||||
);
|
||||
progress.value = Math.min(Math.max((scrollTop / articleHeight) * 100, 0), 100);
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
|
||||
<script>
|
||||
window.addEventListener("message", event => {
|
||||
if (
|
||||
event.data.type === "hsFormCallback" &&
|
||||
event.data.eventName === "onFormReady"
|
||||
) {
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'hsFormCallback' && event.data.eventName === 'onFormReady') {
|
||||
const formEl = document.querySelector(`form[data-form-id="${event.data.id}"]`);
|
||||
if (formEl) {
|
||||
const hiddenCurrentUrl = formEl.querySelector('[name="last_form_fill_url"]');
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{{- $src := .src -}}
|
||||
{{- $alt := .alt | default "" -}}
|
||||
{{- $ext := path.Ext $src -}}
|
||||
|
||||
{{- if eq $ext ".svg" -}}
|
||||
<img
|
||||
src="{{ $src }}"
|
||||
alt="{{ $alt }}"
|
||||
{{- with .class }}class="{{ . }}"{{ end -}}
|
||||
{{- with .loading }}loading="{{ . }}"{{ end -}}
|
||||
{{- with .decoding }}decoding="{{ . }}"{{ end -}}
|
||||
{{- with .width }}width="{{ . }}"{{ end -}}
|
||||
{{- with .height }}height="{{ . }}"{{ end -}}
|
||||
/>
|
||||
{{- else -}}
|
||||
{{- $webpSrc := replace $src $ext ".webp" -}}
|
||||
<picture>
|
||||
<source srcset="{{ $webpSrc }}" type="image/webp" />
|
||||
<img
|
||||
src="{{ $src }}"
|
||||
alt="{{ $alt }}"
|
||||
{{- with .class }}class="{{ . }}"{{ end -}}
|
||||
{{- with .loading }}loading="{{ . }}"{{ end -}}
|
||||
{{- with .decoding }}decoding="{{ . }}"{{ end -}}
|
||||
{{- with .fetchpriority }}fetchpriority="{{ . }}"{{ end -}}
|
||||
{{- with .width }}width="{{ . }}"{{ end -}}
|
||||
{{- with .height }}height="{{ . }}"{{ end -}}
|
||||
/>
|
||||
</picture>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,21 @@
|
||||
{{ $item := .item }}
|
||||
<div class="row align-items-center">
|
||||
<div class="col-12 col-lg-5">
|
||||
<div class="solutions__panel-content">
|
||||
<h3 class="solutions__panel-title">{{ $item.title }}</h3>
|
||||
<p class="solutions__panel-description">{{ $item.description }}</p>
|
||||
<a href="{{ $item.link.url }}" class="solutions__panel-link">
|
||||
{{ $item.link.text }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-7">
|
||||
<div class="solutions__panel-illustration">
|
||||
{{ if $item.image }}
|
||||
{{ partial "picture" (dict "src" $item.image.src "alt" $item.image.alt "loading" "lazy" "decoding" "async") }}
|
||||
{{ else }}
|
||||
<div class="solutions__panel-placeholder" data-label="{{ $item.title }}"></div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1 +1,77 @@
|
||||
{{ $path := "static/icons/outline/" }} {{ $filename := . }} {{ readFile (printf "%s%s" $path $filename) | safeHTML }}
|
||||
<!-- {{/* svg.html partial
|
||||
-----------------
|
||||
This partial renders SVG icons dynamically with Hugo templating.
|
||||
|
||||
- Usage:
|
||||
{{ partial "svg.html" (dict "icon" "icon-filename" "stroke" "color" "fill" "path" "custom/path/")
|
||||
}}
|
||||
|
||||
|
||||
OR just:
|
||||
{{ partial "svg.html" "icon-filename" }}
|
||||
|
||||
|
||||
- Parameters:
|
||||
- icon / filename: Name of SVG file (without or with .svg extension).
|
||||
- path (optional): Folder path (default: static/icons/outline/).
|
||||
- stroke, fill, color (optional): Override respective SVG attributes, using CSS variables (e.g. "primary", "teal", etc).
|
||||
* For Bootstrap color variables, use just the color name (e.g., "primary", "violet"), which becomes: var(--bs-primary).
|
||||
* You can also use "currentColor" to inherit from CSS context.
|
||||
* HEX values are supported as well.
|
||||
|
||||
- How it works:
|
||||
- Loads the SVG file as text.
|
||||
- Replaces stroke/fill/color attributes if provided.
|
||||
- Allows color to be controlled via style or CSS variables.
|
||||
- Safe for direct HTML inclusion.
|
||||
|
||||
- Typical use:
|
||||
For icons in cards, buttons, menus, etc.
|
||||
|
||||
Maintainers: Update this file if icon assets or color conventions change.
|
||||
*/}} -->
|
||||
|
||||
{{ $path := "static/icons/outline/" }}
|
||||
{{ $filename := "" }}
|
||||
{{ $stroke := "" }}
|
||||
{{ $fill := "" }}
|
||||
{{ $color := "" }}
|
||||
{{ if reflect.IsMap . }}
|
||||
{{ $path = default $path .path }}
|
||||
{{ $filename = default .filename .icon }}
|
||||
{{ $stroke = .stroke }}
|
||||
{{ $fill = .fill }}
|
||||
{{ $color = .color }}
|
||||
{{ else }}
|
||||
{{ $filename = . }}
|
||||
{{ end }}
|
||||
{{ if not (strings.HasSuffix $filename ".svg") }}
|
||||
{{ $filename = printf "%s.svg" $filename }}
|
||||
{{ end }}
|
||||
{{ $svg := readFile (printf "%s%s" $path $filename) }}
|
||||
{{ if $stroke }}
|
||||
{{ $strokeVal := $stroke }}
|
||||
{{ if and (ne (strings.HasPrefix $stroke "#") true) (ne (strings.HasPrefix $stroke "var(") true) (ne $stroke "currentColor") (ne $stroke "inherit") (ne $stroke "none") }}
|
||||
{{ $strokeVal = printf "var(--bs-%s)" $stroke }}
|
||||
{{ end }}
|
||||
{{ $svg = replaceRE `stroke="[^"]*"` (printf `stroke="%s"` $strokeVal) $svg }}
|
||||
{{ $svg = replaceRE `stroke='[^']*'` (printf `stroke="%s"` $strokeVal) $svg }}
|
||||
{{ end }}
|
||||
{{ if $fill }}
|
||||
{{ $fillVal := $fill }}
|
||||
{{ if and (ne (strings.HasPrefix $fill "#") true) (ne (strings.HasPrefix $fill "var(") true) (ne $fill "currentColor") (ne $fill "inherit") (ne $fill "none") }}
|
||||
{{ $fillVal = printf "var(--bs-%s)" $fill }}
|
||||
{{ end }}
|
||||
{{ $svg = replaceRE `fill="[^"]*"` (printf `fill="%s"` $fillVal) $svg }}
|
||||
{{ $svg = replaceRE `fill='[^']*'` (printf `fill="%s"` $fillVal) $svg }}
|
||||
{{ end }}
|
||||
{{ if $color }}
|
||||
{{ $colorVal := $color }}
|
||||
{{ if and (ne (strings.HasPrefix $color "#") true) (ne (strings.HasPrefix $color "var(") true) (ne $color "currentColor") (ne $color "inherit") (ne $color "none") }}
|
||||
{{ $colorVal = printf "var(--bs-%s)" $color }}
|
||||
{{ end }}
|
||||
{{ $svg = replaceRE `(<svg)(\s)` (printf `$1 style="color:%s"$2` $colorVal) $svg }}
|
||||
{{ $svg = replaceRE `stroke="[^"]*"` `stroke="currentColor"` $svg }}
|
||||
{{ $svg = replaceRE `stroke='[^']*'` `stroke="currentColor"` $svg }}
|
||||
{{ end }}
|
||||
{{ $svg | safeHTML }}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
{{/*
|
||||
Reusable Tab-Slider Partial
|
||||
|
||||
Params (passed via dict):
|
||||
- sectionClass: CSS class prefix for the section (e.g., "solutions", "healthcare-use-cases")
|
||||
- tabs: array of tab objects with at least .id and .label (or .name)
|
||||
- items: array of slide items to render
|
||||
- slidePartial: name of the partial to render each slide's content
|
||||
- showControls: (optional) whether to show arrow/dot controls (default: true)
|
||||
*/}}
|
||||
|
||||
{{ $sectionClass := .sectionClass }}
|
||||
{{ $tabs := .tabs }}
|
||||
{{ $items := .items }}
|
||||
{{ $slidePartial := .slidePartial }}
|
||||
{{ $showControls := true }}
|
||||
{{ if isset . "showControls" }}
|
||||
{{ $showControls = .showControls }}
|
||||
{{ end }}
|
||||
|
||||
<div class="{{ $sectionClass }}__tabs-wrapper">
|
||||
<div class="{{ $sectionClass }}__tabs">
|
||||
{{ range $index, $tab := $tabs }}
|
||||
<button
|
||||
type="button"
|
||||
class="{{ $sectionClass }}__tab{{ if eq $index 0 }} {{ $sectionClass }}__tab--active{{ end }}"
|
||||
data-tab-index="{{ $index }}"
|
||||
>
|
||||
{{ with $tab.label }}{{ . }}{{ else }}{{ $tab.name }}{{ end }}
|
||||
</button>
|
||||
{{ end }}
|
||||
<div class="{{ $sectionClass }}__tab-indicator"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="{{ $sectionClass }}__slider">
|
||||
{{ range $index, $item := $items }}
|
||||
<div class="{{ $sectionClass }}__slide{{ if eq $index 0 }} {{ $sectionClass }}__slide--active{{ end }}" data-slide="{{ $index }}">
|
||||
{{ partial $slidePartial (dict "item" $item "index" $index "sectionClass" $sectionClass) }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{ if $showControls }}
|
||||
<div class="{{ $sectionClass }}__controls">
|
||||
<button type="button" class="{{ $sectionClass }}__arrow {{ $sectionClass }}__arrow--prev" aria-label="Previous slide">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5 15L7.5 10L12.5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="{{ $sectionClass }}__dots">
|
||||
{{ range $index, $tab := $tabs }}
|
||||
<button
|
||||
type="button"
|
||||
class="{{ $sectionClass }}__dot{{ if eq $index 0 }} {{ $sectionClass }}__dot--active{{ end }}"
|
||||
data-slide-index="{{ $index }}"
|
||||
aria-label="Go to slide {{ add $index 1 }}"
|
||||
>
|
||||
<span></span>
|
||||
</button>
|
||||
{{ end }}
|
||||
</div>
|
||||
<button type="button" class="{{ $sectionClass }}__arrow {{ $sectionClass }}__arrow--next" aria-label="Next slide">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 5L12.5 10L7.5 15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var section = document.querySelector('.{{ $sectionClass }}');
|
||||
if (!section) return;
|
||||
|
||||
var tabs = section.querySelectorAll('.{{ $sectionClass }}__tab');
|
||||
var dots = section.querySelectorAll('.{{ $sectionClass }}__dot');
|
||||
var slides = section.querySelectorAll('.{{ $sectionClass }}__slide');
|
||||
var prevBtn = section.querySelector('.{{ $sectionClass }}__arrow--prev');
|
||||
var nextBtn = section.querySelector('.{{ $sectionClass }}__arrow--next');
|
||||
var indicator = section.querySelector('.{{ $sectionClass }}__tab-indicator');
|
||||
|
||||
var currentIndex = 0;
|
||||
var totalSlides = slides.length;
|
||||
|
||||
function updateIndicator(activeTab) {
|
||||
if (indicator && activeTab) {
|
||||
indicator.style.width = activeTab.offsetWidth + 'px';
|
||||
indicator.style.left = activeTab.offsetLeft + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function goToSlide(index) {
|
||||
if (index < 0) index = totalSlides - 1;
|
||||
if (index >= totalSlides) index = 0;
|
||||
|
||||
currentIndex = index;
|
||||
|
||||
slides.forEach(function(slide, i) {
|
||||
slide.classList.toggle('{{ $sectionClass }}__slide--active', i === index);
|
||||
});
|
||||
|
||||
tabs.forEach(function(tab, i) {
|
||||
tab.classList.toggle('{{ $sectionClass }}__tab--active', i === index);
|
||||
if (i === index) updateIndicator(tab);
|
||||
});
|
||||
|
||||
dots.forEach(function(dot, i) {
|
||||
dot.classList.toggle('{{ $sectionClass }}__dot--active', i === index);
|
||||
});
|
||||
}
|
||||
|
||||
tabs.forEach(function(tab, i) {
|
||||
tab.addEventListener('click', function() { goToSlide(i); });
|
||||
});
|
||||
|
||||
dots.forEach(function(dot, i) {
|
||||
dot.addEventListener('click', function() { goToSlide(i); });
|
||||
});
|
||||
|
||||
if (prevBtn) prevBtn.addEventListener('click', function() { goToSlide(currentIndex - 1); });
|
||||
if (nextBtn) nextBtn.addEventListener('click', function() { goToSlide(currentIndex + 1); });
|
||||
|
||||
// Initialize indicator
|
||||
var activeTab = section.querySelector('.{{ $sectionClass }}__tab--active');
|
||||
if (activeTab) updateIndicator(activeTab);
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
var activeTab = section.querySelector('.{{ $sectionClass }}__tab--active');
|
||||
if (activeTab) updateIndicator(activeTab);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -1,7 +1,11 @@
|
||||
{{ $class := .class }}
|
||||
<section class="testimonial-light {{ $class }}">
|
||||
{{ $ctx := .context }}
|
||||
{{ $hasReview := and $ctx.Params.review (ne (trim (string $ctx.Params.review) " \t\n\r") "") }}
|
||||
{{ $hasEmployees := and $ctx.Params.avatar $ctx.Params.avatar.src }}
|
||||
{{ $hasContent := or $hasReview $hasEmployees }}
|
||||
<section class="testimonial-light {{ $class }}{{ if $ctx.Params.gradient }} testimonial-light--gradient-layout{{ end }}">
|
||||
<div class="container">
|
||||
{{ with .context.Params.partners }}
|
||||
{{ with $ctx.Params.partners }}
|
||||
<div class="testimonial-light__partners">
|
||||
<p class="testimonial-light__partners-title">{{ .title }}</p>
|
||||
<div class="testimonial-light__partners-logos">
|
||||
@@ -14,25 +18,35 @@
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="row gy-4 gx-3">
|
||||
{{ if $hasContent }}
|
||||
<div class="col-12 col-xl-9">
|
||||
<div class="testimonial-light__content">
|
||||
<p class="testimonial-light__review">{{ .context.Params.review | safeHTML }}</p>
|
||||
{{ if $hasReview }}
|
||||
<p class="testimonial-light__review">{{ $ctx.Params.review | safeHTML }}</p>
|
||||
{{ end }}
|
||||
{{ with $ctx.Params.avatar }}
|
||||
{{ if .src }}
|
||||
<div class="testimonial-light__employees">
|
||||
<div class="testimonial-light__employees-avatar">
|
||||
<img src="{{ .context.Params.avatar.src }}" alt="{{ .context.Params.avatar.alt }}" />
|
||||
<img src="{{ .src }}" alt="{{ .alt }}" />
|
||||
</div>
|
||||
<div class="testimonial-light__employees-position">
|
||||
<p>{{ .context.Params.names }}</p>
|
||||
<p>{{ .context.Params.positions }}</p>
|
||||
{{ with $ctx.Params.names }}<p>{{ . }}</p>{{ end }}
|
||||
{{ with $ctx.Params.positions }}<p>{{ . }}</p>{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-3">
|
||||
<div class="testimonial-light__logo">
|
||||
<img src="{{ .context.Params.logo.src }}" alt="{{ .context.Params.logo.alt }}" />
|
||||
{{ end }}
|
||||
{{ with $ctx.Params.logo }}
|
||||
<div class="col-12 {{ if $hasContent }}col-xl-3{{ else }}col-12{{ end }}">
|
||||
<div class="testimonial-light__logo{{ if $ctx.Params.gradient }} testimonial-light__logo--gradient{{ end }}">
|
||||
<img src="{{ .src }}" alt="{{ .alt }}" />
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user