Move coming-soon storefront into website Next.js app.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
e171222b16
commit
2f857f1a90
@@ -0,0 +1,2 @@
|
|||||||
|
NEXT_PUBLIC_STORE_URL=http://store.mana-collection.com:3001
|
||||||
|
NEXT_PUBLIC_API_URL=http://api.mana-collection.com:3002/api/v1
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<!-- BEGIN:nextjs-agent-rules -->
|
||||||
|
|
||||||
|
# This is NOT the Next.js you know
|
||||||
|
|
||||||
|
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
|
||||||
|
|
||||||
|
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
|
||||||
|
|
||||||
|
<!-- END:nextjs-agent-rules -->
|
||||||
@@ -2,4 +2,21 @@
|
|||||||
|
|
||||||
Public storefront for ManaCollections.ir (multi-seller catalog, cart, checkout).
|
Public storefront for ManaCollections.ir (multi-seller catalog, cart, checkout).
|
||||||
|
|
||||||
Scaffold TBD — API lives in `../backend` (`/api/v1/public/...`).
|
## Local
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# once (needs admin password)
|
||||||
|
../scripts/setup-hosts.sh
|
||||||
|
|
||||||
|
cp .env.example .env.local
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
| URL | App |
|
||||||
|
|-----|-----|
|
||||||
|
| http://mana-collection.com:3000 | Website (coming soon) |
|
||||||
|
| http://store.mana-collection.com:3001 | Store dashboard |
|
||||||
|
| http://api.mana-collection.com:3002/api/v1 | Backend API |
|
||||||
|
|
||||||
|
API: `NEXT_PUBLIC_API_URL` · store login: `NEXT_PUBLIC_STORE_URL`
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { defineConfig, globalIgnores } from "eslint/config";
|
||||||
|
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||||
|
import nextTs from "eslint-config-next/typescript";
|
||||||
|
|
||||||
|
const eslintConfig = defineConfig([
|
||||||
|
...nextVitals,
|
||||||
|
...nextTs,
|
||||||
|
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default eslintConfig;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
allowedDevOrigins: [
|
||||||
|
"mana-collection.com",
|
||||||
|
"www.mana-collection.com",
|
||||||
|
"*.mana-collection.com",
|
||||||
|
],
|
||||||
|
images: {
|
||||||
|
localPatterns: [
|
||||||
|
{
|
||||||
|
pathname: "/images/**",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
Generated
+6782
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "mana-collection-website",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev -H 0.0.0.0 -p 3000",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start -H 0.0.0.0 -p 3000",
|
||||||
|
"lint": "eslint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "16.3.4",
|
||||||
|
"react": "19.2.8",
|
||||||
|
"react-dom": "19.2.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/postcss": "^4",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"eslint": "^9",
|
||||||
|
"eslint-config-next": "16.3.4",
|
||||||
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1018 KiB |
@@ -0,0 +1,187 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #fbfbfa;
|
||||||
|
--foreground: #0a0a0a;
|
||||||
|
--surface: #ffffff;
|
||||||
|
--muted: #6b6b6b;
|
||||||
|
--line: #e4e4e4;
|
||||||
|
--ink: #0a0a0a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-surface: var(--surface);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-line: var(--line);
|
||||||
|
--color-ink: var(--ink);
|
||||||
|
--font-sans: var(--font-doran);
|
||||||
|
--font-display: var(--font-cailyne);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-family: var(--font-doran), Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-display {
|
||||||
|
font-family: var(--font-cailyne), Georgia, serif;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-montserrat {
|
||||||
|
font-family: var(--font-montserrat), system-ui, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes coming-soon-rise {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(22px);
|
||||||
|
filter: blur(6px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
filter: blur(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes coming-soon-panel {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(24px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes coming-soon-drift {
|
||||||
|
0% {
|
||||||
|
transform: scale(1) translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1.06) translate3d(-0.8%, 0.5%, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes coming-soon-line {
|
||||||
|
from {
|
||||||
|
transform: scaleX(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scaleX(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes coming-soon-glow {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(10, 10, 10, 0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 4px rgba(10, 10, 10, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq {
|
||||||
|
opacity: 0;
|
||||||
|
animation: coming-soon-rise 0.95s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-1 {
|
||||||
|
animation-delay: 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-2 {
|
||||||
|
animation-delay: 0.38s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-3 {
|
||||||
|
animation-delay: 0.58s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-4 {
|
||||||
|
animation-delay: 0.78s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-5 {
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-5 {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-5::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
inset-inline: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--line);
|
||||||
|
transform-origin: center;
|
||||||
|
animation: coming-soon-line 0.7s cubic-bezier(0.16, 1, 0.3, 1) 1.05s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-seq-image {
|
||||||
|
opacity: 0;
|
||||||
|
animation: coming-soon-panel 1.1s ease-out 0.05s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-image {
|
||||||
|
transform: scale(1);
|
||||||
|
animation: coming-soon-drift 20s ease-in-out 1.2s alternate infinite both;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coming-soon-login {
|
||||||
|
animation: coming-soon-glow 2.4s ease-in-out 1.6s 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.coming-soon-seq,
|
||||||
|
.coming-soon-seq-image,
|
||||||
|
.coming-soon-image,
|
||||||
|
.coming-soon-seq-5::before,
|
||||||
|
.coming-soon-login {
|
||||||
|
animation: none !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
filter: none !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mana-canvas {
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse 80% 60% at 20% 20%, rgba(0, 0, 0, 0.02), transparent 55%),
|
||||||
|
radial-gradient(ellipse 70% 50% at 90% 80%, rgba(0, 0, 0, 0.022), transparent 50%),
|
||||||
|
linear-gradient(160deg, #fcfcfb 0%, #f7f7f5 48%, #f3f3f0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mana-canvas::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.45;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220' viewBox='0 0 220 220'%3E%3Cg fill='%230a0a0a'%3E%3Ccircle cx='12' cy='18' r='1.1' opacity='.28'/%3E%3Ccircle cx='48' cy='9' r='0.7' opacity='.18'/%3E%3Ccircle cx='91' cy='27' r='1.4' opacity='.22'/%3E%3Ccircle cx='133' cy='14' r='0.8' opacity='.3'/%3E%3Ccircle cx='176' cy='31' r='1.2' opacity='.16'/%3E%3Ccircle cx='208' cy='8' r='0.9' opacity='.24'/%3E%3Ccircle cx='27' cy='56' r='0.75' opacity='.2'/%3E%3Ccircle cx='69' cy='71' r='1.3' opacity='.26'/%3E%3Ccircle cx='112' cy='49' r='0.65' opacity='.15'/%3E%3Ccircle cx='154' cy='63' r='1.05' opacity='.29'/%3E%3Ccircle cx='197' cy='78' r='0.85' opacity='.19'/%3E%3Ccircle cx='8' cy='99' r='1.15' opacity='.23'/%3E%3Ccircle cx='41' cy='118' r='0.7' opacity='.17'/%3E%3Ccircle cx='83' cy='104' r='1.45' opacity='.27'/%3E%3Ccircle cx='126' cy='129' r='0.9' opacity='.21'/%3E%3Ccircle cx='168' cy='111' r='0.6' opacity='.14'/%3E%3Ccircle cx='201' cy='136' r='1.2' opacity='.25'/%3E%3Ccircle cx='22' cy='158' r='0.95' opacity='.18'/%3E%3Ccircle cx='58' cy='179' r='1.25' opacity='.3'/%3E%3Ccircle cx='97' cy='163' r='0.7' opacity='.16'/%3E%3Ccircle cx='139' cy='188' r='1.1' opacity='.22'/%3E%3Ccircle cx='181' cy='171' r='0.8' opacity='.28'/%3E%3Ccircle cx='213' cy='194' r='1' opacity='.19'/%3E%3Ccircle cx='35' cy='208' r='0.65' opacity='.24'/%3E%3Ccircle cx='74' cy='201' r='1.35' opacity='.15'/%3E%3Ccircle cx='118' cy='214' r='0.85' opacity='.26'/%3E%3Ccircle cx='160' cy='203' r='0.75' opacity='.2'/%3E%3Ccircle cx='16' cy='78' r='0.55' opacity='.12'/%3E%3Ccircle cx='104' cy='86' r='0.5' opacity='.13'/%3E%3Ccircle cx='187' cy='52' r='0.6' opacity='.11'/%3E%3Ccircle cx='149' cy='149' r='0.55' opacity='.14'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
background-size: 220px 220px;
|
||||||
|
background-repeat: repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mana-canvas::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.16;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.45'/%3E%3C/svg%3E");
|
||||||
|
background-size: 180px 180px;
|
||||||
|
mix-blend-mode: multiply;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Montserrat } from "next/font/google";
|
||||||
|
import localFont from "next/font/local";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
const doran = localFont({
|
||||||
|
src: [
|
||||||
|
{
|
||||||
|
path: "../fonts/doran/Doran-Thin.woff",
|
||||||
|
weight: "100",
|
||||||
|
style: "normal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "../fonts/doran/Doran-Regular.woff",
|
||||||
|
weight: "400",
|
||||||
|
style: "normal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "../fonts/doran/Doran-Medium.woff",
|
||||||
|
weight: "500",
|
||||||
|
style: "normal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "../fonts/doran/Doran-Bold.woff",
|
||||||
|
weight: "700",
|
||||||
|
style: "normal",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variable: "--font-doran",
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
|
const cailyne = localFont({
|
||||||
|
src: [
|
||||||
|
{
|
||||||
|
path: "../fonts/Cailyne.otf",
|
||||||
|
weight: "400",
|
||||||
|
style: "normal",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variable: "--font-cailyne",
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
|
const montserrat = Montserrat({
|
||||||
|
subsets: ["latin"],
|
||||||
|
weight: ["400", "500", "600"],
|
||||||
|
variable: "--font-montserrat",
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "بهزودی | مانا کالکشن",
|
||||||
|
description: "فروشگاه آنلاین مانا کالکشن بهزودی در دسترس خواهد بود.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html
|
||||||
|
lang="fa"
|
||||||
|
dir="rtl"
|
||||||
|
className={`${doran.variable} ${cailyne.variable} ${montserrat.variable} h-full antialiased`}
|
||||||
|
>
|
||||||
|
<body className="min-h-full font-sans text-foreground">{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import ManaLogo from "@/components/ui/ManaLogo";
|
||||||
|
|
||||||
|
const STORE_LOGIN =
|
||||||
|
process.env.NEXT_PUBLIC_STORE_URL?.replace(/\/$/, "") ||
|
||||||
|
"http://store.mana-collection.com:3001";
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<main className="coming-soon relative min-h-dvh overflow-hidden bg-[#f7f7f5] text-ink">
|
||||||
|
{/* dir=ltr keeps content on visual left, image on visual right */}
|
||||||
|
<div dir="ltr" className="grid min-h-dvh grid-cols-1 lg:grid-cols-2">
|
||||||
|
{/* Content — left */}
|
||||||
|
<section className="relative order-2 flex min-h-[52dvh] flex-col lg:order-1 lg:min-h-dvh">
|
||||||
|
<div className="mana-canvas absolute inset-0" aria-hidden />
|
||||||
|
|
||||||
|
<div
|
||||||
|
dir="rtl"
|
||||||
|
className="relative z-10 flex flex-1 flex-col px-7 py-8 sm:px-12 sm:py-10 lg:px-16 lg:py-14"
|
||||||
|
>
|
||||||
|
<header className="coming-soon-seq coming-soon-seq-1 flex shrink-0 justify-center">
|
||||||
|
<ManaLogo
|
||||||
|
href=""
|
||||||
|
width={400}
|
||||||
|
priority
|
||||||
|
showTagline
|
||||||
|
variant="white"
|
||||||
|
blackBg
|
||||||
|
className="w-[400px] max-w-[min(400px,100%)]"
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="flex flex-1 flex-col items-center justify-center py-12 text-center sm:py-16">
|
||||||
|
<div className="max-w-md">
|
||||||
|
<p
|
||||||
|
dir="ltr"
|
||||||
|
className="coming-soon-seq coming-soon-seq-2 mb-4 font-display text-sm tracking-[0.2em] text-muted sm:text-base"
|
||||||
|
>
|
||||||
|
Coming soon
|
||||||
|
</p>
|
||||||
|
<h1 className="coming-soon-seq coming-soon-seq-3 mb-5 text-4xl leading-tight text-ink sm:text-5xl">
|
||||||
|
بهزودی
|
||||||
|
</h1>
|
||||||
|
<p className="coming-soon-seq coming-soon-seq-4 text-sm leading-8 text-muted sm:text-base sm:leading-8">
|
||||||
|
در این آدرس میزبان شما خواهیم بود.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer className="coming-soon-seq coming-soon-seq-5 shrink-0 pt-6">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||||
|
<p className="text-xs leading-6 text-muted">
|
||||||
|
حساب دارید؟ وارد شوید.
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={`${STORE_LOGIN}/login`}
|
||||||
|
className="coming-soon-login inline-flex items-center justify-center border border-ink bg-ink px-7 py-2.5 text-sm text-white transition-colors hover:bg-white hover:text-ink"
|
||||||
|
>
|
||||||
|
ورود
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Image — right */}
|
||||||
|
<aside className="coming-soon-seq-image relative order-1 min-h-[42dvh] overflow-hidden lg:order-2 lg:min-h-dvh">
|
||||||
|
<Image
|
||||||
|
src="/images/image1.jpg"
|
||||||
|
alt="مانا کالکشن"
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
sizes="(max-width: 1024px) 100vw, 50vw"
|
||||||
|
className="coming-soon-image object-cover object-center"
|
||||||
|
/>
|
||||||
|
<div className="pointer-events-none absolute inset-0 bg-gradient-to-l from-transparent via-transparent to-black/10" />
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const LOGO_ASPECT = 1172 / 304;
|
||||||
|
|
||||||
|
export const MANA_LOGO_SRC = {
|
||||||
|
default: "/images/brand/Mana-Logo.png",
|
||||||
|
black: "/images/brand/Mana-Logo-Black.png",
|
||||||
|
white: "/images/brand/Mana-Logo-White.png",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type ManaLogoProps = {
|
||||||
|
href?: string;
|
||||||
|
className?: string;
|
||||||
|
priority?: boolean;
|
||||||
|
/** Visual height of the wordmark */
|
||||||
|
height?: number;
|
||||||
|
/** Visual width of the wordmark (overrides height when set) */
|
||||||
|
width?: number;
|
||||||
|
/** Framed lockup like the brand board */
|
||||||
|
framed?: boolean;
|
||||||
|
/** Show “The Style you desire” under the mark */
|
||||||
|
showTagline?: boolean;
|
||||||
|
taglineClassName?: string;
|
||||||
|
/** Logo color version */
|
||||||
|
variant?: keyof typeof MANA_LOGO_SRC;
|
||||||
|
/** Wrap mark on a black plate (slogan stays outside) */
|
||||||
|
blackBg?: boolean;
|
||||||
|
/** Slogan font size in px (default 24) */
|
||||||
|
taglineSize?: number;
|
||||||
|
/** On hover, invert logo plate + slogan colors */
|
||||||
|
hoverSwap?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function LogoImage({
|
||||||
|
src,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
priority,
|
||||||
|
className = "",
|
||||||
|
}: {
|
||||||
|
src: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
priority?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={src}
|
||||||
|
alt="Mana Collection"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
priority={priority}
|
||||||
|
unoptimized
|
||||||
|
style={{ width, height: "auto" }}
|
||||||
|
className={`object-contain ${className}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ManaLogo({
|
||||||
|
href = "/",
|
||||||
|
className = "",
|
||||||
|
priority = false,
|
||||||
|
height = 36,
|
||||||
|
width: widthProp,
|
||||||
|
framed = false,
|
||||||
|
showTagline = false,
|
||||||
|
taglineClassName = "",
|
||||||
|
variant = "default",
|
||||||
|
blackBg = false,
|
||||||
|
taglineSize = 24,
|
||||||
|
hoverSwap = false,
|
||||||
|
}: ManaLogoProps) {
|
||||||
|
const width = widthProp ?? Math.round(height * LOGO_ASPECT);
|
||||||
|
const renderHeight = widthProp
|
||||||
|
? Math.round(widthProp / LOGO_ASPECT)
|
||||||
|
: height;
|
||||||
|
const src =
|
||||||
|
variant === "white"
|
||||||
|
? MANA_LOGO_SRC.white
|
||||||
|
: variant === "black"
|
||||||
|
? MANA_LOGO_SRC.black
|
||||||
|
: MANA_LOGO_SRC.default;
|
||||||
|
const swapSrc =
|
||||||
|
variant === "white" ? MANA_LOGO_SRC.black : MANA_LOGO_SRC.white;
|
||||||
|
|
||||||
|
const image = (
|
||||||
|
<span className="relative inline-flex">
|
||||||
|
<LogoImage
|
||||||
|
src={src}
|
||||||
|
width={width}
|
||||||
|
height={renderHeight}
|
||||||
|
priority={priority}
|
||||||
|
className={
|
||||||
|
hoverSwap
|
||||||
|
? "transition-opacity duration-500 group-hover:opacity-0"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{hoverSwap ? (
|
||||||
|
<LogoImage
|
||||||
|
src={swapSrc}
|
||||||
|
width={width}
|
||||||
|
height={renderHeight}
|
||||||
|
className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-500 group-hover:opacity-100"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
const markInner = framed ? (
|
||||||
|
<span
|
||||||
|
className="inline-flex border border-ink/80 p-[3px]"
|
||||||
|
style={{ boxShadow: "inset 0 0 0 1px rgba(10,10,10,0.55)" }}
|
||||||
|
>
|
||||||
|
<span className="inline-flex items-center justify-center px-5 py-3 sm:px-7 sm:py-3.5">
|
||||||
|
{image}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
image
|
||||||
|
);
|
||||||
|
|
||||||
|
const mark = blackBg ? (
|
||||||
|
<span
|
||||||
|
className={`inline-flex w-full items-center justify-center bg-ink px-2.5 py-2 sm:px-3 sm:py-2.5 ${
|
||||||
|
hoverSwap ? "mana-logo-swap-plate" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{markInner}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
markInner
|
||||||
|
);
|
||||||
|
|
||||||
|
const tagline = showTagline ? (
|
||||||
|
<span
|
||||||
|
className={`self-start bg-white px-4 py-1 font-display tracking-[0.12em] text-[#8a8a8a] sm:px-5 ${
|
||||||
|
hoverSwap ? "mana-logo-swap-tagline" : ""
|
||||||
|
} ${taglineClassName}`}
|
||||||
|
style={{ fontSize: taglineSize }}
|
||||||
|
>
|
||||||
|
<span>The Style you desire</span>
|
||||||
|
</span>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const lockup = (
|
||||||
|
<span
|
||||||
|
dir="ltr"
|
||||||
|
className={`inline-flex flex-col items-stretch gap-0 ${className}`}
|
||||||
|
>
|
||||||
|
{mark}
|
||||||
|
{tagline}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!href) {
|
||||||
|
return lockup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isExternal = /^https?:\/\//i.test(href);
|
||||||
|
const interactiveClass = `group inline-flex cursor-pointer ${
|
||||||
|
hoverSwap ? "" : "transition-opacity duration-200 hover:opacity-80"
|
||||||
|
}`;
|
||||||
|
|
||||||
|
if (isExternal) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
className={interactiveClass}
|
||||||
|
aria-label="Mana Collection"
|
||||||
|
>
|
||||||
|
{lockup}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={href} className={interactiveClass} aria-label="Mana Collection">
|
||||||
|
{lockup}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [{ "name": "next" }],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
".next/dev/types/**/*.ts",
|
||||||
|
"**/*.mts"
|
||||||
|
],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user