Skip to content

Commit 112bed9

Browse files
committed
feat(cripto): live NFT gallery, SkateHive & Gnars build pages with real PRs and 3D noggles
NFT gallery goes live: server-side Alchemy fetch of the wallet across eth/base/zora (spam + ENS-registrar filtered, hourly ISR), with graceful fallback to the placeholder grid when the key is missing or a fetch fails. Cards render real artwork and link to OpenSea. NOTE: excludeFilters[]=SPAM 500s on base-mainnet, so spam is dropped via contract.isSpam instead. Build detail pages get per-project flavors backed by verifiable data: - SkateHive: dark matrix-rain header (canvas, reduced-motion safe), mixed-font title, Built on Hive glyph, curated merged PRs with links, What I learned section, evidence slots for screenshots/gifs. - Gnars: light noggles-watermark header in a new gold accent, interactive 3D NogglesRail (CC0 model from gnars.com, lazy-loaded via next/dynamic so three.js stays off every other route), same PR/learned/evidence blocks. Copy rewritten (EN + PT) from actual merged PRs in SkateHive/skatehive3.0 and r4topunk/gnars-website instead of generic bullets.
1 parent a276010 commit 112bed9

14 files changed

Lines changed: 1644 additions & 84 deletions

File tree

.env.local.example

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Onchain NFT gallery (/cripto) — copy this file to .env.local and fill in.
2+
# .env.local is gitignored, so your key never gets committed.
3+
4+
# Free Alchemy API key: https://dashboard.alchemy.com → create an app → API Key.
5+
ALCHEMY_API_KEY=
6+
7+
# Your wallet. A raw 0x address is preferred — it lets the gallery read every
8+
# chain below. An ENS name works too, but only resolves on Ethereum mainnet.
9+
CRIPTO_WALLET=louzoshi.eth
10+
11+
# Optional. Chains to scan (only used when CRIPTO_WALLET is a 0x address).
12+
# ALCHEMY_NFT_NETWORKS=eth-mainnet,base-mainnet,zora-mainnet

