feat(ui): update hero button text and enhance welcome screen

- Change primary hero button text from "Start Learning" to "Start Vibe Together!" across all language versions to align with project branding
- Update online reading links in README files to point to /welcome.html for better user onboarding
- Enhance WelcomeScreen.vue with improved animation timing and spacing
- Add dynamic SVG wordmark to homepage hero section for visual appeal
- Implement animated color transition for top promo bar
- Remove testing guidelines comment in AGENTS.md as placeholder
- Adjust feature card styling with increased min-height and icon sizes
This commit is contained in:
sanbuphy
2026-03-18 12:12:42 +08:00
parent 39e75df8c7
commit c8b68e5139
24 changed files with 205 additions and 59 deletions
+55 -2
View File
@@ -6,6 +6,7 @@ import GitHubStars from './components/GitHubStars.vue'
import { onMounted, ref, watch, computed } from 'vue'
import ReadingProgress from './components/ReadingProgress.vue'
import { Setting } from '@element-plus/icons-vue'
import easyVibePaths from './data/easyVibePaths.json'
const { frontmatter } = useData()
const route = useRoute()
@@ -416,6 +417,38 @@ watch(sidebarCollapsed, (collapsed) => {
</el-popover>
</ClientOnly>
</template>
<template #home-hero-info-before>
<div
v-if="frontmatter.layout === 'home'"
class="vp-home-wordmark"
>
<svg
viewBox="0 0 460 220"
class="vp-home-wordmark-svg"
>
<defs>
<linearGradient
id="home-hero-ocean"
x1="0"
y1="0"
x2="460"
y2="0"
gradientUnits="userSpaceOnUse"
>
<stop offset="0%" stop-color="#06b6d4" />
<stop offset="50%" stop-color="#0ea5e9" />
<stop offset="100%" stop-color="#3b82f6" />
</linearGradient>
</defs>
<path
v-for="(path, index) in easyVibePaths"
:key="index"
:d="path"
class="vp-home-wordmark-path"
/>
</svg>
</div>
</template>
<template #home-hero-info-after>
<div
v-if="
@@ -467,7 +500,8 @@ watch(sidebarCollapsed, (collapsed) => {
/* 调整打字机容器的样式,使其看起来像原来的 tagline */
.vp-typed-tagline {
padding-top: 8px;
padding-top: 0;
margin-top: 8px;
line-height: 28px;
font-size: 18px;
font-weight: 500;
@@ -485,7 +519,7 @@ watch(sidebarCollapsed, (collapsed) => {
text-align: center;
}
.VPHomeHero .main {
margin: 0 auto;
margin: -18px auto 0;
}
.VPHomeHero .name,
.VPHomeHero .text {
@@ -493,11 +527,30 @@ watch(sidebarCollapsed, (collapsed) => {
margin-left: auto;
margin-right: auto;
}
.VPHomeHero .name {
display: none !important;
}
.VPHomeHero .text {
color: var(--vp-c-text-1) !important;
}
.VPHomeHero .actions {
justify-content: center;
margin-top: 20px;
}
.vp-home-wordmark {
display: flex;
justify-content: center;
margin-top: -12px;
margin-bottom: 18px;
}
.vp-home-wordmark-svg {
width: min(380px, 52vw);
height: auto;
filter: none;
}
.vp-home-wordmark-path {
fill: url(#home-hero-ocean);
stroke: none;
}
@media (min-width: 640px) {
@@ -8,6 +8,11 @@ const { site, page, lang } = useData()
const activeTab = ref('home')
const showLangMenu = ref(false)
const topPromoProgress = ref(1)
const topPromoIntroProgress = ref(0)
const topPromoColorProgress = ref(0)
let topPromoIntroRaf = 0
let topPromoColorRaf = 0
let topPromoColorTimer = 0
const WELCOME_SEEN_KEY = 'easy-vibe-welcome-seen'
// Appendix Scroll Logic
@@ -1620,12 +1625,27 @@ const updateTopPromoVisibility = () => {
}
const topPromoStyle = computed(() => {
const progress = topPromoProgress.value
const scrollProgress = topPromoProgress.value
const introProgress = topPromoIntroProgress.value
const colorProgress = topPromoColorProgress.value
const progress = scrollProgress * introProgress
const scrollOffset = -100 * (1 - scrollProgress)
const startTextColor = { r: 255, g: 255, b: 255 }
const endTextColor = { r: 29, g: 29, b: 31 }
const startBgColor = { r: 0, g: 113, b: 227 }
const endBgColor = { r: 245, g: 245, b: 247 }
const startLinkColor = { r: 255, g: 255, b: 255 }
const endLinkColor = { r: 0, g: 102, b: 204 }
const textColor = `rgb(${Math.round(startTextColor.r + (endTextColor.r - startTextColor.r) * colorProgress)}, ${Math.round(startTextColor.g + (endTextColor.g - startTextColor.g) * colorProgress)}, ${Math.round(startTextColor.b + (endTextColor.b - startTextColor.b) * colorProgress)})`
const bgColor = `rgb(${Math.round(startBgColor.r + (endBgColor.r - startBgColor.r) * colorProgress)}, ${Math.round(startBgColor.g + (endBgColor.g - startBgColor.g) * colorProgress)}, ${Math.round(startBgColor.b + (endBgColor.b - startBgColor.b) * colorProgress)})`
const linkColor = `rgb(${Math.round(startLinkColor.r + (endLinkColor.r - startLinkColor.r) * colorProgress)}, ${Math.round(startLinkColor.g + (endLinkColor.g - startLinkColor.g) * colorProgress)}, ${Math.round(startLinkColor.b + (endLinkColor.b - startLinkColor.b) * colorProgress)})`
return {
opacity: progress,
transform: `translateY(${-100 * (1 - progress)}%)`,
transform: `translateY(${scrollOffset}%)`,
maxHeight: `${30 * progress}px`,
borderTopColor: `rgba(229, 229, 234, ${progress})`,
backgroundColor: bgColor,
color: textColor,
'--top-promo-link-color': linkColor,
pointerEvents: progress < 0.02 ? 'none' : 'auto'
}
})
@@ -1636,6 +1656,33 @@ const replayIntro = () => {
}
onMounted(() => {
const introDuration = 1800
const colorDelay = 500
const colorDuration = 1800
const introStart = performance.now()
const stepTopPromoIntro = (now) => {
const raw = Math.min(1, (now - introStart) / introDuration)
const eased = 1 - Math.pow(1 - raw, 3)
topPromoIntroProgress.value = eased
if (raw < 1) {
topPromoIntroRaf = window.requestAnimationFrame(stepTopPromoIntro)
return
}
topPromoColorTimer = window.setTimeout(() => {
const colorStart = performance.now()
const stepTopPromoColor = (time) => {
const colorRaw = Math.min(1, (time - colorStart) / colorDuration)
const colorEased = 1 - Math.pow(1 - colorRaw, 3)
topPromoColorProgress.value = colorEased
if (colorRaw < 1) {
topPromoColorRaf = window.requestAnimationFrame(stepTopPromoColor)
}
}
topPromoColorRaf = window.requestAnimationFrame(stepTopPromoColor)
}, colorDelay)
}
topPromoIntroRaf = window.requestAnimationFrame(stepTopPromoIntro)
const currentPath = window.location.pathname
const basePath = site.value.base || '/'
const normalizedBase = basePath.endsWith('/') ? basePath : `${basePath}/`
@@ -1681,6 +1728,18 @@ onMounted(() => {
})
onUnmounted(() => {
if (topPromoIntroRaf) {
window.cancelAnimationFrame(topPromoIntroRaf)
topPromoIntroRaf = 0
}
if (topPromoColorRaf) {
window.cancelAnimationFrame(topPromoColorRaf)
topPromoColorRaf = 0
}
if (topPromoColorTimer) {
window.clearTimeout(topPromoColorTimer)
topPromoColorTimer = 0
}
document.removeEventListener('click', closeLangMenu)
if (appendixWrapper.value) {
appendixWrapper.value.removeEventListener('scroll', onAppendixScroll)
@@ -2365,8 +2424,8 @@ a {
}
.nav-promo {
height: 30px;
max-height: 30px;
border-top: 1px solid #e5e5ea;
display: flex;
align-items: center;
justify-content: center;
@@ -2378,17 +2437,19 @@ a {
transform-origin: top center;
position: relative;
z-index: 1;
will-change: transform, opacity, max-height, border-color;
will-change: transform, opacity, max-height, background-color, color;
transition:
transform 0.16s ease-out,
opacity 0.16s ease-out,
max-height 0.16s ease-out,
border-color 0.16s ease-out;
background-color 0.22s ease-out,
color 0.22s ease-out;
}
.nav-promo a {
color: #0066cc;
color: var(--top-promo-link-color, #0066cc);
text-decoration: none;
transition: color 0.25s ease-out;
}
.button {
@@ -2786,6 +2847,7 @@ a {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04);
scroll-snap-align: start;
width: 100%;
min-height: 360px;
transition: transform 0.25s ease;
}
@@ -2853,14 +2915,14 @@ a {
}
.appendix-icon-wrapper {
width: 64px;
height: 64px;
border-radius: 17.5px; /* Apple continuous curve approximation */
width: 100%;
height: 190px;
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
margin-bottom: 16px;
font-size: 88px;
margin-bottom: 14px;
/* Glassmorphism / VisionOS Style */
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
@@ -2916,7 +2978,7 @@ a {
}
.appendix-text {
font-size: 15px;
font-size: 13px;
line-height: 1.5;
color: #6e6e73;
margin: 0;
@@ -2928,7 +2990,7 @@ a {
font-weight: 600;
color: #1d1d1f;
margin-right: 6px;
font-size: 13px;
font-size: 12px;
}
/* Footer */
@@ -258,23 +258,26 @@ onUnmounted(() => {
}
.welcome-tip {
margin: 24px 0 0;
margin: 44px 0 0;
font-size: 11px;
letter-spacing: 0.08em;
color: rgba(34, 34, 34, 0.38);
letter-spacing: 0.2em;
color: rgba(34, 34, 34, 0.32);
text-transform: uppercase;
animation: welcome-tip-breathe 7s ease-in-out infinite;
animation: welcome-tip-breathe 5s ease-in-out infinite;
}
@keyframes welcome-tip-breathe {
0% {
opacity: 0.24;
opacity: 0;
}
50% {
opacity: 0.72;
40% {
opacity: 0.55;
}
80% {
opacity: 0;
}
100% {
opacity: 0.24;
opacity: 0;
}
}
+31 -3
View File
@@ -305,15 +305,43 @@
}
/* Hero Section Refinements */
.VPHomeHero .name {
background: -webkit-linear-gradient(315deg, #0071e3 10%, #5ac8fa 90%);
.VPHomeHero .name,
.VPHomeHero .name.clip {
position: relative;
top: -8px;
background-image: linear-gradient(
100deg,
#06b6d4 0%,
#0ea5e9 18%,
#3b82f6 36%,
#9d4edd 56%,
#ff3c81 74%,
#f97316 88%,
#06b6d4 100%
);
background-size: 220% 220%;
background-position: 0% 50%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
filter: drop-shadow(0 0 24px rgba(0, 113, 227, 0.28));
filter: drop-shadow(0 0 22px rgba(59, 130, 246, 0.26));
animation: ev-hero-name-flow 8s ease-in-out infinite alternate;
}
@keyframes ev-hero-name-flow {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 35% 50%;
}
}
.VPHomeHero .text {
display: none !important;
font-weight: 600;
color: #1d1d1f;
letter-spacing: -0.02em;