Skip to content
Merged
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
8 changes: 4 additions & 4 deletions apps/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ from the same Markdown frontmatter used by the site; new pages receive cards aut
metadata share the same image, title, description, and accessible image text. Canonical URLs and social image URLs
always point to the published GitHub Pages site, including when previewing locally.

- `lib/og-image.tsx` defines the GitHub-style card layout: a `section/Title` path headline, description, app icon,
a facts row with the wordmark, and the brand palette as a bottom bar.
- `lib/og-image.tsx` defines the GitHub-style card layout shared with the chart and sfx sites: an owner/repo
headline, description, app icon, a stats row counted from the SDK's clients, and the brand palette as a bottom bar.
- `lib/brand.ts` vendors the official brand geometry from the Bloxwap monorepo's `packages/tokens/src/brand.ts`.
Cards draw the wordmark from these SVG paths. Re-copy the geometry from the monorepo when the marks change.
- `lib/social.ts` defines the published URL and metadata. Update it if the repository or public hostname changes.
- `app/og/[...slug]/route.tsx` generates `/og/index.png`, `/og/docs.png`, and `/og/docs/<slug>.png` during the static build.
- `app/og/[...slug]/route.tsx` generates `/og/home.png`, `/og/docs.png`, and `/og/docs/<slug>.png` during the static build.

Cards use Nunito Black for page titles and Nunito Bold for the path prefix, description, and facts. The static Nunito
Cards use Nunito Black for titles and Nunito Bold for the owner, description, and stats. The static Nunito
faces come from the Bloxwap monorepo's `packages/og/assets/`; their license is included as
`public/fonts/Nunito-OG-OFL.txt`.
The renderer uses bundled fonts and brand geometry locally; it needs no font CDN, API credentials, or image server after
Expand Down
218 changes: 214 additions & 4 deletions apps/docs/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,201 @@ textarea:focus-visible {
color: var(--color-text-muted);
}

/* Live market data playground: its own section below the hero, like the chart and sfx sites. */
.playground-section {
margin-top: --spacing(24);
/* The hero's "Try the playground" jump lands the intro below the sticky header. */
scroll-margin-top: --spacing(20);
}
@media (prefers-reduced-motion: no-preference) {
html:has(.landing) {
scroll-behavior: smooth;
}
}
.section-intro {
display: flex;
flex-direction: column;
align-items: center;
gap: --spacing(4);
margin-bottom: --spacing(8);
text-align: center;
}
.section-intro h2 {
font-family: var(--font-docs-display), var(--font-sans);
font-size: var(--text-3xl);
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.04em;
}
.section-intro > p:not(.eyebrow) {
max-width: 440px;
font-size: var(--text-sm);
line-height: 1.7;
color: var(--color-text-muted);
}
.playground {
min-width: 0;
transform: none;
}
/* Wide: controls and code on the left, the live output filling the right column. */
.playground-body {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.playground-editor {
min-width: 0;
}
.playground-controls {
display: grid;
gap: --spacing(4);
padding: --spacing(6) --spacing(6) 0;
}
.playground-controls fieldset {
min-width: 0;
}
.playground-controls legend {
display: flex;
gap: --spacing(3);
margin-bottom: --spacing(2);
font-size: var(--text-2xs);
color: var(--color-text-muted);
}
.playground-controls legend > span {
color: var(--color-text-quiet);
}
.playground-options {
display: flex;
flex-wrap: wrap;
gap: --spacing(1);
}
.playground-options button {
min-height: 32px;
padding: --spacing(1) --spacing(3);
border-radius: calc(var(--radius) - 2px);
font-size: var(--text-xs);
color: var(--color-text-muted);
background: var(--color-surface);
transition:
color var(--duration-interactive) var(--ease-out),
background var(--duration-interactive) var(--ease-out);
}
.playground-options button:hover {
color: var(--color-text);
background: var(--color-fill-subtle);
}
.playground-options button[aria-pressed="true"] {
color: var(--color-accent-on);
background: var(--color-accent);
}
.playground-code {
position: relative;
}
.playground-code-viewport {
overflow: hidden;
}
.playground-code-line {
display: block;
min-height: 1.85em;
}
.playground .playground-code-measure {
position: absolute;
inset: 0 0 auto;
visibility: hidden;
pointer-events: none;
}
/* Size containment: the code column sets the row height and the output scrolls within it. */
.playground-output {
contain: size;
display: flex;
flex-direction: column;
min-width: 0;
margin: --spacing(4) --spacing(4) --spacing(4) 0;
padding: --spacing(4);
border-radius: var(--radius-lg);
background: var(--color-surface);
}
.playground-output-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: --spacing(2);
min-height: 20px;
font-family: var(--font-mono);
font-size: var(--text-2xs);
color: var(--color-text-muted);
}
.playground-status {
display: inline-flex;
align-items: center;
gap: --spacing(2);
}
.playground-status i {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color-text-quiet);
}
.playground-status[data-status="live"] {
color: var(--color-accent);
}
.playground-status[data-status="live"] i {
background: var(--color-accent);
animation: playground-pulse 2s ease-in-out infinite;
}
.playground-status[data-status="connecting"] i,
.playground-status[data-status="waiting"] i,
.playground-status[data-status="reconnecting"] i {
animation: playground-pulse 1s ease-in-out infinite;
}
.playground-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: --spacing(2);
min-height: 128px;
text-align: center;
}
.playground .playground-empty > svg {
color: var(--color-text-quiet);
margin-bottom: --spacing(1);
}
.playground-empty p {
font-size: var(--text-sm);
}
.playground-empty > span {
font-size: var(--text-xs);
color: var(--color-text-muted);
}
.playground .playground-json {
flex: 1;
min-height: 0;
overflow: auto;
padding: --spacing(3) 0 0;
color: var(--color-code-string);
}
.playground-retry {
display: inline-flex;
align-items: center;
gap: --spacing(1);
color: var(--color-text);
}
@keyframes playground-pulse {
50% {
opacity: 0.4;
}
}
@media (prefers-reduced-motion: reduce) {
.playground-status i {
animation: none;
}
.playground-options button {
transition: none;
}
}