app/cripto/_components/Contributions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default function Contributions() {
4242
{
4343
["--row-accent" as string]: `var(${c.accent})`,
4444
["--row-fg" as string]:
45-
c.accent === "--c-lime"
45+
c.accent === "--c-lime" || c.accent === "--c-gold"
4646
? "var(--c-on-bright)"
4747
: "var(--c-white)",
4848
} as CSSProperties

app/cripto/_components/Nfts.tsx

Lines changed: 92 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
import type { CSSProperties } from "react";
44
import Reveal from "@/components/Reveal";
55
import Window from "./Window";
6-
import { nfts } from "../_data";
6+
import type { Nft } from "../_data";
77
import { useLanguage } from "@/lib/useLanguage";
88
import { criptoDict } from "../_i18n";
99

10-
export default function Nfts() {
10+
type NftsProps = {
11+
items: Nft[];
12+
isLive: boolean;
13+
};
14+
15+
export default function Nfts({ items, isLive }: NftsProps) {
1116
const { lang } = useLanguage();
1217
const t = criptoDict[lang].nfts;
1318

@@ -19,61 +24,20 @@ export default function Nfts() {
1924
<div className="max-w-[1160px] mx-auto px-5 py-20">
2025
<Reveal className="flex flex-wrap items-end justify-between gap-4 mb-12">
2126
<div>
22-
<p className="c-label mb-3 !text-[var(--c-white)]/60">
23-
{t.label}
24-
</p>
25-
<h2 className="c-display text-[clamp(30px,5vw,58px)]">
26-
{t.title}
27-
</h2>
27+
<p className="c-label mb-3 !text-[var(--c-white)]/60">{t.label}</p>
28+
<h2 className="c-display text-[clamp(30px,5vw,58px)]">{t.title}</h2>
2829
</div>
2930
<span className="c-tag !border-[var(--c-white)]/40 !bg-transparent !text-[var(--c-white)]/70">
30-
{t.sampleTag}
31+
{isLive ? t.liveTag : t.sampleTag}
3132
</span>
3233
</Reveal>
3334

3435
<div className="grid grid-cols-2 lg:grid-cols-4 gap-5">
35-
{nfts.map((nft, i) => {
36-
const bright = nft.bg === "--c-lime";
37-
return (
38-
<Reveal key={nft.name} delay={i * 0.06}>
39-
<div className="nft-card border-2 border-[var(--c-white)] bg-[var(--c-paper)] text-[var(--c-ink)] shadow-[5px_5px_0_var(--c-white)]">
40-
<div
41-
className="relative aspect-square overflow-hidden border-b-2 border-[var(--c-ink)]"
42-
style={{ background: `var(${nft.bg})` } as CSSProperties}
43-
>
44-
<div
45-
className="halftone absolute inset-0 opacity-30"
46-
style={{ ["--dot" as string]: "var(--c-ink)" }}
47-
/>
48-
<span
49-
className={`absolute inset-0 grid place-items-center c-display text-[clamp(30px,7vw,56px)] ${
50-
bright
51-
? "text-[var(--c-on-bright)] opacity-80"
52-
: "text-[var(--c-white)] mix-blend-overlay"
53-
}`}
54-
>
55-
#{nft.tokenId}
56-
</span>
57-
<span
58-
className={`absolute top-2 left-2 c-mono text-[9px] tracking-[0.12em] ${
59-
bright ? "text-[var(--c-on-bright)]" : "text-[var(--c-white)]"
60-
}`}
61-
>
62-
{nft.collection.toUpperCase()}
63-
</span>
64-
</div>
65-
<div className="p-3 flex items-center justify-between">
66-
<span className="c-mono text-[11px] font-semibold truncate">
67-
{nft.name}
68-
</span>
69-
<span className="c-mono text-[11px] text-[var(--c-ink-soft)]">
70-
71-
</span>
72-
</div>
73-
</div>
36+
{items.map((nft, i) => (
37+
<Reveal key={`${nft.chain ?? "x"}-${nft.collection}-${nft.tokenId}-${i}`} delay={i * 0.06}>
38+
<NftCard nft={nft} />
7439
</Reveal>
75-
);
76-
})}
40+
))}
7741
</div>
7842

7943
<Reveal delay={0.1} className="mt-10">
@@ -83,13 +47,89 @@ export default function Nfts() {
8347
bodyClassName="p-5 bg-[var(--c-paper)] text-[var(--c-ink)]"
8448
>
8549
<p className="c-mono text-[12px] leading-[1.7]">
86-
{t.notePre}
50+
{isLive ? t.liveNotePre : t.notePre}
8751
<b>{t.noteBold}</b>
88-
{t.notePost}
52+
{isLive ? t.liveNotePost : t.notePost}
8953
</p>
9054
</Window>
9155
</Reveal>
9256
</div>
9357
</section>
9458
);
9559
}
60+
61+
function NftCard({ nft }: { nft: Nft }) {
62+
const bright = nft.bg === "--c-lime";
63+
64+
const card = (
65+
<div className="nft-card h-full border-2 border-[var(--c-white)] bg-[var(--c-paper)] text-[var(--c-ink)] shadow-[5px_5px_0_var(--c-white)]">
66+
<div
67+
className="relative aspect-square overflow-hidden border-b-2 border-[var(--c-ink)]"
68+
style={
69+
nft.image
70+
? undefined
71+
: ({ background: `var(${nft.bg})` } as CSSProperties)
72+
}
73+
>
74+
{nft.image ? (
75+
// NFT art comes from arbitrary CDNs / IPFS gateways, so next/image
76+
// (which needs an allowlist of hosts) isn't practical here.
77+
// eslint-disable-next-line @next/next/no-img-element
78+
<img
79+
src={nft.image}
80+
alt={nft.name}
81+
loading="lazy"
82+
className="absolute inset-0 h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.04]"
83+
/>
84+
) : (
85+
<>
86+
<div
87+
className="halftone absolute inset-0 opacity-30"
88+
style={{ ["--dot" as string]: "var(--c-ink)" }}
89+
/>
90+
<span
91+
className={`absolute inset-0 grid place-items-center c-display text-[clamp(30px,7vw,56px)] ${
92+
bright
93+
? "text-[var(--c-on-bright)] opacity-80"
94+
: "text-[var(--c-white)] mix-blend-overlay"
95+
}`}
96+
>
97+
#{nft.tokenId}
98+
</span>
99+
</>
100+
)}
101+
<span
102+
className={
103+
nft.image
104+
? "absolute top-2 left-2 c-mono text-[9px] tracking-[0.12em] px-1.5 py-0.5 bg-[var(--c-ink)] text-[var(--c-white)]"
105+
: `absolute top-2 left-2 c-mono text-[9px] tracking-[0.12em] ${
106+
bright ? "text-[var(--c-on-bright)]" : "text-[var(--c-white)]"
107+
}`
108+
}
109+
>
110+
{nft.collection.toUpperCase()}
111+
</span>
112+
</div>
113+
<div className="p-3 flex items-center justify-between gap-2">
114+
<span className="c-mono text-[11px] font-semibold truncate">
115+
{nft.name}
116+
</span>
117+
<span className="c-mono text-[11px] text-[var(--c-ink-soft)]"></span>
118+
</div>
119+
</div>
120+
);
121+
122+
if (!nft.href) return card;
123+
124+
return (
125+
<a
126+
href={nft.href}
127+
target="_blank"
128+
rel="noopener noreferrer"
129+
className="group block h-full"
130+
aria-label={`${nft.name} — view on OpenSea`}
131+
>
132+
{card}
133+
</a>
134+
);
135+
}

0 commit comments

Comments
 (0)