Skip to content

Commit 5227ef4

Browse files
Changes
Co-authored-by: ghostcodedynamics <295822717+ghostcodedynamics@users.noreply.github.com>
1 parent c6f884c commit 5227ef4

1 file changed

Lines changed: 144 additions & 63 deletions

File tree

src/components/brand/logo.tsx

Lines changed: 144 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,171 @@
1+
/**
2+
* GhostCode Dynamics — Brand Logo System
3+
* ---------------------------------------------------------------
4+
* This file is the single source of truth for the brand mark across
5+
* the navbar, footer, mobile menu, favicon, OG images, etc.
6+
*
7+
* REPLACING THE PLACEHOLDER WITH THE FINAL PNG (≤ 2 min):
8+
* 1. Drop the final files into `src/assets/brand/`:
9+
* - logo-mark.png (square icon, ≥ 512×512)
10+
* - logo-mark-light.png (optional, for light backgrounds)
11+
* - logo-full.png (optional full lockup; if absent we
12+
* keep the SVG monogram + typeset wordmark)
13+
* 2. Flip `USE_PNG_MARK = true` below.
14+
* 3. (Optional) Replace `public/favicon.svg` with the new icon.
15+
*
16+
* Nothing else in the codebase needs to change — every surface
17+
* imports <Logo />, <LogoCompact />, or <LogoMark /> from this file.
18+
* ---------------------------------------------------------------
19+
*/
20+
121
import { cn } from "@/lib/utils";
222