/* Feature links: fill-only cards, matching the code window. */
.feature-grid {
display: grid;
Expand Down Expand Up @@ -499,13 +694,13 @@ textarea:focus-visible {
.hero-copy > .eyebrow,
.hero-copy > h1,
.hero-copy > :not(.eyebrow, h1),
.code-window {
.hero .code-window {
animation: landing-rise 600ms var(--ease-out) both;
}
.hero-copy > :not(.eyebrow, h1) {
animation-delay: 200ms;
}
.code-window {
.hero .code-window {
animation-delay: 400ms;
}

Expand Down Expand Up @@ -744,10 +939,25 @@ textarea:focus-visible {
.hero-description {
max-width: 500px;
}
.code-window {
.hero .code-window {
max-width: 580px;
transform: none;
}
.playground-section {
margin-top: --spacing(16);
}
.playground-body {
grid-template-columns: minmax(0, 1fr);
}
.playground-output {
contain: none;
margin: 0 --spacing(3) --spacing(3);
}
.playground .playground-json {
flex: none;
min-height: 128px;
max-height: 260px;
}
.feature-grid {
margin-top: --spacing(12);
gap: --spacing(4);
Expand Down Expand Up @@ -792,7 +1002,7 @@ textarea:focus-visible {
}
@media (prefers-reduced-motion: reduce) {
.hero-copy > *,
.code-window,
.hero .code-window,
.search-skeleton > span {
animation: none;
}
Expand Down
17 changes: 11 additions & 6 deletions apps/docs/app/og/[...slug]/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,24 @@ export function generateStaticParams() {
export async function GET(_request: Request, { params }: { params: Promise<{ slug: string[] }> }) {
const path = `/og/${(await params).slug.join("/")}`;
if (path === socialImagePath("/")) {
return createSocialImage({ prefix: "bloxwap", title: "hyperliquid", description: homeDescription });
return createSocialImage({
title: "bloxwap/hyperliquid",
description: homeDescription,
category: "Docs",
home: true,
});
}

const page = source.getPages().find((page) => socialImagePath(page.url) === path);
if (!page) return new Response("Not found", { status: 404 });

return createSocialImage({
prefix: page.url.startsWith("/docs/guides")
? "guides"
: page.url.startsWith("/docs/reference")
? "reference"
: "docs",
title: page.data.title,
description: page.data.description ?? homeDescription,
category: page.url.startsWith("/docs/guides")
? "Guides"
: page.url.startsWith("/docs/reference")
? "Reference"
: "Docs",
});
}
21 changes: 16 additions & 5 deletions apps/docs/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { HomeLayout } from "fumadocs-ui/layouts/home";
import { ArrowRight } from "lucide-react";
import { ArrowDown, ArrowRight } from "lucide-react";
import Link from "next/link";
import { CopyButton } from "@/components/copy-button";
import { InstallCommand } from "@/components/install-command";
import { MarketPlayground } from "@/components/market-playground";
import { SiteFooter } from "@/components/site-footer";
import { baseOptions } from "@/lib/layout.shared";
import { siteUrl } from "@/lib/social";
Expand Down Expand Up @@ -109,12 +110,12 @@ export default function Home() {
JavaScript.
</p>
<div className="hero-actions">
<Link className="button-primary" href="/docs/">
<a className="button-primary" href="#playground">
Try the playground <ArrowDown size={16} aria-hidden />
</a>
<Link className="button-secondary" href="/docs/">
Get started <ArrowRight size={16} aria-hidden />
</Link>
<Link className="button-secondary" href="/docs/guides/">
Explore the guides <ArrowRight size={16} aria-hidden />
</Link>
</div>
<InstallCommand />
<p className="runtime-note">Bun · Node.js · Browsers · React Native</p>
Expand Down Expand Up @@ -161,6 +162,16 @@ export default function Home() {
</div>
</div>
</div>
<section id="playground" className="playground-section" aria-labelledby="playground-title">
<div className="section-intro">
<p className="eyebrow">
<span /> LIVE · HYPERLIQUID MAINNET
</p>
<h2 id="playground-title">Market data playground</h2>
<p>Pick an asset and a feed to stream real market data over a WebSocket. The code updates as you go.</p>
</div>
<MarketPlayground />
</section>
<div className="feature-grid">
{features.map((feature) => (
<Link key={feature.number} href={feature.href} className="feature-card">
Expand Down
Loading
Loading