feat: switch to IRANSans FaNum fonts, add product images, rebuild out

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
S. Esmaeili
2026-10-11 17:01:47 +03:30
co-authored by Cursor
parent 0eb23b40e5
commit 31076174e2
283 changed files with 733 additions and 223 deletions
+11 -43
View File
@@ -3,85 +3,53 @@
/* ---- local fonts ---- */ /* ---- local fonts ---- */
@font-face { @font-face {
font-family: "IRANSans"; font-family: "IRANSans";
src: url("/fonts/IRANSansWeb_UltraLight.woff2") format("woff2"); src: url("/fonts/IRANSansWebFaNum_UltraLight.woff2") format("woff2");
font-weight: 200; font-weight: 200;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "IRANSans"; font-family: "IRANSans";
src: url("/fonts/IRANSansWeb_Light.woff2") format("woff2"); src: url("/fonts/IRANSansWebFaNum_Light.woff2") format("woff2");
font-weight: 300; font-weight: 300;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "IRANSans"; font-family: "IRANSans";
src: url("/fonts/IRANSansWeb.woff2") format("woff2"); src: url("/fonts/IRANSansWebFaNum.woff2") format("woff2");
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "IRANSans"; font-family: "IRANSans";
src: url("/fonts/IRANSansWeb_Medium.woff2") format("woff2"); src: url("/fonts/IRANSansWebFaNum_Medium.woff2") format("woff2");
font-weight: 500; font-weight: 500;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "IRANSans"; font-family: "IRANSans";
src: url("/fonts/IRANSansWeb_Bold.woff2") format("woff2"); src: url("/fonts/IRANSansWebFaNum_Bold.woff2") format("woff2");
font-weight: 700; font-weight: 700;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: "IRANSans Fallback"; font-family: "IRANSans";
src: local("Arial"); src: url("/fonts/IRANSansWebFaNum_Black.woff2") format("woff2");
ascent-override: 100%; font-weight: 900;
descent-override: 35%;
line-gap-override: 0%;
size-adjust: 100%;
}
@font-face {
font-family: "ibmPlexMono";
src: url("/fonts/IBMPlexMono-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face {
font-family: "ibmPlexMono";
src: url("/fonts/IBMPlexMono-Medium.woff2") format("woff2");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "ibmPlexMono";
src: url("/fonts/IBMPlexMono-SemiBold.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "ibmPlexMono Fallback";
src: local("Arial");
ascent-override: 77.95%;
descent-override: 20.91%;
line-gap-override: 0%;
size-adjust: 131.49%;
}
:root { :root {
--font-iransans: "IRANSans", "IRANSans Fallback", Tahoma, system-ui, sans-serif; --font-iransans: "IRANSans", sans-serif;
--font-ibm-plex-mono: "ibmPlexMono", "ibmPlexMono Fallback", ui-monospace, monospace;
} }
@theme { @theme {
--font-sans: var(--font-iransans), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; --font-sans: var(--font-iransans);
--font-mono: var(--font-ibm-plex-mono), "IRANSans", ui-monospace, monospace; --font-mono: var(--font-iransans);
--color-bg: #0b1224; --color-bg: #0b1224;
--color-panel: #101a32; --color-panel: #101a32;
+58 -5
View File
@@ -42,6 +42,17 @@ useSeoMeta({
ogDescription: category.fullDesc, ogDescription: category.fullDesc,
}); });
useJsonLd(categoryPageSchema(category), `category-${slug}`); useJsonLd(categoryPageSchema(category), `category-${slug}`);
const activeGroup = ref<string | null>(null);
const visibleProducts = computed(() =>
activeGroup.value
? category.products.filter((p) => p.group === activeGroup.value)
: category.products,
);
const activeGroupDesc = computed(
() => category.groups?.find((g) => g.slug === activeGroup.value)?.desc,
);
const groupCount = (g: string) => category.products.filter((p) => p.group === g).length;
</script> </script>
<template> <template>
@@ -106,20 +117,62 @@ useJsonLd(categoryPageSchema(category), `category-${slug}`);
<section class="relative py-16 md:py-24"> <section class="relative py-16 md:py-24">
<div class="absolute inset-0 bg-grid-fine opacity-30" /> <div class="absolute inset-0 bg-grid-fine opacity-30" />
<div class="relative mx-auto max-w-[1480px] px-5 md:px-8"> <div class="relative mx-auto max-w-[1480px] px-5 md:px-8">
<div v-if="category.groups?.length" class="mb-10">
<div class="flex flex-wrap gap-2" role="tablist" aria-label="فیلتر زیردسته">
<button
type="button"
role="tab"
:aria-selected="activeGroup === null"
class="rounded-full border px-4 py-2 text-sm transition-colors"
:class="
activeGroup === null
? 'border-accent bg-accent text-bg font-bold'
: 'border-line text-muted hover:border-accent/60 hover:text-accent'
"
@click="activeGroup = null"
>
همه
<span class="ms-1 font-mono text-xs opacity-70">{{ faNum(category.products.length) }}</span>
</button>
<button
v-for="g in category.groups"
:key="g.slug"
type="button"
role="tab"
:aria-selected="activeGroup === g.slug"
class="rounded-full border px-4 py-2 text-sm transition-colors"
:class="
activeGroup === g.slug
? 'border-accent bg-accent text-bg font-bold'
: 'border-line text-muted hover:border-accent/60 hover:text-accent'
"
@click="activeGroup = g.slug"
>
{{ g.name }}
<span class="ms-1 font-mono text-xs opacity-70">{{ faNum(groupCount(g.slug)) }}</span>
</button>
</div>
<p v-if="activeGroupDesc" class="mt-4 text-sm text-muted">{{ activeGroupDesc }}</p>
</div>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
<Reveal v-for="(product, i) in category.products" :key="product.code" :delay="i * 0.06"> <Reveal v-for="(product, i) in visibleProducts" :key="product.code" :delay="i * 0.06">
<div <div
class="group h-full overflow-hidden rounded-xl border border-line bg-panel/80 backdrop-blur transition-all duration-500 hover:border-accent/40 hover:shadow-[0_8px_40px_rgba(255,90,0,0.08)]" class="group h-full overflow-hidden rounded-xl border border-line bg-panel/80 backdrop-blur transition-all duration-500 hover:border-accent/40 hover:shadow-[0_8px_40px_rgba(255,90,0,0.08)]"
> >
<div class="relative aspect-[4/3] overflow-hidden"> <div
class="relative aspect-[4/3] overflow-hidden"
:class="product.image && 'bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.14),transparent_70%)]'"
>
<img <img
:src="CATEGORY_IMAGES[slug]" :src="product.image ?? CATEGORY_IMAGES[slug]"
:alt="product.name" :alt="product.name"
loading="lazy" loading="lazy"
decoding="async" decoding="async"
class="size-full object-cover transition-transform duration-700 group-hover:scale-105" class="size-full transition-transform duration-700 group-hover:scale-105"
:class="product.image ? 'object-contain p-8' : 'object-cover'"
/> />
<div class="absolute inset-0 bg-gradient-to-t from-bg/60 to-transparent" /> <div v-if="!product.image" class="absolute inset-0 bg-gradient-to-t from-bg/60 to-transparent" />
<div class="absolute inset-x-0 top-0 flex items-center justify-between p-4"> <div class="absolute inset-x-0 top-0 flex items-center justify-between p-4">
<span class="rounded-full bg-bg/60 px-3 py-1 font-mono text-[10px] tracking-widest text-ink/70 backdrop-blur"> <span class="rounded-full bg-bg/60 px-3 py-1 font-mono text-[10px] tracking-widest text-ink/70 backdrop-blur">
{{ product.code }} {{ product.code }}
+2 -9
View File
@@ -24,21 +24,14 @@ export default defineNuxtConfig({
link: [ link: [
{ {
rel: "preload", rel: "preload",
href: "/fonts/IRANSansWeb.woff2", href: "/fonts/IRANSansWebFaNum.woff2",
as: "font", as: "font",
type: "font/woff2", type: "font/woff2",
crossorigin: "", crossorigin: "",
}, },
{ {
rel: "preload", rel: "preload",
href: "/fonts/IRANSansWeb_Bold.woff2", href: "/fonts/IRANSansWebFaNum_Bold.woff2",
as: "font",
type: "font/woff2",
crossorigin: "",
},
{
rel: "preload",
href: "/fonts/IBMPlexMono-Regular.woff2",
as: "font", as: "font",
type: "font/woff2", type: "font/woff2",
crossorigin: "", crossorigin: "",
+1 -1
View File
@@ -1 +1 @@
<!DOCTYPE html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><script type="importmap">{"imports":{"#entry":"/_nuxt/Dfp1fBWi.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dm-VTTRi.css" crossorigin><link rel="preload" href="/fonts/IRANSansWeb.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/fonts/IRANSansWeb_Bold.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/fonts/IBMPlexMono-Regular.woff2" as="font" type="font/woff2" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dfp1fBWi.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bh52WncL.js"><script type="module" src="/_nuxt/Dfp1fBWi.js" crossorigin></script><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="manifest" href="/manifest.webmanifest"></head><body class="relative min-h-screen bg-bg font-sans text-ink antialiased"><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"c7ba7d9a-5f05-4176-9545-1689facbab14",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1791372518975,false]</script></body></html> <!DOCTYPE html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><script type="importmap">{"imports":{"#entry":"/_nuxt/CwuOmQRC.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.DXyzAtZq.css" crossorigin><link rel="preload" href="/fonts/IRANSansWebFaNum.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/fonts/IRANSansWebFaNum_Bold.woff2" as="font" type="font/woff2" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CwuOmQRC.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BLoJslU_.js"><script type="module" src="/_nuxt/CwuOmQRC.js" crossorigin></script><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="manifest" href="/manifest.webmanifest"></head><body class="relative min-h-screen bg-bg font-sans text-ink antialiased"><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"9bc20942-bdb1-40fa-859b-b1c16104da2f",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1791725332415,false]</script></body></html>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"id":"c7ba7d9a-5f05-4176-9545-1689facbab14","timestamp":1791372451268} {"id":"9bc20942-bdb1-40fa-859b-b1c16104da2f","timestamp":1791725267318}
@@ -0,0 +1 @@
{"id":"9bc20942-bdb1-40fa-859b-b1c16104da2f","timestamp":1791725267318,"prerendered":["/","/categories/respiratory-protection","/categories/safety-helmets","/categories/protective-gloves","/categories/hearing-protection","/__not-found","/categories/traffic-equipment","/categories/eye-protection","/categories/workwear","/categories/safety-footwear"]}
@@ -1 +0,0 @@
{"id":"c7ba7d9a-5f05-4176-9545-1689facbab14","timestamp":1791372451268,"prerendered":["/","/categories/hearing-protection","/categories/safety-helmets","/categories/traffic-equipment","/categories/protective-gloves","/categories/workwear","/categories/eye-protection","/__not-found","/categories/safety-footwear","/categories/respiratory-protection"]}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518597] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725331531]
+1 -1
View File
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518973] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518974] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518973] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518974] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518972] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332413]
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518973] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518974] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332415]
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518972] [{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332413]
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Some files were not shown because too many files have changed in this diff Show More