Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions website/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/node_modules
/dist
*.tsbuildinfo
.env*
.DS_Store
*.log
34 changes: 34 additions & 0 deletions website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ampup

Landing page for [Amp](https://github.com/edgeandnode/amp), aimed at data engineers and data scientists.

Vite 8, React 19, React Router 7, Tailwind 4 (via `@tailwindcss/vite`), Motion, pnpm. A static single-page app: `index.html` carries the metadata and the Google Fonts link, `app/main.tsx` mounts the router. Code blocks are the EDS `CodeBlock` from `@edgeandnode/eds-react` (Shiki highlighting, copy button, dark and light themes).

## EDS

`app/globals.css` imports `@edgeandnode/eds-react` in place of `tailwindcss`, registers the source directories with `@source` (EDS turns off automatic scanning), and then layers the house tokens on top. `components/Providers.tsx` mounts the one `EDSProvider` with `theme="light"`. The `.eds` class in `globals.css` points EDS's `font-sans` and `font-mono` at Messina Sans and Geist Mono and maps `font-medium` to 600, the only heavier weight Messina has. `components/CodeBlock.tsx` is the one wrapper every page uses.

```sh
pnpm install
pnpm dev # http://localhost:3000
pnpm build # typecheck, then vite build into dist/
pnpm preview # serve dist/ with the SPA fallback
```

## Routes

| Route | What | Code |
|---|---|---|
| `/` | The site. EDS tokens, Messina, light only. All new work happens here. | `components/Page.tsx` composes one file per section in `components/sections/`. Each section keeps its own copy as consts at the bottom of the file. |

## Copy rules

Copy follows `../amp-comms/DESIGN.md` §12 (no dashes, no flagged words, no "X, not Y", sentence case, one italic word per page at most). Claims come from `../amp-comms/content/FACTS.md` and the `../amp` repo docs. The build output can be linted with the same dash and word check `amp-comms/tools/build.py` runs.

Fonts: Messina Sans (licensed, in `public/fonts`, declared with `@font-face` in `globals.css`), Newsreader and Geist Mono (Google Fonts, linked from `index.html`).

Hosting: the site is a client-routed SPA, so the host must serve `index.html` for unknown paths (the usual SPA fallback). The security headers the Next config used to set now belong in the host's config.

## Live data

The preview and "Try the flow" sections on `/` render from `lib/amp-api.ts`. `createMockClient()` fabricates chain heads, a streaming feed, a 30-day series, and a dataset catalog behind the `AmpClient` interface. To go live, implement `AmpClient` against the real API and change the one `createMockClient()` call at the top of `components/Live.tsx`, and the `ampClient` singleton in `lib/amp-client.ts` that the figures read.
125 changes: 125 additions & 0 deletions website/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
@import "@edgeandnode/eds-react";
@source "../app";
@source "../components";
@source "../data";


@theme {
--color-brand-500: #2347e5;

--font-serif: "Newsreader", ui-serif, Georgia, "Times New Roman", serif;
--font-sans: "Messina Sans", ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-geist-mono, "Geist Mono"), ui-monospace, "SF Mono", Menlo, monospace;
}

@utility dotgrid {
@apply bg-[radial-gradient(color-mix(in_oklab,var(--border-color-default)_30%,transparent)_1.2px,transparent_1.8px)] bg-size-[12px_12px] bg-position-[-6px_-6px] bg-repeat
}

@utility container {
@apply mx-auto max-w-5xl px-4 sm:px-6 lg:px-8
}

@theme static {
--color-on-dark: rgba(255, 255, 255, 0.95);
--color-on-dark-muted: rgba(255, 255, 255, 0.8);
--color-on-dark-faint: rgba(255, 255, 255, 0.65);
--color-success: #087443;

--spacing-section: clamp(4.5rem, 3.5rem + 4vw, 8rem);
--spacing-hero-top: clamp(8rem, 6.8rem + 4vw, 10rem);

--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
}

