Polish homepage banners and add the Meshkee footer credit.
Keep slider and side banners from overlapping, restore rounded banner corners, and match the safeteb footer bar. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
dfb791eb50
commit
03e377b6be
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -77,3 +77,158 @@ body {
|
|||||||
.rich-content a {
|
.rich-content a {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-bottom-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-bottom-site {
|
||||||
|
color: #9c9c9c;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: #9c9c9c;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:hover {
|
||||||
|
color: #e0e0e0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__frame {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__line {
|
||||||
|
position: absolute;
|
||||||
|
background: #777;
|
||||||
|
transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__line--l {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: top;
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__line--b {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: left;
|
||||||
|
transition-delay: 0.14s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__line--r {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: bottom;
|
||||||
|
transition-delay: 0.28s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__line--t {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: right;
|
||||||
|
transition-delay: 0.42s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:hover .meshkee-credit__line--l,
|
||||||
|
.meshkee-credit:hover .meshkee-credit__line--r {
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:hover .meshkee-credit__line--b,
|
||||||
|
.meshkee-credit:hover .meshkee-credit__line--t {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:not(:hover) .meshkee-credit__line--l {
|
||||||
|
transition-delay: 0.42s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:not(:hover) .meshkee-credit__line--b {
|
||||||
|
transition-delay: 0.28s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:not(:hover) .meshkee-credit__line--r {
|
||||||
|
transition-delay: 0.14s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:not(:hover) .meshkee-credit__line--t {
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__logo {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: auto;
|
||||||
|
height: 42px;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
object-fit: contain;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:hover .meshkee-credit__logo {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__text {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
min-height: 42px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__label {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit__name {
|
||||||
|
color: #cfcfcf;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: color 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meshkee-credit:hover .meshkee-credit__name {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 575px) {
|
||||||
|
.footer-bottom-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,9 +11,13 @@ export function CategoryRow() {
|
|||||||
href={category.href}
|
href={category.href}
|
||||||
className="flex items-center gap-3 rounded-2xl bg-page px-3 py-4"
|
className="flex items-center gap-3 rounded-2xl bg-page px-3 py-4"
|
||||||
>
|
>
|
||||||
<span className="flex h-14 w-14 shrink-0 items-center justify-center rounded-full bg-white">
|
<Image
|
||||||
<Image src={category.icon} alt="" width={40} height={40} />
|
src={category.icon}
|
||||||
</span>
|
alt=""
|
||||||
|
width={64}
|
||||||
|
height={64}
|
||||||
|
className="h-16 w-16 shrink-0 object-contain"
|
||||||
|
/>
|
||||||
<span>
|
<span>
|
||||||
<span className="block font-semibold text-primary">{category.title}</span>
|
<span className="block font-semibold text-primary">{category.title}</span>
|
||||||
<span className="mt-1 block text-xs text-muted">{category.subtitle}</span>
|
<span className="mt-1 block text-xs text-muted">{category.subtitle}</span>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { consultants, navItems, site } from "@/data/site";
|
|||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="mt-8 bg-[#1f2a16] text-white">
|
<footer className="bg-[#1f2a16] text-white">
|
||||||
<div className="mx-auto grid max-w-7xl gap-10 px-4 py-12 md:grid-cols-4">
|
<div className="mx-auto grid max-w-7xl gap-10 px-4 py-12 md:grid-cols-4">
|
||||||
<div>
|
<div>
|
||||||
<Image src="/images/logo-footer.png" alt={site.name} width={180} height={60} className="mb-4 h-12 w-auto brightness-0 invert" />
|
<Image src="/images/logo-footer.png" alt={site.name} width={180} height={60} className="mb-4 h-12 w-auto brightness-0 invert" />
|
||||||
@@ -61,8 +61,35 @@ export function Footer() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-white/10 py-4 text-center text-xs text-white/60">
|
<div className="border-t border-white/10">
|
||||||
طراحی و توسعه توسط تیم تجارت الکترونیک مشکی
|
<div className="footer-bottom-bar mx-auto max-w-7xl px-4 py-3">
|
||||||
|
<p className="footer-bottom-site">{site.name}</p>
|
||||||
|
<a
|
||||||
|
href="https://meshkee.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
dir="ltr"
|
||||||
|
className="meshkee-credit"
|
||||||
|
>
|
||||||
|
<span className="meshkee-credit__frame" aria-hidden="true">
|
||||||
|
<span className="meshkee-credit__line meshkee-credit__line--l" />
|
||||||
|
<span className="meshkee-credit__line meshkee-credit__line--t" />
|
||||||
|
<span className="meshkee-credit__line meshkee-credit__line--r" />
|
||||||
|
<span className="meshkee-credit__line meshkee-credit__line--b" />
|
||||||
|
</span>
|
||||||
|
<Image
|
||||||
|
src="/images/meshkee-logo-white.png"
|
||||||
|
alt="Meshkee"
|
||||||
|
width={42}
|
||||||
|
height={42}
|
||||||
|
className="meshkee-credit__logo"
|
||||||
|
/>
|
||||||
|
<span className="meshkee-credit__text">
|
||||||
|
<span className="meshkee-credit__label">Designed And Developed By</span>
|
||||||
|
<span className="meshkee-credit__name">Meshkee E-Commerce Team</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
|
|||||||
+11
-10
@@ -37,23 +37,24 @@ export function Hero({ slider, beside }: Props) {
|
|||||||
const besideImages = padSlotImages(beside, 2).slice(0, 2);
|
const besideImages = padSlotImages(beside, 2).slice(0, 2);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="mx-auto grid max-w-7xl gap-4 px-4 py-6 lg:grid-cols-[2fr_1fr]">
|
<section className="mx-auto grid max-w-7xl gap-4 px-4 py-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
|
||||||
<h1 className="sr-only">{site.name}</h1>
|
<h1 className="sr-only">{site.name}</h1>
|
||||||
<HeroSlider
|
<div className="min-h-[280px] min-w-0 sm:min-h-[360px]">
|
||||||
images={slotImages(slider)}
|
<HeroSlider
|
||||||
aspectRatio={slider?.aspectRatio}
|
images={slotImages(slider)}
|
||||||
fallback={sliderFallback}
|
fallback={sliderFallback}
|
||||||
imageClassName="h-full min-h-[280px] w-full object-cover sm:min-h-[360px]"
|
className="h-full w-full"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid min-w-0 gap-4">
|
||||||
{besideImages.map((image, index) => (
|
{besideImages.map((image, index) => (
|
||||||
<SlotBanner
|
<SlotBanner
|
||||||
key={image?.id ?? besideFallbacks[index].src}
|
key={image?.id ?? besideFallbacks[index].src}
|
||||||
image={image}
|
image={image}
|
||||||
fallback={besideFallbacks[index]}
|
fallback={besideFallbacks[index]}
|
||||||
aspectRatio={beside?.aspectRatio}
|
aspectRatio={beside?.aspectRatio ?? "74:41"}
|
||||||
imageClassName="h-full min-h-[160px] w-full object-cover"
|
className="h-full min-h-0"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import type { StaticImage } from "@/lib/static-images";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
images: StaticImage[];
|
images: StaticImage[];
|
||||||
aspectRatio?: string | null;
|
|
||||||
fallback: SlotFallback;
|
fallback: SlotFallback;
|
||||||
imageClassName: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function HeroSlider({ images, aspectRatio, fallback, imageClassName }: Props) {
|
export function HeroSlider({ images, fallback, className = "" }: Props) {
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const slides = images.length > 0 ? images : [undefined];
|
const slides = images.length > 0 ? images : [undefined];
|
||||||
|
|
||||||
@@ -28,16 +27,14 @@ export function HeroSlider({ images, aspectRatio, fallback, imageClassName }: Pr
|
|||||||
<SlotBanner
|
<SlotBanner
|
||||||
image={slides[0]}
|
image={slides[0]}
|
||||||
fallback={fallback}
|
fallback={fallback}
|
||||||
aspectRatio={aspectRatio}
|
className={className}
|
||||||
className="h-full"
|
|
||||||
imageClassName={imageClassName}
|
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full">
|
<div className={`relative min-w-0 overflow-hidden rounded-[18px] ${className}`}>
|
||||||
{slides.map((image, slideIndex) => (
|
{slides.map((image, slideIndex) => (
|
||||||
<div
|
<div
|
||||||
key={image?.id ?? slideIndex}
|
key={image?.id ?? slideIndex}
|
||||||
@@ -46,9 +43,7 @@ export function HeroSlider({ images, aspectRatio, fallback, imageClassName }: Pr
|
|||||||
<SlotBanner
|
<SlotBanner
|
||||||
image={image}
|
image={image}
|
||||||
fallback={fallback}
|
fallback={fallback}
|
||||||
aspectRatio={aspectRatio}
|
className="h-full w-full"
|
||||||
className="h-full"
|
|
||||||
imageClassName={imageClassName}
|
|
||||||
priority={slideIndex === 0}
|
priority={slideIndex === 0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function PromoBanners({ slot }: { slot?: StaticImageSlot }) {
|
|||||||
const images = padSlotImages(slot, 2);
|
const images = padSlotImages(slot, 2);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="mx-auto grid max-w-7xl gap-4 px-4 py-4 md:grid-cols-2">
|
<section className="mx-auto grid max-w-7xl gap-4 px-4 py-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
||||||
{images.map((image, index) => {
|
{images.map((image, index) => {
|
||||||
const fallback = rowFallbacks[index] ?? rowFallbacks[1];
|
const fallback = rowFallbacks[index] ?? rowFallbacks[1];
|
||||||
return (
|
return (
|
||||||
@@ -37,8 +37,7 @@ export function PromoBanners({ slot }: { slot?: StaticImageSlot }) {
|
|||||||
key={image?.id ?? `${fallback.src}-${index}`}
|
key={image?.id ?? `${fallback.src}-${index}`}
|
||||||
image={image}
|
image={image}
|
||||||
fallback={fallback}
|
fallback={fallback}
|
||||||
aspectRatio={slot?.aspectRatio}
|
aspectRatio={slot?.aspectRatio ?? "569:264"}
|
||||||
imageClassName="h-48 w-full object-cover sm:h-56"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -61,8 +60,7 @@ export function DiscountBanner({ slot }: { slot?: StaticImageSlot }) {
|
|||||||
width: 1168,
|
width: 1168,
|
||||||
height: 264,
|
height: 264,
|
||||||
}}
|
}}
|
||||||
aspectRatio={slot?.aspectRatio}
|
aspectRatio={slot?.aspectRatio ?? "146:33"}
|
||||||
imageClassName="h-40 w-full object-cover sm:h-48"
|
|
||||||
>
|
>
|
||||||
{(image.titleFa || image.titleEn || image.title || image.subtext) && (
|
{(image.titleFa || image.titleEn || image.title || image.subtext) && (
|
||||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-end px-6 sm:px-10">
|
<div className="pointer-events-none absolute inset-0 flex items-center justify-end px-6 sm:px-10">
|
||||||
@@ -92,8 +90,7 @@ export function DiscountBanner({ slot }: { slot?: StaticImageSlot }) {
|
|||||||
width: 1168,
|
width: 1168,
|
||||||
height: 264,
|
height: 264,
|
||||||
}}
|
}}
|
||||||
aspectRatio={slot?.aspectRatio}
|
aspectRatio={slot?.aspectRatio ?? "146:33"}
|
||||||
imageClassName="h-40 w-full object-cover sm:h-48"
|
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 flex items-center justify-between px-6 sm:px-10">
|
<div className="absolute inset-0 flex items-center justify-between px-6 sm:px-10">
|
||||||
<Image
|
<Image
|
||||||
|
|||||||
@@ -27,13 +27,14 @@ export function SlotBanner({
|
|||||||
fallback,
|
fallback,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
className = "",
|
className = "",
|
||||||
imageClassName,
|
imageClassName = "",
|
||||||
priority,
|
priority,
|
||||||
children,
|
children,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const alt = imageAlt(image, fallback.alt);
|
const alt = imageAlt(image, fallback.alt);
|
||||||
|
const mediaClassName = `block h-full w-full object-cover ${imageClassName}`;
|
||||||
const media = image?.url ? (
|
const media = image?.url ? (
|
||||||
<img src={image.url} alt={alt} className={imageClassName} />
|
<img src={image.url} alt={alt} className={mediaClassName} />
|
||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
src={fallback.src}
|
src={fallback.src}
|
||||||
@@ -41,18 +42,18 @@ export function SlotBanner({
|
|||||||
width={fallback.width}
|
width={fallback.width}
|
||||||
height={fallback.height}
|
height={fallback.height}
|
||||||
priority={priority}
|
priority={priority}
|
||||||
className={imageClassName}
|
className={mediaClassName}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const href = image?.url ? image.linkUrl || undefined : fallback.href;
|
const href = image?.url ? image.linkUrl || undefined : fallback.href;
|
||||||
const wrapped = href ? (
|
const wrapped = href ? (
|
||||||
image?.url ? (
|
image?.url ? (
|
||||||
<a href={href} className="block h-full">
|
<a href={href} className="block h-full w-full">
|
||||||
{media}
|
{media}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<Link href={href} className="block h-full">
|
<Link href={href} className="block h-full w-full">
|
||||||
{media}
|
{media}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
@@ -62,7 +63,7 @@ export function SlotBanner({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative overflow-hidden rounded-[18px] ${className}`}
|
className={`relative min-w-0 overflow-hidden rounded-[18px] ${className}`}
|
||||||
style={aspectRatioStyle(aspectRatio)}
|
style={aspectRatioStyle(aspectRatio)}
|
||||||
>
|
>
|
||||||
{wrapped}
|
{wrapped}
|
||||||
|
|||||||
Reference in New Issue
Block a user