Testimonials: center, scale by length, smoother fade
Center-align the testimonial card (text + attribution row), then size the quote based on character count so short, punchy quotes get the spotlight and long ones tighten down: is-xshort (<=90 chars) 3rem cap is-short (<=180 chars) 2.25rem cap is-medium (<=320 chars) 1.55rem cap (the previous baseline) is-long (<=520 chars) 1.20rem cap is-xlong (>520 chars) 1.05rem cap Counts use [...str] so grapheme-cluster emoji (skin-tone, ZWJ) count as a single visual character instead of inflating the length. Crossfade: drop the secondary translateY (it stuttered against centered content), bump duration 600ms -> 1100ms, swap easing to cubic-bezier(0.65, 0, 0.35, 1) for a softer in/out, and add will-change:opacity so the browser keeps the layer on the GPU.
This commit is contained in:
@@ -493,16 +493,15 @@ body::before {
|
|||||||
grid-area: stack;
|
grid-area: stack;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(8px);
|
transition: opacity 1100ms cubic-bezier(0.65, 0, 0.35, 1), visibility 0s linear 1100ms;
|
||||||
transition: opacity 600ms var(--ease-strong), transform 600ms var(--ease-strong), visibility 0s linear 600ms;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
will-change: opacity;
|
||||||
}
|
}
|
||||||
.carousel__track > .is-active {
|
.carousel__track > .is-active {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transform: none;
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transition: opacity 600ms var(--ease-strong), transform 600ms var(--ease-strong), visibility 0s;
|
transition: opacity 1100ms cubic-bezier(0.65, 0, 0.35, 1), visibility 0s;
|
||||||
}
|
}
|
||||||
.carousel--stacked .carousel__track {
|
.carousel--stacked .carousel__track {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -545,9 +544,11 @@ body::before {
|
|||||||
|
|
||||||
/* ───── testimonial card ───── */
|
/* ───── testimonial card ───── */
|
||||||
.card--testimonial {
|
.card--testimonial {
|
||||||
padding: clamp(1.75rem, 3vw, 2.75rem);
|
padding: clamp(1.75rem, 3vw, 3rem) clamp(1.5rem, 4vw, 3.5rem);
|
||||||
display: flex; flex-direction: column;
|
display: flex; flex-direction: column;
|
||||||
gap: 1.5rem;
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 1.25rem;
|
||||||
min-height: clamp(220px, 24vw, 300px);
|
min-height: clamp(220px, 24vw, 300px);
|
||||||
}
|
}
|
||||||
.card--testimonial::after { display: none; }
|
.card--testimonial::after { display: none; }
|
||||||
@@ -559,25 +560,50 @@ body::before {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-variation-settings: "opsz" 144, "SOFT" 100;
|
font-variation-settings: "opsz" 144, "SOFT" 100;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
align-self: start;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
.testimonial__quote {
|
.testimonial__quote {
|
||||||
font-family: var(--display);
|
font-family: var(--display);
|
||||||
font-size: clamp(1.15rem, 0.95rem + 0.7vw, 1.45rem);
|
font-size: var(--quote-fs, clamp(1.2rem, 0.95rem + 0.85vw, 1.55rem));
|
||||||
line-height: 1.45;
|
line-height: var(--quote-lh, 1.45);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-variation-settings: "opsz" 60, "SOFT" 100;
|
font-variation-settings: "opsz" 60, "SOFT" 100;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
letter-spacing: -0.005em;
|
letter-spacing: -0.005em;
|
||||||
max-width: 60ch;
|
max-width: 64ch;
|
||||||
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* length-driven scale: shorter quotes get the spotlight, longer ones tighten */
|
||||||
|
.card--testimonial.is-xshort {
|
||||||
|
--quote-fs: clamp(1.85rem, 1.2rem + 2.4vw, 3rem);
|
||||||
|
--quote-lh: 1.20;
|
||||||
|
}
|
||||||
|
.card--testimonial.is-short {
|
||||||
|
--quote-fs: clamp(1.5rem, 1.05rem + 1.7vw, 2.25rem);
|
||||||
|
--quote-lh: 1.30;
|
||||||
|
}
|
||||||
|
.card--testimonial.is-medium {
|
||||||
|
--quote-fs: clamp(1.2rem, 0.95rem + 0.85vw, 1.55rem);
|
||||||
|
--quote-lh: 1.45;
|
||||||
|
}
|
||||||
|
.card--testimonial.is-long {
|
||||||
|
--quote-fs: clamp(1.05rem, 0.95rem + 0.35vw, 1.20rem);
|
||||||
|
--quote-lh: 1.55;
|
||||||
|
}
|
||||||
|
.card--testimonial.is-xlong {
|
||||||
|
--quote-fs: clamp(0.95rem, 0.92rem + 0.18vw, 1.05rem);
|
||||||
|
--quote-lh: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
.testimonial__by {
|
.testimonial__by {
|
||||||
display: flex; align-items: center; gap: 0.85rem;
|
display: flex; align-items: center; justify-content: center; gap: 0.85rem;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.85rem;
|
||||||
border-top: 1px solid var(--rule);
|
border-top: 1px solid var(--rule);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 32rem;
|
||||||
}
|
}
|
||||||
.testimonial__avatar {
|
.testimonial__avatar {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
@@ -590,7 +616,7 @@ body::before {
|
|||||||
}
|
}
|
||||||
.testimonial__avatar img { width: 100%; height: 100%; object-fit: cover; }
|
.testimonial__avatar img { width: 100%; height: 100%; object-fit: cover; }
|
||||||
.testimonial__avatar img.is-favicon { width: 60%; height: 60%; object-fit: contain; }
|
.testimonial__avatar img.is-favicon { width: 60%; height: 60%; object-fit: contain; }
|
||||||
.testimonial__attr { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.3; min-width: 0; }
|
.testimonial__attr { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.3; min-width: 0; text-align: left; }
|
||||||
.testimonial__name {
|
.testimonial__name {
|
||||||
font-family: var(--body);
|
font-family: var(--body);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -117,8 +117,15 @@
|
|||||||
const isFavicon = !(it.image || it.icon);
|
const isFavicon = !(it.image || it.icon);
|
||||||
const initial = (it.name || it.org || "·").trim().charAt(0).toUpperCase();
|
const initial = (it.name || it.org || "·").trim().charAt(0).toUpperCase();
|
||||||
const meta = [it.role, it.org].filter(Boolean).join(", ");
|
const meta = [it.role, it.org].filter(Boolean).join(", ");
|
||||||
|
const len = [...String(it.quote || "")].length;
|
||||||
|
const lengthClass =
|
||||||
|
len <= 90 ? " is-xshort" :
|
||||||
|
len <= 180 ? " is-short" :
|
||||||
|
len <= 320 ? " is-medium" :
|
||||||
|
len <= 520 ? " is-long" :
|
||||||
|
" is-xlong";
|
||||||
return `
|
return `
|
||||||
<${tag} class="card card--testimonial reveal" ${attrs}>
|
<${tag} class="card card--testimonial reveal${lengthClass}" ${attrs}>
|
||||||
<span class="testimonial__mark" aria-hidden="true">“</span>
|
<span class="testimonial__mark" aria-hidden="true">“</span>
|
||||||
<blockquote class="testimonial__quote">${esc(it.quote)}</blockquote>
|
<blockquote class="testimonial__quote">${esc(it.quote)}</blockquote>
|
||||||
<figcaption class="testimonial__by">
|
<figcaption class="testimonial__by">
|
||||||
|
|||||||
Reference in New Issue
Block a user