Skip to content

Commit c83bc25

Browse files
committed
docs: redesign sponsors CTA with shimmer border and heart spray
1 parent 40d0d1a commit c83bc25

1 file changed

Lines changed: 289 additions & 0 deletions

File tree

docs/website/.vitepress/components/sponsors/SponsorsGroup.vue

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,60 @@ const props = withDefaults(defineProps<Props>(), {
2323
placement: 'home',
2424
})
2525
26+
const sponsorUrl
27+
= 'https://opencollective.com/masscode/contribute/silver-sponsors-43169/checkout?interval=month&amount=50&contributeAs=me'
28+
29+
const ctaSparks = [
30+
{
31+
tx: '-22px',
32+
ty: '-26px',
33+
rot: '-32deg',
34+
delay: '0s',
35+
duration: '2.4s',
36+
size: '11px',
37+
},
38+
{
39+
tx: '24px',
40+
ty: '-28px',
41+
rot: '34deg',
42+
delay: '0.55s',
43+
duration: '2.6s',
44+
size: '13px',
45+
},
46+
{
47+
tx: '-28px',
48+
ty: '-6px',
49+
rot: '-52deg',
50+
delay: '1.05s',
51+
duration: '2.0s',
52+
size: '9px',
53+
},
54+
{
55+
tx: '26px',
56+
ty: '-4px',
57+
rot: '46deg',
58+
delay: '0.3s',
59+
duration: '2.2s',
60+
size: '10px',
61+
},
62+
{
63+
tx: '-8px',
64+
ty: '-30px',
65+
rot: '-10deg',
66+
delay: '0.85s',
67+
duration: '2.5s',
68+
size: '12px',
69+
},
70+
{
71+
tx: '10px',
72+
ty: '-32px',
73+
rot: '14deg',
74+
delay: '1.45s',
75+
duration: '2.3s',
76+
size: '10px',
77+
},
78+
] as const
79+
2680
const data = ref<SponsorData>(sponsorsData)
2781
const isShow = ref(false)
2882
@@ -79,6 +133,60 @@ onMounted(async () => {
79133
{{ text }}
80134
</div>
81135
</a>
136+
<a
137+
v-if="placement === 'home'"
138+
:href="sponsorUrl"
139+
class="sponsors-item sponsors-item-cta"
140+
target="_blank"
141+
rel="noopener"
142+
>
143+
<span class="cta-heart-wrap">
144+
<svg
145+
class="cta-heart"
146+
viewBox="0 0 24 24"
147+
stroke="currentColor"
148+
stroke-width="1.6"
149+
stroke-linecap="round"
150+
stroke-linejoin="round"
151+
aria-hidden="true"
152+
>
153+
<path
154+
fill="transparent"
155+
d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"
156+
/>
157+
</svg>
158+
<span
159+
class="cta-spark-layer"
160+
aria-hidden="true"
161+
>
162+
<span
163+
v-for="(spark, i) in ctaSparks"
164+
:key="i"
165+
class="cta-spark"
166+
:style="{
167+
'--tx': spark.tx,
168+
'--ty': spark.ty,
169+
'--rot': spark.rot,
170+
'--delay': spark.delay,
171+
'--duration': spark.duration,
172+
'--size': spark.size,
173+
}"
174+
>
175+
<svg
176+
viewBox="0 0 24 24"
177+
aria-hidden="true"
178+
>
179+
<path
180+
fill="currentColor"
181+
d="M12 21s-7-4.5-9.5-9C-0.5 6.5 5 1.5 12 6c7-4.5 12.5 0.5 9.5 6-2.5 4.5-9.5 9-9.5 9z"
182+
/>
183+
</svg>
184+
</span>
185+
</span>
186+
</span>
187+
<span class="cta-title">Become a sponsor</span>
188+
<span class="cta-subtitle">Your logo here</span>
189+
</a>
82190
</div>
83191
</template>
84192
</div>
@@ -142,6 +250,187 @@ onMounted(async () => {
142250
justify-content: center;
143251
}
144252
253+
@property --sponsor-cta-angle {
254+
syntax: "<angle>";
255+
initial-value: 0deg;
256+
inherits: false;
257+
}
258+
259+
.sponsors-item-cta {
260+
/* Multiplier for the heart spray reach. 1 = default, 1.5 = 50% wider, 0.5 = half. */
261+
--spray-scale: 2;
262+
position: relative;
263+
overflow: hidden;
264+
isolation: isolate;
265+
gap: 6px;
266+
text-align: center;
267+
text-decoration: none;
268+
border: 1px solid transparent;
269+
background:
270+
linear-gradient(var(--vp-c-bg-soft), var(--vp-c-bg-soft)) padding-box,
271+
conic-gradient(
272+
from var(--sponsor-cta-angle),
273+
color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent),
274+
color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent) 60deg,
275+
var(--vp-c-brand-1) 110deg,
276+
color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent) 160deg,
277+
color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent) 360deg
278+
)
279+
border-box;
280+
animation: sponsor-cta-shimmer 6s linear infinite;
281+
transition: transform 0.35s ease;
282+
}
283+
284+
.dark .sponsors-item-cta {
285+
background:
286+
linear-gradient(var(--vp-c-bg-soft), var(--vp-c-bg-soft)) padding-box,
287+
conic-gradient(
288+
from var(--sponsor-cta-angle),
289+
color-mix(in srgb, var(--vp-c-brand-1) 18%, transparent),
290+
color-mix(in srgb, var(--vp-c-brand-1) 18%, transparent) 60deg,
291+
var(--vp-c-brand-1) 110deg,
292+
color-mix(in srgb, var(--vp-c-brand-1) 18%, transparent) 160deg,
293+
color-mix(in srgb, var(--vp-c-brand-1) 18%, transparent) 360deg
294+
)
295+
border-box;
296+
}
297+
298+
@keyframes sponsor-cta-shimmer {
299+
to {
300+
--sponsor-cta-angle: 360deg;
301+
}
302+
}
303+
304+
/* .sponsors-item-cta:hover {
305+
transform: translateY(-2px);
306+
} */
307+
308+
.sponsors-item-cta:hover .cta-heart {
309+
color: var(--vp-c-brand-1);
310+
transform: scale(1.08);
311+
}
312+
313+
.sponsors-item-cta:hover .cta-heart path {
314+
fill: color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent);
315+
}
316+
317+
@media (prefers-reduced-motion: reduce) {
318+
.sponsors-item-cta {
319+
animation: none;
320+
}
321+
}
322+
323+
.cta-heart-wrap {
324+
position: relative;
325+
display: block;
326+
width: 26px;
327+
height: 26px;
328+
margin-bottom: 4px;
329+
}
330+
331+
.cta-heart {
332+
display: block;
333+
width: 100%;
334+
height: 100%;
335+
color: color-mix(in srgb, var(--vp-c-brand-1) 78%, transparent);
336+
transition:
337+
color 0.35s ease,
338+
transform 0.35s ease;
339+
}
340+
341+
.cta-heart path {
342+
transition: fill 0.35s ease;
343+
}
344+
345+
/* Override global `.dark svg { fill: ... !important }` for the outline heart. */
346+
.dark .sponsors-item-cta .cta-heart {
347+
fill: none !important;
348+
}
349+
350+
.cta-spark-layer {
351+
position: absolute;
352+
inset: 0;
353+
pointer-events: none;
354+
opacity: 0;
355+
transition: opacity 0.4s ease;
356+
}
357+
358+
.sponsors-item-cta:hover .cta-spark-layer {
359+
opacity: 1;
360+
}
361+
362+
.cta-spark {
363+
position: absolute;
364+
left: 50%;
365+
top: 50%;
366+
width: var(--size);
367+
height: var(--size);
368+
margin-left: calc(var(--size) / -2);
369+
margin-top: calc(var(--size) / -2);
370+
color: var(--vp-c-brand-1);
371+
opacity: 0;
372+
filter: drop-shadow(
373+
0 0 4px color-mix(in srgb, var(--vp-c-brand-1) 45%, transparent)
374+
);
375+
animation: cta-spark-emit var(--duration) ease-out infinite;
376+
animation-delay: var(--delay);
377+
animation-play-state: paused;
378+
}
379+
380+
.sponsors-item-cta:hover .cta-spark {
381+
animation-play-state: running;
382+
}
383+
384+
.cta-spark svg {
385+
display: block;
386+
width: 100%;
387+
height: 100%;
388+
}
389+
390+
@keyframes cta-spark-emit {
391+
0% {
392+
opacity: 0;
393+
transform: translate(0, 0) scale(0.25) rotate(0deg);
394+
}
395+
18% {
396+
opacity: 0.9;
397+
transform: translate(
398+
calc(var(--tx) * 0.28 * var(--spray-scale)),
399+
calc(var(--ty) * 0.28 * var(--spray-scale))
400+
)
401+
scale(0.85) rotate(calc(var(--rot) * 0.3));
402+
}
403+
100% {
404+
opacity: 0;
405+
transform: translate(
406+
calc(var(--tx) * var(--spray-scale)),
407+
calc(var(--ty) * var(--spray-scale))
408+
)
409+
scale(0.55) rotate(var(--rot));
410+
}
411+
}
412+
413+
@media (prefers-reduced-motion: reduce) {
414+
.cta-spark-layer {
415+
display: none;
416+
}
417+
}
418+
419+
.cta-title {
420+
font-size: 15px;
421+
font-weight: 600;
422+
letter-spacing: -0.01em;
423+
color: var(--vp-c-text-1);
424+
}
425+
426+
.cta-subtitle {
427+
font-size: 11px;
428+
font-weight: 500;
429+
letter-spacing: 0.04em;
430+
text-transform: uppercase;
431+
color: var(--vp-c-text-3);
432+
}
433+
145434
.dark .sponsors-item img {
146435
transition: all 0.2s;
147436
filter: grayscale(1) invert(1);

0 commit comments

Comments
 (0)