Files
chore/frontend/vue-app/src/components/landing/LandingPage.vue
Ryan Kegel c922e1180d
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m23s
feat: add landing page components including hero, features, problem, and footer
- Introduced LandingHero component with logo, tagline, and action buttons.
- Created LandingFeatures component to showcase chore system benefits.
- Developed LandingProblem component explaining the importance of a structured chore system.
- Implemented LandingFooter for navigation and copyright information.
- Added LandingPage to assemble all components and manage navigation.
- Included unit tests for LandingHero component to ensure functionality.
2026-03-04 16:21:26 -05:00

119 lines
2.5 KiB
Vue

<template>
<div class="landing-page">
<!-- Sticky nav -->
<nav class="landing-nav" :class="{ 'landing-nav-scrolled': scrolled }">
<div class="nav-inner">
<img src="/images/c_logo.png" alt="Chorly" class="nav-logo" />
<button class="nav-signin" @click="goToLogin">Sign In</button>
</div>
</nav>
<LandingHero />
<LandingProblem />
<LandingFeatures />
<LandingFooter />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import LandingHero from '@/components/landing/LandingHero.vue'
import LandingProblem from '@/components/landing/LandingProblem.vue'
import LandingFeatures from '@/components/landing/LandingFeatures.vue'
import LandingFooter from '@/components/landing/LandingFooter.vue'
const router = useRouter()
const scrolled = ref(false)
function onScroll() {
scrolled.value = window.scrollY > 40
}
function goToLogin() {
router.push({ name: 'Login' })
}
onMounted(() => {
window.addEventListener('scroll', onScroll, { passive: true })
})
onUnmounted(() => {
window.removeEventListener('scroll', onScroll)
})
</script>
<style scoped>
.landing-page {
min-height: 100svh;
overflow-x: hidden;
}
/* ── Sticky nav ── */
.landing-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
padding: 0.9rem 1.5rem;
transition:
background 0.25s,
box-shadow 0.25s,
border-bottom 0.25s;
}
.landing-nav-scrolled {
background: rgba(26, 24, 48, 0.82);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.3);
border-bottom: 1px solid var(--landing-nav-border);
}
.nav-inner {
max-width: 1100px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-logo {
height: 34px;
filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.35));
}
.nav-signin {
background: var(--landing-glass-bg);
border: 1px solid var(--landing-glass-border);
color: #fff;
padding: 0.45rem 1.2rem;
border-radius: 8px;
font-size: 0.88rem;
font-weight: 600;
cursor: pointer;
transition:
background 0.15s,
box-shadow 0.15s;
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
}
.nav-signin:hover {
background: rgba(255, 255, 255, 0.22);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}
/* Mobile */
@media (max-width: 480px) {
.landing-nav {
padding: 0.75rem 1rem;
}
.nav-logo {
height: 28px;
}
}
</style>