html {
scroll-behavior: smooth;
}

[data-section] {
scroll-margin-top: 5.5rem;
}

body {
@apply text-pretty;
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-serif);
}
}

@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}

@keyframes hero-enter {
from {
opacity: 0;
transform: translateY(var(--hero-y, 16px));
}
to {
opacity: 1;
transform: translateY(0);
}
}
.hero-enter {
animation: hero-enter 0.7s var(--ease-smooth) both;
animation-delay: var(--hero-delay, 0s);
}
@keyframes hero-rise {
from {
transform: translateY(var(--hero-y, 16px));
}
to {
transform: translateY(0);
}
}
.hero-enter-rise {
animation-name: hero-rise;
}
@media (prefers-reduced-motion: reduce) {
.hero-enter {
animation-delay: 0s !important;
}
}

@font-face {
font-family: "Messina Sans";
src: url("/fonts/MessinaSans-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Messina Sans";
src: url("/fonts/MessinaSans-SemiBold.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}


::selection {
@apply bg-brand-500 text-inverse-default
}

.elevated { box-shadow: 0 2px 4px rgba(15, 18, 22, 0.05), 0 12px 24px -8px rgba(15, 18, 22, 0.12); }

.crosshair { width: 9px; height: 9px; margin: -4px; }
.crosshair::before, .crosshair::after { content: ""; position: absolute; background: var(--border-color-default); }
.crosshair::before { left: 0; right: 0; top: 4px; height: 1px; }
.crosshair::after { top: 0; bottom: 0; left: 4px; width: 1px; }


/* Display step between EDS's 64 and 96. */
@media (min-width: 64rem) {
.lg\:text-80 { font-size: 5rem; line-height: 1.02; }
}
26 changes: 26 additions & 0 deletions website/app/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router";
import "./globals.css";
import Providers from "@/components/Providers";
import Page from "@/components/Page";

function App() {
return (
<Providers>
<main id="main">
<Routes>
<Route path="/" element={<Page />} />
</Routes>
</main>
</Providers>
);
}

createRoot(document.getElementById("root")!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
);
33 changes: 33 additions & 0 deletions website/components/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { CodeBlock as EdsCodeBlock } from "@edgeandnode/eds-react";
import { cn } from "@/lib/cn";

export type Lang = "sh" | "sql" | "toml" | "json" | "py";

/** The EDS CodeBlock with this site's line-array interface. Square corners per the
* house system; Shiki highlighting and the copy button come from EDS. */
export default function CodeBlock({
lines,
lang,
label,
copy,
className,
}: {
lines: string[];
lang: Lang;
/** Header label; with one set, the copy button is always visible. */
label?: string;
/** Text to copy instead of the rendered code (for prompts and comments). */
copy?: string;
className?: string;
}) {
return (
<EdsCodeBlock
language={lang}
label={label}
copy={copy ?? true}
className={cn("rounded-none", className)}
>
{lines.join("\n")}
</EdsCodeBlock>
);
}
28 changes: 28 additions & 0 deletions website/components/Dots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
interface Props {
corner?: "tl" | "tr" | "bl" | "br";
className?: string;
}

export function Dots({ corner = "tr", className }: Props) {
const at = { tl: "0% 0%", tr: "100% 0%", bl: "0% 100%", br: "100% 100%" }[
corner
];
const fade = `radial-gradient(120% 120% at ${at}, black 0%, transparent 62%)`;
return (
<div
aria-hidden
className={`pointer-events-none absolute inset-0 text-brand-500/10 ${className}`}
style={{
backgroundImage:
"radial-gradient(currentColor 1.2px, transparent 1.8px)",
backgroundSize: "10px 10px",
backgroundRepeat: "repeat",
backgroundPosition: "0 0",
WebkitMaskImage: fade,
maskImage: fade,
WebkitMaskSize: "100% 100%",
maskSize: "100% 100%",
}}
/>
);
}
Loading
Loading