feat(docs): streamline product and privacy pages

This commit is contained in:
2026-07-16 19:49:17 +02:00
parent 0a185077af
commit 526a5da444
16 changed files with 869 additions and 1755 deletions

View File

@@ -37,8 +37,11 @@ export function AppIcon({ className, title }: BrandAssetProps) {
export function Brand({ compact = false }: { compact?: boolean }) {
return (
<Link className="brand" href="/" aria-label="VniDrop home">
<BrandMark className="brand-mark-image" />
{!compact && <span className="brand-name">VniDrop</span>}
<span className="brand-primary">
<BrandMark className="brand-mark-image" />
{!compact && <span className="brand-name">VniDrop</span>}
</span>
{!compact && <span className="brand-tagline">Send files directly. Stay in control.</span>}
</Link>
);
}

View File

@@ -20,10 +20,17 @@
}
.brand {
display: inline-flex;
flex-direction: column;
align-items: flex-start;
width: max-content;
}
.brand-primary {
display: inline-flex;
align-items: center;
gap: 3px;
width: max-content;
height: 47px;
}
.brand-mark-image {
@@ -42,6 +49,17 @@
letter-spacing: -0.045em;
}
.brand-tagline {
margin-top: -4px;
margin-left: 4px;
color: #756c79;
font-size: 9px;
font-weight: 580;
line-height: 1;
letter-spacing: -0.01em;
white-space: nowrap;
}
.header-github-button {
display: inline-flex;
min-height: 40px;
@@ -57,15 +75,19 @@
font-size: 12px;
font-weight: 680;
line-height: 1;
transition: background 160ms ease, border-color 160ms ease, box-shadow 160ms ease,
transform 160ms ease;
animation: none;
transform: none;
}
.header-github-button:hover {
border-color: #403944;
background: #29242d;
box-shadow: 0 10px 26px rgba(23, 20, 25, 0.17), inset 0 1px rgba(255, 255, 255, 0.12);
transform: translateY(-1px);
border-color: #6f6475;
background: #302a34;
box-shadow: 0 11px 28px rgba(23, 20, 25, 0.2), inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.header-github-button:focus-visible {
outline: 3px solid rgba(168, 85, 247, 0.28);
outline-offset: 3px;
}
.header-github-button svg {
@@ -85,10 +107,20 @@
height: 44px;
}
.brand-primary {
height: 43px;
}
.brand-name {
font-size: 17px;
}
.brand-tagline {
margin-top: -3px;
margin-left: 3px;
font-size: 8px;
}
.header-github-button {
min-height: 38px;
gap: 7px;
@@ -107,6 +139,10 @@
font-size: 16px;
}
.brand-tagline {
font-size: 7.5px;
}
.header-github-button {
min-height: 36px;
padding-inline: 10px;

View File

@@ -267,8 +267,9 @@
.platform-orbit { width: 112%; margin-left: -6%; }
.platform-center { width: 112px; height: 112px; border-radius: 30px; }
.platform-center svg { width: 68px; height: 68px; }
.platform-chip { padding: 8px 10px; font-size: 9px; }
.platform-center img { width: 68px; height: 68px; }
.platform-chip { width: 48px; height: 48px; padding: 0; }
.platform-chip .platform-glyph { width: 25px; height: 25px; }
.platform-chip-1 { left: 10%; }
.platform-chip-2 { right: 0; }
.platform-chip-3 { right: -2%; }

View File

@@ -0,0 +1,90 @@
import type { SVGProps } from "react";
export const supportedPlatforms = ["Android", "iOS", "macOS", "Windows", "Linux"] as const;
export type PlatformName = (typeof supportedPlatforms)[number];
type PlatformIconProps = Omit<SVGProps<SVGSVGElement>, "children"> & {
platform: PlatformName;
};
function AndroidIcon() {
return (
<>
<path
d="M7.25 8.15h9.5a1.5 1.5 0 0 1 1.5 1.5v6.1a1.5 1.5 0 0 1-1.5 1.5h-9.5a1.5 1.5 0 0 1-1.5-1.5v-6.1a1.5 1.5 0 0 1 1.5-1.5Z"
fill="currentColor"
/>
<path d="M8.15 8.1a3.95 3.95 0 0 1 7.7 0" fill="currentColor" />
<path d="m8.35 4.45-1.2-1.7M15.65 4.45l1.2-1.7" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" />
<path d="M4.2 10.05v5.15M19.8 10.05v5.15M8.5 17v3.05M15.5 17v3.05" stroke="currentColor" strokeWidth="2.1" strokeLinecap="round" />
<circle cx="9.5" cy="6.65" r="0.62" fill="white" />
<circle cx="14.5" cy="6.65" r="0.62" fill="white" />
</>
);
}
function AppleIcon() {
return (
<path
d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.32.07 2.24.76 3.01.82 1.15-.23 2.25-.89 3.44-.8 1.43.12 2.51.68 3.22 1.7-2.96 1.78-2.26 5.68.46 6.77-.54 1.42-1.24 2.83-2.13 4.48ZM12.03 7.25c-.15-2.13 1.59-3.89 3.58-4.06.27 2.46-2.23 4.3-3.58 4.06Z"
fill="currentColor"
/>
);
}
function MacIcon() {
return (
<>
<rect x="2.5" y="2.5" width="19" height="19" rx="5.5" fill="currentColor" />
<path d="M12 2.5v19" stroke="white" strokeOpacity="0.72" />
<path d="M8 9.15h.01M16 9.15h.01" stroke="white" strokeWidth="1.9" strokeLinecap="round" />
<path d="M7.1 15.2c2.7 1.45 7.1 1.45 9.8 0" stroke="white" strokeWidth="1.35" strokeLinecap="round" />
<path d="m13.55 6.25-2.45 8.3" stroke="white" strokeWidth="1.1" strokeLinecap="round" strokeOpacity="0.82" />
</>
);
}
function WindowsIcon() {
return (
<path
d="M3 5.15 11 4v7H3V5.15Zm9-1.3L21 2.5V11h-9V3.85ZM3 12h8v7l-8-1.15V12Zm9 0h9v8.5l-9-1.35V12Z"
fill="currentColor"
/>
);
}
function LinuxIcon() {
return (
<>
<ellipse cx="12" cy="12.6" rx="5.25" ry="7.55" fill="#242027" />
<ellipse cx="12" cy="14.55" rx="3.45" ry="4.7" fill="#f8f7f9" />
<ellipse cx="9.95" cy="8.45" rx="1.35" ry="1.8" fill="white" />
<ellipse cx="14.05" cy="8.45" rx="1.35" ry="1.8" fill="white" />
<circle cx="10.35" cy="8.65" r="0.55" fill="#242027" />
<circle cx="13.65" cy="8.65" r="0.55" fill="#242027" />
<path d="m9.8 10.15 2.2-1 2.2 1-2.2 2.1-2.2-2.1Z" fill="#f5b82e" />
<path d="M6.15 18.45c1.2-1 2.45-1.3 3.75-.75-.7 1.6-2.35 2.45-4.9 2.2.2-.6.58-1.08 1.15-1.45ZM17.85 18.45c-1.2-1-2.45-1.3-3.75-.75.7 1.6 2.35 2.45 4.9 2.2-.2-.6-.58-1.08-1.15-1.45Z" fill="#f5b82e" />
</>
);
}
export function PlatformIcon({ platform, className, ...props }: PlatformIconProps) {
return (
<svg
className={className}
data-platform={platform.toLowerCase()}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
focusable="false"
{...props}
>
{platform === "Android" && <AndroidIcon />}
{platform === "iOS" && <AppleIcon />}
{platform === "macOS" && <MacIcon />}
{platform === "Windows" && <WindowsIcon />}
{platform === "Linux" && <LinuxIcon />}
</svg>
);
}

View File

@@ -1,23 +1,27 @@
import { BrandMark } from "@/components/brand";
const platforms = ["Android", "iOS", "macOS", "Windows", "Linux"];
import { PlatformIcon, supportedPlatforms } from "@/components/platform-icon";
export function PlatformOrbit() {
return (
<div className="platform-orbit" aria-label="VniDrop supports Android, iOS, macOS, Windows, and Linux">
<div
className="platform-orbit"
role="img"
aria-label="VniDrop supports Android, iOS, macOS, Windows, and Linux"
>
<div className="platform-ring platform-ring-outer" aria-hidden="true" />
<div className="platform-ring platform-ring-inner" aria-hidden="true" />
<div className="platform-center" aria-hidden="true">
<span className="platform-center-glow" />
<BrandMark />
</div>
{platforms.map((platform, index) => (
{supportedPlatforms.map((platform, index) => (
<span
key={platform}
className={`platform-chip platform-chip-${index + 1}`}
title={platform}
>
<i />
{platform}
<PlatformIcon className="platform-glyph" platform={platform} />
<span className="sr-only">{platform}</span>
</span>
))}
</div>

View File

@@ -75,24 +75,20 @@
z-index: 4;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
justify-content: center;
width: 58px;
height: 58px;
border: 1px solid #e2d9e6;
border-radius: 999px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.93);
box-shadow: 0 12px 35px rgba(53, 34, 61, 0.1);
color: #4b4350;
font-size: 11px;
font-weight: 700;
animation: platform-chip-float 5s ease-in-out infinite;
}
.platform-chip i {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--brand-500);
box-shadow: 0 0 0 4px var(--brand-100);
.platform-chip .platform-glyph {
width: 30px;
height: 30px;
}
.platform-chip-1 { top: 8%; left: 17%; animation-delay: -1s; }
@@ -100,3 +96,8 @@
.platform-chip-3 { right: 1%; bottom: 22%; animation-delay: -4s; }
.platform-chip-4 { bottom: 7%; left: 24%; animation-delay: -0.5s; }
.platform-chip-5 { top: 45%; left: 0; animation-delay: -3.2s; }
.platform-glyph[data-platform="android"] { color: #2fbf71; }
.platform-glyph[data-platform="ios"] { color: #201c23; }
.platform-glyph[data-platform="macos"] { color: #1687f8; }
.platform-glyph[data-platform="windows"] { color: #008de5; }

View File

@@ -5,38 +5,24 @@ import { Icon } from "@/components/icons";
export function SiteFooter() {
return (
<footer className="site-footer">
<div className="footer-main page-shell">
<div className="footer-brand">
<span className="footer-mark">
<BrandMark />
</span>
<div>
<p className="footer-name">VniDrop</p>
<p>Send files directly. Stay in control.</p>
</div>
</div>
<div className="footer-links">
<div>
<p className="footer-label">Explore</p>
<Link href="/#how-it-works">How it works</Link>
<Link href="/#privacy">Privacy by design</Link>
<Link href="/#platforms">Platforms</Link>
</div>
<div>
<p className="footer-label">Project</p>
<a href="https://github.com/vnidrop/vnidrop" target="_blank" rel="noreferrer">
GitHub <Icon name="github" />
</a>
<a
href="https://github.com/vnidrop/vnidrop/blob/master/LICENSE"
target="_blank"
rel="noreferrer"
>
Apache 2.0 <Icon name="arrow" />
</a>
<Link href="/privacy/">Privacy policy</Link>
</div>
<div className="footer-inner page-shell">
<div className="footer-identity">
<BrandMark />
<span>VniDrop</span>
</div>
<nav className="footer-links" aria-label="Footer navigation">
<a href="https://github.com/vnidrop/vnidrop" target="_blank" rel="noreferrer">
<Icon name="github" /> GitHub
</a>
<Link href="/privacy/">Privacy</Link>
<a
href="https://github.com/vnidrop/vnidrop/blob/master/LICENSE"
target="_blank"
rel="noreferrer"
>
Apache 2.0
</a>
</nav>
</div>
<div className="footer-bottom page-shell">
<p>© 2026 VniDrop contributors.</p>