3-
interface LogoProps {
4-
className?: string;
5-
iconOnly?: boolean;
23+
// Toggle this to `true` once the founder ships the final PNG assets.
24+
const USE_PNG_MARK = false;
25+
26+
// When USE_PNG_MARK is true these are imported lazily by <LogoMark />.
27+
// Keeping them as string paths means the build never fails if the
28+
// PNGs are not yet present in the repo.
29+
const PNG_MARK_DARK = "/brand/logo-mark.png";
30+
const PNG_MARK_LIGHT = "/brand/logo-mark-light.png";
31+
32+
// ---------------------------------------------------------------
33+
// Variant: <LogoMark /> — icon only (favicon, app icon, avatar)
34+
// ---------------------------------------------------------------
35+
36+
interface LogoMarkProps {
637
size?: number;
38+
className?: string;
39+
/** Force a specific contrast mode. Defaults to currentColor (auto). */
40+
tone?: "auto" | "dark" | "light";
741
}
842

943
/**
10-
* GhostCode Dynamics brand mark.
11-
* The "G" of the wordmark is represented by the ghost-in-circle icon,
12-
* so the visible wordmark reads "hostCode / DYNAMICS".
44+
* Ghost-in-monogram mark. Pure SVG so it is sharp on every density,
45+
* inherits theme color via `currentColor`, and ships zero bytes of
46+
* raster data until the real PNG is wired in.
1347
*/
14-
export function Logo({ className, iconOnly = false, size = 32 }: LogoProps) {
15-
return (
16-
<div
17-
className={cn("flex items-center gap-2.5", className)}
18-
aria-label="GhostCode Dynamics"
19-
>
20-
<GhostIcon size={size} />
21-
{!iconOnly && (
22-
<span className="flex flex-col leading-none font-display">
23-
<span className="text-[1.05rem] font-semibold tracking-tight text-foreground">
24-
hostCode
25-
</span>
26-
<span className="text-[0.6rem] font-medium tracking-[0.32em] text-muted-foreground mt-0.5">
27-
DYNAMICS
28-
</span>
29-
</span>
30-
)}
31-
</div>
32-
);
33-
}
48+
export function LogoMark({ size = 32, className, tone = "auto" }: LogoMarkProps) {
49+
if (USE_PNG_MARK) {
50+
const src = tone === "light" ? PNG_MARK_LIGHT : PNG_MARK_DARK;
51+
return (
52+
<img
53+
src={src}
54+
width={size}
55+
height={size}
56+
alt="GhostCode Dynamics"
57+
className={cn("shrink-0 select-none", className)}
58+
draggable={false}
59+
/>
60+
);
61+
}
62+
63+
const fg =
64+
tone === "dark"
65+
? "#0a0a0f"
66+
: tone === "light"
67+
? "#ffffff"
68+
: "currentColor";
3469

35-
/**
36-
* Ghost-in-G mark. Uses currentColor so it inverts cleanly on light/dark
37-
* backgrounds without altering the original black / white / gray palette.
38-
*/
39-
export function GhostIcon({
40-
size = 32,
41-
className,
42-
}: {
43-
size?: number;
44-
className?: string;
45-
}) {
4670
return (
4771
<svg
4872
width={size}
4973
height={size}
5074
viewBox="0 0 64 64"
5175
fill="none"
5276
xmlns="http://www.w3.org/2000/svg"
53-
className={cn("shrink-0 text-foreground", className)}
77+
className={cn("shrink-0", className)}
5478
role="img"
55-
aria-label="GhostCode Dynamics logo"
79+
aria-label="GhostCode Dynamics"
80+
shapeRendering="geometricPrecision"
5681
>
57-
{/* Solid disc — uses currentColor so it adapts to theme */}
58-
<circle cx="32" cy="32" r="30" fill="currentColor" />
82+
{/* Rounded squircle backdrop — the monogram "G" */}
83+
<rect x="1" y="1" width="62" height="62" rx="16" fill={fg} />
5984

60-
{/* G opening on the right side (notch carved out of the disc) */}
85+
{/* Ghost silhouette cut out of the squircle */}
6186
<path
62-
d="M44 28h-9v8h5v4a10 10 0 1 1-3-15"
63-
stroke="var(--background)"
64-
strokeWidth="4"
65-
strokeLinecap="round"
66-
strokeLinejoin="round"
67-
fill="none"
68-
opacity="0"
69-
/>
70-
71-
{/* Ghost silhouette in the center, in the background color so it
72-
appears as a cut-out on both light and dark themes */}
73-
<path
74-
d="M32 16c-7 0-12 5-12 12v18c0 1.4 1.6 2.2 2.7 1.3l2.2-1.7c.6-.5 1.5-.5 2.1 0l2.2 1.7c.6.5 1.5.5 2.1 0l2.2-1.7c.6-.5 1.5-.5 2.1 0l2.2 1.7c1.1.9 2.7.1 2.7-1.3V28c0-7-5-12-12-12Z"
87+
d="M32 16c-7 0-12 5-12 12v18.2c0 1.4 1.6 2.2 2.7 1.3l2.2-1.7c.6-.5 1.5-.5 2.1 0l2.2 1.7c.6.5 1.5.5 2.1 0l2.2-1.7c.6-.5 1.5-.5 2.1 0l2.2 1.7c1.1.9 2.7.1 2.7-1.3V28c0-7-5-12-12-12Z"
7588
fill="var(--background)"
7689
/>
7790

78-
{/* Soft gray tail/shadow on the ghost for the original 3-tone look */}
91+
{/* Eyes — restate foreground for a crisp 3-tone read */}
92+
<circle cx="28.4" cy="29.5" r="1.9" fill={fg} />
93+
<circle cx="35.6" cy="29.5" r="1.9" fill={fg} />
94+
95+
{/* Subtle "C / D" tick: opening on the right side of the G */}
7996
<path
80-
d="M38 38c2.5 1.5 4.5 4 5 7-1 .5-2.5.4-3.6-.4L37 43.5V38Z"
81-
fill="currentColor"
82-
opacity="0.35"
97+
d="M46 30v6"
98+
stroke="var(--background)"
99+
strokeWidth="3"
100+
strokeLinecap="round"
83101
/>
84-
85-
{/* Eyes */}
86-
<circle cx="28.5" cy="29" r="1.8" fill="currentColor" />
87-
<circle cx="35.5" cy="29" r="1.8" fill="currentColor" />
88102
</svg>
89103
);
90104
}
105+
106+
// ---------------------------------------------------------------
107+
// Variant: <Logo /> — full lockup (icon + "GhostCode Dynamics")
108+
// ---------------------------------------------------------------
109+
110+
interface LogoProps {
111+
className?: string;
112+
size?: number;
113+
/** Hide the wordmark, keep only the mark. */
114+
iconOnly?: boolean;
115+
}
116+
117+
export function Logo({ className, size = 32, iconOnly = false }: LogoProps) {
118+
return (
119+
<span
120+
className={cn(
121+
"inline-flex items-center gap-2.5 transition-transform duration-300 ease-out group-hover:scale-[1.02]",
122+
className,
123+
)}
124+
aria-label="GhostCode Dynamics"
125+
>
126+
<LogoMark size={size} />
127+
{!iconOnly && <Wordmark />}
128+
</span>
129+
);
130+
}
131+
132+
// ---------------------------------------------------------------
133+
// Variant: <LogoCompact /> — icon + "GCD" (tight navs, mobile)
134+
// ---------------------------------------------------------------
135+
136+
export function LogoCompact({ className, size = 28 }: LogoProps) {
137+
return (
138+
<span
139+
className={cn(
140+
"inline-flex items-center gap-2 transition-transform duration-300 ease-out group-hover:scale-[1.02]",
141+
className,
142+
)}
143+
aria-label="GhostCode Dynamics"
144+
>
145+
<LogoMark size={size} />
146+
<span className="font-display text-base font-semibold tracking-[0.18em] text-foreground">
147+
GCD
148+
</span>
149+
</span>
150+
);
151+
}
152+
153+
// ---------------------------------------------------------------
154+
// Wordmark — geometric, Linear / Vercel / Stripe inspired
155+
// ---------------------------------------------------------------
156+
157+
function Wordmark() {
158+
return (
159+
<span className="font-display text-[1.02rem] leading-none tracking-tight text-foreground">
160+
<span className="font-semibold">Ghost</span>
161+
<span className="font-medium text-foreground/85">Code</span>
162+
<span className="mx-1 text-muted-foreground/60">·</span>
163+
<span className="font-medium tracking-[0.02em] text-muted-foreground">
164+
Dynamics
165+
</span>
166+
</span>
167+
);
168+
}
169+
170+
// Re-export the icon under its previous name to keep older imports working.
171+
export { LogoMark as GhostIcon };

0 commit comments

Comments
 (0)