fill most content + improve ux

This commit is contained in:
Salam-vsem
2025-05-13 17:00:04 +03:00
parent 0fe2520261
commit f59c0085a3
41 changed files with 307 additions and 169 deletions

View File

@@ -1,22 +1,28 @@
---
import type { coachesSchema } from '@/content.config'
import { useTranslations } from '@/i18n/utils'
import { Image } from 'astro:assets'
import type { z } from 'astro:content'
type Props = {
name: string
description?: string
image: ImageMetadata
imageAlt: string
}
type Props = z.infer<ReturnType<typeof coachesSchema>> & { lang?: string }
const { name, description, image, imageAlt } = Astro.props
const { lang, name, description, image, imageAlt, link } = Astro.props
const t = useTranslations(lang)
---
<div class="flex h-full w-full flex-col items-center justify-start gap-4">
<div class="relative h-[390px] w-[300px]">
<div class="flex h-full w-full flex-col items-center justify-start gap-5">
<a class="relative h-[390px] w-full max-w-[300px]" href={link} target="_blank">
<Image src={image} class="absolute h-full w-full object-cover" alt={imageAlt} />
</div>
</a>
<div class="inline-flex w-full max-w-[300px] flex-col gap-1">
<h3 class="text-xl font-bold uppercase text-text-light">{name}</h3>
{description && <p>{description}</p>}
{
link && (
<a href={link} target="_blank" class="text-brand hover:text-brand/80 mt-2">
{t('coaches.link')}
</a>
)
}
</div>
</div>

View File

@@ -0,0 +1,29 @@
---
import { useTranslations } from '@/i18n/utils'
import { Image } from 'astro:assets'
import logoSrc from '@/assets/logo.png'
type Props = {
lang?: string
}
const { lang } = Astro.props
const t = useTranslations(lang)
---
<footer id="contacts" class="flex flex-col items-center gap-6 text-center">
<script
is:inline
type="text/javascript"
charset="utf-8"
async
src="https://api-maps.yandex.ru/services/constructor/1.0/js/?um=constructor%3A069b17526dc83cbf87c488f13ae089eb3af0f8f74af57e1be82e183434693562&width=100%25&height=400&lang=ru_RU&scroll=true"
></script>
<div class="flex flex-col items-center gap-4 px-6 py-8 text-center">
<Image src={logoSrc} alt="logo" class="h-32 w-auto md:h-48" />
<p class="text-light text-sm">
© {new Date().getFullYear()}
{t('footer.copyright')}
</p>
</div>
</footer>

29
src/components/Hero.astro Normal file
View File

@@ -0,0 +1,29 @@
---
import type { z } from 'astro:content'
import Section from './Section.astro'
import { heroSchema } from '@/content.config'
import { Image } from 'astro:assets'
type Props = z.infer<ReturnType<typeof heroSchema>>
const { title, description, cta, image, imageAlt } = Astro.props
---
<Section id="hero" className="mt-16 p-0 md:mt-0 md:flex-row md:p-0">
<div class="mb-8 px-6 py-10 md:mb-0 md:w-1/2 md:p-24">
<h1 class="mb-4 text-4xl font-bold leading-tight md:text-6xl" set:html={title} />
<p class="text-brand-text mb-6">{description}</p>
{
cta && (
<div class="flex w-full space-x-4">
<a href="#services" class="brand-btn w-full md:w-auto">
{cta?.title}
</a>
</div>
)
}
</div>
<div class="relative hidden md:block md:w-1/2">
<Image src={image} alt={imageAlt} class="absolute h-full w-full object-cover" />
</div>
</Section>

View File

@@ -1,12 +1,23 @@
---
import { Image } from 'astro:assets'
import wideCrossIconSrc from '@/assets/icons/WideCrossIcon.svg'
type Props = {
texts: string[]
}
const { texts } = Astro.props
const repeatedTexts = texts.length < 10 ? Array.from({ length: 10 / texts.length }, () => texts).flat() : texts;
const repeatedTexts =
texts.length < 10 ? Array.from({ length: 10 / texts.length }, () => texts).flat() : texts
---
<div class="flex h-full w-max animate-marquee items-center gap-10 text-5xl font-bold uppercase">
{repeatedTexts.map((text) => <h2>{text}</h2>)}
{
repeatedTexts.map((text) => (
<>
<h2>{text}</h2>
<Image src={wideCrossIconSrc} alt="cross-icon" />
</>
))
}
</div>

View File

@@ -23,7 +23,7 @@ const { title, charachteristics, highlighted = false, link } = Astro.props
charachteristics?.map((charachteristic) => (
<li class="flex items-start space-x-2 justify-between">
<span class="text-text-light">{charachteristic.text}</span>
{charachteristic.price && <span class="ml-2 text-brand text-nowrap">{charachteristic.price}</span>}
{charachteristic.price && <span class="ml-2 text-brand text-nowrap text-end" set:html={charachteristic.price} />}
</li>
))
}

View File

@@ -0,0 +1,60 @@
---
import MobileNavigation from './MobileNavigation'
import { useTranslations } from '@/i18n/utils'
type Props = {
lang?: string
}
const { lang } = Astro.props
const navItems = [
{ key: 'nav.coaches', href: '#coaches' },
{ key: 'nav.slider', href: '#image-slider' },
{ key: 'nav.services', href: '#services' },
{ key: 'nav.reviews', href: '#reviews' },
{ key: 'nav.contact', href: '#contacts' },
] as const
const t = useTranslations(lang)
---
<header
class="fixed left-0 right-0 top-0 z-50 flex h-[var(--header-height)] items-center justify-between bg-bg-main px-6 transition-transform duration-300 md:px-10"
id="main-header"
>
<a class="text-2xl font-bold text-text-light" set:html={t('header.logo')} href="#hero" />
<nav class="hidden md:block">
<ul class="grid grid-flow-col gap-6">
{
navItems.map((item) => (
<li>
<a href={item.href}>{t(item.key)}</a>
</li>
))
}
</ul>
</nav>
<MobileNavigation navItems={navItems} client:visible />
</header>
<script>
let lastScrollY = window.scrollY
const header = document.getElementById('main-header')
window.addEventListener('scroll', () => {
if (!header) return
const currentScrollY = window.scrollY
if (Math.abs(currentScrollY - lastScrollY) < 10) return
if (currentScrollY > lastScrollY && currentScrollY > 100) {
header.style.transform = 'translateY(-100%)'
} else {
header.style.transform = 'translateY(0)'
}
lastScrollY = currentScrollY
})
</script>