Align ExposedGays theme with Pink Pulse colors, typography, and pill buttons

This commit is contained in:
Ryan Salazar
2026-06-26 21:58:29 -04:00
parent 76834b3d21
commit 9604a18f7c
20 changed files with 236 additions and 119 deletions

View File

@@ -73,7 +73,7 @@ export function AgeGate({ onVerified }: AgeGateProps) {
<div className="mx-auto mb-4 flex justify-center">
<Logo size="lg" showText={false} />
</div>
<h1 className="text-2xl font-bold bg-horny-gradient bg-clip-text text-transparent">
<h1 className="text-2xl font-bold gradient-text">
ExposedGays
</h1>
<p className="mt-2 text-sm text-muted-foreground">
@@ -149,9 +149,9 @@ export function AgeGate({ onVerified }: AgeGateProps) {
)}
</div>
<div className="rounded-lg border border-blue-500/25 bg-blue-500/10 p-3">
<p className="text-xs text-blue-100/90 leading-relaxed">
<strong className="text-blue-200">No account needed.</strong> Verify your age
<div className="rounded-xl border border-primary/25 bg-primary/10 p-3">
<p className="text-xs text-foreground/90 leading-relaxed">
<strong className="text-pulse-pink-light">No account needed.</strong> Verify your age
with date of birth only browse the map, spots, and profiles free. Explicit
photos and chat require a free signup. Government ID is only needed for nude
content in certain states.

View File

