feat: switch to IRANSans FaNum fonts, add product images, rebuild out
Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -3,85 +3,53 @@
|
||||
/* ---- local fonts ---- */
|
||||
@font-face {
|
||||
font-family: "IRANSans";
|
||||
src: url("/fonts/IRANSansWeb_UltraLight.woff2") format("woff2");
|
||||
src: url("/fonts/IRANSansWebFaNum_UltraLight.woff2") format("woff2");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IRANSans";
|
||||
src: url("/fonts/IRANSansWeb_Light.woff2") format("woff2");
|
||||
src: url("/fonts/IRANSansWebFaNum_Light.woff2") format("woff2");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IRANSans";
|
||||
src: url("/fonts/IRANSansWeb.woff2") format("woff2");
|
||||
src: url("/fonts/IRANSansWebFaNum.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IRANSans";
|
||||
src: url("/fonts/IRANSansWeb_Medium.woff2") format("woff2");
|
||||
src: url("/fonts/IRANSansWebFaNum_Medium.woff2") format("woff2");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IRANSans";
|
||||
src: url("/fonts/IRANSansWeb_Bold.woff2") format("woff2");
|
||||
src: url("/fonts/IRANSansWebFaNum_Bold.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IRANSans Fallback";
|
||||
src: local("Arial");
|
||||
ascent-override: 100%;
|
||||
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-family: "IRANSans";
|
||||
src: url("/fonts/IRANSansWebFaNum_Black.woff2") format("woff2");
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
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 {
|
||||
--font-iransans: "IRANSans", "IRANSans Fallback", Tahoma, system-ui, sans-serif;
|
||||
--font-ibm-plex-mono: "ibmPlexMono", "ibmPlexMono Fallback", ui-monospace, monospace;
|
||||
--font-iransans: "IRANSans", sans-serif;
|
||||
}
|
||||
|
||||
@theme {
|
||||
--font-sans: var(--font-iransans), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
--font-mono: var(--font-ibm-plex-mono), "IRANSans", ui-monospace, monospace;
|
||||
--font-sans: var(--font-iransans);
|
||||
--font-mono: var(--font-iransans);
|
||||
|
||||
--color-bg: #0b1224;
|
||||
--color-panel: #101a32;
|
||||
|
||||
@@ -42,6 +42,17 @@ useSeoMeta({
|
||||
ogDescription: category.fullDesc,
|
||||
});
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -106,20 +117,62 @@ useJsonLd(categoryPageSchema(category), `category-${slug}`);
|
||||
<section class="relative py-16 md:py-24">
|
||||
<div class="absolute inset-0 bg-grid-fine opacity-30" />
|
||||
<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">
|
||||
<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
|
||||
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
|
||||
:src="CATEGORY_IMAGES[slug]"
|
||||
:src="product.image ?? CATEGORY_IMAGES[slug]"
|
||||
:alt="product.name"
|
||||
loading="lazy"
|
||||
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">
|
||||
<span class="rounded-full bg-bg/60 px-3 py-1 font-mono text-[10px] tracking-widest text-ink/70 backdrop-blur">
|
||||
{{ product.code }}
|
||||
|
||||
@@ -24,21 +24,14 @@ export default defineNuxtConfig({
|
||||
link: [
|
||||
{
|
||||
rel: "preload",
|
||||
href: "/fonts/IRANSansWeb.woff2",
|
||||
href: "/fonts/IRANSansWebFaNum.woff2",
|
||||
as: "font",
|
||||
type: "font/woff2",
|
||||
crossorigin: "",
|
||||
},
|
||||
{
|
||||
rel: "preload",
|
||||
href: "/fonts/IRANSansWeb_Bold.woff2",
|
||||
as: "font",
|
||||
type: "font/woff2",
|
||||
crossorigin: "",
|
||||
},
|
||||
{
|
||||
rel: "preload",
|
||||
href: "/fonts/IBMPlexMono-Regular.woff2",
|
||||
href: "/fonts/IRANSansWebFaNum_Bold.woff2",
|
||||
as: "font",
|
||||
type: "font/woff2",
|
||||
crossorigin: "",
|
||||
|
||||
@@ -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 @@
|
||||
{"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"]}
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518597]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725331531]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518973]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518974]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518973]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518974]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518972]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332413]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518973]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332414]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518974]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332415]
|
||||
@@ -1 +1 @@
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791372518972]
|
||||
[{"data":1,"prerenderedAt":3},["ShallowReactive",2],{},1791725332413]
|
||||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 65 KiB |