Skrytetall SENT
by SvennMats
HTML
<section class="kpi-section">
<div class="kpi-grid">
<div class="kpi-card">
<div class="kpi-card-top">
<div class="kpi-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="4" y="2" width="16" height="20" rx="2" ry="2"></rect>
<path d="M9 22v-4h6v4"></path>
<path d="M8 6h.01"></path>
<path d="M16 6h.01"></path>
<path d="M12 6h.01"></path>
<path d="M12 10h.01"></path>
<path d="M12 14h.01"></path>
<path d="M16 10h.01"></path>
<path d="M16 14h.01"></path>
<path d="M8 10h.01"></path>
<path d="M8 14h.01"></path>
</svg>
</div>
<h3 class="kpi-title">Bedrifter vi har hjulpet</h3>
</div>
<div class="kpi-value-wrapper">
<span class="kpi-number counter" data-target="500">0</span>
<span class="kpi-suffix">+</span>
</div>
<p class="kpi-description">Høyt volum til tross - vi klarer som regel å sende inn din søknad samme dag du tar kontakt.</p>
</div>
<div class="kpi-card">
<div class="kpi-card-top">
<div class="kpi-icon-wrapper">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
</div>
<h3 class="kpi-title">Godkjent på første forsøk</h3>
</div>
<div class="kpi-value-wrapper">
<span class="kpi-number counter" data-target="100">0</span>
<span class="kpi-suffix">%</span>
</div>
<p class="kpi-description">Behandlingstiden kan vi ikke gjøre noe med - men vi kan sørge for at du bare trenger å søke én gang.</p>
</div>
<div class="kpi-card">
<div class="kpi-card-top">
<div...
CSS
/* --- Sikrer at padding ikke ødelegger bredden --- */
*, *::before, *::after {
box-sizing: border-box;
}
/* --- Brand Variabler --- */
:root {
--svenn-orange: #F96529;
--svenn-dark: #241D2D;
--svenn-res-bg: #EDF3E3;
--svenn-bg: #F8F9FA;
}
/* --- Grunnleggende oppsett --- */
.kpi-section {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: #ffffff;
padding: 60px 20px;
color: var(--svenn-dark);
width: 100%; /* Sikrer at den ikke går utenfor skjermen */
overflow-x: hidden; /* Ekstra sikkerhet mot horisontal scroll */
}
/* --- Layout for kortene --- */
.kpi-grid {
display: grid;
/* min(100%, 280px) er magien her. Den tvinger kortet til å krympe hvis skjermen er bitteliten */
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
gap: 24px;
max-width: 1100px;
margin: 0 auto;
width: 100%;
}
/* --- Selve designet på kortet --- */
.kpi-card {
background: #ffffff;
border: 1px solid #E5E7EB;
border-radius: 16px;
padding: 32px 24px;
box-shadow: 0 4px 6px -1px rgba(36, 29, 45, 0.05), 0 2px 4px -1px rgba(36, 29, 45, 0.03);
display: flex;
flex-direction: column;
transition: transform 0.3s ease, box-shadow 0.3s ease;
/* For animasjonen fra JavaScript */
opacity: 0;
transform: translateY(30px);
}
.kpi-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(36, 29, 45, 0.08), 0 4px 6px -2px rgba(36, 29, 45, 0.04);
}
/* Toppseksjon med ikon og tittel */
.kpi-card-top {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.kpi-icon-wrapper {
background: var(--svenn-res-bg);
color: var(--svenn-orange);
width: 44px;
height: 44px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0; /* Sikrer at ikonet ikke klemmes på små skjermer */
}
.kpi-title...
JavaScript
document.addEventListener("DOMContentLoaded", () => {
// Avansert tellefunksjon for mykere (ease-out) animasjon
const easeOutQuart = (t) => 1 - Math.pow(1 - t, 4);
const animateValue = (obj, start, end, duration) => {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
// Bruker ease-out for at tellingen skal bremse naturlig opp mot slutten
const currentCount = Math.floor(easeOutQuart(progress) * (end - start) + start);
obj.innerHTML = currentCount;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = end; // Sikrer at den treffer nøyaktig
}
};
window.requestAnimationFrame(step);
};
// Trigger animasjonene når kortene er i viewport
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Fade inn kortet
entry.target.classList.add('is-visible');
// Finn tallet og start opptellingen (bruker 2 sekunder for god effekt)
const counterElement = entry.target.querySelector('.counter');
if (counterElement) {
const target = parseInt(counterElement.getAttribute('data-target'));
animateValue(counterElement, 0, target, 2000);
}
// Slutt å observere (animeres kun 1 gang)
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.1, // Starter når 10% av kortet vises
rootMargin: "0px 0px -50px 0px" // Trigger litt før man er helt på elementet
});
// Knytt observeren til alle kort
document.querySelectorAll('.kpi-card').forEach(card => {
observer.observe(card);
});
});