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 {
|
||||
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}
|
||||
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 src={category.icon} alt="" width={40} height={40} />
|
||||
</span>
|
||||
<Image
|
||||
src={category.icon}
|
||||
alt=""
|
||||
width={64}
|
||||
height={64}
|
||||
className="h-16 w-16 shrink-0 object-contain"
|
||||
/>
|
||||
<span>
|
||||
<span className="block font-semibold text-primary">{category.title}</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() {
|
||||
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>
|
||||
<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>
|
||||
</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>
|
||||
</footer>
|
||||
);
|
||||
|
||||
+11
-10
@@ -37,23 +37,24 @@ export function Hero({ slider, beside }: Props) {
|
||||
const besideImages = padSlotImages(beside, 2).slice(0, 2);
|
||||
|
||||
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>
|
||||
<HeroSlider
|
||||
images={slotImages(slider)}
|
||||
aspectRatio={slider?.aspectRatio}
|
||||
fallback={sliderFallback}
|
||||
imageClassName="h-full min-h-[280px] w-full object-cover sm:min-h-[360px]"
|
||||
/>
|
||||
<div className="min-h-[280px] min-w-0 sm:min-h-[360px]">
|
||||
<HeroSlider
|
||||
images={slotImages(slider)}
|
||||
fallback={sliderFallback}
|
||||
className="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4">
|
||||
<div className="grid min-w-0 gap-4">
|
||||
{besideImages.map((image, index) => (
|
||||
<SlotBanner
|
||||
key={image?.id ?? besideFallbacks[index].src}
|
||||
image={image}
|
||||
fallback={besideFallbacks[index]}
|
||||
aspectRatio={beside?.aspectRatio}
|
||||
imageClassName="h-full min-h-[160px] w-full object-cover"
|
||||
aspectRatio={beside?.aspectRatio ?? "74:41"}
|
||||
className="h-full min-h-0"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,11 @@ import type { StaticImage } from "@/lib/static-images";
|
||||
|
||||
type Props = {
|
||||
images: StaticImage[];
|
||||
aspectRatio?: string | null;
|
||||
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 slides = images.length > 0 ? images : [undefined];
|
||||
|
||||
@@ -28,16 +27,14 @@ export function HeroSlider({ images, aspectRatio, fallback, imageClassName }: Pr
|
||||
<SlotBanner
|
||||
image={slides[0]}
|
||||
fallback={fallback}
|
||||
aspectRatio={aspectRatio}
|
||||
className="h-full"
|
||||
imageClassName={imageClassName}
|
||||
className={className}
|
||||
priority
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative h-full">
|
||||
<div className={`relative min-w-0 overflow-hidden rounded-[18px] ${className}`}>
|
||||
{slides.map((image, slideIndex) => (
|
||||
<div
|
||||
key={image?.id ?? slideIndex}
|
||||
@@ -46,9 +43,7 @@ export function HeroSlider({ images, aspectRatio, fallback, imageClassName }: Pr
|
||||
<SlotBanner
|
||||
image={image}
|
||||
fallback={fallback}
|
||||
aspectRatio={aspectRatio}
|
||||
className="h-full"
|
||||
imageClassName={imageClassName}
|
||||
className="h-full w-full"
|
||||
priority={slideIndex === 0}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ export function PromoBanners({ slot }: { slot?: StaticImageSlot }) {
|
||||
const images = padSlotImages(slot, 2);
|
||||
|
||||
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) => {
|
||||
const fallback = rowFallbacks[index] ?? rowFallbacks[1];
|
||||
return (
|
||||
@@ -37,8 +37,7 @@ export function PromoBanners({ slot }: { slot?: StaticImageSlot }) {
|
||||
key={image?.id ?? `${fallback.src}-${index}`}
|
||||
image={image}
|
||||
fallback={fallback}
|
||||
aspectRatio={slot?.aspectRatio}
|
||||
imageClassName="h-48 w-full object-cover sm:h-56"
|
||||
aspectRatio={slot?.aspectRatio ?? "569:264"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -61,8 +60,7 @@ export function DiscountBanner({ slot }: { slot?: StaticImageSlot }) {
|
||||
width: 1168,
|
||||
height: 264,
|
||||
}}
|
||||
aspectRatio={slot?.aspectRatio}
|
||||
imageClassName="h-40 w-full object-cover sm:h-48"
|
||||
aspectRatio={slot?.aspectRatio ?? "146:33"}
|
||||
>
|
||||
{(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">
|
||||
@@ -92,8 +90,7 @@ export function DiscountBanner({ slot }: { slot?: StaticImageSlot }) {
|
||||
width: 1168,
|
||||
height: 264,
|
||||
}}
|
||||
aspectRatio={slot?.aspectRatio}
|
||||
imageClassName="h-40 w-full object-cover sm:h-48"
|
||||
aspectRatio={slot?.aspectRatio ?? "146:33"}
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center justify-between px-6 sm:px-10">
|
||||
<Image
|
||||
|
||||
@@ -27,13 +27,14 @@ export function SlotBanner({
|
||||
fallback,
|
||||
aspectRatio,
|
||||
className = "",
|
||||
imageClassName,
|
||||
imageClassName = "",
|
||||
priority,
|
||||
children,
|
||||
}: Props) {
|
||||
const alt = imageAlt(image, fallback.alt);
|
||||
const mediaClassName = `block h-full w-full object-cover ${imageClassName}`;
|
||||
const media = image?.url ? (
|
||||
<img src={image.url} alt={alt} className={imageClassName} />
|
||||
<img src={image.url} alt={alt} className={mediaClassName} />
|
||||
) : (
|
||||
<Image
|
||||
src={fallback.src}
|
||||
@@ -41,18 +42,18 @@ export function SlotBanner({
|
||||
width={fallback.width}
|
||||
height={fallback.height}
|
||||
priority={priority}
|
||||
className={imageClassName}
|
||||
className={mediaClassName}
|
||||
/>
|
||||
);
|
||||
|
||||
const href = image?.url ? image.linkUrl || undefined : fallback.href;
|
||||
const wrapped = href ? (
|
||||
image?.url ? (
|
||||
<a href={href} className="block h-full">
|
||||
<a href={href} className="block h-full w-full">
|
||||
{media}
|
||||
</a>
|
||||
) : (
|
||||
<Link href={href} className="block h-full">
|
||||
<Link href={href} className="block h-full w-full">
|
||||
{media}
|
||||
</Link>
|
||||
)
|
||||
@@ -62,7 +63,7 @@ export function SlotBanner({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative overflow-hidden rounded-[18px] ${className}`}
|
||||
className={`relative min-w-0 overflow-hidden rounded-[18px] ${className}`}
|
||||
style={aspectRatioStyle(aspectRatio)}
|
||||
>
|
||||
{wrapped}
|
||||
|
||||
Reference in New Issue
Block a user