Compare commits

..
10 Commits
Author SHA1 Message Date
selimaj-dev 399f7f6f57 Merge pull request #6 from nexa-sol/market-ui-and-landing-page-ux
Market UI and landing page ux
2026-02-18 10:35:20 -08:00
selimaj-dev f3a736087f Market/mint 2026-02-18 19:34:43 +01:00
selimaj-dev fef385f246 Improved card background color 2026-02-18 03:01:54 +01:00
selimaj-dev e8cb05b286 Replaced CTA again 2026-02-18 03:00:30 +01:00
selimaj-dev 5b072cd2ab Improved landing page UI/UX by centering hero 2026-02-18 02:51:35 +01:00
selimaj-dev 52f9e47cec Improved landing page UX by placing primary button on the right 2026-02-18 02:46:56 +01:00
selimaj-dev f59a6cab8f Improved landing page button UI 2026-02-18 02:42:14 +01:00
selimaj-dev c18c63377e Simple market chart 2026-02-18 02:13:49 +01:00
selimaj-dev e801be30ac Token info 2026-02-18 01:47:46 +01:00
selimaj-dev df90677975 Collapsible 2026-02-18 01:14:59 +01:00
12 changed files with 344 additions and 56 deletions
+28
View File
@@ -10,6 +10,8 @@
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lightweight-charts": "^5.1.0",
"lightweight-charts-react-components": "^1.4.0",
"lucide-react": "^0.563.0",
"next": "16.1.6",
"radix-ui": "^1.4.3",
@@ -4600,6 +4602,12 @@
"express": ">= 4.11"
}
},
"node_modules/fancy-canvas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-2.1.0.tgz",
"integrity": "sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -5716,6 +5724,26 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightweight-charts": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-5.1.0.tgz",
"integrity": "sha512-jEAYR4ODYeyNZcWUigsoLTl52rbPmgXnvd5FLIv/ZoA/2sSDw63YKnef8n4yhzum7W926yHeFwlm7ididKb7YQ==",
"license": "Apache-2.0",
"dependencies": {
"fancy-canvas": "2.1.0"
}
},
"node_modules/lightweight-charts-react-components": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/lightweight-charts-react-components/-/lightweight-charts-react-components-1.4.0.tgz",
"integrity": "sha512-FWzEiQDhQaQ04l6kV2fzsODK6jCrzbLOai+r6oEvCqC3tniXjKUAFHbTUlemII7PfiWYwyVAVgl1XRv72Q8Phw==",
"license": "MIT",
"peerDependencies": {
"lightweight-charts": ">=5 < 6",
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+2
View File
@@ -10,6 +10,8 @@
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lightweight-charts": "^5.1.0",
"lightweight-charts-react-components": "^1.4.0",
"lucide-react": "^0.563.0",
"next": "16.1.6",
"radix-ui": "^1.4.3",
+1 -1
View File
@@ -60,7 +60,7 @@
--background: oklch(0.13 0.01 260); /* deep charcoal */
--foreground: oklch(0.98 0 0);
--card: oklch(0.18 0.015 260);
--card: oklch(15.349% 0.00726 258.438);
--card-foreground: oklch(0.98 0 0);
--popover: oklch(0.18 0.015 260);
@@ -0,0 +1,55 @@
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ChevronDown, ChevronUp } from "lucide-react";
import { useState } from "react";
export default function BuySell() {
const [isCollapsed, setIsCollapsed] = useState(false);
return (
<Tabs defaultValue="buy" className="px-3 border-b">
<div className="flex items-center justify-between border-b bg-transparent">
<TabsList className="h-14! p-0 px-2 bg-transparent border-none flex-1">
<TabsTrigger
value="buy"
className="h-9 bg-transparent data-[state=active]:bg-bullish data-[state=active]:text-accent-foreground"
>
Buy
</TabsTrigger>
<TabsTrigger
value="sell"
className="ml-0.5 h-9 bg-transparent data-[state=active]:bg-bearish data-[state=active]:text-accent-foreground"
>
Sell
</TabsTrigger>
</TabsList>
{/* The Toggle Button */}
<Button
variant="outline"
size="xs"
className="h-6 w-6 p-0 rounded-full"
onClick={() => setIsCollapsed(!isCollapsed)}
>
{isCollapsed ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
</Button>
</div>
{/* Conditional Rendering or Height Hiding */}
{!isCollapsed && (
<>
<TabsContent value="buy">
<div className="h-72">buy placeholder</div>
</TabsContent>
<TabsContent value="sell">
<div className="h-72">sell placeholder</div>
</TabsContent>
</>
)}
</Tabs>
);
}
@@ -0,0 +1,96 @@
import { CandlestickSeries, Chart } from "lightweight-charts-react-components";
export default function MarketChart() {
return (
<Chart
containerProps={{
style: { width: "100%", height: "100%" },
}}
options={{
layout: {
background: { color: "#131722" }, // Dark background
textColor: "#d1d4dc", // Light text
},
grid: {
vertLines: { color: "#2B2B43" }, // Subtle dark grid lines
horzLines: { color: "#2B2B43" },
},
}}
>
<CandlestickSeries
data={[
{
time: "2018-12-22",
open: 32.5,
high: 33.0,
low: 31.8,
close: 32.51,
},
{
time: "2018-12-23",
open: 32.51,
high: 32.8,
low: 30.5,
close: 31.11,
},
{
time: "2018-12-24",
open: 31.11,
high: 31.5,
low: 26.9,
close: 27.02,
},
{
time: "2018-12-25",
open: 27.02,
high: 27.8,
low: 26.5,
close: 27.32,
},
{
time: "2018-12-26",
open: 27.32,
high: 26.0,
low: 24.8,
close: 25.17,
},
{
time: "2018-12-27",
open: 25.17,
high: 29.0,
low: 25.0,
close: 28.89,
},
{
time: "2018-12-28",
open: 28.89,
high: 26.0,
low: 25.4,
close: 25.46,
},
{
time: "2018-12-29",
open: 25.46,
high: 24.5,
low: 23.5,
close: 23.92,
},
{
time: "2018-12-30",
open: 23.92,
high: 23.0,
low: 22.5,
close: 22.68,
},
{
time: "2018-12-31",
open: 22.68,
high: 23.0,
low: 22.5,
close: 22.67,
},
]}
/>
</Chart>
);
}
@@ -0,0 +1,12 @@
import BuySell from "./BuySell";
import TokenInfo from "./TokenInfo";
export default function MarketSidebar() {
return (
<div className="h-full w-sm flex flex-col gap-4">
<div className="flex h-16 border-b px-3">placeholder</div>
<BuySell />
<TokenInfo />
</div>
);
}
@@ -0,0 +1,126 @@
import { cn } from "@/lib/utils";
import { Token, TokenLiveData } from "@/modules/tokens/types";
import {
BoxesIcon,
ChartCandlestick,
ChefHatIcon,
CrosshairIcon,
FlameIcon,
GhostIcon,
LucideIcon,
ShieldIcon,
UsersIcon,
UserStarIcon,
} from "lucide-react";
export default function TokenInfo() {
const info: Token = {
mint: "",
symbol: "",
name: "",
};
const liveData: TokenLiveData = {
topHolder: 67,
devHold: 20,
snipersHold: 30,
insidersHold: 10,
bundleHold: 3,
};
return (
<div className="px-3">
<div></div>
<div className="flex flex-col gap-4">
<div className="grid grid-cols-3 gap-3 border-b pb-4">
<DataCard
icon={UserStarIcon}
label="Top 10 H"
value={`${liveData.topHolder}%`}
className="text-bearish"
/>
<DataCard
icon={ChefHatIcon}
label="Top 10 H"
value={`${liveData.topHolder}%`}
className="text-bullish"
/>
<DataCard
icon={CrosshairIcon}
label="Snipers H"
value={`${liveData.topHolder}%`}
className="text-bearish"
/>
<DataCard
icon={GhostIcon}
label="Insiders"
value={`${liveData.insidersHold}%`}
className="text-bullish"
/>
<DataCard
icon={BoxesIcon}
label="Bundlers"
value={`${liveData.bundleHold}%`}
className="text-bullish"
/>
<DataCard
icon={FlameIcon}
label="LP Burned"
value={`${100}%`}
className="text-bullish"
/>
</div>
<div className="grid grid-cols-3 gap-3">
<DataCard
icon={UsersIcon}
label="Holders"
value={`56`}
className="text-bearish"
/>
<DataCard
icon={ChartCandlestick}
label="pro traders"
value={`19`}
className="text-bullish"
/>
<DataCard
icon={ShieldIcon}
label="Dex paid"
value={`Paid`}
className="text-bullish"
/>
</div>
<div>CA and DA placeholder</div>
</div>
</div>
);
}
function DataCard({
label,
value,
className,
icon: Icon,
}: {
icon: LucideIcon;
label: string;
value: string;
className: string;
}) {
return (
<div className="rounded-md border flex flex-col gap-1.5 justify-center pt-1 pb-2">
<div
className={cn(
"flex items-center justify-center gap-1 text-xs",
className,
)}
>
<Icon className="h-3 w-3" />
{value}
</div>
<div className="text-center text-xs text-muted-foreground">{label}</div>
</div>
);
}
@@ -2,7 +2,8 @@
import Navbar from "@/components/layout/Navbar";
import Statusbar from "@/components/layout/Statusbar";
import MarketSidebar from "./components/MarketSidebar";
import MarketSidebar from "./components/market-sidebar/MarketSidebar";
import Chart from "./components/market-sidebar/Chart";
export default function Market() {
return (
@@ -10,7 +11,7 @@ export default function Market() {
<Navbar />
<section className="w-full h-full flex flex-row">
<div className="w-full h-svh border-r border-border">
<Chart />
</div>
<MarketSidebar />
</section>
@@ -1,32 +0,0 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
export default function MarketSidebar() {
return (
<div className="h-full w-sm flex flex-col gap-4">
<div className="flex h-16 border-b px-3">placeholder</div>
<Tabs defaultValue="buy" className="px-3 border-b">
<TabsList className="w-full h-14! p-0 px-2 bg-transparent border">
<TabsTrigger
value="buy"
className="h-9 bg-transparent data-[state=active]:bg-bullish data-[state=active]:text-accent-foreground"
>
Buy
</TabsTrigger>
<TabsTrigger
value="sell"
className="h-9 bg-transparent data-[state=active]:bg-bearish data-[state=active]:text-accent-foreground"
>
Sell
</TabsTrigger>
</TabsList>
<TabsContent value="buy">
<div className="h-72">buy placeholder</div>
</TabsContent>
<TabsContent value="sell" className="border-b">
<div className="h-72">sell placeholder</div>
</TabsContent>
</Tabs>
</div>
);
}
+3 -3
View File
@@ -40,13 +40,13 @@ export default function TokenCard({ token }: { token: Token }) {
"
onClick={(e) => {
if (e.ctrlKey || e.metaKey) {
window.open(`/token/${token.mint}`, "_blank", "noopener,noreferrer");
window.open(`/market/${token.mint}`, "_blank", "noopener,noreferrer");
} else {
router.push(`/token/${token.mint}`);
router.push(`/market/${token.mint}`);
}
}}
onContextMenu={() =>
window.open(`/token/${token.mint}`, "_blank", "noopener,noreferrer")
window.open(`/market/${token.mint}`, "_blank", "noopener,noreferrer")
}
>
{/* Left: logo */}
+17 -17
View File
@@ -22,38 +22,38 @@ export default function Landing() {
</div>
{/* Hero */}
<section className="relative z-10 px-6 pt-32 pb-24 max-w-7xl mx-auto">
<div className="max-w-3xl">
<h1 className="text-5xl md:text-6xl font-semibold leading-tight animate-fade-in-up">
<section className="pt-32 pb-24 w-full">
<div className="w-full">
<h1 className="text-center text-5xl md:text-6xl font-semibold leading-tight animate-fade-in-up">
Your copilot for <br />
<span className="text-transparent bg-clip-text bg-linear-to-r from-primary to-accent">
memecoin trading
</span>
</h1>
<p className="mt-6 text-lg text-muted-foreground max-w-xl animate-fade-in-up animate-delay-100">
Catch rugs before they happen, execute trades at Solana
speed, and automate the research pro traders do.
<p className="mx-auto text-center mt-6 text-lg text-muted-foreground max-w-xl animate-fade-in-up animate-delay-100">
Catch rugs before they happen, execute trades at Solana speed, and
automate the research pro traders do.
</p>
<div className="mt-10 flex gap-4">
<div className="mt-10 flex gap-4 mx-auto w-max">
<Link
href="/markets"
className="animate-fade-in-up animate-duration-500 animate-delay-200"
>
<button className="px-6 py-3 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition">
Open Terminal
</button>
</Link>
<Link
className="animate-fade-in-up animate-duration-500 animate-delay-300"
href="https://github.com/nexa-sol/nexa-trade?tab=readme-ov-file#-nexa-roadmap--alpha-release"
target="_blank"
>
<button className="px-6 py-3 rounded-lg border border-border hover:bg-accent/10 transition">
<button className="px-6 py-3 rounded-lg border border-border hover:bg-accent/10 hover:text-accent transition">
View Roadmap
</button>
</Link>
<Link
href="/markets"
className="animate-fade-in-up animate-duration-500 animate-delay-300"
>
<button className="px-6 py-3 rounded-lg font-medium transition border border-border outline-primary text-primary bg-primary/8 hover:border-primary hover:bg-primary/12">
Open Terminal
</button>
</Link>
</div>
</div>
</section>
@@ -61,7 +61,7 @@ export default function Landing() {
{/* Features */}
<section className="relative z-10 px-6 py-24 max-w-7xl mx-auto">
<div className="grid md:grid-cols-3 gap-8">
<Staggered stagger={100}>
<Staggered stagger={150}>
{[
{
icon: Shield,
+1 -1
View File
@@ -3,7 +3,7 @@ export interface Token {
symbol: string;
name: string;
logoURI?: string;
twitterPost: {
twitterPost?: {
link: string;
author: {
username: string;