Add en title

This commit is contained in:
amirhosein.ashourloo
2026-08-15 14:04:09 +03:30
parent 02c7dd51d2
commit 2f6c1a547e
22 changed files with 89 additions and 81 deletions
-1
View File
@@ -36,7 +36,6 @@ export default async function AboutPage() {
<PageHero
image={heroImage}
imageMobile={heroImageMobile}
caption={aboutPage.hero.caption}
title={aboutPage.hero.title}
breadcrumb={aboutPage.hero.breadcrumb}
/>
+6 -1
View File
@@ -45,7 +45,12 @@ export default async function BlogDetailPage({
<>
<SiteHeader />
<main>
<NewsArticleDetail item={toDisplayNewsItem(item)} basePath="/blog" sectionLabel="بلاگ" />
<NewsArticleDetail
item={toDisplayNewsItem(item)}
basePath="/blog"
sectionLabel="بلاگ"
sectionLabelEn="BLOG"
/>
</main>
<SiteFooter />
</>
-1
View File
@@ -36,7 +36,6 @@ export default async function BlogListPage({
<PageHero
image={heroImage}
imageMobile={heroImageMobile}
caption={blogListingPage.hero.caption}
title={blogListingPage.hero.title}
breadcrumb={blogListingPage.hero.breadcrumb}
/>
+4 -8
View File
@@ -17,13 +17,9 @@ export const iranYekan = localFont({
preload: true,
});
/** Montserrat — used for the Latin project names and section kickers. */
export const montserrat = localFont({
src: [
{ path: "../assets/fonts/montserrat-light.ttf", weight: "300", style: "normal" },
{ path: "../assets/fonts/montserrat-regular.woff2", weight: "400", style: "normal" },
{ path: "../assets/fonts/montserrat-bold.ttf", weight: "700", style: "normal" },
],
variable: "--font-montserrat",
/** Arual — used for every English title/kicker across the site. */
export const arual = localFont({
src: "../assets/fonts/arual.ttf",
variable: "--font-arual",
display: "swap",
});
+1 -1
View File
@@ -14,7 +14,7 @@
/* ---- type ---------------------------------------------------------- */
--font-sans: var(--font-iranyekan), system-ui, sans-serif;
--font-latin: var(--font-montserrat), system-ui, sans-serif;
--font-latin: var(--font-arual), Arial, sans-serif;
/* ---- motion -------------------------------------------------------- */
--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
+2 -2
View File
@@ -1,6 +1,6 @@
import type { Metadata, Viewport } from "next";
import { iranYekan, montserrat } from "./fonts";
import { arual, iranYekan } from "./fonts";
import { site } from "@/data/site";
import "./globals.css";
@@ -36,7 +36,7 @@ export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="fa" dir="rtl" className={`${iranYekan.variable} ${montserrat.variable}`}>
<html lang="fa" dir="rtl" className={`${iranYekan.variable} ${arual.variable}`}>
<head>
{/* Scroll reveals are a JS enhancement — without it, show everything. */}
<noscript>
+11 -9
View File
@@ -8,6 +8,7 @@ import { NewsCarousel } from "@/components/news/NewsCarousel";
import { Editorial } from "@/components/shared/Editorial";
import { PageCta } from "@/components/shared/PageCta";
import { PageHero } from "@/components/shared/PageHero";
import { SectionLabel } from "@/components/shared/SectionLabel";
import { SiteFooter } from "@/components/SiteFooter";
import { SiteHeader } from "@/components/SiteHeader";
import { newsAndBlogPage } from "@/data/site";
@@ -37,7 +38,6 @@ export default async function NewsAndBlogPage() {
<PageHero
image={heroImage}
imageMobile={heroImageMobile}
caption={newsAndBlogPage.hero.caption}
title={newsAndBlogPage.hero.title}
breadcrumb={newsAndBlogPage.hero.breadcrumb}
/>
@@ -62,10 +62,11 @@ export default async function NewsAndBlogPage() {
<section className="bg-sand py-16 lg:py-24">
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<p className="kicker mb-10 justify-center">
{newsAndBlogPage.news.caption}
<span className="latin">{newsAndBlogPage.news.captionLatin}</span>
</p>
<SectionLabel
fa={newsAndBlogPage.news.caption}
en={newsAndBlogPage.news.captionLatin}
className="mb-10"
/>
</Reveal>
</div>
@@ -86,10 +87,11 @@ export default async function NewsAndBlogPage() {
<section className={`py-16 lg:py-24 ${hasNews ? "bg-paper" : "bg-sand"}`}>
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<p className="kicker mb-10 justify-center">
{newsAndBlogPage.blog.caption}
<span className="latin">{newsAndBlogPage.blog.captionLatin}</span>
</p>
<SectionLabel
fa={newsAndBlogPage.blog.caption}
en={newsAndBlogPage.blog.captionLatin}
className="mb-10"
/>
</Reveal>
</div>
+6 -1
View File
@@ -45,7 +45,12 @@ export default async function NewsDetailPage({
<>
<SiteHeader />
<main>
<NewsArticleDetail item={toDisplayNewsItem(item)} basePath="/news" sectionLabel="اخبار" />
<NewsArticleDetail
item={toDisplayNewsItem(item)}
basePath="/news"
sectionLabel="اخبار"
sectionLabelEn="NEWS"
/>
</main>
<SiteFooter />
</>
-1
View File
@@ -36,7 +36,6 @@ export default async function NewsListPage({
<PageHero
image={heroImage}
imageMobile={heroImageMobile}
caption={newsListingPage.hero.caption}
title={newsListingPage.hero.title}
breadcrumb={newsListingPage.hero.breadcrumb}
/>
-1
View File
@@ -32,7 +32,6 @@ export default async function ServicesPage() {
<main>
<PageHero
image={servicesHero}
caption={servicesPage.hero.caption}
title={servicesPage.hero.title}
breadcrumb={servicesPage.hero.breadcrumb}
/>
+4 -16
View File
@@ -6,6 +6,7 @@ import { ProjectVideo } from "@/components/portfolios/ProjectVideo";
import { Reveal } from "@/components/Reveal";
import { PageCta } from "@/components/shared/PageCta";
import { PageHero } from "@/components/shared/PageHero";
import { SectionLabel } from "@/components/shared/SectionLabel";
import { SiteFooter } from "@/components/SiteFooter";
import { SiteHeader } from "@/components/SiteHeader";
import { portfoliosPage, projectVideos } from "@/data/site";
@@ -33,12 +34,6 @@ export async function generateMetadata({
};
}
const publishedDateFormatter = new Intl.DateTimeFormat("fa-IR", {
year: "numeric",
month: "long",
day: "numeric",
});
/**
* Structurally inspired by binghatti.com's project pages (hero, in-page jump
* nav, gallery grid, closing CTA) — but scoped strictly to what the meshkee
@@ -58,7 +53,6 @@ export default async function PortfolioDetailPage({
const project = toDisplayProject(item);
const hasGallery = project.gallery.length > 0;
const videoUrl = projectVideos[project.slug];
const publishedLabel = publishedDateFormatter.format(new Date(project.publishedAt));
const navItems = [
{ id: "overview", label: "معرفی پروژه" },
@@ -79,7 +73,6 @@ export default async function PortfolioDetailPage({
<main>
<PageHero
image={project.image}
caption={project.type}
title={project.name}
breadcrumb={[
{ label: "صفحه اصلی", href: "/" },
@@ -109,12 +102,7 @@ export default async function PortfolioDetailPage({
<section id="overview" className="scroll-mt-20 bg-paper py-16 lg:py-24">
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<div className="mx-auto mb-14 flex max-w-[738px] flex-col items-center gap-3 text-center">
<p className="kicker justify-center">معرفی پروژه</p>
<time dateTime={project.publishedAt} className="text-xs text-muted">
{publishedLabel}
</time>
</div>
<SectionLabel fa="معرفی پروژه" en="PROJECT OVERVIEW" className="mb-14" />
</Reveal>
{project.bodyHtml && (
@@ -129,7 +117,7 @@ export default async function PortfolioDetailPage({
<section id="video" className={`scroll-mt-20 ${videoBg} py-16 lg:py-24`}>
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<p className="kicker mb-14 justify-center">ویدیو</p>
<SectionLabel fa="ویدیو" en="VIDEO" className="mb-14" />
</Reveal>
<Reveal delay={90}>
@@ -143,7 +131,7 @@ export default async function PortfolioDetailPage({
<section id="gallery" className={`scroll-mt-20 ${galleryBg} py-16 lg:py-24`}>
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<p className="kicker mb-14 justify-center">گالری تصاویر</p>
<SectionLabel fa="گالری تصاویر" en="GALLERY" className="mb-14" />
</Reveal>
<ProjectGalleryLightbox images={project.gallery} alt={project.name} />
-1
View File
@@ -31,7 +31,6 @@ export default async function PortfoliosPage() {
<PageHero
image={heroImage}
imageMobile={heroImageMobile}
caption={portfoliosPage.hero.caption}
title={portfoliosPage.hero.title}
breadcrumb={portfoliosPage.hero.breadcrumb}
/>
Binary file not shown.
+2 -1
View File
@@ -3,6 +3,7 @@ import Link from "next/link";
import aboutImage from "@/assets/images/about/about.jpg";
import { Reveal } from "@/components/Reveal";
import { SectionLabel } from "@/components/shared/SectionLabel";
import { about, site, stats } from "@/data/site";
export function About() {
@@ -48,7 +49,7 @@ export function About() {
<div className="order-1 lg:order-2">
<Reveal>
<p className="kicker mb-7">{about.kicker}</p>
<SectionLabel fa={about.kicker} en="ABOUT US" align="start" className="mb-7" />
</Reveal>
<Reveal delay={90}>
+2 -3
View File
@@ -3,6 +3,7 @@ import Link from "next/link";
import ctaImage from "@/assets/images/projects/elysium.jpg";
import { Reveal } from "@/components/Reveal";
import { SectionLabel } from "@/components/shared/SectionLabel";
import { contact } from "@/data/site";
export function ContactCta() {
@@ -21,9 +22,7 @@ export function ContactCta() {
<div className="mx-auto max-w-[1400px] px-5 text-center lg:px-10">
<Reveal>
<p className="kicker mb-7 justify-center">
<span className="latin">GET IN TOUCH</span>
</p>
<SectionLabel fa="تماس با ما" en="GET IN TOUCH" className="mb-7" />
</Reveal>
<Reveal delay={90}>
+2 -3
View File
@@ -2,6 +2,7 @@ import Image from "next/image";
import Link from "next/link";
import { Reveal } from "@/components/Reveal";
import { SectionLabel } from "@/components/shared/SectionLabel";
import type { DisplayNewsItem } from "@/lib/api";
export function News({ items }: { items: DisplayNewsItem[] }) {
@@ -13,9 +14,7 @@ export function News({ items }: { items: DisplayNewsItem[] }) {
<div className="flex flex-wrap items-end justify-between gap-8">
<div>
<Reveal>
<p className="kicker mb-6">
<span className="latin">NEWS &amp; ARTICLES</span>
</p>
<SectionLabel fa="مقالات و اخبار" en="NEWS & ARTICLES" align="start" className="mb-6" />
</Reveal>
<Reveal delay={90}>
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-ink lg:text-[2.625rem]">
+2 -3
View File
@@ -5,6 +5,7 @@ import Link from "next/link";
import { useCallback, useEffect, useRef, useState } from "react";
import { Reveal } from "@/components/Reveal";
import { SectionLabel } from "@/components/shared/SectionLabel";
import type { DisplayProject } from "@/lib/api";
export function Projects({ projects }: { projects: DisplayProject[] }) {
@@ -49,9 +50,7 @@ export function Projects({ projects }: { projects: DisplayProject[] }) {
<div className="flex flex-wrap items-end justify-between gap-8">
<div>
<Reveal>
<p className="kicker mb-6">
<span className="latin">PORTFOLIO</span>
</p>
<SectionLabel fa="نمونه کارها" en="PORTFOLIO" align="start" className="mb-6" />
</Reveal>
<Reveal delay={90}>
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-white lg:text-[2.625rem]">
+2 -3
View File
@@ -2,6 +2,7 @@ import Link from "next/link";
import { Reveal } from "@/components/Reveal";
import { serviceIcons } from "@/components/ServiceIcons";
import { SectionLabel } from "@/components/shared/SectionLabel";
import { services } from "@/data/site";
export function Services() {
@@ -11,9 +12,7 @@ export function Services() {
<div className="flex flex-wrap items-end justify-between gap-8">
<div>
<Reveal>
<p className="kicker mb-6">
<span className="latin">WHAT WE DO</span>
</p>
<SectionLabel fa="خدمات ما" en="WHAT WE DO" align="start" className="mb-6" />
</Reveal>
<Reveal delay={90}>
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-ink lg:text-[2.625rem]">
+5 -8
View File
@@ -2,6 +2,7 @@ import { ProjectGalleryLightbox } from "@/components/portfolios/ProjectGalleryLi
import { Reveal } from "@/components/Reveal";
import { PageCta } from "@/components/shared/PageCta";
import { PageHero } from "@/components/shared/PageHero";
import { SectionLabel } from "@/components/shared/SectionLabel";
import { newsAndBlogPage } from "@/data/site";
import type { DisplayNewsItem } from "@/lib/api";
@@ -18,10 +19,12 @@ export function NewsArticleDetail({
item,
basePath,
sectionLabel,
sectionLabelEn,
}: {
item: DisplayNewsItem;
basePath: string;
sectionLabel: string;
sectionLabelEn: string;
}) {
const hasGallery = item.gallery.length > 0;
@@ -34,7 +37,6 @@ export function NewsArticleDetail({
<>
<PageHero
image={item.image}
caption={item.date}
title={item.title}
breadcrumb={[
{ label: "صفحه اصلی", href: "/" },
@@ -64,12 +66,7 @@ export function NewsArticleDetail({
<section id="overview" className="scroll-mt-20 bg-paper py-16 lg:py-24">
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<div className="mx-auto mb-14 flex max-w-[738px] flex-col items-center gap-3 text-center">
<p className="kicker justify-center">{sectionLabel}</p>
<time dateTime={item.publishedAt} className="text-xs text-muted">
{item.date}
</time>
</div>
<SectionLabel fa={sectionLabel} en={sectionLabelEn} className="mb-14" />
</Reveal>
{item.bodyHtml && (
@@ -84,7 +81,7 @@ export function NewsArticleDetail({
<section id="gallery" className="scroll-mt-20 bg-sand py-16 lg:py-24">
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
<Reveal>
<p className="kicker mb-14 justify-center">گالری تصاویر</p>
<SectionLabel fa="گالری تصاویر" en="GALLERY" className="mb-14" />
</Reveal>
<ProjectGalleryLightbox images={item.gallery} alt={item.title} />
+3 -10
View File
@@ -1,4 +1,5 @@
import { Reveal } from "@/components/Reveal";
import { SectionLabel } from "@/components/shared/SectionLabel";
/**
* The centred editorial column that carries every section of this page:
@@ -32,17 +33,9 @@ export function Editorial({
compact ? "py-12 lg:py-16" : "py-16 lg:py-24"
}`}
>
{(caption || captionLatin) && (
<Reveal>
<p className="flex items-center justify-center gap-3 text-sm text-gold">
{caption}
{caption && captionLatin && (
<span aria-hidden="true" className="h-1 w-1 rotate-45 bg-gold/60" />
)}
{captionLatin && (
<span className="latin tracking-[0.1em]">{captionLatin}</span>
)}
</p>
<Reveal>
<SectionLabel fa={caption} en={captionLatin} tone={tone} />
</Reveal>
)}
+3 -7
View File
@@ -11,20 +11,18 @@ function isStaticImage(src: ImageSource): src is StaticImageData {
}
/**
* Full-bleed opening plate shared by the inner pages: tracked Latin kicker,
* light Persian title, breadcrumb — set over a darkened image.
* Full-bleed opening plate shared by the inner pages: light Persian title,
* breadcrumb — set over a darkened image.
*/
export function PageHero({
image,
imageMobile,
caption,
title,
breadcrumb,
}: {
image: ImageSource;
/** Portrait crop, when one exists. Falls back to the landscape plate. */
imageMobile?: ImageSource;
caption: string;
title: string;
breadcrumb: readonly Crumb[];
}) {
@@ -59,10 +57,8 @@ export function PageHero({
<div className="absolute inset-0 bg-gradient-to-b from-black/55 via-black/25 to-black/75" />
<div className="relative z-10 mx-auto w-full max-w-[1400px] px-6 pb-20 text-center lg:px-12 lg:pb-28">
<p className="latin animate-fade-up text-xs tracking-[0.4em] text-gold">{caption}</p>
<h1
className="mt-6 animate-fade-up text-4xl font-light text-white sm:text-5xl lg:text-6xl"
className="animate-fade-up text-4xl font-light text-white sm:text-5xl lg:text-6xl"
style={{ animationDelay: "140ms" }}
>
{title}
+34
View File
@@ -0,0 +1,34 @@
/**
* The section-label pattern used across the site: a short gold rule above a
* Persian title, with its English equivalent set tight beneath it at 70%
* opacity. Replaces the old inline "text + trailing dash" kicker — no
* letter-spacing on the Persian line (it breaks the cursive joining), tracked
* spacing on the Latin line only.
*/
export function SectionLabel({
fa,
en,
tone = "light",
align = "center",
className = "",
}: {
fa: string;
en: string;
tone?: "light" | "dark";
/** "start" left-aligns everything (RTL: right-aligned to the block's start edge). */
align?: "center" | "start";
className?: string;
}) {
const alignClass = align === "start" ? "items-start text-start" : "items-center text-center";
const color = tone === "dark" ? "text-gold-soft" : "text-gold";
return (
<div className={`flex flex-col gap-4 ${alignClass} ${className}`}>
<span aria-hidden="true" className="h-px w-10 bg-gold" />
<div className={`flex flex-col gap-1 ${alignClass}`}>
<p className={`text-xl ${color}`}>{fa}</p>
<p className={`latin text-sm tracking-[0.15em] opacity-70 ${color}`}>{en}</p>
</div>
</div>
);
}