fix landing hero
Kim committed
Mar 19, 2026 at 22:17 UTC
55d066fac393e1505d57e575a55599650027ef90
3 files changed
+74
-61
frontend/src/components/Header.tsx
+5
-5
@@ -28,12 +28,12 @@ export function Header({
28
29
return (
30
<header className="flex flex-wrap items-center justify-between gap-4 px-1 py-2 sm:px-2">
31
- <div className="flex items-center gap-4 text-foreground">
31
+ <div className="flex items-center gap-5 text-foreground">
32
<div className="flex h-12 w-12 items-center justify-center">
33
<svg
34
xmlns="http://www.w3.org/2000/svg"
35
- width="26"
36
- height="26"
35
+ width="27"
36
+ height="27"
37
viewBox="0 0 906.26 1457.543"
38
>
39
<defs>
@@ -58,7 +58,7 @@ export function Header({
58
59
<div className="space-y-1">
60
<div className="flex flex-wrap items-center gap-2">
61
- <h2 className="text-xl font-extrabold tracking-tight text-foreground">
61
+ <h2 className="text-2xl font-extrabold tracking-tight text-foreground">
62
{title}
63
</h2>
64
{releaseVersion && (
@@ -72,7 +72,7 @@ export function Header({
72
73
<div className="flex flex-wrap items-center gap-3 sm:gap-4">
74
{!isAdmin && (
75
- <nav className="hidden items-center gap-5 text-sm font-medium text-text-muted md:flex">
75
+ <nav className="hidden items-center gap-6 text-[15px] font-medium text-text-muted md:flex">
76
<a href="#live-servers" className="transition-colors hover:text-foreground">
77
Live apps
78
</a>
frontend/src/components/LandingHero.tsx
+17
-6
@@ -5,8 +5,15 @@ export function LandingHero() {
5
return (
6
<section
7
aria-labelledby="landing-title"
8
- className="px-0 py-8 sm:py-10 lg:py-12"
8
+ className="relative overflow-hidden px-0 py-10 sm:py-12 lg:py-16"
9
>
10
+ <div
11
+ aria-hidden="true"
12
+ className="pointer-events-none absolute inset-0"
13
+ >
14
+ <div className="absolute inset-0 opacity-45 [background-image:radial-gradient(rgba(99,102,241,0.12)_0.8px,transparent_0.8px)] [background-size:14px_14px] [mask-image:linear-gradient(to_bottom,white,transparent_82%)]" />
15
+ </div>
16
+
17
<a
18
href="#live-servers"
19
className="sr-only focus:not-sr-only focus:absolute focus:left-6 focus:top-6 focus:z-20 focus:rounded-full focus:bg-background focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:text-foreground"
@@ -14,19 +21,23 @@ export function LandingHero() {
21
Skip to live servers
22
</a>
23
17
- <div className="mx-auto max-w-4xl text-center">
24
+ <div className="relative mx-auto max-w-4xl text-center">
25
<h1
26
id="landing-title"
20
- className="text-4xl font-extrabold tracking-tight text-foreground sm:text-5xl lg:text-6xl"
27
+ className="text-5xl font-extrabold tracking-tight text-foreground sm:text-6xl lg:text-7xl"
28
>
22
- Expose localhost instantly
29
+ <span className="block">Expose Local Apps</span>
30
+ <span className="mt-2 block bg-[linear-gradient(90deg,#7c6cff_0%,#6c8fff_46%,#57c6ff_100%)] bg-clip-text text-transparent">
31
+ To The Public Internet
32
+ </span>
33
</h1>
34
<p className="mx-auto mt-5 max-w-2xl text-lg leading-8 text-text-muted">
25
- No signup. No config. Public URL in seconds.
35
+ Portal turns localhost into a public HTTPS URL. No port forwarding,
36
+ NAT setup, or DNS configuration.
37
</p>
38
</div>
39
29
- <div className="mx-auto mt-10 max-w-[560px] rounded-[1.75rem] border border-white/10 bg-slate-950 p-5 text-white shadow-[0_30px_72px_rgba(15,23,42,0.22)] sm:p-6">
40
+ <div className="relative mx-auto mt-10 max-w-[560px] rounded-[1.75rem] border border-white/10 bg-slate-950 p-5 text-white shadow-[0_30px_72px_rgba(15,23,42,0.22)] sm:p-6">
41
<div className="mb-5 flex items-center gap-3">
42
<Terminal className="h-5 w-5 text-green-status" />
43
<h2
frontend/src/components/TunnelCommandForm.tsx
+52
-50
@@ -303,27 +303,26 @@ export function TunnelCommandForm({
303
: tunnelStatus === "registered"
304
? "URL reserved"
305
: "Waiting";
306
- const tunnelStatusLabel =
307
- tunnelStatus === "alive"
308
- ? ""
309
- : tunnelStatus === "registered"
310
- ? "Tunnel registered on this relay"
311
- : "";
306
+ const heroSectionLabelClass = cn(
307
+ "text-xs font-semibold uppercase tracking-[0.24em]",
308
+ isTerminal ? "text-slate-400" : "text-text-muted"
309
+ );
310
311
const commandSection = (
312
<div className={cn("space-y-2", isHero && "space-y-3")}>
315
- <label
316
- className={cn(
317
- "text-sm font-medium",
318
- isTerminal ? "text-slate-200" : "text-foreground"
319
- )}
320
- >
321
- {isHero ? "Copy this command" : "Generated Command"}
322
- </label>
323
- {isHero && (
324
- <p className="text-xs leading-5 text-slate-400 sm:text-sm">
325
- Copy and paste it into your terminal to publish a public URL.
326
- </p>
313
+ {isHero ? (
314
+ <label className={heroSectionLabelClass}>
315
+ Command
316
+ </label>
317
+ ) : (
318
+ <label
319
+ className={cn(
320
+ "text-sm font-medium",
321
+ isTerminal ? "text-slate-200" : "text-foreground"
322
+ )}
323
+ >
324
+ Generated Command
325
+ </label>
326
)}
327
<div className="relative">
328
<pre
@@ -374,43 +373,38 @@ export function TunnelCommandForm({
373
)}
374
375
{isHero && (
377
- <div
378
- className={cn(
379
- "space-y-2 rounded-xl border px-4 py-3",
380
- isTerminal ? "border-white/10 bg-white/5" : "border-border bg-white"
381
- )}
382
- >
383
- <div
384
- className={cn(
385
- "flex items-center gap-2 text-sm font-semibold",
386
- isTerminal ? "text-slate-200" : "text-foreground"
387
- )}
388
- >
389
- <span className={cn("h-2 w-2 rounded-full", tunnelStatusTone)} aria-hidden="true" />
390
- <span>{tunnelStatusHeadline}</span>
391
- </div>
392
- <a
393
- href={previewURL}
394
- target="_blank"
395
- rel="noopener noreferrer"
396
- className={cn(
397
- "block overflow-x-auto whitespace-nowrap font-mono text-base font-medium underline-offset-4 hover:underline sm:text-lg",
398
- isTerminal ? "text-green-status" : "text-primary"
399
- )}
400
- >
401
- {previewURL}
402
- </a>
403
- {tunnelStatusLabel !== "" && (
376
+ <>
377
+ <div className="space-y-1.5 pt-1">
378
+ <p className={heroSectionLabelClass}>Public URL</p>
379
<div
380
className={cn(
406
- "flex items-center gap-2 text-xs font-medium",
407
- isTerminal ? "text-slate-300" : "text-text-muted"
381
+ "space-y-4 rounded-xl border px-4 py-3",
382
+ isTerminal ? "border-white/10 bg-white/5" : "border-border bg-white"
383
)}
384
>
410
- <span>{tunnelStatusLabel}</span>
385
+ <div
386
+ className={cn(
387
+ "flex items-center gap-2 text-sm font-semibold",
388
+ isTerminal ? "text-slate-200" : "text-foreground"
389
+ )}
390
+ >
391
+ <span className={cn("h-2 w-2 rounded-full", tunnelStatusTone)} aria-hidden="true" />
392
+ <span>{tunnelStatusHeadline}</span>
393
+ </div>
394
+ <a
395
+ href={previewURL}
396
+ target="_blank"
397
+ rel="noopener noreferrer"
398
+ className={cn(
399
+ "block overflow-x-auto whitespace-nowrap font-mono text-base font-medium underline-offset-4 hover:underline sm:text-lg",
400
+ isTerminal ? "text-green-status" : "text-primary"
401
+ )}
402
+ >
403
+ {previewURL}
404
+ </a>
405
</div>
412
- )}
413
- </div>
406
+ </div>
407
+ </>
408
)}
409
</div>
410
);
@@ -434,10 +428,18 @@ export function TunnelCommandForm({
428
? "border-white/8 bg-black/20 text-slate-300 hover:text-white"
429
: "border-border bg-white text-text-muted hover:text-foreground"
430
);
431
+ const optionSectionLabel = isHero ? (
432
+ <div className="pt-1">
433
+ <p className={heroSectionLabelClass}>
434
+ Option
435
+ </p>
436
+ </div>
437
+ ) : null;
438
439
return (
440
<div className={cn("space-y-5", className)}>
441
{isHero && commandSection}
442
+ {optionSectionLabel}
443
444
{isHero ? (
445
<div className="grid gap-3 sm:grid-cols-2">