fill most content + improve ux
This commit is contained in:
@@ -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>
|
||||
|
||||
29
src/components/Footer.astro
Normal file
29
src/components/Footer.astro
Normal 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
29
src/components/Hero.astro
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
))
|
||||
}
|
||||
|
||||
60
src/components/header/Header.astro
Normal file
60
src/components/header/Header.astro
Normal 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>
|
||||
Reference in New Issue
Block a user