@@ -97,7 +97,7 @@ export function CannedMessagesPanel({ compact, onSelect }: CannedMessagesPanelPr
key={msg.id}
type="button"
onClick={() => onSelect?.(msg.content)}
className="text-xs px-2.5 py-1.5 rounded-full border border-border hover:border-horny-pink/50 hover:bg-horny-pink/10 transition-colors truncate max-w-[140px]"
className="text-xs px-2.5 py-1.5 rounded-full pulse-pill-inactive truncate max-w-[140px]"
title={msg.content}
>
{msg.title}

View File

@@ -111,14 +111,14 @@ export function CruiserProfileView({
return (
<div className="fixed inset-0 z-[2000] bg-black flex flex-col md:items-center md:justify-center md:p-4">
<div className="w-full h-full md:max-w-lg md:max-h-[92vh] md:rounded-2xl md:overflow-hidden flex flex-col bg-[#0a1628]">
<div className="w-full h-full md:max-w-lg md:max-h-[92vh] md:rounded-2xl md:overflow-hidden flex flex-col bg-pulse-deep-space">
{/* Header — Sniffies style */}
<div className="shrink-0 bg-black px-3 py-2 safe-top">
<div className="flex items-center gap-2">
<button
type="button"
onClick={onClose}
className="p-2 text-blue-400 touch-manipulation"
className="p-2 text-pulse-pink-light touch-manipulation"
aria-label="Back"
>
<ArrowLeft className="h-5 w-5" />
@@ -126,7 +126,7 @@ export function CruiserProfileView({
<div className="flex-1 min-w-0">
<div className="flex items-center gap-1.5">
{profile.is_verified && (
<ShieldCheck className="h-4 w-4 text-blue-400 shrink-0" />
<ShieldCheck className="h-4 w-4 text-pulse-pink-light shrink-0" />
)}
<span className="font-bold text-white truncate">{name}</span>
</div>
@@ -188,7 +188,7 @@ export function CruiserProfileView({
type="button"
onClick={() => setActivePhoto(i + 1)}
className={cn(
"relative w-14 h-14 rounded-full overflow-hidden border-2 border-blue-400",
"relative w-14 h-14 rounded-full overflow-hidden border-2 border-primary",
shouldBlurPhoto(ph.is_explicit) && "id-blur"
)}
>
@@ -200,7 +200,7 @@ export function CruiserProfileView({
</div>
{/* Stats + headline */}
<div className="px-4 py-3 space-y-2 bg-[#0a1628]">
<div className="px-4 py-3 space-y-2 bg-pulse-deep-space">
{stats && (
<p className="flex items-center gap-2 text-sm text-white/90">
<Ruler className="h-4 w-4 text-white/50" />
@@ -233,7 +233,7 @@ export function CruiserProfileView({
onClick={() => setTab(t)}
className={cn(
"flex-1 py-2.5 text-xs font-semibold uppercase tracking-wide touch-manipulation",
tab === t ? "text-blue-400 border-b-2 border-blue-400" : "text-white/50"
tab === t ? "text-pulse-pink-light border-b-2 border-primary" : "text-white/50"
)}
>
{t}
@@ -391,7 +391,7 @@ export function CruiserProfileView({
<div className="flex items-center gap-3 text-xs text-white/70">
<span>{timeAgo(profile.last_active)}</span>
{profile.distance_miles != null && (
<span className="flex items-center gap-1 text-blue-400">
<span className="flex items-center gap-1 text-pulse-pink-light">
<MapPin className="h-3 w-3" />
{formatDistance(profile.distance_miles)}
</span>

View File

@@ -56,7 +56,7 @@ export function GuestUpgradeBanner({
return (
<>
<div className="rounded-xl border border-amber-500/25 bg-[#0a1628]/90 p-4 relative">
<div className="rounded-xl border border-primary/25 bg-pulse-midnight/90 p-4 relative">
{onDismiss && (
<button
type="button"
@@ -68,11 +68,11 @@ export function GuestUpgradeBanner({
</button>
)}
<div className="flex gap-3">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-blue-500/20">
<ShieldCheck className="h-5 w-5 text-blue-400" />
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/20">
<ShieldCheck className="h-5 w-5 text-pulse-pink-light" />
</div>
<div className="flex-1 min-w-0 pr-6">
<p className="text-sm font-semibold text-blue-100">{msg.title}</p>
<p className="text-sm font-semibold text-foreground">{msg.title}</p>
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">{msg.body}</p>
<Button
variant="horny"

View File

@@ -115,7 +115,7 @@ export function LocalGuidePanel({
>
<div
className={cn(
"pointer-events-auto border border-white/10 bg-[#0a1628]/96 backdrop-blur-md shadow-2xl overflow-hidden",
"pointer-events-auto border border-primary/15 bg-pulse-midnight/96 backdrop-blur-md shadow-pulse-card overflow-hidden",
isDock ? "rounded-t-2xl md:rounded-2xl" : "rounded-xl"
)}
>
@@ -126,7 +126,7 @@ export function LocalGuidePanel({
className="w-full flex items-center gap-2 px-3 py-2.5 sm:py-3 touch-manipulation active:bg-white/5 transition-colors"
aria-expanded={expanded}
>
<span className="shrink-0 text-[10px] font-semibold uppercase tracking-wider text-blue-300/90">
<span className="shrink-0 pulse-pill-tag text-[10px] py-0.5 px-2">
Local
</span>
<div className="flex-1 min-w-0 overflow-x-auto scrollbar-none">
@@ -139,7 +139,7 @@ export function LocalGuidePanel({
e.stopPropagation();
openSection(section.key);
}}
className="shrink-0 text-[11px] sm:text-xs px-2.5 py-1 rounded-full bg-white/5 border border-white/10 text-foreground/90 hover:bg-white/10 hover:border-blue-400/30 transition-colors"
className="shrink-0 text-[11px] sm:text-xs px-2.5 py-1 rounded-full pulse-pill-inactive"
>
{section.localLabel.replace("Local ", "")}
</span>
@@ -169,7 +169,7 @@ export function LocalGuidePanel({
onClick={() =>
handleOutboundClick("banner", partnerHomeHref(placement))
}
className="block relative w-full overflow-hidden rounded-lg border border-white/10 bg-[#1a003a]/30"
className="block relative w-full overflow-hidden rounded-xl border border-primary/20 bg-pulse-midnight/50"
>
<Image
src={PULSE_ATTRIBUTION.bannerRect}
@@ -200,12 +200,12 @@ export function LocalGuidePanel({
className={cn(
"rounded-lg border px-2.5 py-2.5 transition-colors group touch-manipulation min-h-[52px]",
active
? "border-[#f92b9b]/40 bg-[#f92b9b]/10"
: "border-white/10 hover:border-[#f92b9b]/25 hover:bg-[#f92b9b]/5"
? "border-primary/40 bg-primary/10 shadow-pulse-glow"
: "border-pulse-border/50 hover:border-primary/30 hover:bg-primary/5"
)}
>
<div className="flex items-center gap-1.5 mb-0.5">
<Icon className="h-3 w-3 text-blue-300/80 group-hover:text-[#f92b9b]" />
<Icon className="h-3 w-3 text-pulse-lavender/80 group-hover:text-primary" />
<p className="text-xs font-medium leading-tight">
{section.localLabel}
</p>
@@ -238,7 +238,7 @@ export function LocalGuidePanel({
onClick={() =>
handleOutboundClick("visit_tpp", partnerHomeHref(placement))
}
className="shrink-0 inline-flex items-center gap-1 text-[11px] font-medium text-[#f92b9b] hover:underline touch-manipulation py-1"
className="shrink-0 inline-flex items-center gap-1 text-[11px] font-medium text-primary hover:underline touch-manipulation py-1"
>
thepinkpulse.com
<ExternalLink className="h-3 w-3 opacity-70" />

View File

@@ -25,7 +25,7 @@ export function Logo({ size = "md", showText = true, className }: LogoProps) {
{showText && (
<span
className={cn(
"font-bold bg-horny-gradient bg-clip-text text-transparent",
"font-bold gradient-text",
size === "sm" && "text-base",
size === "md" && "text-xl",
size === "lg" && "text-2xl"

View File

@@ -175,7 +175,7 @@ export function CruisingMap({ vanillaMode, onActiveCountChange }: MapProps) {
};
return (
<div className="relative h-full w-full bg-[#0a1628]">
<div className="relative h-full w-full bg-pulse-deep-space">
{/* Minimal top controls — Sniffies style */}
<div className="absolute top-2 left-2 right-2 z-[1000] flex gap-2 items-start pointer-events-none">
<div className="pointer-events-auto flex gap-2 flex-wrap">
@@ -183,13 +183,13 @@ export function CruisingMap({ vanillaMode, onActiveCountChange }: MapProps) {
variant="secondary"
size="sm"
onClick={() => setShowFilters(!showFilters)}
className="shadow-lg bg-[#0a1628]/90 border-white/10 text-white h-9"
className="shadow-lg rounded-full bg-pulse-midnight/90 border-primary/20 text-white h-9"
>
<Filter className="h-4 w-4" />
</Button>
<Badge
variant="online"
className="shadow-lg self-center bg-blue-500/20 text-blue-300 border-blue-500/30"
className="shadow-lg self-center bg-primary/20 text-pulse-pink-light border-primary/30"
>
{filteredProfiles.filter((p) => p.status !== "offline").length} cruising
</Badge>
@@ -202,7 +202,7 @@ export function CruisingMap({ vanillaMode, onActiveCountChange }: MapProps) {
</div>
{showFilters && (
<div className="absolute top-12 left-2 z-[1000] w-72 rounded-xl border border-white/10 bg-[#0a1628]/98 backdrop-blur-md p-4 shadow-2xl space-y-4 max-h-[70vh] overflow-y-auto">
<div className="absolute top-12 left-2 z-[1000] w-72 rounded-xl border border-primary/15 bg-pulse-midnight/98 backdrop-blur-md p-4 shadow-pulse-card space-y-4 max-h-[70vh] overflow-y-auto">
<div className="flex items-center justify-between">
<span className="font-semibold text-sm text-white">Filters</span>
<Button variant="ghost" size="icon" onClick={() => setShowFilters(false)}>
@@ -331,7 +331,7 @@ export function CruisingMap({ vanillaMode, onActiveCountChange }: MapProps) {
{/* Recenter — above local guide dock on mobile */}
<button
type="button"
className="absolute bottom-32 right-3 z-[1000] p-3 rounded-full bg-[#0a1628]/90 border border-white/15 text-blue-400 shadow-lg touch-manipulation md:bottom-6"
className="absolute bottom-32 right-3 z-[1000] p-3 rounded-full bg-pulse-midnight/90 border border-primary/25 text-pulse-pink-light shadow-pulse-glow touch-manipulation md:bottom-6"
aria-label="Recenter map"
>
<Locate className="h-5 w-5" />

View File

@@ -23,7 +23,7 @@ export function MobileHeader({
<header className="md:hidden fixed top-0 left-0 right-0 z-50 flex h-12 items-center justify-between border-b border-border bg-horny-dark/95 backdrop-blur-md px-3 safe-top">
<Link href="/" className="flex items-center gap-1.5 min-w-0">
<Logo size="sm" showText={false} />
<span className="text-sm font-bold bg-horny-gradient bg-clip-text text-transparent truncate">
<span className="text-sm font-bold gradient-text truncate">
ExposedGays
</span>
{activeCount > 0 && (
@@ -44,7 +44,7 @@ export function MobileHeader({
{vanillaMode ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</Button>
{isGuest ? (
<Badge variant="outline" className="text-[8px] px-1.5 text-blue-400 border-blue-400/30">
<Badge variant="pill" className="text-[8px] px-1.5">
Guest
</Badge>
) : (

View File

@@ -99,7 +99,7 @@ export function Nav({
Vanilla
</Button>
{isGuest && (
<Badge variant="outline" className="text-[10px] text-blue-400 border-blue-400/30">
<Badge variant="pill" className="text-[10px]">
Guest
</Badge>
)}

View File

@@ -48,7 +48,7 @@ export function Shop() {
return (
<div className="min-h-screen bg-horny-dark">
{/* Hero */}
<div className="bg-horny-gradient px-4 py-8 md:py-12">
<div className="bg-pulse-gradient-bold px-4 py-8 md:py-12">
<div className="mx-auto max-w-6xl">
<h1 className="text-3xl md:text-4xl font-bold text-white">
ExposedGays Shop

View File

@@ -3,19 +3,25 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-bold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
top: "border-transparent bg-blue-500/20 text-blue-400",
bottom: "border-transparent bg-orange-500/20 text-orange-400",
vers: "border-transparent bg-purple-500/20 text-purple-400",
side: "border-transparent bg-green-500/20 text-green-400",
online: "border-transparent bg-green-500/20 text-green-400 animate-pulse",
default:
"border-transparent bg-pulse-gradient-bold text-white shadow-sm",
secondary:
"border-transparent bg-secondary text-secondary-foreground",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow",
outline: "border-pulse-border/60 text-foreground bg-pulse-midnight/30",
pill:
"border-primary/30 bg-primary/15 text-pulse-pink-light",
top: "border-transparent bg-primary/20 text-pulse-pink-light",
bottom: "border-transparent bg-orange-500/20 text-orange-300",
vers: "border-transparent bg-pulse-violet/25 text-pulse-lavender",
side: "border-transparent bg-pulse-teal/20 text-pulse-teal",
online:
"border-transparent bg-emerald-500/20 text-emerald-300 animate-pulse",
},
},
defaultVariants: { variant: "default" },

View File

@@ -4,23 +4,33 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-semibold transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 touch-manipulation active:scale-[0.98]",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
horny: "bg-horny-gradient text-white shadow-lg hover:opacity-90 font-semibold",
default:
"rounded-full bg-pulse-gradient-bold text-white shadow-pulse-glow hover:opacity-90 hover:-translate-y-px",
destructive:
"rounded-full bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"rounded-full border border-pulse-border/80 bg-pulse-midnight/40 text-foreground hover:border-primary/40 hover:bg-primary/10",
secondary:
"rounded-full bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost:
"rounded-full hover:bg-primary/10 hover:text-primary",
link: "text-primary underline-offset-4 hover:underline rounded-none",
horny:
"rounded-full bg-pulse-gradient-bold text-white shadow-pulse-glow hover:opacity-90 hover:-translate-y-px font-bold",
pill:
"rounded-full pulse-pill-inactive font-bold hover:border-primary/35",
"pill-active":
"rounded-full pulse-pill-active font-bold",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-12 rounded-md px-8 text-base",
icon: "h-10 w-10",
default: "h-10 px-5 py-2",
sm: "h-8 px-4 text-xs",
lg: "h-12 px-8 text-base",
icon: "h-10 w-10 rounded-full",
},
},
defaultVariants: { variant: "default", size: "default" },

View File

@@ -3,7 +3,14 @@ import { cn } from "@/lib/utils";
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("rounded-xl border bg-card text-card-foreground shadow", className)} {...props} />
<div
ref={ref}
className={cn(
"rounded-xl border border-white/[0.06] bg-card/90 text-card-foreground shadow-pulse-card backdrop-blur-sm",
className
)}
{...props}
/>
)
);
Card.displayName = "Card";

View File

@@ -6,7 +6,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"flex h-11 w-full rounded-xl border border-input bg-pulse-midnight/50 px-3 py-2 text-base text-foreground ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60 focus-visible:border-primary/40 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}

View File

@@ -13,7 +13,7 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
"inline-flex h-auto min-h-10 items-center gap-1 rounded-full bg-pulse-midnight/60 p-1 text-muted-foreground border border-pulse-border/40",
className
)}
{...props}
@@ -28,7 +28,7 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
"inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-bold ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-pulse-gradient-bold data-[state=active]:text-white data-[state=active]:shadow-pulse-glow data-[state=inactive]:text-pulse-muted data-[state=inactive]:hover:text-pulse-pink-light touch-manipulation",
className
)}
{...props}
@@ -42,7 +42,10 @@ const TabsContent = React.forwardRef<
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", className)}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
));